@fjyueke/arco-mcp 1.0.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/README.md +85 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +244 -0
- package/metaData/components/action-sheet/doc.md +102 -0
- package/metaData/components/action-sheet/example/index.tsx +199 -0
- package/metaData/components/action-sheet/example/methods.tsx +6 -0
- package/metaData/components/avatar/doc.md +57 -0
- package/metaData/components/avatar/example/group.tsx +69 -0
- package/metaData/components/avatar/example/index.tsx +196 -0
- package/metaData/components/badge/doc.md +28 -0
- package/metaData/components/badge/example/index.tsx +128 -0
- package/metaData/components/button/doc.md +45 -0
- package/metaData/components/button/example/index.tsx +264 -0
- package/metaData/components/carousel/doc.md +89 -0
- package/metaData/components/carousel/example/index.tsx +1481 -0
- package/metaData/components/cell/doc.md +68 -0
- package/metaData/components/cell/example/arrow.tsx +13 -0
- package/metaData/components/cell/example/cell.tsx +71 -0
- package/metaData/components/cell/example/group.tsx +63 -0
- package/metaData/components/cell/example/index.tsx +19 -0
- package/metaData/components/checkbox/doc.md +91 -0
- package/metaData/components/checkbox/example/checkbox.tsx +158 -0
- package/metaData/components/checkbox/example/group.tsx +103 -0
- package/metaData/components/checkbox/example/index.tsx +25 -0
- package/metaData/components/circle-progress/doc.md +40 -0
- package/metaData/components/circle-progress/example/index.tsx +323 -0
- package/metaData/components/collapse/doc.md +74 -0
- package/metaData/components/collapse/example/collapse.tsx +183 -0
- package/metaData/components/collapse/example/group.tsx +136 -0
- package/metaData/components/collapse/example/index.tsx +16 -0
- package/metaData/components/collapse/example/utils.tsx +20 -0
- package/metaData/components/context-provider/doc.md +44 -0
- package/metaData/components/context-provider/example/index.tsx +208 -0
- package/metaData/components/count-down/doc.md +40 -0
- package/metaData/components/count-down/example/hooks.tsx +118 -0
- package/metaData/components/count-down/example/index.tsx +63 -0
- package/metaData/components/date-picker/doc.md +102 -0
- package/metaData/components/date-picker/example/index.tsx +545 -0
- package/metaData/components/dialog/doc.md +200 -0
- package/metaData/components/dialog/example/index.tsx +328 -0
- package/metaData/components/dialog/example/methods.tsx +56 -0
- package/metaData/components/divider/doc.md +25 -0
- package/metaData/components/divider/example/index.tsx +87 -0
- package/metaData/components/dropdown/doc.md +80 -0
- package/metaData/components/dropdown/example/dropdown.tsx +308 -0
- package/metaData/components/dropdown/example/index.tsx +19 -0
- package/metaData/components/dropdown/example/options.tsx +103 -0
- package/metaData/components/dropdown-menu/doc.md +91 -0
- package/metaData/components/dropdown-menu/example/dropdown-menu.tsx +270 -0
- package/metaData/components/dropdown-menu/example/index.tsx +16 -0
- package/metaData/components/ellipsis/doc.md +33 -0
- package/metaData/components/ellipsis/example/index.tsx +93 -0
- package/metaData/components/ellipsis/example/js-ellipsis.tsx +229 -0
- package/metaData/components/ellipsis/example/native-ellipsis.tsx +48 -0
- package/metaData/components/form/doc.md +90 -0
- package/metaData/components/form/example/form-item-context.tsx +8 -0
- package/metaData/components/form/example/form-item.tsx +378 -0
- package/metaData/components/form/example/index.tsx +87 -0
- package/metaData/components/form/example/linked-container.tsx +66 -0
- package/metaData/components/grid/doc.md +39 -0
- package/metaData/components/grid/example/index.tsx +252 -0
- package/metaData/components/icon/doc.md +15 -0
- package/metaData/components/icon/example/index.tsx +32 -0
- package/metaData/components/image/doc.md +62 -0
- package/metaData/components/image/example/index.tsx +528 -0
- package/metaData/components/image-picker/doc.md +116 -0
- package/metaData/components/image-picker/example/add-icon.tsx +14 -0
- package/metaData/components/image-picker/example/index.tsx +175 -0
- package/metaData/components/image-preview/doc.md +119 -0
- package/metaData/components/image-preview/example/index.tsx +1290 -0
- package/metaData/components/image-preview/example/methods.tsx +72 -0
- package/metaData/components/index-bar/doc.md +83 -0
- package/metaData/components/index-bar/example/group.tsx +93 -0
- package/metaData/components/index-bar/example/index.tsx +271 -0
- package/metaData/components/index-bar/example/side-bar.tsx +82 -0
- package/metaData/components/input/doc.md +60 -0
- package/metaData/components/input/example/hooks.tsx +259 -0
- package/metaData/components/input/example/index.tsx +134 -0
- package/metaData/components/input/example/props.tsx +183 -0
- package/metaData/components/keyboard/doc.md +73 -0
- package/metaData/components/keyboard/example/index.tsx +238 -0
- package/metaData/components/load-more/doc.md +48 -0
- package/metaData/components/load-more/example/index.tsx +395 -0
- package/metaData/components/loading/doc.md +34 -0
- package/metaData/components/loading/example/index.tsx +312 -0
- package/metaData/components/masking/doc.md +93 -0
- package/metaData/components/masking/example/index.tsx +410 -0
- package/metaData/components/masking/example/methods.tsx +88 -0
- package/metaData/components/nav-bar/doc.md +40 -0
- package/metaData/components/nav-bar/example/back-icon.tsx +28 -0
- package/metaData/components/nav-bar/example/index.tsx +305 -0
- package/metaData/components/notice-bar/doc.md +34 -0
- package/metaData/components/notice-bar/example/index.tsx +294 -0
- package/metaData/components/notify/doc.md +81 -0
- package/metaData/components/notify/example/index.tsx +171 -0
- package/metaData/components/notify/example/methods.tsx +83 -0
- package/metaData/components/pagination/doc.md +41 -0
- package/metaData/components/pagination/example/arrow.tsx +14 -0
- package/metaData/components/pagination/example/index.tsx +292 -0
- package/metaData/components/picker/doc.md +89 -0
- package/metaData/components/picker/example/index.tsx +217 -0
- package/metaData/components/picker-view/doc.md +57 -0
- package/metaData/components/picker-view/example/cascader.tsx +141 -0
- package/metaData/components/picker-view/example/index.tsx +297 -0
- package/metaData/components/picker-view/example/multi-picker.tsx +66 -0
- package/metaData/components/picker-view/example/picker-cell.tsx +416 -0
- package/metaData/components/popover/doc.md +144 -0
- package/metaData/components/popover/example/index.tsx +23 -0
- package/metaData/components/popover/example/menu.tsx +145 -0
- package/metaData/components/popover/example/popover-inner.tsx +147 -0
- package/metaData/components/popover/example/popover.tsx +268 -0
- package/metaData/components/popup/doc.md +101 -0
- package/metaData/components/popup/example/index.tsx +125 -0
- package/metaData/components/popup/example/methods.tsx +6 -0
- package/metaData/components/popup-swiper/doc.md +108 -0
- package/metaData/components/popup-swiper/example/index.tsx +332 -0
- package/metaData/components/popup-swiper/example/methods.tsx +6 -0
- package/metaData/components/portal/doc.md +14 -0
- package/metaData/components/portal/example/index.tsx +35 -0
- package/metaData/components/progress/doc.md +42 -0
- package/metaData/components/progress/example/index.tsx +256 -0
- package/metaData/components/pull-refresh/doc.md +37 -0
- package/metaData/components/pull-refresh/example/android-pull-refresh.tsx +260 -0
- package/metaData/components/pull-refresh/example/index.tsx +44 -0
- package/metaData/components/pull-refresh/example/ios-pull-refresh.tsx +216 -0
- package/metaData/components/radio/doc.md +89 -0
- package/metaData/components/radio/example/group.tsx +92 -0
- package/metaData/components/radio/example/index.tsx +25 -0
- package/metaData/components/radio/example/radio.tsx +22 -0
- package/metaData/components/rate/doc.md +33 -0
- package/metaData/components/rate/example/index.tsx +281 -0
- package/metaData/components/search-bar/doc.md +97 -0
- package/metaData/components/search-bar/example/association.tsx +89 -0
- package/metaData/components/search-bar/example/cancel-button.tsx +34 -0
- package/metaData/components/search-bar/example/highlight.tsx +66 -0
- package/metaData/components/search-bar/example/index.tsx +251 -0
- package/metaData/components/show-monitor/doc.md +34 -0
- package/metaData/components/show-monitor/example/index.tsx +646 -0
- package/metaData/components/skeleton/doc.md +101 -0
- package/metaData/components/skeleton/example/elements.tsx +290 -0
- package/metaData/components/skeleton/example/index.tsx +96 -0
- package/metaData/components/skeleton/example/skeleton-context.tsx +7 -0
- package/metaData/components/slider/doc.md +42 -0
- package/metaData/components/slider/example/index.tsx +326 -0
- package/metaData/components/slider/example/marks.tsx +50 -0
- package/metaData/components/slider/example/popover.tsx +38 -0
- package/metaData/components/slider/example/thumb.tsx +48 -0
- package/metaData/components/slider/example/useSliderIcon.tsx +38 -0
- package/metaData/components/slider/example/useSliderInit.tsx +170 -0
- package/metaData/components/stepper/doc.md +46 -0
- package/metaData/components/stepper/example/index.tsx +166 -0
- package/metaData/components/stepper/example/useButtonClick.tsx +74 -0
- package/metaData/components/stepper/example/useInputEvent.tsx +65 -0
- package/metaData/components/stepper/example/useValue.tsx +43 -0
- package/metaData/components/steps/doc.md +57 -0
- package/metaData/components/steps/example/index.tsx +123 -0
- package/metaData/components/steps/example/step.tsx +126 -0
- package/metaData/components/sticky/doc.md +45 -0
- package/metaData/components/sticky/example/index.tsx +430 -0
- package/metaData/components/swipe-action/doc.md +44 -0
- package/metaData/components/swipe-action/example/index.tsx +338 -0
- package/metaData/components/swipe-action/example/item.tsx +40 -0
- package/metaData/components/swipe-load/doc.md +43 -0
- package/metaData/components/swipe-load/example/index.tsx +339 -0
- package/metaData/components/swipe-load/example/type.tsx +153 -0
- package/metaData/components/switch/doc.md +38 -0
- package/metaData/components/switch/example/index.tsx +237 -0
- package/metaData/components/tab-bar/doc.md +57 -0
- package/metaData/components/tab-bar/example/index.tsx +18 -0
- package/metaData/components/tab-bar/example/item.tsx +74 -0
- package/metaData/components/tab-bar/example/tab-bar.tsx +105 -0
- package/metaData/components/tabs/doc.md +187 -0
- package/metaData/components/tabs/example/index.tsx +514 -0
- package/metaData/components/tabs/example/tab-cell-underline.tsx +320 -0
- package/metaData/components/tabs/example/tab-cell.tsx +433 -0
- package/metaData/components/tabs/example/tab-pane.tsx +349 -0
- package/metaData/components/tag/doc.md +80 -0
- package/metaData/components/tag/example/index.tsx +23 -0
- package/metaData/components/tag/example/list.tsx +97 -0
- package/metaData/components/tag/example/tag.tsx +85 -0
- package/metaData/components/textarea/doc.md +67 -0
- package/metaData/components/textarea/example/index.tsx +258 -0
- package/metaData/components/toast/doc.md +90 -0
- package/metaData/components/toast/example/index.tsx +335 -0
- package/metaData/components/toast/example/methods.tsx +87 -0
- package/metaData/components/transition/doc.md +19 -0
- package/metaData/components/transition/example/index.tsx +113 -0
- package/metaData/components/uploader/doc.md +82 -0
- package/metaData/components/uploader/example/index.tsx +208 -0
- package/metaData/components-index.json +242 -0
- package/package.json +56 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import React, { forwardRef, Ref, useContext, useImperativeHandle, useMemo, useRef } from 'react';
|
|
2
|
+
import { cls } from '@arco-design/mobile-utils';
|
|
3
|
+
import { IconCheckBold, IconCloseBold } from '../icon';
|
|
4
|
+
import { ContextLayout } from '../context-provider';
|
|
5
|
+
import { StepsContext } from './index';
|
|
6
|
+
import { StepProps, StepRef } from './type';
|
|
7
|
+
|
|
8
|
+
const Step = forwardRef((props: StepProps, ref: Ref<StepRef | null>) => {
|
|
9
|
+
const { title, description, icon } = props;
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
iconType,
|
|
13
|
+
current,
|
|
14
|
+
direction,
|
|
15
|
+
index = 0,
|
|
16
|
+
status: contextStatus,
|
|
17
|
+
align: contextAlign,
|
|
18
|
+
changeIndex,
|
|
19
|
+
} = useContext(StepsContext);
|
|
20
|
+
const status = props.status || contextStatus;
|
|
21
|
+
const align = props.align || contextAlign;
|
|
22
|
+
|
|
23
|
+
const domRef = useRef<HTMLDivElement | null>(null);
|
|
24
|
+
useImperativeHandle(ref, () => ({
|
|
25
|
+
dom: domRef.current,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
const currentStatus = useMemo(() => {
|
|
29
|
+
if (status) return status;
|
|
30
|
+
if (current! < index) {
|
|
31
|
+
return 'wait';
|
|
32
|
+
}
|
|
33
|
+
if (current! === index) {
|
|
34
|
+
return 'process';
|
|
35
|
+
}
|
|
36
|
+
return 'finish';
|
|
37
|
+
}, [status, index, current]);
|
|
38
|
+
|
|
39
|
+
function renderNumIcon(prefixCls) {
|
|
40
|
+
let content;
|
|
41
|
+
if (currentStatus === 'finish') {
|
|
42
|
+
content = <IconCheckBold />;
|
|
43
|
+
} else if (currentStatus === 'error') {
|
|
44
|
+
content = <IconCloseBold />;
|
|
45
|
+
}
|
|
46
|
+
return (
|
|
47
|
+
content || <span className={cls(`${prefixCls}-steps-item-icon-num`)}>{index + 1}</span>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<ContextLayout>
|
|
53
|
+
{({ prefixCls }) => (
|
|
54
|
+
<div
|
|
55
|
+
ref={domRef}
|
|
56
|
+
className={cls(
|
|
57
|
+
`${prefixCls}-steps-item`,
|
|
58
|
+
`${prefixCls}-steps-item-align-${align}`,
|
|
59
|
+
currentStatus,
|
|
60
|
+
direction,
|
|
61
|
+
iconType,
|
|
62
|
+
)}
|
|
63
|
+
onClick={() => changeIndex(index)}
|
|
64
|
+
>
|
|
65
|
+
<div
|
|
66
|
+
className={cls(
|
|
67
|
+
`${prefixCls}-steps-item-tail`,
|
|
68
|
+
`${prefixCls}-steps-item-tail-status-${currentStatus}`,
|
|
69
|
+
`${prefixCls}-steps-item-tail-align-${align}`,
|
|
70
|
+
`${currentStatus}-tail-color-with-config`,
|
|
71
|
+
)}
|
|
72
|
+
/>
|
|
73
|
+
{icon ? (
|
|
74
|
+
<div
|
|
75
|
+
className={cls(
|
|
76
|
+
`${prefixCls}-steps-item-custom-icon`,
|
|
77
|
+
`${currentStatus}-custom-icon-bg-color-with-config`,
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
{icon}
|
|
81
|
+
</div>
|
|
82
|
+
) : (
|
|
83
|
+
<div
|
|
84
|
+
className={cls(
|
|
85
|
+
`${prefixCls}-steps-item-icon`,
|
|
86
|
+
`${currentStatus}-bg-color-with-config`,
|
|
87
|
+
)}
|
|
88
|
+
>
|
|
89
|
+
{iconType === 'number' ? (
|
|
90
|
+
renderNumIcon(prefixCls)
|
|
91
|
+
) : (
|
|
92
|
+
<span className={cls(`${prefixCls}-steps-item-icon-dot`)} />
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
{title || description ? (
|
|
97
|
+
<div
|
|
98
|
+
className={cls(
|
|
99
|
+
`${prefixCls}-steps-item-content`,
|
|
100
|
+
`${prefixCls}-steps-item-content-align-${align}`,
|
|
101
|
+
)}
|
|
102
|
+
>
|
|
103
|
+
{title ? (
|
|
104
|
+
<div
|
|
105
|
+
className={cls(
|
|
106
|
+
`${prefixCls}-steps-item-title`,
|
|
107
|
+
`${currentStatus}-title-color-with-config`,
|
|
108
|
+
)}
|
|
109
|
+
>
|
|
110
|
+
{title}
|
|
111
|
+
</div>
|
|
112
|
+
) : null}
|
|
113
|
+
{description ? (
|
|
114
|
+
<div className={cls(`${prefixCls}-steps-item-description`)}>
|
|
115
|
+
{description}
|
|
116
|
+
</div>
|
|
117
|
+
) : null}
|
|
118
|
+
</div>
|
|
119
|
+
) : null}
|
|
120
|
+
</div>
|
|
121
|
+
)}
|
|
122
|
+
</ContextLayout>
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
export default Step;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
### 布局
|
|
2
|
+
|
|
3
|
+
# 粘性布局 Sticky
|
|
4
|
+
|
|
5
|
+
粘性布局组件,元素相对于窗口或指定容器的吸顶效果。
|
|
6
|
+
|
|
7
|
+
======
|
|
8
|
+
|
|
9
|
+
> 属性/Props
|
|
10
|
+
|
|
11
|
+
|参数|描述|类型|默认值|
|
|
12
|
+
|----------|-------------|------|------|
|
|
13
|
+
|position|吸附位置,top 表示吸顶,bottom 表示吸底,both 表示既吸顶又吸底|"top" \| "bottom" \| "both"|"top"|
|
|
14
|
+
|topOffset|当距离容器顶部距离为该值时吸顶|number|0|
|
|
15
|
+
|bottomOffset|当距离容器底部距离为该值时吸底|number|0|
|
|
16
|
+
|followOffset|当sticky元素需要跟随依附容器离开视口时距离依附容器边缘的距离|number|0|
|
|
17
|
+
|className|自定义类名|string|-|
|
|
18
|
+
|style|自定义样式|CSSProperties|-|
|
|
19
|
+
|children|组件内部内容|ReactNode|-|
|
|
20
|
+
|zIndex|层级指数,z\-index的值|number|100|
|
|
21
|
+
|portalWhenSticky|在处于sticky状态时是否portal|boolean|false|
|
|
22
|
+
|stickyStyle|处于sticky状态时,元素固定样式。推荐用fixed,如果为absolute则需自行为滚动容器的父容器设置 position: relative|"fixed" \| "absolute"|"fixed"|
|
|
23
|
+
|stickyCssStyle|处于sticky状态时的自定义样式|CSSProperties|-|
|
|
24
|
+
|getPortalContainer|被portal时挂载的容器|() =\> HTMLElement|() => document.body|
|
|
25
|
+
|onStickyStateChange|吸顶状态切换时触发 payload\.isSticky: 当前是否为吸顶/吸底状态 payload\.wasSticky: 前一个状态是否为吸顶/吸底状态|(payload: StickyEventPayload) =\> void|-|
|
|
26
|
+
|onTopChange|滚动时top变化回调,参数为元素距离容器顶部减去topOffset的距离|(top: number) =\> void|-|
|
|
27
|
+
|getContainer|指定sticky元素的依附容器,sticky元素不会超出容器,在容器离开视口时会跟随 如果返回string则使用querySelector选取容器|() =\> string \| HTMLElement|-|
|
|
28
|
+
|getScrollContainer|指定滚动容器;如果返回string则使用querySelector选取容器|() =\> string \| HTMLElement \| Window|() => window|
|
|
29
|
+
|
|
30
|
+
> 引用/Refs
|
|
31
|
+
|
|
32
|
+
|参数|描述|类型|
|
|
33
|
+
|----------|-------------|------|
|
|
34
|
+
|dom|最外层元素 DOM|HTMLDivElement|
|
|
35
|
+
|recalculatePosition|局部滚动模式下,如果容器外部还有嵌套滚动,可主动调用此方法,让 sticky 的元素主动更新 fixed 位置|() =\> void|
|
|
36
|
+
|updatePlaceholderLayout|手动更新占位模块的高度|() =\> void|
|
|
37
|
+
|
|
38
|
+
> StickyEventPayload
|
|
39
|
+
|
|
40
|
+
|参数|描述|类型|
|
|
41
|
+
|----------|-------------|------|
|
|
42
|
+
|isSticky|当前是否为吸顶/吸底状态|boolean|
|
|
43
|
+
|wasSticky|前一个状态是否为吸顶/吸底状态|boolean|
|
|
44
|
+
|isTopSticky|当前是否为吸顶状态|boolean|
|
|
45
|
+
|isBottomSticky|当前是否为吸底状态|boolean|
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useRef,
|
|
3
|
+
forwardRef,
|
|
4
|
+
Ref,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useState,
|
|
9
|
+
CSSProperties,
|
|
10
|
+
useCallback,
|
|
11
|
+
ReactNode,
|
|
12
|
+
} from 'react';
|
|
13
|
+
import {
|
|
14
|
+
cls,
|
|
15
|
+
execRAF,
|
|
16
|
+
getActualContainer,
|
|
17
|
+
getScrollContainerRect,
|
|
18
|
+
safeGetComputedStyle,
|
|
19
|
+
convertCssPropertyToNumber,
|
|
20
|
+
} from '@arco-design/mobile-utils';
|
|
21
|
+
import { ContextLayout } from '../context-provider';
|
|
22
|
+
import { useRefState } from '../_helpers';
|
|
23
|
+
import Portal from '../portal';
|
|
24
|
+
|
|
25
|
+
export interface StickyRef {
|
|
26
|
+
/**
|
|
27
|
+
* 最外层元素 DOM
|
|
28
|
+
* @en The outermost element DOM
|
|
29
|
+
*/
|
|
30
|
+
dom: HTMLDivElement | null;
|
|
31
|
+
/**
|
|
32
|
+
* 局部滚动模式下,如果容器外部还有嵌套滚动,可主动调用此方法,让 sticky 的元素主动更新 fixed 位置
|
|
33
|
+
* @en In the local scrolling mode, if there is nested scrolling outside the container, this method can be actively called to make the sticky element actively update the fixed position
|
|
34
|
+
*/
|
|
35
|
+
recalculatePosition: () => void;
|
|
36
|
+
/**
|
|
37
|
+
* 手动更新占位模块的高度
|
|
38
|
+
* @en Manually update the height of the placeholder
|
|
39
|
+
*/
|
|
40
|
+
updatePlaceholderLayout: () => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface StickyEventPayload {
|
|
44
|
+
/**
|
|
45
|
+
* 当前是否为吸顶/吸底状态
|
|
46
|
+
* @en Whether it is currently in the top/bottom suction state
|
|
47
|
+
*/
|
|
48
|
+
isSticky: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* 前一个状态是否为吸顶/吸底状态
|
|
51
|
+
* @en Whether the previous state is ceiling / bottom suction state
|
|
52
|
+
*/
|
|
53
|
+
wasSticky: boolean;
|
|
54
|
+
/** 当前是否为吸顶状态 */
|
|
55
|
+
isTopSticky: boolean;
|
|
56
|
+
/** 当前是否为吸底状态 */
|
|
57
|
+
isBottomSticky: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface StickyProps {
|
|
61
|
+
/**
|
|
62
|
+
* 吸附位置,top 表示吸顶,bottom 表示吸底,both 表示既吸顶又吸底
|
|
63
|
+
* @en Adsorption position, top means fixed top, bottom means fixed bottom, both means both top and bottom
|
|
64
|
+
* @default "top"
|
|
65
|
+
*/
|
|
66
|
+
position?: 'top' | 'bottom' | 'both';
|
|
67
|
+
/**
|
|
68
|
+
* 当距离容器顶部距离为该值时吸顶
|
|
69
|
+
* @en Fixed top when the distance from the top of the container is this value
|
|
70
|
+
* @default 0
|
|
71
|
+
*/
|
|
72
|
+
topOffset?: number;
|
|
73
|
+
/**
|
|
74
|
+
* 当距离容器底部距离为该值时吸底
|
|
75
|
+
* @en Fixed bottom when the distance from the bottom of the container is this value
|
|
76
|
+
* @default 0
|
|
77
|
+
*/
|
|
78
|
+
bottomOffset?: number;
|
|
79
|
+
/**
|
|
80
|
+
* 当sticky元素需要跟随依附容器离开视口时距离依附容器边缘的距离
|
|
81
|
+
* @en The distance from the edge of the attached container when the sticky element needs to follow the attached container to leave the viewport
|
|
82
|
+
* @default 0
|
|
83
|
+
*/
|
|
84
|
+
followOffset?: number;
|
|
85
|
+
/**
|
|
86
|
+
* 自定义类名
|
|
87
|
+
* @en Custom classname
|
|
88
|
+
*/
|
|
89
|
+
className?: string;
|
|
90
|
+
/**
|
|
91
|
+
* 自定义样式
|
|
92
|
+
* @en Custom stylesheet
|
|
93
|
+
*/
|
|
94
|
+
style?: CSSProperties;
|
|
95
|
+
/**
|
|
96
|
+
* 组件内部内容
|
|
97
|
+
* @en Children elements of the component
|
|
98
|
+
*/
|
|
99
|
+
children?: ReactNode;
|
|
100
|
+
/**
|
|
101
|
+
* 层级指数,z-index的值
|
|
102
|
+
* @en z-index
|
|
103
|
+
* @default 100
|
|
104
|
+
*/
|
|
105
|
+
zIndex?: number;
|
|
106
|
+
/**
|
|
107
|
+
* 在处于sticky状态时是否portal
|
|
108
|
+
* @en Whether it is portal in sticky state
|
|
109
|
+
* @default false
|
|
110
|
+
*/
|
|
111
|
+
portalWhenSticky?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* 处于sticky状态时,元素固定样式。推荐用fixed,如果为absolute则需自行为滚动容器的父容器设置 position: relative
|
|
114
|
+
* @en When in the sticky state, the fixed style of the element. Fixed is recommended. If absolute, need to set position: relative for the parent container of the scrolling container.
|
|
115
|
+
* @default "fixed"
|
|
116
|
+
*/
|
|
117
|
+
stickyStyle?: 'fixed' | 'absolute';
|
|
118
|
+
/**
|
|
119
|
+
* 处于sticky状态时的自定义样式
|
|
120
|
+
* @en Custom style when in sticky state
|
|
121
|
+
*/
|
|
122
|
+
stickyCssStyle?: CSSProperties;
|
|
123
|
+
/**
|
|
124
|
+
* 被portal时挂载的容器
|
|
125
|
+
* @en Container mounted when being portaled
|
|
126
|
+
* @default () => document.body
|
|
127
|
+
*/
|
|
128
|
+
getPortalContainer?: () => HTMLElement;
|
|
129
|
+
/**
|
|
130
|
+
* 吸顶状态切换时触发
|
|
131
|
+
* payload.isSticky: 当前是否为吸顶/吸底状态
|
|
132
|
+
* payload.wasSticky: 前一个状态是否为吸顶/吸底状态
|
|
133
|
+
* @en Triggered when the sticky state is switched
|
|
134
|
+
* @en isSticky: Whether it is currently in the top/bottom state
|
|
135
|
+
* @en Whether the previous state is the top/bottom state
|
|
136
|
+
*/
|
|
137
|
+
onStickyStateChange?: (payload: StickyEventPayload) => void;
|
|
138
|
+
/**
|
|
139
|
+
* 滚动时top变化回调,参数为元素距离容器顶部减去topOffset的距离
|
|
140
|
+
* @en The top change callback when scrolling, the parameter is the distance of the element from the top of the container minus topOffset
|
|
141
|
+
*/
|
|
142
|
+
onTopChange?: (top: number) => void;
|
|
143
|
+
/**
|
|
144
|
+
* 指定sticky元素的依附容器,sticky元素不会超出容器,在容器离开视口时会跟随
|
|
145
|
+
* 如果返回string则使用querySelector选取容器
|
|
146
|
+
* @en Specifies the attachment container of the sticky element. The sticky element will not exceed the container and will follow when the container leaves the viewport
|
|
147
|
+
* @en If a string is returned, use querySelector to select the container
|
|
148
|
+
*/
|
|
149
|
+
getContainer?: () => HTMLElement | string;
|
|
150
|
+
/**
|
|
151
|
+
* 指定滚动容器;如果返回string则使用querySelector选取容器
|
|
152
|
+
* @en Specifies the scrolling container. If this value is specified, the relative property is always regarded as false; if a string is input, use querySelector to select the container
|
|
153
|
+
* @default () => window
|
|
154
|
+
*/
|
|
155
|
+
getScrollContainer?: () => HTMLElement | Window | string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 粘性布局组件,元素相对于窗口或指定容器的吸顶效果。
|
|
160
|
+
* @en Sticky layout component, The sticky-to-top effect of the element relative to the window or specified container
|
|
161
|
+
* @type 布局
|
|
162
|
+
* @type_en Layout
|
|
163
|
+
* @name 粘性布局
|
|
164
|
+
* @name_en Sticky
|
|
165
|
+
*/
|
|
166
|
+
const Sticky = forwardRef((props: StickyProps, ref: Ref<StickyRef>) => {
|
|
167
|
+
const {
|
|
168
|
+
position = 'top',
|
|
169
|
+
topOffset = 0,
|
|
170
|
+
bottomOffset = 0,
|
|
171
|
+
followOffset = 0,
|
|
172
|
+
children,
|
|
173
|
+
className,
|
|
174
|
+
style,
|
|
175
|
+
zIndex = 100,
|
|
176
|
+
portalWhenSticky = false,
|
|
177
|
+
stickyStyle = 'fixed',
|
|
178
|
+
stickyCssStyle: userSetStickyCssStyle,
|
|
179
|
+
getPortalContainer,
|
|
180
|
+
getContainer,
|
|
181
|
+
getScrollContainer,
|
|
182
|
+
onStickyStateChange,
|
|
183
|
+
onTopChange,
|
|
184
|
+
} = props;
|
|
185
|
+
const [isSticky, isStickyRef, setIsSticky] = useRefState(false);
|
|
186
|
+
const [wasSticky, wasStickyRef, setWasSticky] = useRefState(false);
|
|
187
|
+
const [stickyStateStyle, setStickyStateStyle] = useState<CSSProperties>({});
|
|
188
|
+
const contentRef = useRef<HTMLDivElement | null>(null);
|
|
189
|
+
const placeholderRef = useRef<HTMLDivElement | null>(null);
|
|
190
|
+
const contentCalculateHeightRef = useRef<number>(0);
|
|
191
|
+
const containerRef = useRef<HTMLElement | null>(null);
|
|
192
|
+
const scrollContainerRef = useRef<HTMLElement | null | Window>(null);
|
|
193
|
+
const framePendingRef = useRef(false);
|
|
194
|
+
|
|
195
|
+
const containerEventHandler = useCallback(
|
|
196
|
+
({ followTop, followBottom }) => {
|
|
197
|
+
if (!contentRef.current) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const needTop = position === 'top' || position === 'both';
|
|
201
|
+
const needBottom = position === 'bottom' || position === 'both';
|
|
202
|
+
|
|
203
|
+
const placeholderClientRect = placeholderRef.current!.getBoundingClientRect();
|
|
204
|
+
const contentClientRect = contentRef.current.getBoundingClientRect();
|
|
205
|
+
const calculatedHeight = contentClientRect.height;
|
|
206
|
+
contentCalculateHeightRef.current = contentClientRect.height;
|
|
207
|
+
const { containerRect } = getScrollContainerRect(scrollContainerRef.current);
|
|
208
|
+
const { top: rectTop, bottom: rectBottom, height: containerHeight } = containerRect;
|
|
209
|
+
const scrollStyle = safeGetComputedStyle(scrollContainerRef.current as HTMLElement);
|
|
210
|
+
const borderTop = needTop
|
|
211
|
+
? convertCssPropertyToNumber(scrollStyle, 'borderTopWidth')
|
|
212
|
+
: 0;
|
|
213
|
+
const borderBottom = needBottom
|
|
214
|
+
? convertCssPropertyToNumber(scrollStyle, 'borderBottomWidth')
|
|
215
|
+
: 0;
|
|
216
|
+
const containerTop = rectTop + borderTop;
|
|
217
|
+
const containerBottom = rectBottom - borderBottom;
|
|
218
|
+
|
|
219
|
+
const disFromTop = Math.round(placeholderClientRect.top - containerTop);
|
|
220
|
+
const disFromBottom = Math.round(
|
|
221
|
+
placeholderClientRect.top + calculatedHeight - containerBottom,
|
|
222
|
+
);
|
|
223
|
+
const topFollowDifference =
|
|
224
|
+
followBottom - followOffset - calculatedHeight - topOffset - containerTop;
|
|
225
|
+
const bottomFollowDifference =
|
|
226
|
+
containerHeight - followTop - followOffset - calculatedHeight - bottomOffset;
|
|
227
|
+
|
|
228
|
+
setWasSticky(Boolean(isStickyRef.current));
|
|
229
|
+
const isTopSticky = needTop
|
|
230
|
+
? disFromTop <= topOffset && followBottom > containerTop + followOffset
|
|
231
|
+
: false;
|
|
232
|
+
const isBottomSticky = needBottom
|
|
233
|
+
? disFromBottom >= -bottomOffset && followTop < containerBottom - followOffset
|
|
234
|
+
: false;
|
|
235
|
+
const newStickyState = isTopSticky || isBottomSticky;
|
|
236
|
+
|
|
237
|
+
let stickyTop = containerTop;
|
|
238
|
+
let stickyBottom = window.innerHeight - containerBottom;
|
|
239
|
+
let stickyLeft = placeholderClientRect.left;
|
|
240
|
+
|
|
241
|
+
if (stickyStyle === 'absolute') {
|
|
242
|
+
const offsetParent = contentRef.current.offsetParent as HTMLElement;
|
|
243
|
+
if (offsetParent) {
|
|
244
|
+
const { containerRect: parentRect } = getScrollContainerRect(offsetParent);
|
|
245
|
+
const parentStyle = safeGetComputedStyle(offsetParent);
|
|
246
|
+
const parentBorderTop = needTop
|
|
247
|
+
? convertCssPropertyToNumber(parentStyle, 'borderTopWidth')
|
|
248
|
+
: 0;
|
|
249
|
+
const parentBorderBottom = needBottom
|
|
250
|
+
? convertCssPropertyToNumber(parentStyle, 'borderBottomWidth')
|
|
251
|
+
: 0;
|
|
252
|
+
const parentBorderLeft = convertCssPropertyToNumber(
|
|
253
|
+
parentStyle,
|
|
254
|
+
'borderLeftWidth',
|
|
255
|
+
);
|
|
256
|
+
stickyTop = containerTop - parentRect.top - parentBorderTop;
|
|
257
|
+
stickyBottom = parentRect.bottom - containerBottom - parentBorderBottom;
|
|
258
|
+
stickyLeft = placeholderClientRect.left - parentRect.left - parentBorderLeft;
|
|
259
|
+
} else {
|
|
260
|
+
stickyTop = 0;
|
|
261
|
+
stickyBottom = 0;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const cssTop = stickyTop + topOffset;
|
|
266
|
+
const cssBottom = stickyBottom + bottomOffset;
|
|
267
|
+
let stickyCssStyle: CSSProperties = {};
|
|
268
|
+
if (newStickyState) {
|
|
269
|
+
stickyCssStyle = {
|
|
270
|
+
transform: 'translateZ(0)',
|
|
271
|
+
WebkitTransform: 'translateZ(0)',
|
|
272
|
+
position: stickyStyle === 'absolute' ? 'absolute' : 'fixed',
|
|
273
|
+
zIndex,
|
|
274
|
+
...(isTopSticky
|
|
275
|
+
? {
|
|
276
|
+
top: topFollowDifference > 0 ? cssTop : cssTop + topFollowDifference,
|
|
277
|
+
}
|
|
278
|
+
: {}),
|
|
279
|
+
...(isBottomSticky
|
|
280
|
+
? {
|
|
281
|
+
bottom:
|
|
282
|
+
bottomFollowDifference > 0
|
|
283
|
+
? cssBottom
|
|
284
|
+
: cssBottom + bottomFollowDifference,
|
|
285
|
+
}
|
|
286
|
+
: {}),
|
|
287
|
+
left: stickyLeft,
|
|
288
|
+
width: placeholderClientRect.width,
|
|
289
|
+
...(userSetStickyCssStyle || {}),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
onTopChange?.(Math.max(0, contentClientRect.top - containerTop - topOffset));
|
|
293
|
+
|
|
294
|
+
setIsSticky(newStickyState);
|
|
295
|
+
setStickyStateStyle(stickyCssStyle);
|
|
296
|
+
|
|
297
|
+
if (newStickyState !== wasStickyRef.current) {
|
|
298
|
+
onStickyStateChange &&
|
|
299
|
+
onStickyStateChange({
|
|
300
|
+
isTopSticky,
|
|
301
|
+
isBottomSticky,
|
|
302
|
+
isSticky: newStickyState,
|
|
303
|
+
wasSticky: wasStickyRef.current,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
[
|
|
308
|
+
position,
|
|
309
|
+
topOffset,
|
|
310
|
+
bottomOffset,
|
|
311
|
+
followOffset,
|
|
312
|
+
zIndex,
|
|
313
|
+
stickyStyle,
|
|
314
|
+
onStickyStateChange,
|
|
315
|
+
onTopChange,
|
|
316
|
+
userSetStickyCssStyle,
|
|
317
|
+
],
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const recalculatePosition = useCallback(() => {
|
|
321
|
+
if (framePendingRef.current) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
execRAF(() => {
|
|
326
|
+
framePendingRef.current = false;
|
|
327
|
+
|
|
328
|
+
if (containerRef.current) {
|
|
329
|
+
const { top, bottom } = containerRef.current.getBoundingClientRect();
|
|
330
|
+
|
|
331
|
+
containerEventHandler({
|
|
332
|
+
followTop: top,
|
|
333
|
+
followBottom: bottom,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
framePendingRef.current = true;
|
|
339
|
+
}, [containerEventHandler]);
|
|
340
|
+
|
|
341
|
+
const updatePlaceholderLayoutInner = useCallback(() => {
|
|
342
|
+
if (placeholderRef.current) {
|
|
343
|
+
const contentHeight = contentCalculateHeightRef.current;
|
|
344
|
+
// 当元素吸顶时,默认有一个占位的元素占住该元素的位置,避免布局产生抖动
|
|
345
|
+
// @en When an element is sticky to the top, a placeholder element occupies the element's position by default to avoid jitter in the layout
|
|
346
|
+
placeholderRef.current.style.height = `${isStickyRef.current ? contentHeight : 0}px`;
|
|
347
|
+
}
|
|
348
|
+
}, []);
|
|
349
|
+
|
|
350
|
+
const updatePlaceholderLayout = useCallback(() => {
|
|
351
|
+
if (contentRef.current) {
|
|
352
|
+
const contentClientRect = contentRef.current.getBoundingClientRect();
|
|
353
|
+
contentCalculateHeightRef.current = contentClientRect.height;
|
|
354
|
+
}
|
|
355
|
+
updatePlaceholderLayoutInner();
|
|
356
|
+
}, []);
|
|
357
|
+
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
const containerEle = getActualContainer(getContainer) as HTMLElement;
|
|
360
|
+
|
|
361
|
+
containerRef.current = containerEle || document.body;
|
|
362
|
+
|
|
363
|
+
const eventTarget = getScrollContainer ? getActualContainer(getScrollContainer) : window;
|
|
364
|
+
if (!eventTarget) return;
|
|
365
|
+
scrollContainerRef.current = eventTarget;
|
|
366
|
+
|
|
367
|
+
const attachEventListeners = target => {
|
|
368
|
+
target.addEventListener('scroll', recalculatePosition);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const removeEventListeners = target => {
|
|
372
|
+
target.removeEventListener('scroll', recalculatePosition);
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
attachEventListeners(eventTarget);
|
|
376
|
+
recalculatePosition();
|
|
377
|
+
|
|
378
|
+
return () => {
|
|
379
|
+
removeEventListeners(eventTarget);
|
|
380
|
+
};
|
|
381
|
+
}, [getContainer, getScrollContainer, recalculatePosition]);
|
|
382
|
+
|
|
383
|
+
useEffect(() => {
|
|
384
|
+
updatePlaceholderLayoutInner();
|
|
385
|
+
}, [isSticky, wasSticky]);
|
|
386
|
+
|
|
387
|
+
useImperativeHandle(
|
|
388
|
+
ref,
|
|
389
|
+
() => ({
|
|
390
|
+
dom: contentRef.current,
|
|
391
|
+
recalculatePosition,
|
|
392
|
+
updatePlaceholderLayout,
|
|
393
|
+
}),
|
|
394
|
+
[recalculatePosition, updatePlaceholderLayout],
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
const computedStyle = useMemo(
|
|
398
|
+
() => ({ ...(style || {}), ...stickyStateStyle }),
|
|
399
|
+
[stickyStateStyle, style],
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
function renderSticky(prefixCls?: string) {
|
|
403
|
+
return (
|
|
404
|
+
<div
|
|
405
|
+
className={cls(`${prefixCls}-sticky`, className)}
|
|
406
|
+
ref={contentRef}
|
|
407
|
+
style={computedStyle}
|
|
408
|
+
>
|
|
409
|
+
{children}
|
|
410
|
+
</div>
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return (
|
|
415
|
+
<ContextLayout>
|
|
416
|
+
{({ prefixCls }) => (
|
|
417
|
+
<div>
|
|
418
|
+
<div ref={placeholderRef} />
|
|
419
|
+
{portalWhenSticky && isSticky ? (
|
|
420
|
+
<Portal getContainer={getPortalContainer}>{renderSticky(prefixCls)}</Portal>
|
|
421
|
+
) : (
|
|
422
|
+
renderSticky(prefixCls)
|
|
423
|
+
)}
|
|
424
|
+
</div>
|
|
425
|
+
)}
|
|
426
|
+
</ContextLayout>
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
export default Sticky;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
### 反馈
|
|
2
|
+
|
|
3
|
+
# 滑动操作 SwipeAction
|
|
4
|
+
|
|
5
|
+
滑动操作组件,左右滑动拉出菜单栏
|
|
6
|
+
|
|
7
|
+
======
|
|
8
|
+
|
|
9
|
+
> 属性/Props
|
|
10
|
+
|
|
11
|
+
|参数|描述|类型|默认值|
|
|
12
|
+
|----------|-------------|------|------|
|
|
13
|
+
|className|自定义类名|string|-|
|
|
14
|
+
|style|自定义样式|CSSProperties|-|
|
|
15
|
+
|children|子元素|ReactNode|必填|
|
|
16
|
+
|rightActions|右侧菜单|Action\[\]|-|
|
|
17
|
+
|leftActions|左侧菜单|Action\[\]|-|
|
|
18
|
+
|closeOnTouchOutside|点击外部是否自动归位|boolean|false|
|
|
19
|
+
|threshold|菜单滑出多少距离后自动滑出/关闭的系数 范围 0\-1|number|0.15|
|
|
20
|
+
|disabled|是否禁用|boolean|false|
|
|
21
|
+
|transitionDuration|动画的执行时间 (单位ms)|number|300|
|
|
22
|
+
|dampRate|阻尼系数|number|15|
|
|
23
|
+
|openStyleType|菜单滑入时的样式类型,layer \- 分层堆叠进入,push \- 直接依次推入|"layer" \| "push"|"layer"|
|
|
24
|
+
|onOpen|滑出动画结束后触发|(direction: "left" \| "right") =\> void|-|
|
|
25
|
+
|onClose|滑入动画结束后触发|(direction: "left" \| "right") =\> void|-|
|
|
26
|
+
|
|
27
|
+
> 引用/Refs
|
|
28
|
+
|
|
29
|
+
|参数|描述|类型|
|
|
30
|
+
|----------|-------------|------|
|
|
31
|
+
|dom|最外层元素 DOM|HTMLDivElement|
|
|
32
|
+
|close|使滑动条归位|() =\> void|
|
|
33
|
+
|open|打开滑动条,默认打开右边|(direction: "left" \| "right") =\> void|
|
|
34
|
+
|
|
35
|
+
> Action
|
|
36
|
+
|
|
37
|
+
|参数|描述|类型|
|
|
38
|
+
|----------|-------------|------|
|
|
39
|
+
|text|自定义内容|ReactNode|
|
|
40
|
+
|icon|自定义图标|ReactNode|
|
|
41
|
+
|style|自定义样式|CSSProperties|
|
|
42
|
+
|className|自定义类名|string|
|
|
43
|
+
|onClick|点击事件 返回true可以阻止菜单的关闭|() =\> boolean \| void \| Promise\<boolean \| void\>|
|
|
44
|
+
|children|子元素没有text或者icon的传递children|ReactNode|
|