@rainbow-oh/yee-c 0.6.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/cjs/Button/style/index.less +5 -4
  2. package/dist/cjs/Drawer/drawer.js +1 -1
  3. package/dist/cjs/Drawer/interface.d.ts +1 -1
  4. package/dist/cjs/Drawer/style/index.less +1 -0
  5. package/dist/cjs/List/demo/virtual.d.ts +3 -0
  6. package/dist/cjs/List/demo/virtual.js +53 -0
  7. package/dist/cjs/List/hooks/useListKeyboard.d.ts +28 -0
  8. package/dist/cjs/List/hooks/useListKeyboard.js +188 -0
  9. package/dist/cjs/List/interface.d.ts +29 -2
  10. package/dist/cjs/List/list.js +141 -21
  11. package/dist/cjs/List/style/index.less +24 -0
  12. package/dist/cjs/Select/demo/columns.d.ts +3 -0
  13. package/dist/cjs/Select/demo/columns.js +62 -0
  14. package/dist/cjs/Select/hooks/useSelectKeyboard.d.ts +13 -1
  15. package/dist/cjs/Select/hooks/useSelectKeyboard.js +81 -10
  16. package/dist/cjs/Select/interface.d.ts +30 -3
  17. package/dist/cjs/Select/option.js +4 -3
  18. package/dist/cjs/Select/options.js +90 -8
  19. package/dist/cjs/Select/select.js +32 -8
  20. package/dist/cjs/Select/style/index.less +14 -0
  21. package/dist/cjs/Selector/interface.d.ts +4 -0
  22. package/dist/cjs/Selector/selector.js +5 -3
  23. package/dist/cjs/Table/interface.d.ts +1 -1
  24. package/dist/cjs/Tree/demo/draggable.js +2 -0
  25. package/dist/cjs/Tree/interface.d.ts +46 -23
  26. package/dist/cjs/Tree/tree-node.d.ts +1 -1
  27. package/dist/cjs/Tree/tree-node.js +30 -23
  28. package/dist/cjs/Tree/tree.d.ts +13 -12
  29. package/dist/cjs/Tree/tree.js +452 -227
  30. package/dist/cjs/Tree/utils/tree-tools.d.ts +43 -10
  31. package/dist/cjs/Tree/utils/tree-tools.js +129 -91
  32. package/dist/cjs/locale/context.d.ts +5 -4
  33. package/dist/cjs/locale/context.js +15 -8
  34. package/dist/cjs/locale/interface.d.ts +20 -0
  35. package/dist/cjs/locale/util.d.ts +3 -3
  36. package/dist/cjs/style/color.css +1 -0
  37. package/dist/cjs/style/size.css +2 -1
  38. package/dist/cjs/style/themes.css +1 -1
  39. package/dist/cjs/style/variables.css +4 -2
  40. package/dist/cjs/utils/isEqual.js +5 -7
  41. package/dist/cjs/version.d.ts +1 -1
  42. package/dist/cjs/version.js +1 -1
  43. package/dist/esm/Button/style/index.less +5 -4
  44. package/dist/esm/Drawer/drawer.js +2 -1
  45. package/dist/esm/Drawer/interface.d.ts +1 -1
  46. package/dist/esm/Drawer/style/index.less +1 -0
  47. package/dist/esm/List/demo/virtual.d.ts +3 -0
  48. package/dist/esm/List/demo/virtual.js +22 -0
  49. package/dist/esm/List/hooks/useListKeyboard.d.ts +28 -0
  50. package/dist/esm/List/hooks/useListKeyboard.js +173 -0
  51. package/dist/esm/List/interface.d.ts +29 -2
  52. package/dist/esm/List/list.js +171 -14
  53. package/dist/esm/List/style/index.less +24 -0
  54. package/dist/esm/Select/demo/columns.d.ts +3 -0
  55. package/dist/esm/Select/demo/columns.js +36 -0
  56. package/dist/esm/Select/hooks/useSelectKeyboard.d.ts +13 -1
  57. package/dist/esm/Select/hooks/useSelectKeyboard.js +91 -14
  58. package/dist/esm/Select/interface.d.ts +30 -3
  59. package/dist/esm/Select/option.js +6 -2
  60. package/dist/esm/Select/options.js +114 -32
  61. package/dist/esm/Select/select.js +40 -11
  62. package/dist/esm/Select/style/index.less +14 -0
  63. package/dist/esm/Selector/interface.d.ts +4 -0
  64. package/dist/esm/Selector/selector.js +10 -4
  65. package/dist/esm/Table/interface.d.ts +1 -1
  66. package/dist/esm/Tree/demo/draggable.js +2 -0
  67. package/dist/esm/Tree/interface.d.ts +46 -23
  68. package/dist/esm/Tree/tree-node.d.ts +1 -1
  69. package/dist/esm/Tree/tree-node.js +26 -31
  70. package/dist/esm/Tree/tree.d.ts +13 -12
  71. package/dist/esm/Tree/tree.js +572 -163
  72. package/dist/esm/Tree/utils/tree-tools.d.ts +43 -10
  73. package/dist/esm/Tree/utils/tree-tools.js +219 -104
  74. package/dist/esm/locale/context.d.ts +5 -4
  75. package/dist/esm/locale/context.js +25 -9
  76. package/dist/esm/locale/interface.d.ts +20 -0
  77. package/dist/esm/locale/util.d.ts +3 -3
  78. package/dist/esm/style/color.css +1 -0
  79. package/dist/esm/style/size.css +2 -1
  80. package/dist/esm/style/themes.css +1 -1
  81. package/dist/esm/style/variables.css +4 -2
  82. package/dist/esm/utils/isEqual.js +5 -7
  83. package/dist/esm/version.d.ts +1 -1
  84. package/dist/esm/version.js +1 -1
  85. package/dist/umd/yee-c.min.css +2 -2
  86. package/dist/umd/yee-c.min.js +1 -1
  87. package/package.json +1 -1
  88. package/src/Button/style/index.less +5 -4
  89. package/src/Drawer/drawer.tsx +1 -1
  90. package/src/Drawer/interface.ts +1 -0
  91. package/src/Drawer/style/index.less +1 -0
  92. package/src/List/demo/virtual.tsx +20 -0
  93. package/src/List/hooks/useListKeyboard.ts +235 -0
  94. package/src/List/index.en-US.md +6 -0
  95. package/src/List/index.zh-CN.md +6 -0
  96. package/src/List/interface.ts +29 -2
  97. package/src/List/list.tsx +157 -19
  98. package/src/List/style/index.less +24 -0
  99. package/src/Select/demo/columns.tsx +35 -0
  100. package/src/Select/hooks/useSelectKeyboard.ts +112 -15
  101. package/src/Select/index.en-US.md +4 -0
  102. package/src/Select/index.zh-CN.md +4 -0
  103. package/src/Select/interface.ts +31 -4
  104. package/src/Select/option.tsx +9 -3
  105. package/src/Select/options.tsx +119 -26
  106. package/src/Select/select.tsx +41 -8
  107. package/src/Select/style/index.less +14 -0
  108. package/src/Selector/interface.ts +4 -0
  109. package/src/Selector/selector.tsx +8 -3
  110. package/src/Table/interface.ts +1 -1
  111. package/src/Tree/demo/draggable.tsx +2 -0
  112. package/src/Tree/interface.ts +46 -23
  113. package/src/Tree/tree-node.tsx +26 -29
  114. package/src/Tree/tree.tsx +573 -274
  115. package/src/Tree/utils/tree-tools.ts +231 -125
  116. package/src/locale/_README.md +22 -13
  117. package/src/locale/context.tsx +33 -16
  118. package/src/locale/interface.ts +24 -0
  119. package/src/locale/util.ts +6 -6
  120. package/src/style/color.css +1 -0
  121. package/src/style/size.css +2 -1
  122. package/src/utils/isEqual.ts +5 -7
  123. package/src/version.ts +1 -1
