@rainbow-oh/yee-c 0.6.4 → 0.8.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 (145) 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/Form/form-store.d.ts +2 -1
  6. package/dist/cjs/Form/form-store.js +32 -17
  7. package/dist/cjs/List/demo/virtual.d.ts +3 -0
  8. package/dist/cjs/List/demo/virtual.js +53 -0
  9. package/dist/cjs/List/hooks/useListKeyboard.d.ts +28 -0
  10. package/dist/cjs/List/hooks/useListKeyboard.js +188 -0
  11. package/dist/cjs/List/interface.d.ts +29 -2
  12. package/dist/cjs/List/list.js +141 -21
  13. package/dist/cjs/List/style/index.less +24 -0
  14. package/dist/cjs/Select/demo/columns.d.ts +3 -0
  15. package/dist/cjs/Select/demo/columns.js +62 -0
  16. package/dist/cjs/Select/hooks/useSelectKeyboard.d.ts +13 -1
  17. package/dist/cjs/Select/hooks/useSelectKeyboard.js +81 -10
  18. package/dist/cjs/Select/interface.d.ts +30 -3
  19. package/dist/cjs/Select/option.js +4 -3
  20. package/dist/cjs/Select/options.js +90 -8
  21. package/dist/cjs/Select/select.js +32 -8
  22. package/dist/cjs/Select/style/index.less +14 -0
  23. package/dist/cjs/Selector/interface.d.ts +4 -0
  24. package/dist/cjs/Selector/selector.js +5 -3
  25. package/dist/cjs/Table/hooks/useColumns.js +1 -1
  26. package/dist/cjs/Table/interface.d.ts +1 -1
  27. package/dist/cjs/Table/style/index.less +35 -11
  28. package/dist/cjs/Table/table.js +57 -7
  29. package/dist/cjs/Table/tbody/cell.js +1 -1
  30. package/dist/cjs/Table/util/header.d.ts +3 -1
  31. package/dist/cjs/Table/util/header.js +30 -3
  32. package/dist/cjs/Tree/demo/draggable.js +2 -0
  33. package/dist/cjs/Tree/interface.d.ts +46 -23
  34. package/dist/cjs/Tree/tree-node.d.ts +1 -1
  35. package/dist/cjs/Tree/tree-node.js +30 -23
  36. package/dist/cjs/Tree/tree.d.ts +13 -12
  37. package/dist/cjs/Tree/tree.js +452 -227
  38. package/dist/cjs/Tree/utils/tree-tools.d.ts +43 -10
  39. package/dist/cjs/Tree/utils/tree-tools.js +129 -91
  40. package/dist/cjs/locale/context.d.ts +5 -4
  41. package/dist/cjs/locale/context.js +15 -8
  42. package/dist/cjs/locale/interface.d.ts +20 -0
  43. package/dist/cjs/locale/util.d.ts +3 -3
  44. package/dist/cjs/style/color.css +3 -0
  45. package/dist/cjs/style/size.css +2 -1
  46. package/dist/cjs/style/themes.css +1 -1
  47. package/dist/cjs/style/variables.css +6 -2
  48. package/dist/cjs/utils/isEqual.js +5 -7
  49. package/dist/cjs/version.d.ts +1 -1
  50. package/dist/cjs/version.js +1 -1
  51. package/dist/esm/Button/style/index.less +5 -4
  52. package/dist/esm/Drawer/drawer.js +2 -1
  53. package/dist/esm/Drawer/interface.d.ts +1 -1
  54. package/dist/esm/Drawer/style/index.less +1 -0
  55. package/dist/esm/Form/form-store.d.ts +2 -1
  56. package/dist/esm/Form/form-store.js +40 -19
  57. package/dist/esm/List/demo/virtual.d.ts +3 -0
  58. package/dist/esm/List/demo/virtual.js +22 -0
  59. package/dist/esm/List/hooks/useListKeyboard.d.ts +28 -0
  60. package/dist/esm/List/hooks/useListKeyboard.js +173 -0
  61. package/dist/esm/List/interface.d.ts +29 -2
  62. package/dist/esm/List/list.js +171 -14
  63. package/dist/esm/List/style/index.less +24 -0
  64. package/dist/esm/Select/demo/columns.d.ts +3 -0
  65. package/dist/esm/Select/demo/columns.js +36 -0
  66. package/dist/esm/Select/hooks/useSelectKeyboard.d.ts +13 -1
  67. package/dist/esm/Select/hooks/useSelectKeyboard.js +91 -14
  68. package/dist/esm/Select/interface.d.ts +30 -3
  69. package/dist/esm/Select/option.js +6 -2
  70. package/dist/esm/Select/options.js +114 -32
  71. package/dist/esm/Select/select.js +40 -11
  72. package/dist/esm/Select/style/index.less +14 -0
  73. package/dist/esm/Selector/interface.d.ts +4 -0
  74. package/dist/esm/Selector/selector.js +10 -4
  75. package/dist/esm/Table/hooks/useColumns.js +1 -1
  76. package/dist/esm/Table/interface.d.ts +1 -1
  77. package/dist/esm/Table/style/index.less +35 -11
  78. package/dist/esm/Table/table.js +54 -2
  79. package/dist/esm/Table/tbody/cell.js +1 -1
  80. package/dist/esm/Table/util/header.d.ts +3 -1
  81. package/dist/esm/Table/util/header.js +30 -2
  82. package/dist/esm/Tree/demo/draggable.js +2 -0
  83. package/dist/esm/Tree/interface.d.ts +46 -23
  84. package/dist/esm/Tree/tree-node.d.ts +1 -1
  85. package/dist/esm/Tree/tree-node.js +26 -31
  86. package/dist/esm/Tree/tree.d.ts +13 -12
  87. package/dist/esm/Tree/tree.js +572 -163
  88. package/dist/esm/Tree/utils/tree-tools.d.ts +43 -10
  89. package/dist/esm/Tree/utils/tree-tools.js +219 -104
  90. package/dist/esm/locale/context.d.ts +5 -4
  91. package/dist/esm/locale/context.js +25 -9
  92. package/dist/esm/locale/interface.d.ts +20 -0
  93. package/dist/esm/locale/util.d.ts +3 -3
  94. package/dist/esm/style/color.css +3 -0
  95. package/dist/esm/style/size.css +2 -1
  96. package/dist/esm/style/themes.css +1 -1
  97. package/dist/esm/style/variables.css +6 -2
  98. package/dist/esm/utils/isEqual.js +5 -7
  99. package/dist/esm/version.d.ts +1 -1
  100. package/dist/esm/version.js +1 -1
  101. package/dist/umd/yee-c.min.css +4 -4
  102. package/dist/umd/yee-c.min.js +1 -1
  103. package/package.json +1 -1
  104. package/src/Button/style/index.less +5 -4
  105. package/src/Drawer/drawer.tsx +1 -1
  106. package/src/Drawer/interface.ts +1 -0
  107. package/src/Drawer/style/index.less +1 -0
  108. package/src/Form/form-store.ts +42 -15
  109. package/src/List/demo/virtual.tsx +20 -0
  110. package/src/List/hooks/useListKeyboard.ts +235 -0
  111. package/src/List/index.en-US.md +6 -0
  112. package/src/List/index.zh-CN.md +6 -0
  113. package/src/List/interface.ts +29 -2
  114. package/src/List/list.tsx +157 -19
  115. package/src/List/style/index.less +24 -0
  116. package/src/Select/demo/columns.tsx +35 -0
  117. package/src/Select/hooks/useSelectKeyboard.ts +112 -15
  118. package/src/Select/index.en-US.md +4 -0
  119. package/src/Select/index.zh-CN.md +4 -0
  120. package/src/Select/interface.ts +31 -4
  121. package/src/Select/option.tsx +9 -3
  122. package/src/Select/options.tsx +119 -26
  123. package/src/Select/select.tsx +41 -8
  124. package/src/Select/style/index.less +14 -0
  125. package/src/Selector/interface.ts +4 -0
  126. package/src/Selector/selector.tsx +8 -3
  127. package/src/Table/hooks/useColumns.ts +1 -1
  128. package/src/Table/interface.ts +1 -1
  129. package/src/Table/style/index.less +35 -11
  130. package/src/Table/table.tsx +60 -1
  131. package/src/Table/tbody/cell.tsx +1 -1
  132. package/src/Table/util/header.ts +48 -2
  133. package/src/Tree/demo/draggable.tsx +2 -0
  134. package/src/Tree/interface.ts +46 -23
  135. package/src/Tree/tree-node.tsx +26 -29
  136. package/src/Tree/tree.tsx +573 -274
  137. package/src/Tree/utils/tree-tools.ts +231 -125
  138. package/src/locale/_README.md +22 -13
  139. package/src/locale/context.tsx +33 -16
  140. package/src/locale/interface.ts +24 -0
  141. package/src/locale/util.ts +6 -6
  142. package/src/style/color.css +3 -0
  143. package/src/style/size.css +2 -1
  144. package/src/utils/isEqual.ts +5 -7
  145. package/src/version.ts +1 -1
