@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.
Files changed (191) hide show
  1. package/README.md +85 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +244 -0
  4. package/metaData/components/action-sheet/doc.md +102 -0
  5. package/metaData/components/action-sheet/example/index.tsx +199 -0
  6. package/metaData/components/action-sheet/example/methods.tsx +6 -0
  7. package/metaData/components/avatar/doc.md +57 -0
  8. package/metaData/components/avatar/example/group.tsx +69 -0
  9. package/metaData/components/avatar/example/index.tsx +196 -0
  10. package/metaData/components/badge/doc.md +28 -0
  11. package/metaData/components/badge/example/index.tsx +128 -0
  12. package/metaData/components/button/doc.md +45 -0
  13. package/metaData/components/button/example/index.tsx +264 -0
  14. package/metaData/components/carousel/doc.md +89 -0
  15. package/metaData/components/carousel/example/index.tsx +1481 -0
  16. package/metaData/components/cell/doc.md +68 -0
  17. package/metaData/components/cell/example/arrow.tsx +13 -0
  18. package/metaData/components/cell/example/cell.tsx +71 -0
  19. package/metaData/components/cell/example/group.tsx +63 -0
  20. package/metaData/components/cell/example/index.tsx +19 -0
  21. package/metaData/components/checkbox/doc.md +91 -0
  22. package/metaData/components/checkbox/example/checkbox.tsx +158 -0
  23. package/metaData/components/checkbox/example/group.tsx +103 -0
  24. package/metaData/components/checkbox/example/index.tsx +25 -0
  25. package/metaData/components/circle-progress/doc.md +40 -0
  26. package/metaData/components/circle-progress/example/index.tsx +323 -0
  27. package/metaData/components/collapse/doc.md +74 -0
  28. package/metaData/components/collapse/example/collapse.tsx +183 -0
  29. package/metaData/components/collapse/example/group.tsx +136 -0
  30. package/metaData/components/collapse/example/index.tsx +16 -0
  31. package/metaData/components/collapse/example/utils.tsx +20 -0
  32. package/metaData/components/context-provider/doc.md +44 -0
  33. package/metaData/components/context-provider/example/index.tsx +208 -0
  34. package/metaData/components/count-down/doc.md +40 -0
  35. package/metaData/components/count-down/example/hooks.tsx +118 -0
  36. package/metaData/components/count-down/example/index.tsx +63 -0
  37. package/metaData/components/date-picker/doc.md +102 -0
  38. package/metaData/components/date-picker/example/index.tsx +545 -0
  39. package/metaData/components/dialog/doc.md +200 -0
  40. package/metaData/components/dialog/example/index.tsx +328 -0
  41. package/metaData/components/dialog/example/methods.tsx +56 -0
  42. package/metaData/components/divider/doc.md +25 -0
  43. package/metaData/components/divider/example/index.tsx +87 -0
  44. package/metaData/components/dropdown/doc.md +80 -0
  45. package/metaData/components/dropdown/example/dropdown.tsx +308 -0
  46. package/metaData/components/dropdown/example/index.tsx +19 -0
  47. package/metaData/components/dropdown/example/options.tsx +103 -0
  48. package/metaData/components/dropdown-menu/doc.md +91 -0
  49. package/metaData/components/dropdown-menu/example/dropdown-menu.tsx +270 -0
  50. package/metaData/components/dropdown-menu/example/index.tsx +16 -0
  51. package/metaData/components/ellipsis/doc.md +33 -0
  52. package/metaData/components/ellipsis/example/index.tsx +93 -0
  53. package/metaData/components/ellipsis/example/js-ellipsis.tsx +229 -0
  54. package/metaData/components/ellipsis/example/native-ellipsis.tsx +48 -0
  55. package/metaData/components/form/doc.md +90 -0
  56. package/metaData/components/form/example/form-item-context.tsx +8 -0
  57. package/metaData/components/form/example/form-item.tsx +378 -0
  58. package/metaData/components/form/example/index.tsx +87 -0
  59. package/metaData/components/form/example/linked-container.tsx +66 -0
  60. package/metaData/components/grid/doc.md +39 -0
  61. package/metaData/components/grid/example/index.tsx +252 -0
  62. package/metaData/components/icon/doc.md +15 -0
  63. package/metaData/components/icon/example/index.tsx +32 -0
  64. package/metaData/components/image/doc.md +62 -0
  65. package/metaData/components/image/example/index.tsx +528 -0
  66. package/metaData/components/image-picker/doc.md +116 -0
  67. package/metaData/components/image-picker/example/add-icon.tsx +14 -0
  68. package/metaData/components/image-picker/example/index.tsx +175 -0
  69. package/metaData/components/image-preview/doc.md +119 -0
  70. package/metaData/components/image-preview/example/index.tsx +1290 -0
  71. package/metaData/components/image-preview/example/methods.tsx +72 -0
  72. package/metaData/components/index-bar/doc.md +83 -0
  73. package/metaData/components/index-bar/example/group.tsx +93 -0
  74. package/metaData/components/index-bar/example/index.tsx +271 -0
  75. package/metaData/components/index-bar/example/side-bar.tsx +82 -0
  76. package/metaData/components/input/doc.md +60 -0
  77. package/metaData/components/input/example/hooks.tsx +259 -0
  78. package/metaData/components/input/example/index.tsx +134 -0
  79. package/metaData/components/input/example/props.tsx +183 -0
  80. package/metaData/components/keyboard/doc.md +73 -0
  81. package/metaData/components/keyboard/example/index.tsx +238 -0
  82. package/metaData/components/load-more/doc.md +48 -0
  83. package/metaData/components/load-more/example/index.tsx +395 -0
  84. package/metaData/components/loading/doc.md +34 -0
  85. package/metaData/components/loading/example/index.tsx +312 -0
  86. package/metaData/components/masking/doc.md +93 -0
  87. package/metaData/components/masking/example/index.tsx +410 -0
  88. package/metaData/components/masking/example/methods.tsx +88 -0
  89. package/metaData/components/nav-bar/doc.md +40 -0
  90. package/metaData/components/nav-bar/example/back-icon.tsx +28 -0
  91. package/metaData/components/nav-bar/example/index.tsx +305 -0
  92. package/metaData/components/notice-bar/doc.md +34 -0
  93. package/metaData/components/notice-bar/example/index.tsx +294 -0
  94. package/metaData/components/notify/doc.md +81 -0
  95. package/metaData/components/notify/example/index.tsx +171 -0
  96. package/metaData/components/notify/example/methods.tsx +83 -0
  97. package/metaData/components/pagination/doc.md +41 -0
  98. package/metaData/components/pagination/example/arrow.tsx +14 -0
  99. package/metaData/components/pagination/example/index.tsx +292 -0
  100. package/metaData/components/picker/doc.md +89 -0
  101. package/metaData/components/picker/example/index.tsx +217 -0
  102. package/metaData/components/picker-view/doc.md +57 -0
  103. package/metaData/components/picker-view/example/cascader.tsx +141 -0
  104. package/metaData/components/picker-view/example/index.tsx +297 -0
  105. package/metaData/components/picker-view/example/multi-picker.tsx +66 -0
  106. package/metaData/components/picker-view/example/picker-cell.tsx +416 -0
  107. package/metaData/components/popover/doc.md +144 -0
  108. package/metaData/components/popover/example/index.tsx +23 -0
  109. package/metaData/components/popover/example/menu.tsx +145 -0
  110. package/metaData/components/popover/example/popover-inner.tsx +147 -0
  111. package/metaData/components/popover/example/popover.tsx +268 -0
  112. package/metaData/components/popup/doc.md +101 -0
  113. package/metaData/components/popup/example/index.tsx +125 -0
  114. package/metaData/components/popup/example/methods.tsx +6 -0
  115. package/metaData/components/popup-swiper/doc.md +108 -0
  116. package/metaData/components/popup-swiper/example/index.tsx +332 -0
  117. package/metaData/components/popup-swiper/example/methods.tsx +6 -0
  118. package/metaData/components/portal/doc.md +14 -0
  119. package/metaData/components/portal/example/index.tsx +35 -0
  120. package/metaData/components/progress/doc.md +42 -0
  121. package/metaData/components/progress/example/index.tsx +256 -0
  122. package/metaData/components/pull-refresh/doc.md +37 -0
  123. package/metaData/components/pull-refresh/example/android-pull-refresh.tsx +260 -0
  124. package/metaData/components/pull-refresh/example/index.tsx +44 -0
  125. package/metaData/components/pull-refresh/example/ios-pull-refresh.tsx +216 -0
  126. package/metaData/components/radio/doc.md +89 -0
  127. package/metaData/components/radio/example/group.tsx +92 -0
  128. package/metaData/components/radio/example/index.tsx +25 -0
  129. package/metaData/components/radio/example/radio.tsx +22 -0
  130. package/metaData/components/rate/doc.md +33 -0
  131. package/metaData/components/rate/example/index.tsx +281 -0
  132. package/metaData/components/search-bar/doc.md +97 -0
  133. package/metaData/components/search-bar/example/association.tsx +89 -0
  134. package/metaData/components/search-bar/example/cancel-button.tsx +34 -0
  135. package/metaData/components/search-bar/example/highlight.tsx +66 -0
  136. package/metaData/components/search-bar/example/index.tsx +251 -0
  137. package/metaData/components/show-monitor/doc.md +34 -0
  138. package/metaData/components/show-monitor/example/index.tsx +646 -0
  139. package/metaData/components/skeleton/doc.md +101 -0
  140. package/metaData/components/skeleton/example/elements.tsx +290 -0
  141. package/metaData/components/skeleton/example/index.tsx +96 -0
  142. package/metaData/components/skeleton/example/skeleton-context.tsx +7 -0
  143. package/metaData/components/slider/doc.md +42 -0
  144. package/metaData/components/slider/example/index.tsx +326 -0
  145. package/metaData/components/slider/example/marks.tsx +50 -0
  146. package/metaData/components/slider/example/popover.tsx +38 -0
  147. package/metaData/components/slider/example/thumb.tsx +48 -0
  148. package/metaData/components/slider/example/useSliderIcon.tsx +38 -0
  149. package/metaData/components/slider/example/useSliderInit.tsx +170 -0
  150. package/metaData/components/stepper/doc.md +46 -0
  151. package/metaData/components/stepper/example/index.tsx +166 -0
  152. package/metaData/components/stepper/example/useButtonClick.tsx +74 -0
  153. package/metaData/components/stepper/example/useInputEvent.tsx +65 -0
  154. package/metaData/components/stepper/example/useValue.tsx +43 -0
  155. package/metaData/components/steps/doc.md +57 -0
  156. package/metaData/components/steps/example/index.tsx +123 -0
  157. package/metaData/components/steps/example/step.tsx +126 -0
  158. package/metaData/components/sticky/doc.md +45 -0
  159. package/metaData/components/sticky/example/index.tsx +430 -0
  160. package/metaData/components/swipe-action/doc.md +44 -0
  161. package/metaData/components/swipe-action/example/index.tsx +338 -0
  162. package/metaData/components/swipe-action/example/item.tsx +40 -0
  163. package/metaData/components/swipe-load/doc.md +43 -0
  164. package/metaData/components/swipe-load/example/index.tsx +339 -0
  165. package/metaData/components/swipe-load/example/type.tsx +153 -0
  166. package/metaData/components/switch/doc.md +38 -0
  167. package/metaData/components/switch/example/index.tsx +237 -0
  168. package/metaData/components/tab-bar/doc.md +57 -0
  169. package/metaData/components/tab-bar/example/index.tsx +18 -0
  170. package/metaData/components/tab-bar/example/item.tsx +74 -0
  171. package/metaData/components/tab-bar/example/tab-bar.tsx +105 -0
  172. package/metaData/components/tabs/doc.md +187 -0
  173. package/metaData/components/tabs/example/index.tsx +514 -0
  174. package/metaData/components/tabs/example/tab-cell-underline.tsx +320 -0
  175. package/metaData/components/tabs/example/tab-cell.tsx +433 -0
  176. package/metaData/components/tabs/example/tab-pane.tsx +349 -0
  177. package/metaData/components/tag/doc.md +80 -0
  178. package/metaData/components/tag/example/index.tsx +23 -0
  179. package/metaData/components/tag/example/list.tsx +97 -0
  180. package/metaData/components/tag/example/tag.tsx +85 -0
  181. package/metaData/components/textarea/doc.md +67 -0
  182. package/metaData/components/textarea/example/index.tsx +258 -0
  183. package/metaData/components/toast/doc.md +90 -0
  184. package/metaData/components/toast/example/index.tsx +335 -0
  185. package/metaData/components/toast/example/methods.tsx +87 -0
  186. package/metaData/components/transition/doc.md +19 -0
  187. package/metaData/components/transition/example/index.tsx +113 -0
  188. package/metaData/components/uploader/doc.md +82 -0
  189. package/metaData/components/uploader/example/index.tsx +208 -0
  190. package/metaData/components-index.json +242 -0
  191. package/package.json +56 -0
