Skip to content

Commit 4dd65dd

Browse files
windolepengjin
andauthored
feat: 新增嵌套功能、添加stopPropagation属性 (#57)
* chore: 更改release-it版本 * feat: 新增嵌套功能、添加stopPropagation属性 * fix: 修改eslint报错 * test: 修改测试案例 * test: 调整单元测试 --------- Co-authored-by: pengjin <pengjin@vcredit.com>
1 parent 2990c49 commit 4dd65dd

File tree

6 files changed

+105
-2
lines changed

6 files changed

+105
-2
lines changed

packages/rc-ui-lib/src/cascader/__test__/__snapshots__/index.spec.tsx.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ exports[`Cascader should render option correctly when using defaultValue prop 1`
160160
loop={false}
161161
onIndexChange={[Function]}
162162
slideSize={100}
163+
stopPropagation={Array []}
163164
stuckAtBoundary={false}
164165
trackOffset={0}
165166
>
@@ -473,6 +474,7 @@ exports[`Cascader when value change 1`] = `
473474
loop={false}
474475
onIndexChange={[Function]}
475476
slideSize={100}
477+
stopPropagation={Array []}
476478
stuckAtBoundary={false}
477479
trackOffset={0}
478480
>

packages/rc-ui-lib/src/swiper/PropsType.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { BaseTypeProps } from '../utils';
44
type DirectionTypes = 'horizontal' | 'vertical';
55
type IndicatorColorTypes = 'primary' | 'white';
66

7+
type PropagationEvent = "mousedown" | "mousemove" | "mouseup"
8+
79
export type PageIndicatorProps = {
810
total: number;
911
current: number;
@@ -43,6 +45,8 @@ export interface SwiperProps extends BaseTypeProps {
4345
stuckAtBoundary?: boolean;
4446
/** 子元素 */
4547
children?: React.ReactElement | React.ReactElement[];
48+
/** 阻止事件冒泡 */
49+
stopPropagation?: PropagationEvent[];
4650
}
4751

4852
export interface SwiperInstance {

packages/rc-ui-lib/src/swiper/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ export default () => {
196196
| stuckAtBoundary | 是否在边界两边卡住,避免出现空白,仅在非 `loop` 模式且 `slideSize` < 100 时生效 | _boolean_ | `false` |
197197
| indicator | 自定义指示器 | _boolean \| (total, current) => ReactNode_ | - |
198198
| indicatorProps | 指示器属性 | _IndicatorProps_ | - |
199+
| stopPropagation | 阻止某些事件的冒泡[2.0.2] | _PropagationEvent[]_ | [] |
200+
201+
```ts
202+
type PropagationEvent = 'mouseup' | 'mousemove' | 'mousedown';
203+
```
199204

200205
### DirectionTypes 格式
201206

packages/rc-ui-lib/src/swiper/Swiper.tsx

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,29 @@ import { devWarning } from '../utils/dev-log';
2121
import { noop } from '../utils';
2222
import { getRect } from '../hooks/get-rect';
2323
import useMountedRef from '../hooks/use-mounted-ref';
24+
import { mergeFuncProps } from '../utils/with-func-props';
2425

2526
function modulus(value: number, division: number) {
2627
const remainder = value % division;
2728
return remainder < 0 ? remainder + division : remainder;
2829
}
2930

31+
const eventToPropRecord = {
32+
mousedown: 'onMouseDown',
33+
mousemove: 'onMouseMove',
34+
mouseup: 'onMouseUp',
35+
} as const;
36+
37+
let currentUid: undefined | {};
38+
3039
const Swiper = forwardRef<SwiperInstance, SwiperProps>((props, ref) => {
3140
const { prefixCls, createNamespace } = useContext(ConfigProviderContext);
3241
const [bem] = createNamespace('swiper', prefixCls);
3342

3443
const { loop: outerLoop, autoplay, direction, autoplayInterval } = props;
3544

45+
const [uid] = useState({});
46+
3647
const lock = useRef<boolean>(false);
3748
const trackRef = useRef<HTMLDivElement>(null);
3849
const [root, setRoot] = useState<HTMLDivElement>(null);
@@ -123,10 +134,24 @@ const Swiper = forwardRef<SwiperInstance, SwiperProps>((props, ref) => {
123134
[count],
124135
);
125136

137+
const dragCancelRef = useRef<(() => void) | null>(null);
138+
function forceCancelDrag() {
139+
dragCancelRef.current?.();
140+
draggingRef.current = false;
141+
}
142+
126143
const bind = useDrag(
127144
(state) => {
128-
if (lock.current) return;
145+
dragCancelRef.current = state.cancel;
146+
if (!state.intentional) return;
147+
if (state.first && !currentUid) {
148+
currentUid = uid;
149+
}
150+
if (currentUid !== uid) return;
151+
currentUid = state.last ? undefined : uid;
129152
const slidePixels = getSlidePixels();
153+
154+
if (lock.current) return;
130155
if (!slidePixels) return;
131156
const paramIndex = isVertical ? 1 : 0;
132157
const offset = state.offset[paramIndex];
@@ -203,6 +228,7 @@ const Swiper = forwardRef<SwiperInstance, SwiperProps>((props, ref) => {
203228
if (draggingRef.current) {
204229
e.stopPropagation();
205230
}
231+
forceCancelDrag();
206232
};
207233

208234
function swipeTo(index: number, immediate = false) {
@@ -260,6 +286,19 @@ const Swiper = forwardRef<SwiperInstance, SwiperProps>((props, ref) => {
260286
devWarning('Swiper', '`Swiper` needs at least one child.');
261287
}
262288

289+
const dragProps = { ...(props.allowTouchMove ? bind() : {}) };
290+
291+
const stopPropagationProps: Partial<Record<any, any>> = {};
292+
props.stopPropagation.forEach(key => {
293+
const prop = eventToPropRecord[key];
294+
stopPropagationProps[prop] = function (e: Event) {
295+
e.stopPropagation();
296+
};
297+
})
298+
299+
300+
const mergedProps = mergeFuncProps(dragProps, stopPropagationProps);
301+
263302
return (
264303
<div
265304
ref={setRoot}
@@ -274,7 +313,7 @@ const Swiper = forwardRef<SwiperInstance, SwiperProps>((props, ref) => {
274313
}),
275314
)}
276315
onClickCapture={onClickCapture}
277-
{...(props.allowTouchMove ? bind() : {})}
316+
{...mergedProps}
278317
>
279318
<div
280319
className={classnames(
@@ -318,6 +357,7 @@ Swiper.defaultProps = {
318357
slideSize: 100,
319358
stuckAtBoundary: false,
320359
trackOffset: 0,
360+
stopPropagation: [],
321361
};
322362

323363
Swiper.displayName = 'Swiper';

packages/rc-ui-lib/src/swiper/__test__/index.spec.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,4 +303,27 @@ describe('Swipe test with testing library', () => {
303303

304304
expect(container).toMatchSnapshot();
305305
});
306+
307+
it('stop propagation should be work', async () => {
308+
const onMouseDown = jest.fn();
309+
const onMouseMove = jest.fn();
310+
const onMouseUp = jest.fn();
311+
312+
const { container } = render(
313+
<Swiper style={swipeStyle} stopPropagation={['mousedown', 'mousemove', 'mouseup']}>
314+
<Swiper.Item>1</Swiper.Item>
315+
<Swiper.Item>2</Swiper.Item>
316+
<Swiper.Item>3</Swiper.Item>
317+
</Swiper>,
318+
);
319+
320+
const track = container.querySelector('.rc-swiper__track');
321+
mockOffset(track);
322+
await TestsEvent.triggerDrag(track, [-100, 0]);
323+
await sleep(100);
324+
325+
expect(onMouseDown).not.toBeCalled();
326+
expect(onMouseMove).not.toBeCalled();
327+
expect(onMouseUp).not.toBeCalled();
328+
});
306329
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
type Merge<T, P> = {
2+
[K in keyof T & keyof P]: P[K] | T[K];
3+
};
4+
5+
export function mergeFuncProps<T extends Record<string, any>, P extends Record<string, any>>(
6+
p1: T,
7+
p2: P,
8+
): Merge<T, P> {
9+
const p1Keys = Object.keys(p1);
10+
const p2Keys = Object.keys(p2);
11+
const keys = new Set([...p1Keys, ...p2Keys]);
12+
const res: any = {};
13+
14+
keys.forEach((key) => {
15+
const p1Value = p1[key];
16+
const p2Value = p2[key];
17+
18+
if (typeof p1Value === 'function' && typeof p2Value === 'function') {
19+
res[key] = function (...args: any[]) {
20+
p1Value(...args);
21+
p2Value(...args);
22+
};
23+
} else {
24+
res[key] = p1Value || p2Value;
25+
}
26+
});
27+
28+
return res;
29+
}

0 commit comments

Comments
 (0)