@@ -171,7 +171,7 @@
171
171
  --m-btn-hover-color: var(--yee-primary-color-hover);
172
172
  --m-btn-active-color: var(--yee-primary-color-active);
173
173
  --m-btn-bg-color: var(--yee-btn-primary-bg-color);
174
- --m-btn-hover-bg-color: var(--yee-primary-color-hover);
174
+ --m-btn-hover-bg-color: var(--yee-primary-color-border);
175
175
  --m-btn-active-bg-color: var(--yee-primary-color-active);
176
176
  --m-btn-border-color: var(--yee-primary-color);
177
177
  --m-btn-border-hover-color: var(--yee-primary-color-hover);
@@ -186,7 +186,7 @@
186
186
  --m-btn-border-color: var(--yee-success-color);
187
187
  --m-btn-border-hover-color: var(--yee-success-color-hover);
188
188
  --m-btn-border-active-color: var(--yee-success-color-active);
189
- --m-btn-hover-bg-color: var(--yee-success-color-hover);
189
+ --m-btn-hover-bg-color: var(--yee-success-color-border);
190
190
  --m-btn-active-bg-color: var(--yee-success-color-active);
191
191
  --m-btn-active-shadow-color: var(--yee-success-color-active);
192
192
  }
@@ -198,7 +198,8 @@
198
198
  --m-btn-border-color: var(--yee-error-color);
