@rainbow-oh/yee-c 0.12.2 → 0.14.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 (56) hide show
  1. package/dist/cjs/Field/demo/standalone.d.ts +3 -0
  2. package/dist/cjs/Field/demo/standalone.js +56 -0
  3. package/dist/cjs/Field/field.js +20 -11
  4. package/dist/cjs/Field/interface.d.ts +7 -4
  5. package/dist/cjs/Field/style/index.less +1 -0
  6. package/dist/cjs/Table/demo/controlled-filter.d.ts +3 -0
  7. package/dist/cjs/Table/demo/controlled-filter.js +105 -0
  8. package/dist/cjs/Table/hooks/useFilter.d.ts +5 -1
  9. package/dist/cjs/Table/hooks/useFilter.js +80 -42
  10. package/dist/cjs/Table/index.d.ts +1 -1
  11. package/dist/cjs/Table/interface.d.ts +14 -4
  12. package/dist/cjs/Table/table.js +22 -25
  13. package/dist/cjs/Table/theader/cell.js +0 -3
  14. package/dist/cjs/Table/theader/filter.js +22 -5
  15. package/dist/cjs/style/themes.css +1 -1
  16. package/dist/cjs/style/variables.css +1 -1
  17. package/dist/cjs/version.d.ts +1 -1
  18. package/dist/cjs/version.js +1 -1
  19. package/dist/esm/Field/demo/standalone.d.ts +3 -0
  20. package/dist/esm/Field/demo/standalone.js +43 -0
  21. package/dist/esm/Field/field.js +24 -11
  22. package/dist/esm/Field/interface.d.ts +7 -4
  23. package/dist/esm/Field/style/index.less +1 -0
  24. package/dist/esm/Table/demo/controlled-filter.d.ts +3 -0
  25. package/dist/esm/Table/demo/controlled-filter.js +122 -0
  26. package/dist/esm/Table/hooks/useFilter.d.ts +5 -1
  27. package/dist/esm/Table/hooks/useFilter.js +112 -69
  28. package/dist/esm/Table/index.d.ts +1 -1
  29. package/dist/esm/Table/interface.d.ts +14 -4
  30. package/dist/esm/Table/table.js +34 -21
  31. package/dist/esm/Table/theader/cell.js +1 -3
  32. package/dist/esm/Table/theader/filter.js +22 -1
  33. package/dist/esm/style/themes.css +1 -1
  34. package/dist/esm/style/variables.css +1 -1
  35. package/dist/esm/version.d.ts +1 -1
  36. package/dist/esm/version.js +1 -1
  37. package/dist/umd/yee-c.min.css +1 -1
  38. package/dist/umd/yee-c.min.js +1 -1
  39. package/package.json +1 -1
  40. package/src/Field/demo/standalone.tsx +30 -0
  41. package/src/Field/field.tsx +38 -22
  42. package/src/Field/index.en-US.md +8 -4
  43. package/src/Field/index.zh-CN.md +8 -4
  44. package/src/Field/interface.ts +7 -4
  45. package/src/Field/style/index.less +1 -0
  46. package/src/Table/demo/controlled-filter.tsx +107 -0
  47. package/src/Table/hooks/useFilter.ts +107 -49
  48. package/src/Table/index.en-US.md +22 -1
  49. package/src/Table/index.ts +1 -0
  50. package/src/Table/index.zh-CN.md +22 -1
  51. package/src/Table/interface.ts +14 -2
  52. package/src/Table/table-filter.test.tsx +343 -0
  53. package/src/Table/table.tsx +29 -27
  54. package/src/Table/theader/cell.tsx +0 -2
  55. package/src/Table/theader/filter.tsx +26 -1
  56. package/src/version.ts +1 -1
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const _default: () => React.JSX.Element;
3
+ export default _default;
@@ -0,0 +1,56 @@
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/Field/demo/standalone.tsx
30
+ var standalone_exports = {};
31
+ __export(standalone_exports, {
32
+ default: () => standalone_default
33
+ });
34
+ module.exports = __toCommonJS(standalone_exports);
35
+ var import_yee_c = require("../..");
36
+ var import_react = __toESM(require("react"));
37
+ var standalone_default = () => {
38
+ const [name, setName] = (0, import_react.useState)("");
39
+ const [remark, setRemark] = (0, import_react.useState)("");
40
+ return /* @__PURE__ */ import_react.default.createElement("div", { style: { maxWidth: 400 } }, /* @__PURE__ */ import_react.default.createElement(import_yee_c.Field, { label: "Name", required: true }, /* @__PURE__ */ import_react.default.createElement(
41
+ import_yee_c.Input,
42
+ {
43
+ value: name,
44
+ onChange: (v) => setName(v),
45
+ placeholder: "Standalone controlled Input"
46
+ }
47
+ )), /* @__PURE__ */ import_react.default.createElement(import_yee_c.Field, { label: "Remark" }, /* @__PURE__ */ import_react.default.createElement(
48
+ import_yee_c.TextArea,
49
+ {
50
+ value: remark,
51
+ onChange: (v) => setRemark(v),
52
+ placeholder: "Standalone controlled TextArea",
53
+ rows: 3
54
+ }
55
+ )), /* @__PURE__ */ import_react.default.createElement(import_yee_c.Field, null, /* @__PURE__ */ import_react.default.createElement(import_yee_c.Input, { placeholder: "Field without label" })));
56
+ };
@@ -55,7 +55,7 @@ var Field = (props) => {
55
55
  disabled
56
56
  } = props;
