@cleartrip/ct-design-segment 5.7.0-SNAPSHOT-native-main.5 → 5.7.0
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/Segment.d.ts.map +1 -1
- package/dist/Segment.native.d.ts.map +1 -1
- package/dist/ct-design-segment.browser.cjs.js +1 -1
- package/dist/ct-design-segment.browser.cjs.js.map +1 -1
- package/dist/ct-design-segment.browser.esm.js +1 -1
- package/dist/ct-design-segment.browser.esm.js.map +1 -1
- package/dist/ct-design-segment.cjs.js +27 -61
- package/dist/ct-design-segment.cjs.js.map +1 -1
- package/dist/ct-design-segment.esm.js +27 -61
- package/dist/ct-design-segment.esm.js.map +1 -1
- package/dist/ct-design-segment.umd.js +30 -65
- package/dist/ct-design-segment.umd.js.map +1 -1
- package/dist/style.d.ts +4 -26
- package/dist/style.d.ts.map +1 -1
- package/package.json +10 -10
- package/src/Segment.native.tsx +1 -7
- package/src/Segment.tsx +21 -23
- package/src/SegmentButton.native.tsx +2 -2
- package/src/SegmentButton.tsx +4 -4
- package/src/style.ts +7 -52
package/src/Segment.tsx
CHANGED
|
@@ -2,8 +2,9 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { useTheme } from '@cleartrip/ct-design-theme';
|
|
4
4
|
import { useStyles, useWebMergeStyles } from '@cleartrip/ct-design-style-manager';
|
|
5
|
+
import { Container } from '@cleartrip/ct-design-container';
|
|
5
6
|
|
|
6
|
-
import { getSegmentVariantStyles, segmentStaticStyles,
|
|
7
|
+
import { getSegmentVariantStyles, segmentStaticStyles, segmentWebThumbStyles } from './style';
|
|
7
8
|
import { ISegmentProps } from './type';
|
|
8
9
|
import { SegmentVariant } from './constants';
|
|
9
10
|
import { SegmentButton } from './SegmentButton';
|
|
@@ -43,7 +44,6 @@ const Segment: React.FC<ISegmentProps> = ({
|
|
|
43
44
|
const variantStyles = useMemo(() => getSegmentVariantStyles(variant, theme), [variant, theme]);
|
|
44
45
|
|
|
45
46
|
const rootRef = useRef<HTMLDivElement | null>(null);
|
|
46
|
-
const trackRef = useRef<HTMLDivElement | null>(null);
|
|
47
47
|
const tabRefs = useRef<Array<HTMLDivElement | null>>([]);
|
|
48
48
|
|
|
49
49
|
const sanitizedOptions = useMemo(
|
|
@@ -67,15 +67,15 @@ const Segment: React.FC<ISegmentProps> = ({
|
|
|
67
67
|
const baseSegmentTextRoot = useMemo(() => toStyleArray(baseSegmentTextConfig.root), [baseSegmentTextConfig.root]);
|
|
68
68
|
|
|
69
69
|
const measureTabs = useCallback(() => {
|
|
70
|
-
const
|
|
71
|
-
if (!
|
|
72
|
-
const
|
|
73
|
-
setTrackWidth(
|
|
70
|
+
const rootNode = rootRef.current;
|
|
71
|
+
if (!rootNode) return;
|
|
72
|
+
const rootRect = rootNode.getBoundingClientRect();
|
|
73
|
+
setTrackWidth(rootRect.width);
|
|
74
74
|
const layouts = sanitizedOptions.map((_, index) => {
|
|
75
75
|
const tabNode = tabRefs.current[index];
|
|
76
76
|
if (!tabNode) return { x: 0, width: 0 };
|
|
77
77
|
const rect = tabNode.getBoundingClientRect();
|
|
78
|
-
return { x: rect.left -
|
|
78
|
+
return { x: rect.left - rootRect.left, width: rect.width };
|
|
79
79
|
});
|
|
80
80
|
setTabLayouts(layouts);
|
|
81
81
|
}, [sanitizedOptions]);
|
|
@@ -89,11 +89,11 @@ const Segment: React.FC<ISegmentProps> = ({
|
|
|
89
89
|
}, [measureTabs]);
|
|
90
90
|
|
|
91
91
|
useEffect(() => {
|
|
92
|
-
if (typeof ResizeObserver === 'undefined' || !
|
|
92
|
+
if (typeof ResizeObserver === 'undefined' || !rootRef.current) return;
|
|
93
93
|
const observer = new ResizeObserver(() => {
|
|
94
94
|
measureTabs();
|
|
95
95
|
});
|
|
96
|
-
observer.observe(
|
|
96
|
+
observer.observe(rootRef.current);
|
|
97
97
|
tabRefs.current.forEach((tabNode) => {
|
|
98
98
|
if (tabNode) observer.observe(tabNode);
|
|
99
99
|
});
|
|
@@ -196,13 +196,9 @@ const Segment: React.FC<ISegmentProps> = ({
|
|
|
196
196
|
],
|
|
197
197
|
[segmentStaticStyles.root, dynamicStyles.root, variantStyles.root?.style, root],
|
|
198
198
|
);
|
|
199
|
-
const animatedContainerClassName = useWebMergeStyles(
|
|
200
|
-
[segmentStaticStyles.animatedContainer, segmentWebLayoutStyles.animatedContainer, ...animatedContainer],
|
|
201
|
-
[segmentStaticStyles.animatedContainer, segmentWebLayoutStyles.animatedContainer, animatedContainer],
|
|
202
|
-
);
|
|
203
199
|
const tabMeasureCellClassName = useWebMergeStyles(
|
|
204
|
-
[segmentStaticStyles.tabMeasureCell
|
|
205
|
-
[segmentStaticStyles.tabMeasureCell
|
|
200
|
+
[segmentStaticStyles.tabMeasureCell],
|
|
201
|
+
[segmentStaticStyles.tabMeasureCell],
|
|
206
202
|
);
|
|
207
203
|
const webThumbBaseClassName = useWebMergeStyles(
|
|
208
204
|
[
|
|
@@ -223,11 +219,9 @@ const Segment: React.FC<ISegmentProps> = ({
|
|
|
223
219
|
if (hasStableMeasurements && optionLength > 0) {
|
|
224
220
|
const safeIndex = Math.max(0, Math.min(activeIndex, optionLength - 1));
|
|
225
221
|
const activeLayout = tabLayouts[safeIndex];
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
const left = Math.min(rawLeft, maxLeft);
|
|
230
|
-
const width = Math.min(rawWidth, Math.max(trackWidth - left, 0));
|
|
222
|
+
const width = Math.max(activeLayout?.width ?? trackWidth / Math.max(optionLength, 1), 0);
|
|
223
|
+
const maxLeft = Math.max(trackWidth - width, 0);
|
|
224
|
+
const left = Math.min(Math.max(activeLayout?.x ?? 0, 0), maxLeft);
|
|
231
225
|
return css({
|
|
232
226
|
left: `${left}px`,
|
|
233
227
|
width: `${width}px`,
|
|
@@ -248,8 +242,11 @@ const Segment: React.FC<ISegmentProps> = ({
|
|
|
248
242
|
|
|
249
243
|
return (
|
|
250
244
|
<div className={rootClassName} ref={rootRef}>
|
|
251
|
-
<
|
|
252
|
-
|
|
245
|
+
<Container
|
|
246
|
+
styleConfig={{
|
|
247
|
+
root: [segmentStaticStyles.animatedContainer, ...animatedContainer],
|
|
248
|
+
}}
|
|
249
|
+
>
|
|
253
250
|
{!isEmptyOptions(sanitizedOptions) &&
|
|
254
251
|
sanitizedOptions.map((option, index) => (
|
|
255
252
|
<div
|
|
@@ -271,7 +268,8 @@ const Segment: React.FC<ISegmentProps> = ({
|
|
|
271
268
|
/>
|
|
272
269
|
</div>
|
|
273
270
|
))}
|
|
274
|
-
</
|
|
271
|
+
</Container>
|
|
272
|
+
<div className={`${webThumbBaseClassName} ${dynamicWebStyles}`} />
|
|
275
273
|
</div>
|
|
276
274
|
);
|
|
277
275
|
};
|
|
@@ -33,8 +33,8 @@ export const SegmentButton: React.FC<ISegmentButtonProps> = ({
|
|
|
33
33
|
minHeight: 0,
|
|
34
34
|
width: undefined,
|
|
35
35
|
alignSelf: 'stretch',
|
|
36
|
-
height:
|
|
37
|
-
maxHeight:
|
|
36
|
+
height: t.size[10],
|
|
37
|
+
maxHeight: t.size[10],
|
|
38
38
|
paddingHorizontal: 0,
|
|
39
39
|
paddingTop: 0,
|
|
40
40
|
paddingBottom: 0,
|
package/src/SegmentButton.tsx
CHANGED
|
@@ -31,10 +31,10 @@ export const SegmentButton: React.FC<ISegmentButtonProps> = ({
|
|
|
31
31
|
flexShrink: 1,
|
|
32
32
|
minWidth: 0,
|
|
33
33
|
minHeight: 0,
|
|
34
|
-
width:
|
|
34
|
+
width: undefined,
|
|
35
35
|
alignSelf: 'stretch',
|
|
36
|
-
height:
|
|
37
|
-
maxHeight:
|
|
36
|
+
height: t.size[10],
|
|
37
|
+
maxHeight: t.size[10],
|
|
38
38
|
paddingHorizontal: 0,
|
|
39
39
|
paddingTop: 0,
|
|
40
40
|
paddingBottom: 0,
|
|
@@ -91,7 +91,7 @@ export const SegmentButton: React.FC<ISegmentButtonProps> = ({
|
|
|
91
91
|
variant={ButtonVariant.BARE}
|
|
92
92
|
color={ButtonColor.TERTIARY}
|
|
93
93
|
size={ButtonSize.SMALL}
|
|
94
|
-
isFullWidth
|
|
94
|
+
isFullWidth={false}
|
|
95
95
|
prefixIcon={option?.icon}
|
|
96
96
|
onClick={() => {
|
|
97
97
|
if (disabled) return;
|
package/src/style.ts
CHANGED
|
@@ -15,7 +15,6 @@ export const segmentThumbShadowNative = {
|
|
|
15
15
|
elevation: 3,
|
|
16
16
|
} as const;
|
|
17
17
|
|
|
18
|
-
/** Shared layout — do not add web-only flex rules here; native uses this as-is. */
|
|
19
18
|
export const segmentStaticStyles = makeStyles((theme) => {
|
|
20
19
|
const trackHeight = theme.size[10];
|
|
21
20
|
const borderWidth = theme.border.width.sm;
|
|
@@ -52,6 +51,8 @@ export const segmentStaticStyles = makeStyles((theme) => {
|
|
|
52
51
|
},
|
|
53
52
|
animatedBlock: {
|
|
54
53
|
position: 'absolute',
|
|
54
|
+
top: 0,
|
|
55
|
+
height: '100%',
|
|
55
56
|
borderRadius: thumbRadius,
|
|
56
57
|
backgroundColor: theme.color.background.neutral,
|
|
57
58
|
},
|
|
@@ -75,69 +76,23 @@ export const segmentStaticStyles = makeStyles((theme) => {
|
|
|
75
76
|
};
|
|
76
77
|
});
|
|
77
78
|
|
|
78
|
-
/** Web-only flex layout — `<div>` needs explicit display; native Container must not use these. */
|
|
79
|
-
export const segmentWebLayoutStyles = makeStyles(() => ({
|
|
80
|
-
animatedContainer: {
|
|
81
|
-
display: 'flex',
|
|
82
|
-
},
|
|
83
|
-
tabMeasureCell: {
|
|
84
|
-
display: 'flex',
|
|
85
|
-
flexDirection: 'row',
|
|
86
|
-
justifyContent: 'center',
|
|
87
|
-
alignItems: 'center',
|
|
88
|
-
position: 'relative',
|
|
89
|
-
zIndex: 1,
|
|
90
|
-
},
|
|
91
|
-
}));
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Native thumb — inset within the padded track (web uses segmentWebThumbStyles instead).
|
|
95
|
-
*/
|
|
96
|
-
export const segmentNativeThumbStyles = makeStyles((theme) => {
|
|
97
|
-
const trackHeight = theme.size[10];
|
|
98
|
-
const borderWidth = theme.border.width.sm;
|
|
99
|
-
const trackPadding = theme.spacing[1];
|
|
100
|
-
const trackInnerHeight = Math.max(trackHeight - borderWidth * 2, 0);
|
|
101
|
-
const paddedInnerHeight = Math.max(trackInnerHeight - trackPadding * 2, 0);
|
|
102
|
-
const thumbInset = Math.max(theme.spacing[0.25], 0);
|
|
103
|
-
const paddedInnerRadius = Math.max(0, paddedInnerHeight / 2);
|
|
104
|
-
const thumbRadius = Math.max(0, paddedInnerRadius - thumbInset);
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
root: {
|
|
108
|
-
position: 'absolute',
|
|
109
|
-
top: thumbInset,
|
|
110
|
-
bottom: thumbInset,
|
|
111
|
-
borderRadius: thumbRadius,
|
|
112
|
-
backgroundColor: theme.color.background.neutral,
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Web-only (Segment.tsx) — native uses segmentNativeThumbStyles instead.
|
|
119
|
-
* Thumb fills animatedContainer via top/bottom inset, not a fixed height from trackHeight.
|
|
120
|
-
*/
|
|
121
79
|
export const segmentWebThumbStyles = makeStyles((theme) => {
|
|
122
80
|
const trackHeight = theme.size[10];
|
|
123
81
|
const borderWidth = theme.border.width.sm;
|
|
124
|
-
const trackPadding = theme.spacing[1];
|
|
125
82
|
const trackInnerHeight = Math.max(trackHeight - borderWidth * 2, 0);
|
|
126
|
-
const paddedInnerHeight = Math.max(trackInnerHeight - trackPadding * 2, 0);
|
|
127
83
|
const thumbInset = Math.max(theme.spacing[0.25], 0);
|
|
128
|
-
const
|
|
129
|
-
const
|
|
84
|
+
const thumbHeight = Math.max(trackInnerHeight - thumbInset * 2, 0);
|
|
85
|
+
const trackInnerRadius = Math.max(0, trackInnerHeight / 2);
|
|
86
|
+
const thumbRadius = Math.max(0, trackInnerRadius - thumbInset);
|
|
130
87
|
|
|
131
88
|
return {
|
|
132
89
|
root: {
|
|
133
90
|
position: 'absolute',
|
|
134
|
-
|
|
135
|
-
bottom: thumbInset,
|
|
91
|
+
height: thumbHeight,
|
|
136
92
|
borderRadius: thumbRadius,
|
|
137
93
|
backgroundColor: theme.color.background.neutral,
|
|
94
|
+
top: thumbInset,
|
|
138
95
|
boxShadow: SEGMENT_THUMB_BOX_SHADOW,
|
|
139
|
-
pointerEvents: 'none',
|
|
140
|
-
zIndex: 0,
|
|
141
96
|
},
|
|
142
97
|
};
|
|
143
98
|
});
|