@@ -54,6 +54,20 @@
54
54
  align-items: center;
55
55
  }
56
56
 
57
+ // Multi-column grid layout (non-virtual mode).
58
+ // The column count is driven by the CSS custom property --yee-select-columns
59
+ // set inline by the Options component.
60
+ &-popup-grid {
61
+ display: grid;
62
+ grid-template-columns: repeat(var(--yee-select-columns, 1), 1fr);
63
+ }
64
+
65
+ // Virtual multi-column: each row container is already a grid via inline style;
66
+ // options inside it should fill cells evenly and center vertically.
67
+ &-popup-grid-row {
68
+ align-items: center;
69
+ }
70
+
57
71
  &-empty {
58
72
  padding: var(--yee-select-option-padding);
59
73
  }
@@ -63,6 +63,10 @@ export interface PureSelectorProps {
63
63
  * Selected keys
64
64
  */
65
65
  selectedKeys: Array<string | number>;
66
+ /**
67
+ * Enable loose value matching (String comparison instead of strict equality)
68
+ */
69
+ looseMatch?: boolean;
66
70
  /**
67
71
  * Custom suffix
68
72
  */
@@ -37,6 +37,7 @@ const Selector = React.forwardRef(
37
37
  disabled,
38
38
  options = [],
39
39
  selectedKeys,
40
+ looseMatch = false,
40
41
  orphanClassName,
41
42
  orphanStyle,
42
43
  allowClear = true,
@@ -67,7 +68,9 @@ const Selector = React.forwardRef(
67
68
  }
68
69
  // 按 selectedKeys 顺序渲染;找不到的 key 作为孤儿值回退显示 value 本身
69
70
  return selectedKeys.map((key) => {
70
- const opt = options.find((o) => o.value === key);
71
+ const opt = looseMatch
72
+ ? options.find((o) => String(o.value) === String(key))
73
+ : options.find((o) => o.value === key);
71
74
  if (opt) {
72
75
  return {
73
76
  ...opt,
@@ -85,7 +88,7 @@ const Selector = React.forwardRef(
85
88
  _orphan: true,
86
89
  };
87
90
  });
88
- }, [options, selectedKeys, optionLabelProp]);
91
+ }, [options, selectedKeys, optionLabelProp, looseMatch]);
89
92
 