57
57
  const [, forceUpdate] = (0, import_react2.useReducer)((x) => x + 1, 0);
58
- const { form } = (0, import_useVirtualField.default)(formName, name, {
58
+ const { form } = (0, import_useVirtualField.default)(formName != null ? formName : "", name != null ? name : "", {
59
59
  props,
60
60
  onStoreChange: forceUpdate
61
61
  });
@@ -71,11 +71,20 @@ var Field = (props) => {
71
71
  return Array.isArray(rules) && rules.some((rule) => rule.required) || required;
72
72
  };
73
73
  const getControlled = (children2) => {
74
- const mergedDisabled = disabled || children2.props.disabled;
74
+ const childProps = children2.props;
75
+ if (!form || name === void 0) {
76
+ if (disabled === void 0)
77
+ return children2;
78
+ return import_react2.default.cloneElement(children2, {
79
+ disabled: disabled || childProps.disabled
80
+ });
81
+ }
82
+ const mergedDisabled = disabled || childProps.disabled;
83
+ const fieldName = name;
75
84
  const attr = {
76
- value: getFieldValue == null ? void 0 : getFieldValue(name),
85
+ value: getFieldValue == null ? void 0 : getFieldValue(fieldName),
77
86
  name: formName,
78
- id: name,
87
+ id: fieldName,
79
88
  disabled: mergedDisabled,
80
89
  onChange: (value, ...rest) => {
81
90
  var _a, _b, _c;
@@ -84,21 +93,21 @@ var Field = (props) => {
84
93
  const fieldsValue = getFieldsValue == null ? void 0 : getFieldsValue();
85
94
  const feedback = (_a = callbacks == null ? void 0 : callbacks.onValuesBeforeChange) == null ? void 0 : _a.call(
86
95
  callbacks,
87
- { [name]: _value },
96
+ { [fieldName]: _value },
88
97
  fieldsValue
89
98
  );
90
99
  if (feedback !== void 0) {
91
100
  _value = feedback;
92
101
  }
93
- setFieldsValue == null ? void 0 : setFieldsValue({ [name]: _value }, "onChange");
102
+ setFieldsValue == null ? void 0 : setFieldsValue({ [fieldName]: _value }, "onChange");
94
103
  if ((_b = children2.props) == null ? void 0 : _b.onChange) {
95
104
  children2.props.onChange(_value, ...rest);
96
105
  }
97
- (_c = callbacks == null ? void 0 : callbacks["onValuesChange"]) == null ? void 0 : _c.call(callbacks, { [name]: _value }, fieldsValue);
106
+ (_c = callbacks == null ? void 0 : callbacks["onValuesChange"]) == null ? void 0 : _c.call(callbacks, { [fieldName]: _value }, fieldsValue);
98
107
  forceUpdate();
99
108
  },
100
109
  onBlur: (event) => {
101
- void (validateField == null ? void 0 : validateField(name, "onBlur"));
110
+ void (validateField == null ? void 0 : validateField(fieldName, "onBlur"));
102
111
  if (children2.props.onBlur) {
103
112
  children2.props.onBlur(event);
104
113
  }
@@ -106,7 +115,7 @@ var Field = (props) => {
106
115
  };
107
116
  return import_react2.default.cloneElement(children2, attr);
108
117
  };
109
- const validate = getFieldValidate == null ? void 0 : getFieldValidate(name);
118
+ const validate = name !== void 0 ? getFieldValidate == null ? void 0 : getFieldValidate(name) : void 0;
110
119
  return /* @__PURE__ */ import_react2.default.createElement(
111
120
  "div",
112
121
  {
@@ -120,7 +129,7 @@ var Field = (props) => {
120
129
  ),
121
130
  style
122
131
  },
123
- /* @__PURE__ */ import_react2.default.createElement(
132
+ label ? /* @__PURE__ */ import_react2.default.createElement(
124
133
  "label",
125
134
  {
126
135
  className: (0, import_clsx.default)(
@@ -133,7 +142,7 @@ var Field = (props) => {
133
142
  style: styles == null ? void 0 : styles.label
134
143
  },
135
144
  label
136
- ),
145
+ ) : null,
137
146
  /* @__PURE__ */ import_react2.default.createElement(
138
147
  "div",
139
148
  {
@@ -27,13 +27,16 @@ export interface FieldProps {
27
27
  */
28
28
  label?: React.ReactNode;
29
29
  /**
30
- * Field name
30
+ * Field name (required only when used with a registered virtual form)
31
31
  */
32
- name: string;
32
+ name?: string;
33
33
  /**
34
- * Name of the parent form
34
+ * Name of the parent form.
35
+ * When omitted (or no form is registered under this name), Field renders
36
+ * as a pure layout wrapper: the child keeps its own value/onChange and
37
+ * no validation runs.
35
38
  */
36
- formName: string;
39
+ formName?: string;
37
40
  /**
38
41
  * Whether disabled
39
42
  */
@@ -25,6 +25,7 @@
25
25
  &-content {
26
26
  position: relative;
27
27
  padding-bottom: var(--yee-field-content-padding-bottom);
28
+ width: stretch;
28
29
  }
29
30
 
30
31
  &-error {
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const _default: () => React.JSX.Element;
3
+ export default _default;
@@ -0,0 +1,105 @@
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/Table/demo/controlled-filter.tsx
30
+ var controlled_filter_exports = {};
31
+ __export(controlled_filter_exports, {
32
+ default: () => controlled_filter_default
33
+ });
34
+ module.exports = __toCommonJS(controlled_filter_exports);
35
+ var import_yee_c = require("../..");
36
+ var import_react = __toESM(require("react"));
37
+ var allData = [
38
+ { key: "1", name: "John Brown", age: 32, gender: "male" },
39
+ { key: "2", name: "Jim Green", age: 42, gender: "male" },
40
+ { key: "3", name: "Joe Black", age: 32, gender: "male" },
41
+ { key: "4", name: "Jon Snow", age: 42, gender: "female" },
42
+ { key: "5", name: "Arya Stark", age: 16, gender: "female" }
43
+ ];
44
+ var controlled_filter_default = () => {
45
+ const { messageApi, messageHolder } = import_yee_c.message.useMessage();
46
+ const [nameKeyword, setNameKeyword] = (0, import_react.useState)("");
47
+ const [genders, setGenders] = (0, import_react.useState)([]);
48
+ const columns = [
49
+ {
50
+ title: "Name",
51
+ dataIndex: "name",
52
+ key: "name",
53
+ filter: {
54
+ searchable: true,
55
+ // No onFilter: no local matching, the search hits the "server".
56
+ filteredValue: nameKeyword
57
+ }
58
+ },
59
+ {
60
+ title: "Age",
61
+ dataIndex: "age",
62
+ key: "age"
63
+ },
64
+ {
65
+ title: "Gender",
66
+ dataIndex: "gender",
67
+ key: "gender",
68
+ filter: {
69
+ filterMode: "tree",
70
+ items: [
71
+ { label: "Male", key: "male" },
72
+ { label: "Female", key: "female" }
73
+ ],
74
+ filteredValue: genders,
75
+ onFilter: (value, record) => value === record.gender
76
+ }
77
+ }
78
+ ];
79
+ const handleChange = (info) => {
80
+ var _a, _b, _c;
81
+ if (info.action !== "filter")
82
+ return;
83
+ const filters = (_a = info.filters) != null ? _a : {};
84
+ const name = (_b = filters.name) != null ? _b : "";
85
+ if (name !== nameKeyword) {
86
+ messageApi.info(`Fetch: /api/users?name=${name}`);
87
+ setNameKeyword(name);
88
+ }
89
+ setGenders((_c = filters.gender) != null ? _c : []);
90
+ };
91
+ const data = allData.filter((item) => {
92
+ const matchName = nameKeyword ? item.name.toLowerCase().includes(nameKeyword.toLowerCase()) : true;
93
+ const matchGender = genders.length ? genders.includes(item.gender) : true;
94
+ return matchName && matchGender;
95
+ });
96
+ return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, messageHolder, /* @__PURE__ */ import_react.default.createElement(import_yee_c.Space, { block: true, style: { marginBottom: 16 } }, /* @__PURE__ */ import_react.default.createElement(import_yee_c.Button, { onClick: () => setNameKeyword("") }, "Reset name search"), /* @__PURE__ */ import_react.default.createElement(import_yee_c.Button, { onClick: () => setGenders([]) }, "Reset gender filter")), /* @__PURE__ */ import_react.default.createElement(
97
+ import_yee_c.Table,
98
+ {
99
+ columns,
100
+ dataSource: data,
101
+ onChange: handleChange,
102
+ pagination: false
103
+ }
104
+ ));
105
+ };
@@ -8,12 +8,16 @@ type FilterRecord = {
8
8
  value: FilterInput;
9
9
  dataIndex: string;
10
10
  };
11
+ export declare const isControlledFilter: (column?: ColumnProps) => boolean;
11
12
  export default function useFilter({ data, columns, }: {
12
13
  data: Array<Record<string, unknown>>;
13
14
  columns: ColumnProps[];
14
15
  }): {
15
16
  data: Record<string, unknown>[];
16
17
  filterRecords: FilterRecord[];
17
- onFilter: ({ value, dataIndex }: FilterOptions) => void;
18
+ onFilter: ({ value, dataIndex }: FilterOptions) => {
19
+ data: Record<string, unknown>[];
20
+ filterRecords: FilterRecord[];
21
+ };
18
22
  };
19
23
  export {};
@@ -36,17 +36,71 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
36
36
  // src/Table/hooks/useFilter.ts
37
37
  var useFilter_exports = {};
38
38
  __export(useFilter_exports, {
39
- default: () => useFilter
39
+ default: () => useFilter,
40
+ isControlledFilter: () => isControlledFilter
40
41
  });
41
42
  module.exports = __toCommonJS(useFilter_exports);
42
43
  var import_react = require("react");
43
44
  var isEmptyFilterValue = (value) => value === "" || Array.isArray(value) && value.length === 0;
45
+ var isControlledFilter = (column) => {
46
+ var _a;
47
+ return ((_a = column == null ? void 0 : column.filter) == null ? void 0 : _a.filteredValue) !== void 0;
48
+ };
44
49
  var normalizeValue = (value) => value === null || value === void 0 ? "" : String(value).toLowerCase();
50
+ var mergeRecords = (internalRecords, columnMap) => {
51
+ const controlled = [];
52
+ columnMap.forEach((column, dataIndex) => {
53
+ var _a;
54
+ if (!isControlledFilter(column))
55
+ return;
56
+ const value = (_a = column.filter) == null ? void 0 : _a.filteredValue;
57
+ if (!isEmptyFilterValue(value)) {
58
+ controlled.push({ dataIndex, value });
59
+ }
60
+ });
61
+ const merged = internalRecords.filter(
62
+ (record) => !isControlledFilter(columnMap.get(record.dataIndex))
63
+ );
64
+ if (!controlled.length && merged.length === internalRecords.length) {
65
+ return internalRecords;
66
+ }
67
+ return [...merged, ...controlled];
68
+ };
69
+ var applyFilters = (data, records, columnMap) => {
70
+ const handled = records.filter(
71
+ ({ dataIndex, value }) => Boolean(dataIndex) && columnMap.has(dataIndex) && !isEmptyFilterValue(value)
72
+ );
73
+ if (!handled.length) {
74
+ return data;
75
+ }
76
+ return data.filter((item) => {
77
+ return handled.every(({ dataIndex, value }) => {
78
+ var _a, _b;
79
+ const column = columnMap.get(dataIndex);
80
+ if (isControlledFilter(column) && !((_a = column == null ? void 0 : column.filter) == null ? void 0 : _a.onFilter)) {
81
+ return true;
82
+ }
83
+ const customFilter = (_b = column == null ? void 0 : column.filter) == null ? void 0 : _b.onFilter;
84
+ if (customFilter) {
85
+ return Array.isArray(value) ? value.some((filterValue) => customFilter(filterValue, item)) : customFilter(value, item);
86
+ }
87
+ const targetValue = normalizeValue(item[dataIndex]);
88
+ if (Array.isArray(value)) {
89
+ return value.some(
90
+ (filterValue) => targetValue === normalizeValue(filterValue)
91
+ );
92
+ }
93
+ return targetValue.includes(normalizeValue(value));
94
+ });
95
+ });
96
+ };
45
97
  function useFilter({
46
98
  data,
47
99
  columns
48
100
  }) {
49
- const [filterRecords, setFilterRecords] = (0, import_react.useState)([]);
101
+ const [internalRecords, setInternalRecords] = (0, import_react.useState)(
102
+ []
103
+ );
50
104
  const columnMap = (0, import_react.useMemo)(
51
105
  () => new Map(
52
106
  columns.flatMap(
@@ -56,52 +110,36 @@ function useFilter({
56
110
  [columns]
57
111
  );
58
112
  (0, import_react.useEffect)(() => {
59
- setFilterRecords((current) => {
113
+ setInternalRecords((current) => {
60
114
  const next = current.filter(({ dataIndex }) => columnMap.has(dataIndex));
61
115
  return next.length === current.length ? current : next;
62
116
  });
63
117
  }, [columnMap]);
64
- const onFilter = ({ value, dataIndex }) => {
65
- setFilterRecords((current) => {
66
- if (isEmptyFilterValue(value)) {
67
- return current.filter((item) => item.dataIndex !== dataIndex);
68
- }
69
- const index = current.findIndex((item) => item.dataIndex === dataIndex);
70
- if (index === -1) {
71
- return [...current, { dataIndex, value }];
72
- }
73
- return current.map(
74
- (item, itemIndex) => itemIndex === index ? __spreadProps(__spreadValues({}, item), { value }) : item
75
- );
76
- });
77
- };
78
- const filtered = (0, import_react.useMemo)(() => {
79
- const handled = filterRecords.filter(
80
- ({ dataIndex, value }) => Boolean(dataIndex) && columnMap.has(dataIndex) && !isEmptyFilterValue(value)
81
- );
82
- if (!handled.length) {
83
- return data;
84
- }
85
- return data.filter((item) => {
86
- return handled.every(({ dataIndex, value }) => {
87
- var _a, _b;
88
- const customFilter = (_b = (_a = columnMap.get(dataIndex)) == null ? void 0 : _a.filter) == null ? void 0 : _b.onFilter;
89
- if (customFilter) {
90
- return Array.isArray(value) ? value.some((filterValue) => customFilter(filterValue, item)) : customFilter(value, item);
91
- }
92
- const targetValue = normalizeValue(item[dataIndex]);
93
- if (Array.isArray(value)) {
94
- return value.some(
95
- (filterValue) => targetValue === normalizeValue(filterValue)
96
- );
97
- }
98
- return targetValue.includes(normalizeValue(value));
99
- });
100
- });
101
- }, [columnMap, data, filterRecords]);
118
+ const activeRecords = (0, import_react.useMemo)(
119
+ () => mergeRecords(internalRecords, columnMap),
120
+ [internalRecords, columnMap]
121
+ );
122
+ const filtered = (0, import_react.useMemo)(
123
+ () => applyFilters(data, activeRecords, columnMap),
124
+ [columnMap, data, activeRecords]
125
+ );
126
+ const onFilter = (0, import_react.useCallback)(
127
+ ({ value, dataIndex }) => {
128
+ const nextInternal = isEmptyFilterValue(value) ? internalRecords.filter((item) => item.dataIndex !== dataIndex) : internalRecords.some((item) => item.dataIndex === dataIndex) ? internalRecords.map(
129
+ (item) => item.dataIndex === dataIndex ? __spreadProps(__spreadValues({}, item), { value }) : item
130
+ ) : [...internalRecords, { dataIndex, value }];
131
+ setInternalRecords(nextInternal);
132
+ const nextActive = mergeRecords(nextInternal, columnMap);
133
+ return {
134
+ data: applyFilters(data, nextActive, columnMap),
135
+ filterRecords: nextActive
136
+ };
137
+ },
138
+ [internalRecords, columnMap, data]
139
+ );
102
140
  return {
103
141
  data: filtered,
104
- filterRecords,
142
+ filterRecords: activeRecords,
105
143
  onFilter
106
144
  };
107
145
  }
@@ -2,7 +2,7 @@ import Column from './column';
2
2
  import Summary from './summary';
3
3
  import InternalTable from './table';
4
4
  import Head from './theader';
5
- export type { ColumnProps, HeaderCell, PaginationType, RowSelectionType, TableBodyComponents, TableComponents, TableProps, TableRowRendererProps, onChangeParams, } from './interface';
5
+ export type { ColumnProps, FilterValue, HeaderCell, PaginationType, RowSelectionType, TableBodyComponents, TableComponents, TableProps, TableRowRendererProps, onChangeParams, } from './interface';
6
6
  type TableType = typeof InternalTable & {
7
7
  Column: typeof Column;
8
8
  Summary: typeof Summary;
@@ -62,11 +62,13 @@ export interface HeadCellProps extends Omit<WrapedColumnProps, 'title'> {
62
62
  [prop: string]: number;
63
63
  };
64
64
  showSorterTooltip?: boolean;
65
- internalFilters?: {
66
- [dataIndex: string]: boolean;
67
- };
68
65
  onSort?: (dataIndex: string, sorter: any) => void;
69
- onInternalFilter?: (dataIndex: string, value: string) => void;
66
+ onInternalFilter?: (options: {
67
+ dataIndex: string;
68
+ value: FilterValue | FilterValue[];
69
+ type?: 'filter' | 'search';
70
+ column: WrapedColumnProps;
71
+ }) => void;
70
72
  /**
71
73
  * `resizeKey` of the column currently being dragged, if any
72
74
  */
@@ -188,6 +190,14 @@ export interface ColumnProps {
188
190
  * Whether searchable, when items is undefined, it acts as a search component
189
191
  */
190
192
  searchable?: boolean;
193
+ /**
194
+ * Controlled filter state. When defined, this column's filter state is
195
+ * controlled: the dropdown echo, icon highlight and the `filters` in
196
+ * onChange all derive from it. With no `onFilter`, local matching is
197
+ * skipped entirely (server mode — filter in your onChange handler).
198
+ * Reset with `[]` / `''`.
199
+ */
200
+ filteredValue?: FilterValue | FilterValue[];
191
201
  /**
192
202
  * Filter icon
193
203
  */
@@ -248,6 +248,27 @@ var Table = import_react.default.forwardRef((baseprops, ref) => {
248
248
  },
249
249
  [onSortInternal, onChange, pagination, current, pageSize, filters]
250
250
  );
251
+ const handleFilter = import_react.default.useCallback(
252
+ (options) => {
253
+ const next = onFilterInternal(options);
254
+ const emitted = next.filterRecords.filter(
255
+ (record) => record.dataIndex !== options.dataIndex
256
+ );
257
+ emitted.push({ dataIndex: options.dataIndex, value: options.value });
258
+ onChange == null ? void 0 : onChange({
259
+ pagination: pagination === false ? void 0 : { current, pageSize },
260
+ sorter: sorters,
261
+ filters: Object.fromEntries(
262
+ emitted.map(({ dataIndex, value }) => [dataIndex, value])
263
+ ),
264
+ // Filtered but not yet re-sorted; symmetric with handleSort, whose
265
+ // payload is sorted but not re-paginated.
266
+ currentDataSource: next.data,
267
+ action: "filter"
268
+ });
269
+ },
270
+ [onFilterInternal, onChange, pagination, current, pageSize, sorters]
271
+ );
251
272
  const { expandedRowKeys, onExpand } = (0, import_useExpand.default)(
252
273
  expandable,
253
274
  allKeys,
@@ -259,7 +280,6 @@ var Table = import_react.default.forwardRef((baseprops, ref) => {
259
280
  onCheckAll,
260
281
  onChange: onSelectionChange
261
282
  } = (0, import_useSelection.default)({ pageData, dataSource, getRowKey, rowSelection, allKeys });
262
- const prevFilterRecordsRef = (0, import_react.useRef)(filterRecords);
263
283
  const [horizontalScroll, setHorizontalScroll] = import_react.default.useState({
264
284
  left: false,
265
285
  right: false
@@ -309,29 +329,6 @@ var Table = import_react.default.forwardRef((baseprops, ref) => {
309
329
  return unchanged ? current2 : nextWidths;
310
330
  });
311
331
  }, [wrapedColumnsRef]);
312
- (0, import_react.useEffect)(() => {
313
- if (prevFilterRecordsRef.current === filterRecords)
314
- return;
315
- prevFilterRecordsRef.current = filterRecords;
316
- if (!onChange)
317
- return;
318
- onChange({
319
- pagination: pagination === false ? void 0 : { current, pageSize },
320
- sorter: sorters,
321
- filters,
322
- currentDataSource: sortedData,
323
- action: "filter"
324
- });
325
- }, [
326
- filterRecords,
327
- filters,
328
- sortedData,
329
- pagination,
330
- current,
331
- pageSize,
332
- sorters,
333
- onChange
334
- ]);
335
332
  (0, import_react.useEffect)(() => {
336
333
  const content = contentRef.current;
337
334
  const tableElement = tableRef.current;
@@ -387,7 +384,7 @@ var Table = import_react.default.forwardRef((baseprops, ref) => {
387
384
  sorters,
388
385
  onSort: handleSort,
389
386
  onCheckAll,
390
- onInternalFilter: onFilterInternal,
387
+ onInternalFilter: handleFilter,
391
388
  resizingKey,
392
389
  onResizeStart,
393
390
  onResizeStep
@@ -84,7 +84,6 @@ var HeadCell = (props) => {
84
84
  classNames,
85
85
  showSorterTooltip = true,
86
86
  tableActionBarShowOnHover,
87
- internalFilters,
88
87
  style,
89
88
  width,
90
89
  colSpan,
@@ -112,7 +111,6 @@ var HeadCell = (props) => {
112
111
  "classNames",
113
112
  "showSorterTooltip",
114
113
  "tableActionBarShowOnHover",
115
- "internalFilters",
116
114
  "style",
117
115
  "width",
118
116
  "colSpan",
@@ -190,7 +188,6 @@ var HeadCell = (props) => {
190
188
  __spreadProps(__spreadValues({}, rest), {
191
189
  filter,
192
190
  column: props,
193
- internalFilters,
194
191
  onInternalFilter,
195
192
  dataIndex
196
193
  })
@@ -56,6 +56,7 @@ var areFilterValuesEqual = (a, b) => {
56
56
  };
57
57
  var hasFilterValue = (value) => Array.isArray(value) ? value.length > 0 : value !== "";
58
58
  var HeaderFilter = import_react.default.memo((props) => {
59
+ var _a;
59
60
  const { filter, column, getPopupContainer, onInternalFilter } = props;
60
61
  const { prefixCls } = (0, import_react.useContext)(import_table.TableCtx);
61
62
  const { locale } = (0, import_locale.useLocale)();
@@ -77,12 +78,28 @@ var HeaderFilter = import_react.default.memo((props) => {
77
78
  const [open, setOpen] = (0, import_react.useState)(false);
78
79
  const [selectNodes, setSelectNodes] = (0, import_react.useState)([]);
79
80
  const [searchValue, setSearchValue] = (0, import_react.useState)("");
81
+ const controlled = filter.filteredValue !== void 0;
82
+ (0, import_react.useEffect)(() => {
83
+ if (!controlled || open)
84
+ return;
85
+ const value = filter.filteredValue;
86
+ if (Array.isArray(value)) {
87
+ setSelectNodes([...value]);
88
+ setSearchValue("");
89
+ } else {
90
+ setSearchValue(String(value));
91
+ setSelectNodes([]);
92
+ }
93
+ const committed = Array.isArray(value) ? [...value] : value;
94
+ committedValueRef.current = committed;
95
+ setCommittedValue(committed);
96
+ }, [controlled, filter.filteredValue, open]);
80
97
  (0, import_react.useEffect)(() => {
81
98
  if (!open)
82
99
  return;
83
100
  const raf = requestAnimationFrame(() => {
84
- var _a;
85
- (_a = inputRef.current) == null ? void 0 : _a.focus({ preventScroll: true });
101
+ var _a2;
102
+ (_a2 = inputRef.current) == null ? void 0 : _a2.focus({ preventScroll: true });
86
103
  });
87
104
  return () => cancelAnimationFrame(raf);
88
105
  }, [open]);
@@ -139,8 +156,8 @@ var HeaderFilter = import_react.default.memo((props) => {
139
156
  const inputValue = getStrLower(searchValue);
140
157
  const filteredItems = inputValue ? items.filter(
141
158
  (item) => {
142
- var _a;
143
- return (_a = getStrLower(item.label)) == null ? void 0 : _a.includes(getStrLower(searchValue));
159
+ var _a2;
160
+ return (_a2 = getStrLower(item.label)) == null ? void 0 : _a2.includes(getStrLower(searchValue));
144
161
  }
145
162
  ) : items;
146
163
  if (filterMode === "menu") {
@@ -186,7 +203,7 @@ var HeaderFilter = import_react.default.memo((props) => {
186
203
  }
187
204
  setOpen(nextOpen);
188
205
  };
189
- const isFiltered = filtered != null ? filtered : hasFilterValue(committedValue);
206
+ const isFiltered = filtered != null ? filtered : controlled ? hasFilterValue((_a = filter.filteredValue) != null ? _a : "") : hasFilterValue(committedValue);
190
207
  const renderTrigger = () => {
191
208
  const trigger = typeof icon === "function" ? icon(isFiltered) : /* @__PURE__ */ import_react.default.createElement(
192
209
  import_Button.default,
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * @rainbow-oh/yee-c CSS Variables
3
3
  * Auto-generated, do not edit manually
4
- * Generated on 2026-09-14T02:29:50.905Z
4
+ * Generated on 2026-09-17T09:06:26.119Z
5
5
  */
6
6
 
7
7
  /* ========== color.blue.css ========== */