@rainbow-oh/yee-c 0.8.5 → 0.8.7

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 (59) hide show
  1. package/dist/cjs/Alert/alert.js +1 -0
  2. package/dist/cjs/Anchor/anchor.js +195 -12
  3. package/dist/cjs/Anchor/interface.d.ts +1 -1
  4. package/dist/cjs/Input/input.js +3 -0
  5. package/dist/cjs/Input/style/index.less +2 -2
  6. package/dist/cjs/Select/option.js +2 -1
  7. package/dist/cjs/Select/options.js +6 -0
  8. package/dist/cjs/Selector/selector.js +20 -1
  9. package/dist/cjs/Spin/style/index.less +11 -6
  10. package/dist/cjs/Table/theader/filter.js +1 -1
  11. package/dist/cjs/Tag/tag.js +23 -8
  12. package/dist/cjs/Tree/interface.d.ts +2 -1
  13. package/dist/cjs/Tree/tree-node.js +6 -1
  14. package/dist/cjs/Tree/tree.js +56 -12
  15. package/dist/cjs/Trigger/interface.d.ts +2 -1
  16. package/dist/cjs/style/themes.css +1 -1
  17. package/dist/cjs/style/variables.css +1 -1
  18. package/dist/cjs/version.d.ts +1 -1
  19. package/dist/cjs/version.js +1 -1
  20. package/dist/esm/Alert/alert.js +1 -0
  21. package/dist/esm/Anchor/anchor.js +133 -12
  22. package/dist/esm/Anchor/interface.d.ts +1 -1
  23. package/dist/esm/Input/input.js +3 -0
  24. package/dist/esm/Input/style/index.less +2 -2
  25. package/dist/esm/Select/option.js +2 -1
  26. package/dist/esm/Select/options.js +6 -0
  27. package/dist/esm/Selector/selector.js +13 -1
  28. package/dist/esm/Spin/style/index.less +11 -6
  29. package/dist/esm/Table/theader/filter.js +1 -1
  30. package/dist/esm/Tag/tag.js +6 -1
  31. package/dist/esm/Tree/interface.d.ts +2 -1
  32. package/dist/esm/Tree/tree-node.js +6 -1
  33. package/dist/esm/Tree/tree.js +14 -5
  34. package/dist/esm/Trigger/interface.d.ts +2 -1
  35. package/dist/esm/style/themes.css +1 -1
  36. package/dist/esm/style/variables.css +1 -1
  37. package/dist/esm/version.d.ts +1 -1
  38. package/dist/esm/version.js +1 -1
  39. package/dist/umd/yee-c.min.css +1 -1
  40. package/dist/umd/yee-c.min.js +1 -1
  41. package/package.json +1 -1
  42. package/src/Alert/alert.tsx +1 -0
  43. package/src/Anchor/anchor.tsx +224 -13
  44. package/src/Anchor/index.en-US.md +1 -1
  45. package/src/Anchor/index.zh-CN.md +1 -1
  46. package/src/Anchor/interface.ts +1 -1
  47. package/src/Input/input.tsx +3 -0
  48. package/src/Input/style/index.less +2 -2
  49. package/src/Select/option.tsx +2 -1
  50. package/src/Select/options.tsx +6 -0
  51. package/src/Selector/selector.tsx +16 -1
  52. package/src/Spin/style/index.less +11 -6
  53. package/src/Table/theader/filter.tsx +1 -1
  54. package/src/Tag/tag.tsx +14 -1
  55. package/src/Tree/interface.ts +3 -1
  56. package/src/Tree/tree-node.tsx +5 -0
  57. package/src/Tree/tree.tsx +8 -0
  58. package/src/Trigger/interface.ts +2 -1
  59. package/src/version.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rainbow-oh/yee-c",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "yee basic components",