199
199
  --m-btn-border-hover-color: var(--yee-error-color-hover);
200
200
  --m-btn-border-active-color: var(--yee-error-color-active);
201
- --m-btn-hover-bg-color: var(--yee-error-color-hover);
201
+ // --m-btn-hover-bg-color: var(--yee-error-color-hover);
202
+ --m-btn-hover-bg-color: var(--yee-error-color-border);
202
203
  --m-btn-active-bg-color: var(--yee-error-color-active);
203
204
  --m-btn-active-shadow-color: var(--yee-error-color-active);
204
205
  }
@@ -210,7 +211,7 @@
210
211
  --m-btn-border-color: var(--yee-warning-color);
211
212
  --m-btn-border-hover-color: var(--yee-warning-color-hover);
212
213
  --m-btn-border-active-color: var(--yee-warning-color-active);
213
- --m-btn-hover-bg-color: var(--yee-warning-color-hover);
214
+ --m-btn-hover-bg-color: var(--yee-warning-color-border);
214
215
  --m-btn-active-bg-color: var(--yee-warning-color-active);
215
216
  --m-btn-active-shadow-color: var(--yee-warning-color-active);
216
217
  }
@@ -256,7 +256,7 @@ var Drawer = (baseprops) => {
256
256
  className: (0, import_clsx.default)(`${prefixCls}-header`, classNames == null ? void 0 : classNames.header),
257
257
  style: styles == null ? void 0 : styles.header
258
258
  },
259
- /* @__PURE__ */ import_react2.default.createElement("div", { className: `${prefixCls}-title` }, title)
259
+ /* @__PURE__ */ import_react2.default.createElement("div", { className: (0, import_clsx.default)(`${prefixCls}-title`, classNames == null ? void 0 : classNames.title), style: styles == null ? void 0 : styles.title }, title)
260
260
  ) : null,