@@ -0,0 +1,646 @@
1
+ import React, {
2
+ useRef,
3
+ forwardRef,
4
+ Ref,
5
+ useImperativeHandle,
6
+ useEffect,
7
+ useCallback,
8
+ useMemo,
9
+ } from 'react';
10
+ import lodashThrottle from 'lodash.throttle';
11
+ import {
12
+ scrollParent,
13
+ checkOverflowVisible,
14
+ checkNormalVisible,
15
+ nextTick,
16
+ cls,
17
+ } from '@arco-design/mobile-utils';
18
+ import { ContextLayout } from '../context-provider';
19
+
20
+ export interface ShowMonitorProps {
21
+ /**
22
+ * 自定义类名
23
+ * @en Custom classname
24
+ */
25
+ className?: string;
26
+ /**
27
+ * 自定义样式
28
+ * @en Custom stylesheet
29
+ */
30
+ style?: React.CSSProperties;
31
+ /**
32
+ * 滚动视口选择器
33
+ * @en Scroll viewport selector
34
+ * @default () => window
35
+ */
36
+ getScrollContainer?: () => HTMLElement | Window | null;
37
+ /**
38
+ * 节流粒度(ms)
39
+ * @en Throttle granularity(ms)
40
+ * @default 300
41
+ */
42
+ throttle?: number;
43
+ /**
44
+ * 是否监听 resize
45
+ * @en Whether to monitor resize
46
+ * @default true
47
+ */
48
+ listenResize?: boolean;
49
+ /**
50
+ * 是否监听 scroll
51
+ * @en Whether to monitor scroll
52
+ * @default true
53
+ */
54
+ listenScroll?: boolean;
55
+ /**
56
+ * 只监听变化一次,为 true 时仅在进入视口时触发一次 onVisibleChange
57
+ * @en Only listen for changes once, if true, only trigger onVisibleChange once when entering the viewport
58
+ * @default false
59
+ */
60
+ once?: boolean;
61
+ /**
62
+ * 是否为局部滚动
63
+ * @en Whether it is a local scroll
64
+ * @default false
65
+ */
66
+ overflow?: boolean;
67
+ /**
68
+ * 元素进入视口区域触发回调比例,[0-1]
69
+ * @en Threshold of callback triggered when element enters viewport area, [0-1]
70
+ * @default 0
71
+ */
72
+ threshold?: number;
73
+ /**
74
+ * 预加载提前量 [offsetTop, offsetRight, offsetBottom, offsetLeft]。
75
+ * 如果类型为 number,等效为[number, number, number, number];
76
+ * 如果类型为 [number1, number2], 等效为[number1, 0, number2, 0];
77
+ * 支持 Intersection Observer 的浏览器需要搭配 getScrollContainer, 设置父级容器
78
+ * @en Preload advance amount [offsetTop, offsetRight, offsetBottom, offsetLeft].
79
+ * @en If the type is number, it is equivalent to [number, number, number, number];
80
+ * @en If the type is [number1, number2], it is equivalent to [number1, 0, number2, 0];
81
+ * @en Browsers that support Intersection Observer need to use getScrollContainer to set the parent container
82
+ * @default 0
83
+ */
84
+ offset?: number | [number, number] | [number, number, number, number];
85
+ /**
86
+ * 子节点
87
+ * @en Children nodes
88
+ */
89
+ children: React.ReactNode;
90
+ /**
91
+ * 是否禁用监听
92
+ * @en Whether to disable monitoring
93
+ * @default false
94
+ */
95
+ disabled?: boolean;
96
+ /**
97
+ * 进入或离开视口的回调函数
98
+ * @en Callback for entering or leaving the viewport
99
+ */
100
+ onVisibleChange: (visible: boolean, node: HTMLDivElement) => void;
101
+ /**
102
+ * 点击组件的回调事件
103
+ * @en Callback when clicked
104
+ */
105
+ onClick?: (e: React.MouseEvent) => void;
106
+ }
107
+
108
+ export interface ShowMonitorRef {
109
+ /**
110
+ * 最外层元素 DOM
111
+ * @en The outermost element DOM
112
+ */
113
+ dom: HTMLDivElement | null;
114
+ /**
115
+ * 忽略元素前后状态,手动检查元素是否在视口内,触发onVisibleChange回调函数
116
+ * @en Ignore the state of the element before and after, manually check whether the element is in the viewport, trigger the onVisibleChange callback function
117
+ */
118
+ checkVisible: () => boolean;
119
+ /**
120
+ * 重置元素初始可见态为false,并重新对元素可见度发起检测,优先级低于disabled(通常用在对ShowMonitor内部元素变化时发起的重新监听)
121
+ * @en Reset the initial visible state of the element to false, and re-detect the visibility of the element, the priority is lower than 'disabled'(Usually used to re-listen when elements inside ShowMonitor change)
122
+ */
123
+ flushVisibleStatus: () => void;
124
+ }
125
+
126
+ type TListenerEntity = {
127
+ node: HTMLDivElement;
128
+ isVisible: boolean;
129
+ overflow: boolean;
130
+ once: boolean;
131
+ offset: number | [number, number] | [number, number, number, number];
132
+ threshold: number;
133
+ } & Pick<ShowMonitorProps, 'onVisibleChange'>;
134
+
135
+ type TListenerEntityList = {
136
+ [key: string]: TListenerEntity[];
137
+ };
138
+
139
+ const LISTEN_FLAG = 'data-show-listened';
140
+ /**
141
+ * 父 dom 节点集合
142
+ * @en Parent dom node collection
143
+ */
144
+ const wrapperNodeList: (HTMLElement | Window)[] = [];
145
+ /**
146
+ * 同一父节点下监听队列
147
+ * @en Listening queue under the same parent node
148
+ */
149
+ const listeners: TListenerEntityList = {};
150
+ const onOnceEmittedListeners: TListenerEntityList = {};
151
+ /**
152
+ * Intersection Observer 同一 root 节点下的监听队列
153
+ * @en Intersection Observer Listening queue under the same root node
154
+ */
155
+ const ioListeners: {
156
+ root: HTMLElement;
157
+ key: string;
158
+ listener: IntersectionObserver;
159
+ }[] = [];
160
+ /**
161
+ * Intersection Observer 监听 visible 状态队列
162
+ * @en Intersection Observer listens to the visible status queue
163
+ */
164
+ const ioVisibleList: ({
165
+ node: HTMLDivElement;
166
+ isVisible: boolean;
167
+ once: boolean;
168
+ } & Pick<ShowMonitorProps, 'onVisibleChange'>)[] = [];
169
+ let throttlingVisibleChange: () => void;
170
+
171
+ /**
172
+ * 通过滚动事件监测 children 是否进入视口或离开视口。
173
+ * @en Use scroll events to monitor whether children enter or leave the viewport.
174
+ * @type 其他
175
+ * @type_en Others
176
+ * @name 滚动视口监听
177
+ * @name_en ShowMonitor
178
+ */
179
+ const ShowMonitor = forwardRef((props: ShowMonitorProps, ref: Ref<ShowMonitorRef>) => {
180
+ const {
181
+ className,
182
+ style,
183
+ getScrollContainer,
184
+ throttle = 300,
185
+ listenResize = true,
186
+ listenScroll = true,
187
+ overflow = false,
188
+ offset = 0,
189
+ threshold = 0,
190
+ once = false,
191
+ children = null,
192
+ disabled = false,
193
+ onVisibleChange,
194
+ onClick,
195
+ } = props;
196
+ const domRef = useRef<HTMLDivElement | null>(null);
197
+ /**
198
+ * 滚动容器父级元素
199
+ * @en Scroll container parent element
200
+ */
201
+ const scrollPort = useRef<HTMLElement | Window | null>(null);
202
+ /**
203
+ * 局部滚动元素父节点
204
+ * @en Parent node of the local scroll element
205
+ */
206
+ const domRefParent = useRef<HTMLElement | Document | null>(null);
207
+ /**
208
+ * 保存当前节点信息,类似于 class component 中 this
209
+ * @en Save current node information, similar to this in class component
210
+ */
211
+ const listener = useRef<TListenerEntity | null>(null);
212
+ /**
213
+ * Intersection Observer 实例化对象
214
+ * @en Instantiated object of Intersection Observer
215
+ */
216
+ const io = useRef<IntersectionObserver | null>(null);
217
+ /**
218
+ * 父容器唯一 key(wrapperNodeList index)
219
+ * @en The unique key of the parent container (wrapperNodeList index)
220
+ */
221
+ const wrapperKey = useRef<number>(-1);
222
+ const isChildrenExist = useRef(Boolean(children));
223
+ const isSupportNativeApi = useMemo(() => typeof IntersectionObserver === 'function', []); // 是否支持 Intersection Observer
224
+
225
+ useImperativeHandle(ref, () => ({
226
+ dom: domRef.current,
227
+ /**
228
+ * 提供检查元素是否可见的api
229
+ * @en Provides an api to check whether the element is visible
230
+ */
231
+ checkVisible: () =>
232
+ checkVisible(
233
+ {
234
+ node: domRef.current!,
235
+ overflow,
236
+ onVisibleChange,
237
+ offset,
238
+ threshold,
239
+ } as TListenerEntity,
240
+ true,
241
+ ) as boolean,
242
+ /**
243
+ * 重置元素初始可见态为false,并重新对元素可见度发起检测,优先级低于disabled(通常用在对ShowMonitor内部元素变化时发起的重新监听)
244
+ * @en Reset the initial visible state of the element to false, and re-detect the visibility of the element, the priority is lower than 'disabled'(Usually used to re-listen when elements inside ShowMonitor change)
245
+ */
246
+ flushVisibleStatus() {
247
+ if (isSupportNativeApi) {
248
+ disabled ? ioUnobserve() : ioObserve();
249
+ } else if (listener.current) {
250
+ const key = wrapperKey.current;
251
+ if (once && onOnceEmittedListeners?.[key]) {
252
+ onOnceEmittedListeners[key] = onOnceEmittedListeners[key].filter(
253
+ emitListener => emitListener !== listener.current,
254
+ );
255
+ }
256
+ if (
257
+ !disabled &&
258
+ !listeners[key].find(_listener => _listener === listener.current)
259
+ ) {
260
+ listeners[key].push(listener.current);
261
+ }
262
+ !disabled && checkVisible(listener.current);
263
+ }
264
+ },
265
+ }));
266
+
267
+ function checkVisible(component: TListenerEntity, ignoreCheckPreVisibleStatus = false) {
268
+ const {
269
+ node,
270
+ overflow: compOverflow,
271
+ once: compOnce,
272
+ onVisibleChange: onCompVisibleChange,
273
+ isVisible: preVisible,
274
+ } = component;
275
+ if (!(node instanceof HTMLElement)) {
276
+ return;
277
+ }
278
+ const parent = scrollParent(node);
279
+ /**
280
+ * 当前元素为非 document 元素,即为局部滚动
281
+ * @en If the current element is a non-document element, it's local scrolling
282
+ */
283
+ const isOverflow =
284
+ compOverflow &&
285
+ [node.ownerDocument, document, document.documentElement].indexOf(parent!) === -1;
286
+ /**
287
+ * 检查元素是否可见
288
+ * @en Check if element is visible
289
+ */
290
+ const curVisible = isOverflow
291
+ ? checkOverflowVisible<TListenerEntity>(component, parent)
292
+ : checkNormalVisible<TListenerEntity>(component);
293
+ /**
294
+ * 忽略前后可见状态判断
295
+ * @en Ignore the visible state judgment before and after
296
+ */
297
+ if (ignoreCheckPreVisibleStatus) {
298
+ return curVisible;
299
+ }
300
+ /**
301
+ * 当前元素 visible 对比之前发生改变,触发回调函数
302
+ * @en The current element visible is changed before the comparison, and the callback function is triggered
303
+ */
304
+ curVisible !== preVisible &&
305
+ handleCheckChildrenExist() &&
306
+ onCompVisibleChange(curVisible, node);
307
+ component.isVisible = curVisible;
308
+ const key = wrapperKey.current;
309
+ /**
310
+ * 监听一次后加入 pendingList 队列,随后被 listeners 过滤掉
311
+ * @en After listening once, it is added to the pendingList queue, and then filtered out by listeners
312
+ */
313
+ if (!onOnceEmittedListeners[key]) {
314
+ onOnceEmittedListeners[key] = [];
315
+ }
316
+ /**
317
+ * 当前元素不可见 -> 可见,且 once, 触发回调函数
318
+ * @en The current element is invisible -> visible, and once, triggers the callback function
319
+ */
320
+ curVisible && !preVisible && compOnce && onOnceEmittedListeners[key].push(component);
321
+ }
322
+
323
+ const checkVisibleHandler = useCallback(() => {
324
+ const key = wrapperKey.current;
325
+ const curListeners = listeners[key] || [];
326
+ curListeners.forEach(listen => checkVisible(listen));
327
+ listeners[key] = curListeners.filter(
328
+ item => onOnceEmittedListeners[key].indexOf(item) === -1,
329
+ );
330
+ onOnceEmittedListeners[key] = [];
331
+ }, []);
332
+
333
+ function handleCheckChildrenExist(target?: Element) {
334
+ const dom = target || domRef.current;
335
+ return isChildrenExist.current && dom?.children.length;
336
+ }
337
+
338
+ function handleObserverStatusChange(entries: IntersectionObserverEntry[]) {
339
+ entries.forEach(entry => {
340
+ const { isIntersecting, target } = entry;
341
+ const visibleItem = ioVisibleList.find(item => item.node === target);
342
+ if (visibleItem) {
343
+ const {
344
+ isVisible: curVisible,
345
+ onVisibleChange: onCompVisibleChange,
346
+ once: onceEmit,
347
+ } = visibleItem;
348
+ /**
349
+ * 当前元素 visible 对比之前发生改变,触发回调函数
350
+ * @en Callback when the visible status of current element changes before the comparison
351
+ */
352
+ isIntersecting !== curVisible &&
353
+ handleCheckChildrenExist(target) &&
354
+ onCompVisibleChange(isIntersecting, target as HTMLDivElement);
355
+ /**
356
+ * 当前元素状态由不可见变为可见,且只触发一次
357
+ * @en The current element is invisible -> visible, and once, triggers the callback
358
+ */
359
+ isIntersecting &&
360
+ !curVisible &&
361
+ onceEmit &&
362
+ target &&
363
+ ioUnobserve(target as HTMLDivElement);
364
+ visibleItem.isVisible = isIntersecting;
365
+ }
366
+ });
367
+ }
368
+
369
+ /**
370
+ * 获取 io 单例
371
+ * @en Get the io singleton
372
+ */
373
+ function getIOSingleton(ioOptions: {
374
+ root: HTMLElement;
375
+ rootMargin: string;
376
+ threshold: number;
377
+ }) {
378
+ const { root, rootMargin, threshold: ioThreshold } = ioOptions;
379
+ const ioKey = JSON.stringify({ rootMargin, threshold: ioThreshold });
380
+
381
+ const _ioListener = ioListeners.find(
382
+ ioListener => ioListener.root === root && ioListener.key === ioKey,
383
+ );
384
+
385
+ if (!_ioListener) {
386
+ ioListeners.push({
387
+ root,
388
+ key: ioKey,
389
+ listener: (io.current = new IntersectionObserver(
390
+ handleObserverStatusChange,
391
+ ioOptions,
392
+ )),
393
+ });
394
+ } else {
395
+ io.current = _ioListener.listener;
396
+ }
397
+ }
398
+
399
+ function ioObserve() {
400
+ if (domRef.current && io.current) {
401
+ const curIdx = ioVisibleList.findIndex(
402
+ ioVisibleItem => ioVisibleItem.node === domRef.current,
403
+ );
404
+ if (curIdx !== -1) {
405
+ ioVisibleList[curIdx].isVisible = false;
406
+ } else {
407
+ ioVisibleList.push({
408
+ node: domRef.current,
409
+ isVisible: false,
410
+ once,
411
+ onVisibleChange,
412
+ });
413
+ }
414
+ io.current.observe(domRef.current);
415
+ }
416
+ }
417
+
418
+ function ioUnobserve(target?: HTMLDivElement) {
419
+ const targetNode = target || domRef.current;
420
+ if (io.current && targetNode) {
421
+ const curIdx = ioVisibleList.findIndex(
422
+ ioVisibleItem => ioVisibleItem.node === targetNode,
423
+ );
424
+ if (curIdx !== -1) {
425
+ ioVisibleList.splice(curIdx, 1);
426
+ }
427
+ io.current.unobserve(targetNode!);
428
+ }
429
+ }
430
+
431
+ /**
432
+ * 非首次下disabled变化时,重新监听元素
433
+ * @en Re-listen to the element when it is not the first time that the disabled status changes,
434
+ */
435
+ useEffect(() => {
436
+ if (isSupportNativeApi) {
437
+ // 非首次 render 下
438
+ // @en Not for the first time
439
+ disabled ? ioUnobserve() : ioObserve();
440
+ } else {
441
+ // 非首次 render 下
442
+ // @en Not for the first time
443
+ if (wrapperKey.current !== -1 && listener.current && listeners[wrapperKey.current]) {
444
+ // 禁用监听,找到对应listener并删除
445
+ // @en Disable listening, find the corresponding listener and delete it
446
+ if (disabled) {
447
+ const isExist = listeners[wrapperKey.current].findIndex(
448
+ item => item.node === domRef.current,
449
+ );
450
+ if (isExist !== -1) {
451
+ listeners[wrapperKey.current].splice(0, 1);
452
+ }
453
+ } else {
454
+ // 开启监听
455
+ // @en Start to listen
456
+ listeners[wrapperKey.current].push(listener.current);
457
+ }
458
+ }
459
+ }
460
+ }, [disabled]);
461
+
462
+ useEffect(() => {
463
+ isChildrenExist.current = Boolean(children);
464
+ }, [children]);
465
+
466
+ useEffect(() => {
467
+ scrollPort.current = getScrollContainer ? getScrollContainer() : window;
468
+ if (!scrollPort.current) return;
469
+ const wrapperNodeIndex = wrapperNodeList.indexOf(scrollPort.current);
470
+ if (wrapperNodeIndex === -1) {
471
+ // 当前滚动视口元素不在 wrapperNodeList 中, 入队列
472
+ // @en The current scroll viewport element is not in the wrapperNodeList, push it into the list
473
+ wrapperNodeList.push(scrollPort.current);
474
+ wrapperKey.current = wrapperNodeList.length - 1;
475
+ } else {
476
+ // 取当前 index 作为 key
477
+ // @en Take the current index as the key
478
+ wrapperKey.current = wrapperNodeIndex;
479
+ }
480
+ const curWrapperKey = wrapperKey.current;
481
+ if (isSupportNativeApi) {
482
+ if (domRef.current) {
483
+ domRefParent.current = scrollParent(domRef.current);
484
+ const parent = domRefParent.current;
485
+ const isHTMLElement =
486
+ [domRef.current.ownerDocument, document, document.documentElement].indexOf(
487
+ parent!,
488
+ ) === -1;
489
+ const overflowRoot = isHTMLElement ? parent : null;
490
+ const root = (
491
+ overflow
492
+ ? overflowRoot
493
+ : scrollPort.current === window
494
+ ? null
495
+ : scrollPort.current
496
+ ) as HTMLElement;
497
+ let rootMargin = '';
498
+ if (Array.isArray(offset)) {
499
+ if (offset.length === 2) {
500
+ rootMargin = `${offset[0]}px 0px ${offset[1]}px`;
501
+ } else {
502
+ offset.forEach((val, index) => {
503
+ const space = index === 0 ? '' : ' ';
504
+ rootMargin += `${space}${val}px`;
505
+ });
506
+ }
507
+ } else {
508
+ rootMargin = `${offset}px`;
509
+ }
510
+
511
+ getIOSingleton({
512
+ root,
513
+ rootMargin,
514
+ threshold,
515
+ });
516
+
517
+ !disabled && ioObserve();
518
+ }
519
+ } else {
520
+ // 节流后回调函数
521
+ // @en Callback after throttling
522
+ throttlingVisibleChange = checkVisibleHandler;
523
+ throttle &&
524
+ (throttlingVisibleChange = lodashThrottle(throttlingVisibleChange, throttle));
525
+ if (overflow) {
526
+ if (domRef.current) {
527
+ domRefParent.current = scrollParent(domRef.current);
528
+ const parent = domRefParent.current;
529
+ if (
530
+ parent &&
531
+ parent instanceof HTMLElement &&
532
+ typeof parent.getAttribute === 'function'
533
+ ) {
534
+ const listenerCount =
535
+ 1 + Number(parent.getAttribute(LISTEN_FLAG) as string);
536
+ if (!listeners[curWrapperKey]) {
537
+ listeners[curWrapperKey] = [];
538
+ }
539
+ if (listenerCount === 1) {
540
+ // listener 数量为1时监听事件(列表中插入 ShowMonitor 时做判断)
541
+ // @en Listen when the number of listeners is 1 (judging when ShowMonitor is inserted into the list)
542
+ parent.addEventListener('scroll', throttlingVisibleChange);
543
+ }
544
+ parent.setAttribute(LISTEN_FLAG, String(listenerCount));
545
+ }
546
+ }
547
+ } else if (
548
+ !listeners[curWrapperKey] ||
549
+ listeners[curWrapperKey].length === 0 ||
550
+ // 相同curWrapperKey内全部为overflow场景,需要单独注册scroll事件
551
+ // @en It need to register the scroll event separately when all values in the same curWrapperKey are overflow
552
+ (listeners[curWrapperKey] && listeners[curWrapperKey].every(it => it.overflow))
553
+ ) {
554
+ if (!listeners[curWrapperKey]) {
555
+ listeners[curWrapperKey] = [];
556
+ }
557
+ listenScroll &&
558
+ scrollPort.current.addEventListener('scroll', throttlingVisibleChange);
559
+ listenResize &&
560
+ scrollPort.current.addEventListener('resize', throttlingVisibleChange);
561
+ }
562
+ listener.current = {
563
+ node: domRef.current!,
564
+ isVisible: false,
565
+ overflow,
566
+ once,
567
+ offset,
568
+ threshold,
569
+ onVisibleChange,
570
+ };
571
+ // 将当前 listener 存入 listeners
572
+ !disabled && listeners[curWrapperKey].push(listener.current);
573
+ nextTick(() => {
574
+ !disabled && checkVisible(listener.current!);
575
+ });
576
+ }
577
+ }, []);
578
+
579
+ useEffect(() => {
580
+ return () => {
581
+ if (isSupportNativeApi) {
582
+ ioUnobserve();
583
+ } else {
584
+ if (overflow) {
585
+ const parent = domRefParent.current;
586
+ if (
587
+ parent &&
588
+ parent instanceof HTMLElement &&
589
+ typeof parent.getAttribute === 'function'
590
+ ) {
591
+ const listenerCount = Number(parent.getAttribute(LISTEN_FLAG) || 0) - 1;
592
+ if (listenerCount === 0) {
593
+ // 如果监听队列中无节点,解绑事件
594
+ // @en If there is no node in the listening queue, unbind the event
595
+ parent.removeEventListener('scroll', throttlingVisibleChange);
596
+ } else {
597
+ // 如果监听队列中有元素节点,将 dataset 减1
598
+ // @en If there are element nodes in the listening queue, decrement dataset by 1
599
+ parent.setAttribute(LISTEN_FLAG, String(listenerCount));
600
+ }
601
+ }
602
+ }
603
+ const key = wrapperKey.current;
604
+ const curListeners = listeners[key];
605
+ if (curListeners) {
606
+ // 不在监听队列中,移除
607
+ // @en Not in the listening queue, remove it
608
+ const index = curListeners.indexOf(listener.current!);
609
+ index !== -1 && curListeners.splice(index, 1);
610
+ // 监听队列为空,解绑 scroll、resize 事件
611
+ // @en When the listening queue is empty, unbind scroll and resize events
612
+ if (curListeners.length === 0) {
613
+ delete listeners[key];
614
+ if (scrollPort.current) {
615
+ scrollPort.current.removeEventListener(
616
+ 'scroll',
617
+ throttlingVisibleChange,
618
+ );
619
+ scrollPort.current.removeEventListener(
620
+ 'resize',
621
+ throttlingVisibleChange,
622
+ );
623
+ }
624
+ }
625
+ }
626
+ }
627
+ };
628
+ }, []);
629
+
630
+ return (
631
+ <ContextLayout>
632
+ {({ prefixCls }) => (
633
+ <div
634
+ className={cls(`${prefixCls}-show-monitor`, className)}
635
+ style={style}
636
+ ref={domRef}
637
+ onClick={onClick}
638
+ >
639
+ {children}
640
+ </div>
641
+ )}
642
+ </ContextLayout>
643
+ );
644
+ });
645
+
646
+ export default ShowMonitor;