5
5
  "keywords": [
6
6
  "react",
@@ -87,6 +87,7 @@ const Alert: React.FC<AlertProps> = (baseprops) => {
87
87
  return (
88
88
  <div
89
89
  {...rest}
90
+ role="alert"
90
91
  className={clsx(
91
92
  prefixCls,
92
93
  [`${prefixCls}-${mergedState}`],
@@ -25,6 +25,20 @@ export const AnchorContext = createContext<AnchorContextType>(
25
25
  {} as AnchorContextType,
26
26
  );
27
27
 
28
+ const NAVIGATION_LOCK_TIMEOUT = 5000;
29
+ const NAVIGATION_SETTLE_DELAY = 100;
30
+ const TARGET_POSITION_TOLERANCE = 2;
31
+ const SCROLL_KEYS = new Set([
32
+ 'ArrowUp',
33
+ 'ArrowDown',
34
+ 'PageUp',
35
+ 'PageDown',
36
+ 'Home',
37
+ 'End',
38
+ ' ',
39
+ 'Spacebar',
40
+ ]);
41
+
28
42
  const Anchor = forwardRef<HTMLDivElement, AnchorProps>((baseprops, ref) => {
29
43
  const { anchor } = useContext(GlobalContext);
30
44
  const props = mergeContextToProps(baseprops, anchor);
@@ -57,11 +71,89 @@ const Anchor = forwardRef<HTMLDivElement, AnchorProps>((baseprops, ref) => {
57
71
  const [anchorList, setAnchorList] = useState<Array<AnchorItemType>>([]);
58
72
 
59
73
  const containerRef = useRef<HTMLElement | Window | null>(null);
74
+ const anchorRootRef = useRef<HTMLDivElement | null>(null);
60
75
  const activeKeyRef = useRef(mergedActiveKey);
76
+ const handleScrollRef = useRef<() => void>(() => {});
77
+ const pendingTargetKeyRef = useRef<string | undefined>(undefined);
78
+ const navigationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
79
+ null,
80
+ );
81
+ const navigationSettleTimeoutRef = useRef<
82
+ ReturnType<typeof setTimeout> | null
83
+ >(null);
84
+
61
85
  useEffect(() => {
62
86
  activeKeyRef.current = mergedActiveKey;
63
87
  }, [mergedActiveKey]);
64
88
 
89
+ const setAnchorRootRef = useCallback(
90
+ (node: HTMLDivElement | null) => {
91
+ anchorRootRef.current = node;
92
+ if (typeof ref === 'function') {
93
+ ref(node);
94
+ } else if (ref) {
95
+ ref.current = node;
96
+ }
97
+ },
98
+ [ref],
99
+ );
100
+
101
+ const clearNavigationSettleTimer = useCallback(() => {
102
+ if (navigationSettleTimeoutRef.current !== null) {
103
+ clearTimeout(navigationSettleTimeoutRef.current);
104
+ navigationSettleTimeoutRef.current = null;
105
+ }
106
+ }, []);
107
+
108
+ const clearNavigationLock = useCallback(() => {
109
+ pendingTargetKeyRef.current = undefined;
110
+ clearNavigationSettleTimer();
111
+ if (navigationTimeoutRef.current !== null) {
112
+ clearTimeout(navigationTimeoutRef.current);
113
+ navigationTimeoutRef.current = null;
114
+ }
115
+ }, [clearNavigationSettleTimer]);
116
+
117
+ const scheduleNavigationUnlock = useCallback(
118
+ (key: string) => {
119
+ clearNavigationSettleTimer();
120
+ navigationSettleTimeoutRef.current = setTimeout(() => {
121
+ if (pendingTargetKeyRef.current === key) {
122
+ clearNavigationLock();
123
+ }
124
+ }, NAVIGATION_SETTLE_DELAY);
125
+ },
126
+ [clearNavigationLock, clearNavigationSettleTimer],
127
+ );
128
+
129
+ const startNavigationLock = useCallback(
130
+ (key: string) => {
131
+ clearNavigationLock();
132
+ pendingTargetKeyRef.current = key;
133
+ navigationTimeoutRef.current = setTimeout(() => {
134
+ if (pendingTargetKeyRef.current === key) {
135
+ clearNavigationLock();
136
+ handleScrollRef.current();
137
+ }
138
+ }, NAVIGATION_LOCK_TIMEOUT);
139
+ },
140
+ [clearNavigationLock],
141
+ );
142
+
143
+ useEffect(() => clearNavigationLock, [clearNavigationLock]);
144
+
145
+ const commitActiveKey = useCallback(
146
+ (key: string, forceNotify = false) => {
147
+ const changed = activeKeyRef.current !== key;
148
+ activeKeyRef.current = key;
149
+ setMergedActiveKey(key);
150
+ if (changed || forceNotify) {
151
+ onChange?.(key);
152
+ }
153
+ },
154
+ [onChange],
155
+ );
156
+
65
157
  // Auto-generate anchor list from DOM
66
158
  const generateAnchorList = useCallback(() => {
67
159
  if (!auto || !name) return;
@@ -107,11 +199,51 @@ const Anchor = forwardRef<HTMLDivElement, AnchorProps>((baseprops, ref) => {
107
199
  return currentScroll + (target.getBoundingClientRect().top - containerTop);
108
200
  }, []);
109
201
 
202
+ const hasReachedTarget = useCallback(
203
+ (key: string) => {
204
+ const container = containerRef.current;
205
+ const target = document.getElementById(key);
206
+ if (!container || !target) return false;
207
+
208
+ const currentScroll =
209
+ container === window
210
+ ? window.pageYOffset || document.documentElement.scrollTop
211
+ : (container as HTMLElement).scrollTop;
212
+ const maxScroll =
213
+ container === window
214
+ ? Math.max(
215
+ document.documentElement.scrollHeight,
216
+ document.body?.scrollHeight || 0,
217
+ ) - window.innerHeight
218
+ : (container as HTMLElement).scrollHeight -
219
+ (container as HTMLElement).clientHeight;
220
+ const targetScroll = Math.min(
221
+ Math.max(getTargetOffsetTop(target) - offsetTop, 0),
222
+ Math.max(maxScroll, 0),
223
+ );
224
+
225
+ return (
226
+ Math.abs(currentScroll - targetScroll) <= TARGET_POSITION_TOLERANCE
227
+ );
228
+ },
229
+ [getTargetOffsetTop, offsetTop],
230
+ );
231
+
110
232
  // Handle scroll to update active key
111
233
  const handleScroll = useCallback(() => {
112
234
  const container = containerRef.current;
113
235
  if (!container) return;
114
236
 
237
+ const pendingTargetKey = pendingTargetKeyRef.current;
238
+ if (pendingTargetKey) {
239
+ if (hasReachedTarget(pendingTargetKey)) {
240
+ scheduleNavigationUnlock(pendingTargetKey);
241
+ } else {
242
+ clearNavigationSettleTimer();
243
+ }
244
+ return;
245
+ }
246
+
115
247
  const scrollTop =
116
248
  container === window
117
249
  ? window.pageYOffset || document.documentElement.scrollTop
@@ -145,11 +277,25 @@ const Anchor = forwardRef<HTMLDivElement, AnchorProps>((baseprops, ref) => {
145
277
  nextActiveKey = first?.key;
146
278
  }
147
279
 
148
- if (nextActiveKey && activeKeyRef.current !== nextActiveKey) {
149
- setMergedActiveKey(nextActiveKey);
150
- onChange?.(nextActiveKey);
280
+ if (nextActiveKey) {
281
+ commitActiveKey(nextActiveKey);
151
282
  }
152
- }, [auto, anchorList, items, offsetTop, onChange, getTargetOffsetTop]);
283
+ }, [
284
+ auto,
285
+ anchorList,
286
+ items,
287
+ offsetTop,
288
+ getTargetOffsetTop,
289
+ hasReachedTarget,
290
+ scheduleNavigationUnlock,
291
+ clearNavigationSettleTimer,
292
+ clearNavigationLock,
293
+ commitActiveKey,
294
+ ]);
295
+
296
+ useEffect(() => {
297
+ handleScrollRef.current = handleScroll;
298
+ }, [handleScroll]);
153
299
 
154
300
  useEffect(() => {
155
301
  // Resolve in a passive effect: parent-owned refs passed via `getContainer`
@@ -161,31 +307,83 @@ const Anchor = forwardRef<HTMLDivElement, AnchorProps>((baseprops, ref) => {
161
307
  // rAF throttle so the active key tracks scroll live.
162
308
  let rafId: ReturnType<typeof requestAnimationFrame> | null = null;
163
309
  const onScroll = () => {
310
+ if (pendingTargetKeyRef.current) {
311
+ clearNavigationSettleTimer();
312
+ }
164
313
  if (rafId !== null) return;
165
314
  rafId = requestAnimationFrame(() => {
166
315
  rafId = null;
167
316
  handleScroll();
168
317
  });
169
318
  };
319
+ const onScrollEnd = () => {
320
+ const pendingTargetKey = pendingTargetKeyRef.current;
321
+ if (pendingTargetKey && hasReachedTarget(pendingTargetKey)) {
322
+ scheduleNavigationUnlock(pendingTargetKey);
323
+ }
324
+ };
325
+ const onUserInterrupt = (event: Event) => {
326
+ if (event.type === 'pointerdown' && event.target instanceof Element) {
327
+ const item = event.target.closest(
328
+ `.${CSS.escape(`${prefixCls}-item`)}`,
329
+ );
330
+ const owner = item?.closest(`.${CSS.escape(prefixCls)}`);
331
+ if (owner === anchorRootRef.current) {
332
+ return;
333
+ }
334
+ }
335
+ if (
336
+ event.type === 'keydown' &&
337
+ !SCROLL_KEYS.has((event as KeyboardEvent).key)
338
+ ) {
339
+ return;
340
+ }
341
+ clearNavigationLock();
342
+ };
343
+
170
344
  container.addEventListener('scroll', onScroll as EventListener);
345
+ container.addEventListener('scrollend', onScrollEnd as EventListener);
346
+ container.addEventListener('wheel', onUserInterrupt as EventListener);
347
+ container.addEventListener('touchstart', onUserInterrupt as EventListener);
348
+ document.addEventListener('pointerdown', onUserInterrupt as EventListener);
349
+ container.addEventListener('keydown', onUserInterrupt as EventListener);
171
350
  handleScroll();
172
351
 
173
352
  return () => {
174
353
  container.removeEventListener('scroll', onScroll as EventListener);
354
+ container.removeEventListener('scrollend', onScrollEnd as EventListener);
355
+ container.removeEventListener('wheel', onUserInterrupt as EventListener);
356
+ container.removeEventListener(
357
+ 'touchstart',
358
+ onUserInterrupt as EventListener,
359
+ );
360
+ document.removeEventListener(
361
+ 'pointerdown',
362
+ onUserInterrupt as EventListener,
363
+ );
364
+ container.removeEventListener('keydown', onUserInterrupt as EventListener);
175
365
  if (rafId !== null) {
176
366
  cancelAnimationFrame(rafId);
177
367
  }
178
368
  };
179
- }, [getContainer, handleScroll]);
369
+ }, [
370
+ getContainer,
371
+ handleScroll,
372
+ hasReachedTarget,
373
+ scheduleNavigationUnlock,
374
+ clearNavigationSettleTimer,
375
+ clearNavigationLock,
376
+ prefixCls,
377
+ ]);
180
378
 
181
379
  // Scroll to target when activeKey changes
182
380
  const scrollToAnchor = useCallback(
183
381
  (key: string) => {
184
382
  const target = document.getElementById(key);
185
- if (!target) return;
383
+ if (!target) return false;
186
384
 
187
385
  const container = containerRef.current;
188
- if (!container) return;
386
+ if (!container) return false;
189
387
 
190
388
  const targetTop = getTargetOffsetTop(target) - offsetTop;
191
389
 
@@ -197,6 +395,7 @@ const Anchor = forwardRef<HTMLDivElement, AnchorProps>((baseprops, ref) => {
197
395
  behavior: 'smooth',
198
396
  });
199
397
  }
398
+ return true;
200
399
  },
201
400
  [offsetTop, getTargetOffsetTop],
202
401
  );
@@ -210,11 +409,23 @@ const Anchor = forwardRef<HTMLDivElement, AnchorProps>((baseprops, ref) => {
210
409
 
211
410
  const handleItemClick = useCallback(
212
411
  (key: string) => {
213
- setMergedActiveKey(key);
214
- scrollToAnchor(key);
215
- onChange?.(key);
216
- },
217
- [scrollToAnchor, onChange],
412
+ startNavigationLock(key);
413
+ commitActiveKey(key, true);
414
+
415
+ if (!scrollToAnchor(key)) {
416
+ clearNavigationLock();
417
+ return;
418
+ }
419
+
420
+ // Handles instant scrolling and targets that are already in position.
421
+ handleScroll();
422
+ }, [
423
+ startNavigationLock,
424
+ commitActiveKey,
425
+ scrollToAnchor,
426
+ clearNavigationLock,
427
+ handleScroll,
428
+ ],
218
429
  );
219
430
 
220
431
  const cls = clsx(
@@ -250,7 +461,7 @@ const Anchor = forwardRef<HTMLDivElement, AnchorProps>((baseprops, ref) => {
250
461
  };
251
462
 
252
463
  return (
253
- <div {...rest} className={cls} style={style} ref={ref}>
464
+ <div {...rest} className={cls} style={style} ref={setAnchorRootRef}>
254
465
  {renderNavArrow()}
255
466
  <AnchorContext.Provider value={contextValue}>
256
467
  <div className={`${prefixCls}-content-wrapper`}>
@@ -35,7 +35,7 @@ Anchor component for linking to specific sections within a page.
35
35
  | auto | `boolean` | Whether to auto-generate anchors | `false` |
36
36
  | name | `string` | Anchor group name (for auto-generation) | - |
37
37
  | defaultActiveKey | `string` | Default active anchor key | - |
38
- | activeKey | `string` | Active anchor key (controlled) | - |
38
+ | activeKey | `string` | Active anchor key (controlled; update it in `onChange`) | - |
39
39
  | affix | `boolean` | Whether to fix position | `true` |
40
40
  | direction | `'vertical' \| 'horizontal'` | Navigation direction | `'vertical'` |
41
41
  | offsetTop | `number` | Offset top to trigger anchor change | `0` |
@@ -35,7 +35,7 @@ toc: 'content'
35
35
  | auto | `boolean` | 是否自动生成锚点 | `false` |
36
36
  | name | `string` | 锚点组名称(用于自动生成) | - |
37
37
  | defaultActiveKey | `string` | 锚点默认位置 | - |
38
- | activeKey | `string` | 锚点位置(受控) | - |
38
+ | activeKey | `string` | 锚点位置(受控,需在 `onChange` 中同步回写) | - |
39
39
  | affix | `boolean` | 是否固定 | `true` |
40
40
  | direction | `'vertical' \| 'horizontal'` | 设置导航方向 | `'vertical'` |
41
41
  | offsetTop | `number` | 距离顶部距离为多少时,触发锚点改变 | `0` |
@@ -91,7 +91,7 @@ export interface AnchorProps extends DataAttributeProps {
91
91
  */
92
92
  defaultActiveKey?: string;
93
93
  /**
94
- * Anchor position (controlled)
94
+ * Anchor position (controlled). When provided, update it from onChange.
95
95
  */
96
96
  activeKey?: string;
97
97
  /**
@@ -104,6 +104,9 @@ const Input = forwardRef<HTMLInputElement, InputProps>((baseprops, ref) => {
104
104
  <span
105
105
  className={clsx(`${prefixCls}-clear`, classNames?.clear)}
106
106
  style={styles?.clear}
107
+ role="button"
108
+ aria-label="clear"
109
+ tabIndex={0}
107
110
  onClick={onClear}
108
111
  onKeyDown={(event: React.KeyboardEvent<HTMLSpanElement>) => {
109
112
  if (event.key === 'Enter') {
@@ -8,7 +8,7 @@
8
8
  width: 100%;
9
9
  padding: var(--yee-input-padding, 2px 8px);
10
10
  font-size: var(--yee-input-font-size);
11
- line-height: var(--yee-input-line-height);
11
+ // line-height: var(--yee-input-line-height);
12
12
  background-color: var(--yee-input-content-bg-color);
13
13
  border: var(--yee-input-border-width) solid var(--yee-input-border-color);
14
14
  border-radius: var(--yee-input-border-radius, 4px);
@@ -49,7 +49,7 @@
49
49
  &-inner {
50
50
  width: 100%;
51
51
  font-family: var(--yee-font-family);
52
- line-height: var(--yee-input-line-height);
52
+ // line-height: var(--yee-input-line-height);
53
53
  background-color: inherit;
54
54
  border: none;
55
55
  outline: none;
@@ -43,7 +43,8 @@ const Option = (props: OptionProps) => {
43
43
  [`${prefixCls}-option-focused`]: focused,
44
44
  [`${prefixCls}-option-disabled`]: disabled,
45
45
  })}
46
- aria-label="option"
46
+ aria-selected={selected}
47
+ aria-disabled={disabled || undefined}
47
48
  onClick={handleClick}
48
49
  onKeyDown={(e) => {
49
50
  if (e.key === 'Enter') {
@@ -166,6 +166,8 @@ const Options = forwardRef<HTMLDivElement, OptionsProps>(
166
166
  )}
167
167
  style={popupStyle}
168
168
  ref={rootRef}
169
+ role="listbox"
170
+ aria-multiselectable={multiple || undefined}
169
171
  tabIndex={-1}
170
172
  >
171
173
  <OptionsCtx.Provider value={ctxValue}>
@@ -220,6 +222,8 @@ const Options = forwardRef<HTMLDivElement, OptionsProps>(
220
222
  )}
221
223
  style={popupStyle}
222
224
  ref={rootRef}
225
+ role="listbox"
226
+ aria-multiselectable={multiple || undefined}
223
227
  tabIndex={-1}
224
228
  >
225
229
  <OptionsCtx.Provider value={ctxValue}>
@@ -268,6 +272,8 @@ const Options = forwardRef<HTMLDivElement, OptionsProps>(
268
272
  : undefined),
269
273
  }}
270
274
  ref={rootRef}
275
+ role="listbox"
276
+ aria-multiselectable={multiple || undefined}
271
277
  tabIndex={-1}
272
278
  >
273
279
  {isEmpty ? (
@@ -246,7 +246,18 @@ const Selector = React.forwardRef(
246
246
  return null;
247
247
  }
248
248
  return (
249
- <span className={`${prefixCls}-clear`} onClick={handleClear}>
249
+ <span
250
+ className={`${prefixCls}-clear`}
251
+ role="button"
252
+ aria-label="clear"
253
+ tabIndex={0}
254
+ onClick={handleClear}
255
+ onKeyDown={(e) => {
256
+ if (e.key === 'Enter') {
257
+ handleClear(e as unknown as React.MouseEvent<HTMLSpanElement>);
258
+ }
259
+ }}
260
+ >
250
261
  <X size={12} strokeWidth={1} />
251
262
  </span>
252
263
  );
@@ -258,6 +269,10 @@ const Selector = React.forwardRef(
258
269
  tabIndex={disabled || searchable ? -1 : 0}
259
270
  className={cls}
260
271
  style={style}
272
+ role="combobox"
273
+ aria-expanded={open || false}
274
+ aria-haspopup="listbox"
275
+ aria-disabled={disabled || undefined}
261
276
  onKeyDown={(event) => {
262
277
  if (disabled) return;
263
278
  // For non-searchable single select, handle Enter to open dropdown
@@ -38,7 +38,7 @@
38
38
  }
39
39
 
40
40
  &-ring {
41
- .yee-spin-ring {
41
+ @{pls}-ring {
42
42
  box-sizing: border-box;
43
43
  display: block;
44
44
  border-style: solid;
@@ -53,13 +53,13 @@
53
53
  }
54
54
 
55
55
  &-spokes {
56
- .yee-spin-spokes {
56
+ @{pls}-spokes {
57
57
  position: relative;
58
58
  width: 100%;
59
59
  height: 100%;
60
60
  }
61
61
 
62
- .yee-spin-spokes-line {
62
+ @{pls}-spokes-line {
63
63
  position: absolute;
64
64
  top: 0;
65
65
  left: 50%;
@@ -101,9 +101,14 @@
101
101
 
102
102
  &-blur {
103
103
  position: absolute;
104
- top: 50%;
105
- left: 50%;
106
- transform: translate(-50%, -50%);
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ width: 100%;
108
+ height: 100%;
109
+ // top: 50%;
110
+ // left: 50%;
111
+ // transform: translate(-50%, -50%);
107
112
  background-color: var(--yee-color-bg-mask-light, rgba(255, 255, 255, 40%));
108
113
  }
109
114
 
@@ -175,7 +175,7 @@ const HeaderFilter = React.memo((props: any) => {
175
175
  <Search size={14} strokeWidth={1.5} />
176
176
  )
177
177
  }
178
- variant={selectNodes.length ? 'filled' : undefined}
178
+ variant={selectNodes.length || searchValue ? 'filled' : undefined}
179
179
  type="text"
180
180
  size="small"
181
181
  />
package/src/Tag/tag.tsx CHANGED
@@ -56,6 +56,7 @@ const Tag = forwardRef(
56
56
  style={styles?.close}
57
57
  type="text"
58
58
  size="small"
59
+ aria-label="close"
59
60
  onClick={onClose}
60
61
  >
61
62
  {typeof closable === 'boolean' ? (
@@ -69,7 +70,19 @@ const Tag = forwardRef(
69
70
  const closeNode = renderClose();
70
71
 
71
72
  return (
72
- <div {...rest} className={cls} onClick={handleClick} ref={ref}>
73
+ <div
74
+ {...rest}
75
+ className={cls}
76
+ onClick={handleClick}
77
+ ref={ref}
78
+ {...(checkable
79
+ ? {
80
+ role: 'checkbox',
81
+ 'aria-checked': checked || false,
82
+ tabIndex: 0,
83
+ }
84
+ : undefined)}
85
+ >
73
86
  {icon && (
74
87
  <span
75
88
  className={clsx(`${prefixCls}-icon`, classNames?.icon)}
@@ -1,3 +1,5 @@
1
+ import type { DataAttributeProps } from '../utils/types';
2
+
1
3
  export type SemanticType = '';
2
4
 
3
5
  export type DragPosition = 'before' | 'after' | 'inside';
@@ -73,7 +75,7 @@ export interface TreeNodeProps<T> {
73
75
  style?: React.CSSProperties;
74
76
  }
75
77
 
76
- export interface TreeProps<T> {
78
+ export interface TreeProps<T> extends DataAttributeProps {
77
79
  /**
78
80
  * Custom class name prefix
79
81
  */
@@ -170,6 +170,11 @@ const Node = <T extends Record<string, unknown> = any>(
170
170
  [`${prefixCls}-node-drag-over-inside`]: dropPosition === 'inside',
171
171
  })}
172
172
  style={nodeStyle}
173
+ role="treeitem"
174
+ aria-expanded={isLeaf ? undefined : expanded}
175
+ aria-selected={selected}
176
+ aria-level={depth + 1}
177
+ aria-disabled={disabled || undefined}
173
178
  {...dragProps}
174
179
  >
175
180
  {renderEmptyNodes(depth)}
package/src/Tree/tree.tsx CHANGED
@@ -13,6 +13,7 @@ import useEvent from '../hooks/useEvent';
13
13
  import useMergedState from '../hooks/useMergedState';
14
14
  import useVirtualList from '../hooks/useVirtualList';
15
15
  import mergeContextToProps from '../utils/mergeContextToProps';
16
+ import { pickDataAttrs } from '../utils/types';
16
17
  import type { DragPosition, TreeNode, TreeProps } from './interface';
17
18
  import TreeNodeComp from './tree-node';
18
19
  import { getCheckedKeys, tree2array } from './utils/tree-tools';
@@ -205,8 +206,11 @@ const Tree = <T extends Record<string, unknown> = any>(
205
206
  virtual = false,
206
207
  height,
207
208
  itemHeight = 28,
209
+ ...rest
208
210
  } = props;
209
211
 
212
+ const dataAttrs = pickDataAttrs(rest as Record<string, unknown>);
213
+
210
214
  // ======================== Data ========================
211
215
  const [flatItems, itemMap] = useMemo(
212
216
  () => tree2array(dataSource ?? [], fieldNames),
@@ -606,7 +610,9 @@ const Tree = <T extends Record<string, unknown> = any>(
606
610
  <div
607
611
  className={clsx(prefixCls, className)}
608
612
  style={style}
613
+ role="tree"
609
614
  onDragEnd={handleDragEnd}
615
+ {...dataAttrs}
610
616
  >
611
617
  <TreeContext.Provider value={contextValue}>
612
618
  <div
@@ -675,7 +681,9 @@ const Tree = <T extends Record<string, unknown> = any>(
675
681
  <div
676
682
  className={clsx(prefixCls, className)}
677
683
  style={style}
684
+ role="tree"
678
685
  onDragEnd={handleDragEnd}
686
+ {...dataAttrs}
679
687
  >
680
688
  <TreeContext.Provider value={contextValue}>
681
689
  <AnimatePresence initial={false}>
@@ -1,4 +1,5 @@
1
1
  import { TriggerProps as RcTriggerProps } from '@rc-component/trigger';
2
+ import type { DataAttributeProps } from '../utils/types';
2
3
 
3
4
  export type Placement =
4
5
  | 'top'
@@ -31,7 +32,7 @@ export type Point =
31
32
  | 'cl'
32
33
  | 'cr';
33
34
 
34
- export interface TriggerProps extends Omit<RcTriggerProps, 'children'> {
35
+ export interface TriggerProps extends Omit<RcTriggerProps, 'children'>, DataAttributeProps {
35
36
  /**
36
37
  * Custom class name prefix
37
38
  */
package/src/version.ts CHANGED
@@ -4,7 +4,7 @@ declare global {
4
4
  }
5
5
  }
6
6
 
7
- export const version = "0.8.5";
7
+ export const version = "0.8.7";
8
8
 
9
9
  if (typeof window !== 'undefined') {
10
10
  window.__yee_c_version__ = version;