261
261
  /* @__PURE__ */ import_react2.default.createElement(
262
262
  "div",
@@ -1,6 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import type { DataAttributeProps } from '../utils/types';
3
- export type SemanticDOM = 'mask' | 'wrapper' | 'close' | 'header' | 'content' | 'body' | 'footer';
3
+ export type SemanticDOM = 'mask' | 'wrapper' | 'close' | 'header' | 'title' | 'content' | 'body' | 'footer';
4
4
  export interface DrawerProps extends DataAttributeProps {
5
5
  /**
6
6
  * Custom prefix class name
@@ -38,6 +38,7 @@
38
38
  }
39
39
 
40
40
  &-title {
41
+ font-size: var(--yee-drawer-title-font-size);
41
42
  font-weight: var(--yee-drawer-title-font-weight);
42
43
  }
43
44
 
@@ -28,7 +28,8 @@ export declare class FormStore {
28
28
  clearFields: (name?: Name[]) => void;
29
29
  validateField: (name: Name, trigger?: TRIGGER) => Promise<ValidateMessage[]>;
30
30
  validateFields: (names?: string[], trigger?: TRIGGER) => Promise<ValidateMessage[]>;
31
- refreshField: (newStore?: Store) => void;
31
+ private getRelatedFieldNames;
32
+ refreshField: (newStore?: Store, relatedFieldNames?: Name[]) => void;
32
33
  subscribe: (namePath?: NamePath, callback?: () => void) => (() => void);
33
34
  private notifyWatchers;
34
35
  registerGroupEntity: (id: string, entity: GroupEntity) => (() => void);
@@ -80,6 +80,7 @@ function move(array, from, to) {
80
80
  return result;
81
81
  }
82
82
  var GLOBAL_WATCH_KEY = "__ALL__";
83
+ var isRelatedNamePath = (first, second) => first === second || first.startsWith(`${second}.`) || second.startsWith(`${first}.`);
83
84
  var FormStore = class {
84
85
  constructor() {
85
86
  this.store = {};
@@ -170,10 +171,12 @@ var FormStore = class {
170
171
  }
171
172
  });
172
173
  }
174
+ const changedKeys = Object.keys(newStore);
175
+ const relatedFieldNames = this.getRelatedFieldNames(changedKeys);
173
176
  if (trigger === "onChange" || trigger === "onBlur" || trigger === "onSubmit") {
174
- void this.validateFields(Object.keys(newStore), trigger);
177
+ void this.validateFields(relatedFieldNames, trigger);
175
178
  }
176
- this.refreshField(newStore);
179
+ this.refreshField(newStore, relatedFieldNames);
177
180
  };
178
181
  this.resetFields = (name) => {
179
182
  var _a, _b;
@@ -281,16 +284,20 @@ var FormStore = class {
281
284
  });
282
285
  return err;
283
286
  });
284
- this.refreshField = (newStore) => {
287
+ this.getRelatedFieldNames = (changedKeys) => Array.from(this.fieldMap.keys()).filter(
288
+ (fieldName) => changedKeys.some(
289
+ (changedKey) => isRelatedNamePath(String(fieldName), changedKey)
290
+ )
291
+ );
292
+ this.refreshField = (newStore, relatedFieldNames) => {
285
293
  if (newStore) {
286
- const keys = Object.keys(newStore);
287
- keys.forEach((key) => {
288
- const entity = this.fieldMap.get(key);
289
- if (entity) {
290
- entity == null ? void 0 : entity.onStoreChange();
291
- }
292
- this.notifyWatchers(key);
294
+ const changedKeys = Object.keys(newStore);
295
+ const fieldNames = relatedFieldNames != null ? relatedFieldNames : this.getRelatedFieldNames(changedKeys);
296
+ fieldNames.forEach((fieldName) => {
297
+ var _a;
298
+ (_a = this.fieldMap.get(fieldName)) == null ? void 0 : _a.onStoreChange();
293
299
  });
300
+ this.notifyWatchers(changedKeys);
294
301
  } else {
295
302
  this.fieldEntities.forEach((entity) => {
296
303
  entity == null ? void 0 : entity.onStoreChange();
@@ -308,15 +315,23 @@ var FormStore = class {
308
315
  }
309
316
  this.watchers.get(key).add(callback);
310
317
  return () => {
311
- var _a;
312
- (_a = this.watchers.get(key)) == null ? void 0 : _a.delete(callback);
318
+ const watchers = this.watchers.get(key);
319
+ watchers == null ? void 0 : watchers.delete(callback);
320
+ if ((watchers == null ? void 0 : watchers.size) === 0) {
321
+ this.watchers.delete(key);
322
+ }
313
323
  };
314
324
  };
315
- this.notifyWatchers = (changedKey) => {
316
- var _a, _b;
317
- if (changedKey) {
318
- (_a = this.watchers.get(changedKey)) == null ? void 0 : _a.forEach((cb) => cb());
319
- (_b = this.watchers.get(GLOBAL_WATCH_KEY)) == null ? void 0 : _b.forEach((cb) => cb());
325
+ this.notifyWatchers = (changedKeys) => {
326
+ if (changedKeys) {
327
+ const keys = Array.isArray(changedKeys) ? changedKeys : [changedKeys];
328
+ const callbacks = /* @__PURE__ */ new Set();
329
+ this.watchers.forEach((watchers, watchedKey) => {
330
+ if (watchedKey === GLOBAL_WATCH_KEY || keys.some((changedKey) => isRelatedNamePath(watchedKey, changedKey))) {
331
+ watchers.forEach((callback) => callbacks.add(callback));
332
+ }
333
+ });
334
+ callbacks.forEach((callback) => callback());
320
335
  } else {
321
336
  this.watchers.forEach((set) => set.forEach((cb) => cb()));
322
337
  }
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const _default: () => React.JSX.Element;
3
+ export default _default;
@@ -0,0 +1,53 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/List/demo/virtual.tsx
30
+ var virtual_exports = {};
31
+ __export(virtual_exports, {
32
+ default: () => virtual_default
33
+ });
34
+ module.exports = __toCommonJS(virtual_exports);
35
+ var import_yee_c = require("../..");
36
+ var import_react = __toESM(require("react"));
37
+ var items = Array.from({ length: 1e4 }, (_, i) => ({
38
+ key: String(i),
39
+ label: `List item ${i + 1}`,
40
+ value: String(i)
41
+ }));
42
+ var virtual_default = () => {
43
+ return /* @__PURE__ */ import_react.default.createElement(
44
+ import_yee_c.List,
45
+ {
46
+ virtual: true,
47
+ height: 300,
48
+ itemHeight: 32,
49
+ items,
50
+ onClick: (item) => console.log("Clicked:", item.label)
51
+ }
52
+ );
53
+ };
@@ -0,0 +1,28 @@
1
+ import type { ListItemProps } from '../interface';
2
+ interface UseListKeyboardParams {
3
+ items: Array<ListItemProps>;
4
+ /**
5
+ * Number of columns in the grid layout.
6
+ * Affects ArrowUp/Down (jump by columns) and ArrowLeft/Right behavior.
7
+ * @default 1
8
+ */
9
+ columns?: number;
10
+ /**
11
+ * Controlled focused key from the parent.
12
+ * When provided, the hook uses this value instead of internal state.
13
+ */
14
+ focusedKey?: string | number;
15
+ /**
16
+ * Callback when focused key changes.
17
+ */
18
+ onFocusChange?: (key: string | number) => void;
19
+ /**
20
+ * Callback when Enter is pressed on a focused item.
21
+ */
22
+ onClick?: (item: ListItemProps) => void;
23
+ }
24
+ export default function useListKeyboard({ items, columns, focusedKey: controlledFocusedKey, onFocusChange, onClick, }: UseListKeyboardParams): {
25
+ focusedKey: string | number;
26
+ onKeyDown: (event: React.KeyboardEvent) => void;
27
+ };
28
+ export {};
@@ -0,0 +1,188 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/List/hooks/useListKeyboard.ts
20
+ var useListKeyboard_exports = {};
21
+ __export(useListKeyboard_exports, {
22
+ default: () => useListKeyboard
23
+ });
24
+ module.exports = __toCommonJS(useListKeyboard_exports);
25
+ var import_react = require("react");
26
+ function useListKeyboard({
27
+ items,
28
+ columns = 1,
29
+ focusedKey: controlledFocusedKey,
30
+ onFocusChange,
31
+ onClick
32
+ }) {
33
+ const [internalFocusedKey, setInternalFocusedKey] = (0, import_react.useState)("");
34
+ const isControlled = controlledFocusedKey !== void 0;
35
+ const focusedKey = isControlled ? controlledFocusedKey : internalFocusedKey;
36
+ const setFocusedKey = (0, import_react.useCallback)(
37
+ (key) => {
38
+ if (!isControlled) {
39
+ setInternalFocusedKey(key);
40
+ }
41
+ onFocusChange == null ? void 0 : onFocusChange(key);
42
+ },
43
+ [isControlled, onFocusChange]
44
+ );
45
+ (0, import_react.useEffect)(() => {
46
+ if (!isControlled && internalFocusedKey !== "" && !items.some((item) => item.key === internalFocusedKey)) {
47
+ setInternalFocusedKey("");
48
+ }
49
+ }, [items, isControlled, internalFocusedKey]);
50
+ const getFocusedIndex = () => {
51
+ if (focusedKey === "" || focusedKey === void 0)
52
+ return -1;
53
+ return items.findIndex((item) => item.key === focusedKey);
54
+ };
55
+ const findNextEnabledIndex = (startIndex, direction) => {
56
+ const count = items.length;
57
+ if (count === 0)
58
+ return -1;
59
+ let next = direction === 1 ? startIndex + 1 >= count ? 0 : startIndex + 1 : startIndex - 1 < 0 ? count - 1 : startIndex - 1;
60
+ let attempts = 0;
61
+ while (items[next].disabled && attempts < count) {
62
+ next = direction === 1 ? next + 1 >= count ? 0 : next + 1 : next - 1 < 0 ? count - 1 : next - 1;
63
+ attempts++;
64
+ }
65
+ return items[next].disabled ? -1 : next;
66
+ };
67
+ const handleArrowUp = () => {
68
+ var _a;
69
+ const focusedIndex = getFocusedIndex();
70
+ let nextIndex;
71
+ if (columns > 1) {
72
+ if (focusedIndex === -1) {
73
+ nextIndex = findNextEnabledIndex(items.length - 1, -1);
74
+ } else {
75
+ const target = focusedIndex - columns;
76
+ if (target >= 0) {
77
+ nextIndex = items[target].disabled ? findNextEnabledIndex(target, -1) : target;
78
+ } else {
79
+ const lastRowStart = Math.floor((items.length - 1) / columns) * columns;
80
+ const col = focusedIndex % columns;
81
+ const wrapTarget = Math.min(lastRowStart + col, items.length - 1);
82
+ nextIndex = ((_a = items[wrapTarget]) == null ? void 0 : _a.disabled) ? findNextEnabledIndex(wrapTarget, -1) : wrapTarget;
83
+ }
84
+ }
85
+ } else {
86
+ if (focusedIndex === -1) {
87
+ nextIndex = findNextEnabledIndex(items.length - 1, -1);
88
+ } else {
89
+ nextIndex = findNextEnabledIndex(focusedIndex, -1);
90
+ }
91
+ }
92
+ if (nextIndex !== -1) {
93
+ setFocusedKey(items[nextIndex].key);
94
+ }
95
+ };
96
+ const handleArrowDown = () => {
97
+ var _a;
98
+ const focusedIndex = getFocusedIndex();
99
+ let nextIndex;
100
+ if (columns > 1) {
101
+ if (focusedIndex === -1) {
102
+ nextIndex = findNextEnabledIndex(-1, 1);
103
+ } else {
104
+ const target = focusedIndex + columns;
105
+ if (target < items.length) {
106
+ nextIndex = items[target].disabled ? findNextEnabledIndex(target, 1) : target;
107
+ } else {
108
+ const col = focusedIndex % columns;
109
+ const wrapTarget = col;
110
+ nextIndex = ((_a = items[wrapTarget]) == null ? void 0 : _a.disabled) ? findNextEnabledIndex(wrapTarget, 1) : wrapTarget;
111
+ }
112
+ }
113
+ } else {
114
+ if (focusedIndex === -1) {
115
+ nextIndex = findNextEnabledIndex(-1, 1);
116
+ } else {
117
+ nextIndex = findNextEnabledIndex(focusedIndex, 1);
118
+ }
119
+ }
120
+ if (nextIndex !== -1) {
121
+ setFocusedKey(items[nextIndex].key);
122
+ }
123
+ };
124
+ const handleArrowLeft = () => {
125
+ const focusedIndex = getFocusedIndex();
126
+ if (focusedIndex <= 0)
127
+ return;
128
+ const nextIndex = findNextEnabledIndex(focusedIndex, -1);
129
+ if (nextIndex !== -1) {
130
+ setFocusedKey(items[nextIndex].key);
131
+ }
132
+ };
133
+ const handleArrowRight = () => {
134
+ const focusedIndex = getFocusedIndex();
135
+ if (focusedIndex >= items.length - 1 && focusedIndex !== -1)
136
+ return;
137
+ const nextIndex = findNextEnabledIndex(
138
+ focusedIndex === -1 ? -1 : focusedIndex,
139
+ 1
140
+ );
141
+ if (nextIndex !== -1) {
142
+ setFocusedKey(items[nextIndex].key);
143
+ }
144
+ };
145
+ const handleEnter = () => {
146
+ if (focusedKey === "" || focusedKey === void 0)
147
+ return;
148
+ const item = items.find((i) => i.key === focusedKey);
149
+ if (item && !item.disabled) {
150
+ onClick == null ? void 0 : onClick(item);
151
+ }
152
+ };
153
+ const onKeyDown = (event) => {
154
+ if (!items || items.length === 0)
155
+ return;
156
+ switch (event.key) {
157
+ case "ArrowUp":
158
+ event.preventDefault();
159
+ handleArrowUp();
160
+ break;
161
+ case "ArrowDown":
162
+ event.preventDefault();
163
+ handleArrowDown();
164
+ break;
165
+ case "ArrowLeft":
166
+ if (columns > 1) {
167
+ event.preventDefault();
168
+ handleArrowLeft();
169
+ }
170
+ break;
171
+ case "ArrowRight":
172
+ if (columns > 1) {
173
+ event.preventDefault();
174
+ handleArrowRight();
175
+ }
176
+ break;
177
+ case "Enter":
178
+ event.preventDefault();
179
+ handleEnter();
180
+ break;
181
+ default:
182
+ }
183
+ };
184
+ return {
185
+ focusedKey,
186
+ onKeyDown
187
+ };
188
+ }
@@ -47,8 +47,8 @@ export interface ListProps extends Omit<React.HTMLAttributes<HTMLUListElement>,
47
47
  */
48
48
  bordered?: boolean;
49
49
  /**
50
- * Set the focused item
51
- * */
50
+ * Set the focused item (controlled)
51
+ */
52
52
  focusedKey?: string | number;
53
53
  /**
54
54
  * Render list item as a function
@@ -58,6 +58,33 @@ export interface ListProps extends Omit<React.HTMLAttributes<HTMLUListElement>,
58
58
  * Click event for list items
59
59
  */
60
60
  onClick?: (item: ListItemProps) => void;
61
+ /**
62
+ * Callback when focused item changes via keyboard navigation
63
+ */
64
+ onFocusChange?: (key: string | number) => void;
65
+ /**
66
+ * Enable virtual scrolling for large lists.
67
+ * Requires `height` to be set.
68
+ * @default false
69
+ */
70
+ virtual?: boolean;
71
+ /**
72
+ * Height of the scrollable container in pixels.
73
+ * Required when `virtual` is true.
74
+ */
75
+ height?: number;
76
+ /**
77
+ * Fixed height of each list item row in pixels.
78
+ * Used for virtual scroll calculation.
79
+ * @default 32
80
+ */
81
+ itemHeight?: number;
82
+ /**
83
+ * Number of columns in the list layout.
84
+ * When greater than 1, items are displayed in a grid.
85
+ * @default 1
86
+ */
87
+ columns?: number;
61
88
  }