90
93
  const handleClick = () => {
91
94
  if (inputRef.current && !disabled) {
@@ -183,7 +186,9 @@ const Selector = React.forwardRef(
183
186
 
184
187
  const key = selectedKeys.length ? selectedKeys[0] : '';
185
188
  const opt = Array.isArray(options)
186
- ? options.find((opt) => opt.value === key)
189
+ ? looseMatch
190
+ ? options.find((opt) => String(opt.value) === String(key))
191
+ : options.find((opt) => opt.value === key)
187
192
  : null;
188
193
  // 孤儿值:key 非空但在 options 中找不到时,回退显示 value 本身
189
194
  const isOrphan = !opt && key !== '';
@@ -577,7 +577,7 @@ export interface TableProps
577
577
  /**
578
578
  * Row class name, unlike classNames, this prop can be set dynamically via a function
579
579
  */
580
- rowClassName?: (record: object) => string | undefined;
580
+ rowClassName?: (record: Record<string, any>) => string | undefined;
581
581
  /**
582
582
  * Row style, unlike styles, this prop can be set dynamically via a function
583
583
  */
@@ -47,6 +47,8 @@ export default () => {
47
47
  {
48
48
  dragKey: info.dragKey,
49
49
  dropKey: info.dropKey,
50
+ dragPath: info.dragPath,
51
+ dropPath: info.dropPath,
50
52
  position: info.position,
51
53
  },
52
54
  FIELD_NAMES,
@@ -6,13 +6,14 @@ export type { MoveInfo } from './utils/tree-tools';
6
6
 
7
7
  export type TreeNode<T> = {
8
8
  /**
9
- * uid
9
+ * Unique internal identifier built from key path joined by '\x00'.
10
+ * Guaranteed unique even when user-provided `key` values repeat across branches.
10
11
  */
11
12
  uid: string;
12
13
  /**
13
- * Unique key
14
- * */
15
- key: string;
14
+ * User-provided key (may not be globally unique)
15
+ */
16
+ key: string | number;
16
17
  /**
17
18
  * Display node text
18
19
  */
@@ -22,7 +23,7 @@ export type TreeNode<T> = {
22
23
  */
23
24
  title?: string;
24
25
  /**
25
- * Child nodes
26
+ * Child nodes (only present in raw tree; not used in flat list)
26
27
  */
27
28
  children?: Array<TreeNode<T>>;
28
29
  /**
@@ -30,16 +31,16 @@ export type TreeNode<T> = {
30
31
  */
31
32
  depth: number;
32
33
  /**
33
- * Parent node key
34
- * */
35
- pKey: string | null;
34
+ * Parent node uid (null for root nodes)
35
+ */
36
+ pUid: string | null;
36
37
  /**
37
38
  * Whether this is a leaf node
38
- * */
39
+ */
39
40
  isLeaf: boolean;
40
41
  /**
41
42
  * Whether this is the last node at its level
42
- * */
43
+ */
43
44
  isLast: boolean;
44
45
  /**
45
46
  * Whether disabled
@@ -50,17 +51,17 @@ export type TreeNode<T> = {
50
51
  */
51
52
  icon?: React.ReactNode | (() => React.ReactNode);
52
53
  /**
53
- * Ancestor keys
54
+ * Ancestor keys (user-provided keys, for building uid path)
54
55
  */
55
- path: string[];
56
+ path: Array<string | number>;
56
57
  /**
57
- * Whether to show connecting lines
58
+ * Whether to show connecting lines at given depth levels
58
59
  */
59
60
  lines: number[];
60
61
  /**
61
- * List of child node keys
62
+ * All descendant uids (collected bottom-up). Used for check/uncheck propagation.
62
63
  */
63
- childKeys?: Array<string | number>;
64
+ childUids?: Array<string>;
64
65
  /**
65
66
  * Original data
66
67
  */
@@ -69,6 +70,7 @@ export type TreeNode<T> = {
69
70
 
70
71
  export interface TreeNodeProps<T> {
71
72
  node: TreeNode<T>;
73
+ style?: React.CSSProperties;
72
74
  }
73
75
 
74
76
  export interface TreeProps<T> {
@@ -93,11 +95,11 @@ export interface TreeProps<T> {
93
95
  */
94
96
  defaultExpandAll?: boolean;
95
97
  /**
96
- * Specify which tree nodes to expand by default
98
+ * Specify which tree nodes to expand by default (user keys)
97
99
  */
98
100
  defaultExpandedKeys?: string[];
99
101
  /**
100
- * Controlled expanded tree nodes
102
+ * Controlled expanded tree nodes (user keys)
101
103
  */
102
104
  expandedKeys?: string[];
103
105
  /**
@@ -128,6 +130,10 @@ export interface TreeProps<T> {
128
130
  dropNode: T;
129
131
  dragKey: string | number;
130
132
  dropKey: string | number;
133
+ /** Full key path from root to the dragged node (for non-unique key support) */
134
+ dragPath: Array<string | number>;
135
+ /** Full key path from root to the drop target (for non-unique key support) */
136
+ dropPath: Array<string | number>;
131
137
  position: DragPosition;
132
138
  /** Convenience flag: true when position is 'before' or 'after' */
133
139
  dropToGap: boolean;
@@ -164,22 +170,39 @@ export interface TreeProps<T> {
164
170
  loadData?: () => void;
165
171
  /**
166
172
  * Data source
167
- * */
173
+ */
168
174
  dataSource?: Array<T> | T;
169
175
  /**
170
- * Checked tree nodes (controlled)
176
+ * Enable virtual scrolling for large datasets.
177
+ * Requires `height` to be set.
178
+ * @default false
179
+ */
180
+ virtual?: boolean;
181
+ /**
182
+ * Height of the scrollable container in pixels.
183
+ * Required when `virtual` is true.
184
+ */
185
+ height?: number;
186
+ /**
187
+ * Fixed height of each tree node row in pixels.
188
+ * Used for virtual scroll calculation.
189
+ * @default 28
190
+ */
191
+ itemHeight?: number;
192
+ /**
193
+ * Checked tree nodes (controlled, user keys)
171
194
  */
172
195
  checkedKeys?: Array<string | number>;
173
196
  /**
174
- * Checked tree nodes (uncontrolled)
197
+ * Checked tree nodes (uncontrolled, user keys)
175
198
  */
176
199
  defaultCheckedKeys?: Array<string | number>;
177
200
  /**
178
- * Set selected tree nodes, set multiple to true for multi-select
201
+ * Set selected tree nodes, set multiple to true for multi-select (user keys)
179
202
  */
180
203
  selectedKeys?: Array<string | number>;
181
204
  /**
182
- * Set default selected tree nodes, set multiple to true for multi-select
205
+ * Set default selected tree nodes (user keys)
183
206
  */
184
207
  defaultSelectedKeys?: Array<string | number>;
185
208
  /**
@@ -212,7 +235,7 @@ export interface TreeProps<T> {
212
235
  params: {
213
236
  expanded: boolean;
214
237
  expandedNodes: T[];
215
- node: T[];
238
+ node: T;
216
239
  },
217
240
  ) => void;
218
241
  }
@@ -11,12 +11,11 @@ import './style/index.less';
11
11
  const Node = <T extends Record<string, unknown> = any>(
12
12
  props: TreeNodeProps<T>,
13
13
  ) => {
14
- const { node } = props;
14
+ const { node, style: nodeStyle } = props;
15
15
  const {
16
- key,
16
+ uid,
17
17
  isLeaf,
18
18
  isLast,
19
- path,
20
19
  label,
21
20
  title,
22
21
  disabled,
@@ -31,12 +30,13 @@ const Node = <T extends Record<string, unknown> = any>(
31
30
  showIcon,
32
31
  icon: contextIcon,
33
32
  showLine,
34
- selectedKeys,
35
- checkedKeys,
36
- expandedKeys,
33
+ selectedUids,
34
+ checkedUids,
35
+ indeterminateUids,
36
+ expandedUids,
37
37
  switcherIcon,
38
38
  draggable,
39
- draggingKey,
39
+ draggingUid,
40
40
  dropState,
41
41
  onCheck,
42
42
  onSelect,
@@ -47,14 +47,10 @@ const Node = <T extends Record<string, unknown> = any>(
47
47
  onNodeDrop,
48
48
  } = useContext(TreeContext);
49
49
 
50
- const expanded = expandedKeys.includes(key);
51
- const checked = checkedKeys.includes(key);
52
- const selected = selectedKeys.includes(key);
53
-
54
- // All nodes on the path must be visible for this node to display
55
- const visible = path.every((p) => expandedKeys.includes(p));
56
-
57
- if (!visible) return null;
50
+ const expanded = expandedUids.has(uid);
51
+ const checked = checkedUids.has(uid);
52
+ const indeterminate = indeterminateUids.has(uid);
53
+ const selected = selectedUids.has(uid);
58
54
 
59
55
  const renderExpandedIcon = () => {
60
56
  const expandedIcon = switcherIcon ? (
@@ -82,7 +78,7 @@ const Node = <T extends Record<string, unknown> = any>(
82
78
  size="small"
83
79
  type="text"
84
80
  icon={expanded ? expandedIcon : collapseIcon}
85
- onClick={() => onExpand(key)}
81
+ onClick={() => onExpand(uid)}
86
82
  />
87
83
  )}
88
84
  </span>
@@ -93,7 +89,12 @@ const Node = <T extends Record<string, unknown> = any>(
93
89
  if (!checkable) return null;
94
90
  return (
95
91
  <span className={`${prefixCls}-node-checkbox`}>
96
- <Checkbox value={key} checked={checked} onChange={onCheck} />
92
+ <Checkbox
93
+ value={uid}
94
+ checked={checked}
95
+ indeterminate={indeterminate}
96
+ onChange={(e) => onCheck(e, uid)}
97
+ />
97
98
  </span>
98
99
  );
99
100
  };
@@ -113,7 +114,7 @@ const Node = <T extends Record<string, unknown> = any>(
113
114
  type="text"
114
115
  title={_title}
115
116
  icon={showIcon ? _icon : undefined}
116
- onClick={disabled ? undefined : () => onSelect(key)}
117
+ onClick={disabled ? undefined : () => onSelect(uid)}
117
118
  >
118
119
  {label}
119
120
  </Button>
@@ -121,8 +122,6 @@ const Node = <T extends Record<string, unknown> = any>(
121
122
  };
122
123
 
123
124
  const renderEmptyNodes = (depth: number) => {
124
- // eslint-disable-line @typescript-eslint/no-unused-vars
125
-
126
125
  const temp = new Array(depth).fill(0);
127
126
  if (showLine) {
128
127
  return temp.map((_, index) => (
@@ -145,17 +144,17 @@ const Node = <T extends Record<string, unknown> = any>(
145
144
  ));
146
145
  };
147
146
 
148
- const isDropTarget = dropState?.key === key;
147
+ const isDropTarget = dropState?.uid === uid;
149
148
  const dropPosition = isDropTarget ? dropState.position : null;
150
149
 
151
150
  const dragProps =
152
151
  draggable && !disabled
153
152
  ? {
154
153
  draggable: true as const,
155
- onDragStart: (e: React.DragEvent) => onNodeDragStart(key, e),
156
- onDragOver: (e: React.DragEvent) => onNodeDragOver(key, e),
157
- onDragLeave: (e: React.DragEvent) => onNodeDragLeave(key, e),
158
- onDrop: (e: React.DragEvent) => onNodeDrop(key, e),
154
+ onDragStart: (e: React.DragEvent) => onNodeDragStart(uid, e),
155
+ onDragOver: (e: React.DragEvent) => onNodeDragOver(uid, e),
156
+ onDragLeave: (e: React.DragEvent) => onNodeDragLeave(uid, e),
157
+ onDrop: (e: React.DragEvent) => onNodeDrop(uid, e),
159
158
  }
160
159
  : {};
161
160
 
@@ -165,14 +164,12 @@ const Node = <T extends Record<string, unknown> = any>(
165
164
  [`${prefixCls}-node-with-line`]: showLine,
166
165
  [`${prefixCls}-leaf-node`]: isLeaf,
167
166
  [`${prefixCls}-last-node`]: isLast,
168
- [`${prefixCls}-node-dragging`]: draggingKey === key,
167
+ [`${prefixCls}-node-dragging`]: draggingUid === uid,
169
168
  [`${prefixCls}-node-drag-over-before`]: dropPosition === 'before',
170
169
  [`${prefixCls}-node-drag-over-after`]: dropPosition === 'after',
171
170
  [`${prefixCls}-node-drag-over-inside`]: dropPosition === 'inside',
172
171
  })}
173
- // style={{
174
- // paddingLeft: depth * 28 + (isLeaf && checkable ? 24 : 0),
175
- // }}
172
+ style={nodeStyle}
176
173
  {...dragProps}
177
174
  >
178
175
  {renderEmptyNodes(depth)}