62
89
  export type ListCtxType = {
63
90
  prefixCls: string;
@@ -65,7 +65,9 @@ module.exports = __toCommonJS(list_exports);
65
65
  var import_clsx = __toESM(require("clsx"));
66
66
  var import_react = __toESM(require("react"));
67
67
  var import_Config_Provider = require("../Config-Provider");
68
+ var import_useVirtualList = __toESM(require("../hooks/useVirtualList"));
68
69
  var import_mergeContextToProps = __toESM(require("../utils/mergeContextToProps"));
70
+ var import_useListKeyboard = __toESM(require("./hooks/useListKeyboard"));
69
71
  var import_list_item = __toESM(require("./list-item"));
70
72
  var import_style = require("./style/index.less");
71
73
  var ListCtx = (0, import_react.createContext)({});
@@ -75,46 +77,164 @@ var List = (0, import_react.forwardRef)((baseprops, ref) => {
75
77
  const _a = props, {
76
78
  prefixCls = "yee-list",
77
79
  className,
80
+ style,
78
81
  items,
79
82
  bordered,
80
- focusedKey,
83
+ focusedKey: controlledFocusedKey,
81
84
  itemRender,
82
- onClick
85
+ onClick,
86
+ onFocusChange,
87
+ virtual = false,
88
+ height,
89
+ itemHeight = 32,
90
+ columns = 1
83
91
  } = _a, rest = __objRest(_a, [
84
92
  "prefixCls",
85
93
  "className",
94
+ "style",
86
95
  "items",
87
96
  "bordered",
88
97
  "focusedKey",
89
98
  "itemRender",
90
- "onClick"
99
+ "onClick",
100
+ "onFocusChange",
101
+ "virtual",
102
+ "height",
103
+ "itemHeight",
104
+ "columns"
91
105
  ]);
106
+ const multiColumn = columns > 1;
107
+ const useVirtual = virtual && height !== null && height !== void 0 && Array.isArray(items);
108
+ const safeItems = Array.isArray(items) ? items : [];
109
+ const { focusedKey, onKeyDown } = (0, import_useListKeyboard.default)({
110
+ items: safeItems,
111
+ columns,
112
+ focusedKey: controlledFocusedKey,
113
+ onFocusChange,
114
+ onClick
115
+ });
116
+ const rowCount = multiColumn ? Math.ceil(safeItems.length / columns) : safeItems.length;
117
+ const { start, end, offsetY, totalHeight, viewportRef, onScroll } = (0, import_useVirtualList.default)({
118
+ itemCount: useVirtual ? rowCount : 0,
119
+ itemHeight,
120
+ overscan: 4
121
+ });
92
122
  const cls = (0, import_clsx.default)(
93
123
  prefixCls,
94
124
  {
95
- [`${prefixCls}-bordered`]: bordered
125
+ [`${prefixCls}-bordered`]: bordered,
126
+ [`${prefixCls}-grid`]: multiColumn && !useVirtual
96
127
  },
97
128
  className
98
129
  );
99
- const itemRenders = () => {
100
- return Array.isArray(items) ? items.map((item, index) => {
101
- if (typeof itemRender === "function") {
102
- return itemRender(item);
103
- }
104
- return /* @__PURE__ */ import_react.default.createElement(import_list_item.default, __spreadProps(__spreadValues({}, item), { $key: item.key, key: item.key || index }));
105
- }) : null;
130
+ const renderItem = (item, index) => {
131
+ if (typeof itemRender === "function") {
132
+ return itemRender(item);
133
+ }
134
+ return /* @__PURE__ */ import_react.default.createElement(import_list_item.default, __spreadProps(__spreadValues({}, item), { $key: item.key, key: item.key || index }));
106
135
  };
107
- return /* @__PURE__ */ import_react.default.createElement("ul", __spreadValues({ ref, className: cls, tabIndex: 0, "aria-label": "list" }, rest), /* @__PURE__ */ import_react.default.createElement(
108
- ListCtx.Provider,
109
- {
110
- value: {
111
- prefixCls,
112
- focusedKey,
113
- onClick
136
+ if (useVirtual && multiColumn) {
137
+ const visibleRows = [];
138
+ for (let rowIdx = start; rowIdx < end; rowIdx++) {
139
+ const rowItems = [];
140
+ for (let col = 0; col < columns; col++) {
141
+ const flatIdx = rowIdx * columns + col;
142
+ if (flatIdx < safeItems.length) {
143
+ rowItems.push({ item: safeItems[flatIdx], flatIdx });
144
+ }
114
145
  }
115
- },
116
- itemRenders()
117
- ));
146
+ visibleRows.push({ rowIdx, rowItems });
147
+ }
148
+ return /* @__PURE__ */ import_react.default.createElement(
149
+ "ul",
150
+ __spreadValues({
151
+ ref,
152
+ className: cls,
153
+ tabIndex: 0,
154
+ "aria-label": "list",
155
+ onKeyDown
156
+ }, rest),
157
+ /* @__PURE__ */ import_react.default.createElement(ListCtx.Provider, { value: { prefixCls, focusedKey, onClick } }, /* @__PURE__ */ import_react.default.createElement(
158
+ "div",
159
+ {
160
+ ref: viewportRef,
161
+ onScroll,
162
+ style: { height, overflowY: "auto" }
163
+ },
164
+ /* @__PURE__ */ import_react.default.createElement("div", { style: { height: totalHeight, position: "relative" } }, visibleRows.map(({ rowIdx, rowItems }, ri) => /* @__PURE__ */ import_react.default.createElement(
165
+ "div",
166
+ {
167
+ key: rowIdx,
168
+ className: `${prefixCls}-grid-row`,
169
+ style: {
170
+ position: "absolute",
171
+ top: 0,
172
+ left: 0,
173
+ right: 0,
174
+ height: itemHeight,
175
+ transform: `translateY(${offsetY + ri * itemHeight}px)`,
176
+ display: "grid",
177
+ gridTemplateColumns: `repeat(${columns}, 1fr)`
178
+ }
179
+ },
180
+ rowItems.map(
181
+ ({ item, flatIdx }) => renderItem(item, flatIdx)
182
+ )
183
+ )))
184
+ ))
185
+ );
186
+ }
187
+ if (useVirtual) {
188
+ const slicedItems = safeItems.slice(start, end);
189
+ return /* @__PURE__ */ import_react.default.createElement(
190
+ "ul",
191
+ __spreadValues({
192
+ ref,
193
+ className: cls,
194
+ tabIndex: 0,
195
+ "aria-label": "list",
196
+ onKeyDown
197
+ }, rest),
198
+ /* @__PURE__ */ import_react.default.createElement(ListCtx.Provider, { value: { prefixCls, focusedKey, onClick } }, /* @__PURE__ */ import_react.default.createElement(
199
+ "div",
200
+ {
201
+ ref: viewportRef,
202
+ onScroll,
203
+ style: { height, overflowY: "auto" }
204
+ },
205
+ /* @__PURE__ */ import_react.default.createElement("div", { style: { height: totalHeight, position: "relative" } }, slicedItems.map((item, i) => {
206
+ const element = renderItem(item, start + i);
207
+ return /* @__PURE__ */ import_react.default.createElement(
208
+ "div",
209
+ {
210
+ key: item.key || start + i,
211
+ style: {
212
+ position: "absolute",
213
+ top: 0,
214
+ left: 0,
215
+ width: "100%",
216
+ height: itemHeight,
217
+ transform: `translateY(${offsetY + i * itemHeight}px)`
218
+ }
219
+ },
220
+ element
221
+ );
222
+ }))
223
+ ))
224
+ );
225
+ }
226
+ return /* @__PURE__ */ import_react.default.createElement(
227
+ "ul",
228
+ __spreadValues({
229
+ ref,
230
+ className: cls,
231
+ tabIndex: 0,
232
+ "aria-label": "list",
233
+ onKeyDown,
234
+ style: multiColumn ? __spreadValues({ "--yee-list-columns": columns }, style) : style
235
+ }, rest),
236
+ /* @__PURE__ */ import_react.default.createElement(ListCtx.Provider, { value: { prefixCls, focusedKey, onClick } }, safeItems.map((item, index) => renderItem(item, index)))
237
+ );
118
238
  });
119
239
  List.displayName = "List";
120
240
  var list_default = List;