@rainbow-oh/yee-c 0.7.0 → 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 (37) hide show
  1. package/dist/cjs/Form/form-store.d.ts +2 -1
  2. package/dist/cjs/Form/form-store.js +32 -17
  3. package/dist/cjs/Table/hooks/useColumns.js +1 -1
  4. package/dist/cjs/Table/style/index.less +35 -11
  5. package/dist/cjs/Table/table.js +57 -7
  6. package/dist/cjs/Table/tbody/cell.js +1 -1
  7. package/dist/cjs/Table/util/header.d.ts +3 -1
  8. package/dist/cjs/Table/util/header.js +30 -3
  9. package/dist/cjs/style/color.css +2 -0
  10. package/dist/cjs/style/themes.css +1 -1
  11. package/dist/cjs/style/variables.css +3 -1
  12. package/dist/cjs/version.d.ts +1 -1
  13. package/dist/cjs/version.js +1 -1
  14. package/dist/esm/Form/form-store.d.ts +2 -1
  15. package/dist/esm/Form/form-store.js +40 -19
  16. package/dist/esm/Table/hooks/useColumns.js +1 -1
  17. package/dist/esm/Table/style/index.less +35 -11
  18. package/dist/esm/Table/table.js +54 -2
  19. package/dist/esm/Table/tbody/cell.js +1 -1
  20. package/dist/esm/Table/util/header.d.ts +3 -1
  21. package/dist/esm/Table/util/header.js +30 -2
  22. package/dist/esm/style/color.css +2 -0
  23. package/dist/esm/style/themes.css +1 -1
  24. package/dist/esm/style/variables.css +3 -1
  25. package/dist/esm/version.d.ts +1 -1
  26. package/dist/esm/version.js +1 -1
  27. package/dist/umd/yee-c.min.css +3 -3
  28. package/dist/umd/yee-c.min.js +1 -1
  29. package/package.json +1 -1
  30. package/src/Form/form-store.ts +42 -15
  31. package/src/Table/hooks/useColumns.ts +1 -1
  32. package/src/Table/style/index.less +35 -11
  33. package/src/Table/table.tsx +60 -1
  34. package/src/Table/tbody/cell.tsx +1 -1
  35. package/src/Table/util/header.ts +48 -2
  36. package/src/style/color.css +2 -0
  37. 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.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "yee basic components",
5
5
  "keywords": [
6
6
  "react",
@@ -32,6 +32,11 @@ function move<T>(array: T[], from: number, to: number): T[] {
32
32
 
33
33
  const GLOBAL_WATCH_KEY = '__ALL__';
34
34
 
35
+ const isRelatedNamePath = (first: string, second: string): boolean =>
36
+ first === second ||
37
+ first.startsWith(`${second}.`) ||
38
+ second.startsWith(`${first}.`);
39
+
35
40
  export class FormStore {
36
41
  private store: Store = {};
37
42
  private callbacks: Callbacks = {};
@@ -145,6 +150,9 @@ export class FormStore {
145
150
  });
146
151
  }
147
152
 
153
+ const changedKeys = Object.keys(newStore);
154
+ const relatedFieldNames = this.getRelatedFieldNames(changedKeys);
155
+
148
156
  // Validate on onChange and onBlur (async — fire-and-forget so setFieldsValue
149
157
  // stays synchronous for its callers; results surface via onStoreChange).
150
158
  if (
@@ -152,10 +160,10 @@ export class FormStore {
152
160
  trigger === 'onBlur' ||
153
161
  trigger === 'onSubmit'
154
162
  ) {
155
- void this.validateFields(Object.keys(newStore), trigger);
163
+ void this.validateFields(relatedFieldNames, trigger);
156
164
  }
157
165
 
158
- this.refreshField(newStore);
166
+ this.refreshField(newStore, relatedFieldNames);
159
167
  };
160
168
 
161
169
  resetFields = (name?: Name[]) => {
@@ -279,16 +287,22 @@ export class FormStore {
279
287
  return err;
280
288
  };
281
289
 
282
- refreshField = (newStore?: Store) => {
290
+ private getRelatedFieldNames = (changedKeys: string[]): Name[] =>
291
+ Array.from(this.fieldMap.keys()).filter((fieldName) =>
292
+ changedKeys.some((changedKey) =>
293
+ isRelatedNamePath(String(fieldName), changedKey),
294
+ ),
295
+ );
296
+
297
+ refreshField = (newStore?: Store, relatedFieldNames?: Name[]) => {
283
298
  if (newStore) {
284
- const keys = Object.keys(newStore);
285
- keys.forEach((key) => {
286
- const entity = this.fieldMap.get(key);
287
- if (entity) {
288
- entity?.onStoreChange();
289
- }
290
- this.notifyWatchers(key);
299
+ const changedKeys = Object.keys(newStore);
300
+ const fieldNames =
301
+ relatedFieldNames ?? this.getRelatedFieldNames(changedKeys);
302
+ fieldNames.forEach((fieldName) => {
303
+ this.fieldMap.get(fieldName)?.onStoreChange();
291
304
  });
305
+ this.notifyWatchers(changedKeys);
292
306
  } else {
293
307
  this.fieldEntities.forEach((entity) => {
294
308
  entity?.onStoreChange();
@@ -308,14 +322,27 @@ export class FormStore {
308
322
  }
309
323
  this.watchers.get(key)!.add(callback);
310
324
  return () => {
311
- this.watchers.get(key)?.delete(callback);
325
+ const watchers = this.watchers.get(key);
326
+ watchers?.delete(callback);
327
+ if (watchers?.size === 0) {
328
+ this.watchers.delete(key);
329
+ }
312
330
  };
313
331
  };
314
332
 
315
- private notifyWatchers = (changedKey?: string) => {
316
- if (changedKey) {
317
- this.watchers.get(changedKey)?.forEach((cb) => cb());
318
- this.watchers.get(GLOBAL_WATCH_KEY)?.forEach((cb) => cb());
333
+ private notifyWatchers = (changedKeys?: string | string[]) => {
334
+ if (changedKeys) {
335
+ const keys = Array.isArray(changedKeys) ? changedKeys : [changedKeys];
336
+ const callbacks = new Set<() => void>();
337
+ this.watchers.forEach((watchers, watchedKey) => {
338
+ if (
339
+ watchedKey === GLOBAL_WATCH_KEY ||
340
+ keys.some((changedKey) => isRelatedNamePath(watchedKey, changedKey))
341
+ ) {
342
+ watchers.forEach((callback) => callbacks.add(callback));
343
+ }
344
+ });
345
+ callbacks.forEach((callback) => callback());
319
346
  } else {
320
347
  this.watchers.forEach((set) => set.forEach((cb) => cb()));
321
348
  }
@@ -58,7 +58,7 @@ export default function useColumns({
58
58
  const tree = injectSelectionExpand(base, rowSelection, expandable);
59
59
  const leaves = flattenLeafColumns(tree);
60
60
  const wraped = handleColumns(leaves);
61
- const rows = parseHeaderRows(tree);
61
+ const rows = parseHeaderRows(tree, wraped);
62
62
  return { wrapedColumns: wraped, headerRows: rows };
63
63
  }, [childColumns, columns, shouldRenderSelection, shouldRenderExpand]);
64
64
 
@@ -49,6 +49,12 @@
49
49
  background-color: var(--yee-table-head-cell-split-line-color, #eee);
50
50
  }
51
51
  }
52
+
53
+ &-fixed-left,
54
+ &-fixed-right {
55
+ position: sticky;
56
+ z-index: 1;
57
+ }
52
58
  }
53
59
 
54
60
  &-cell {
@@ -84,6 +90,16 @@
84
90
  &-wrapper {
85
91
  overflow: auto;
86
92
  width: 100%;
93
+
94
+ &-scroll-left @{pls}-cell-fixed-left-last {
95
+ box-shadow: 6px 0 6px -4px
96
+ var(--yee-table-fixed-left-after-bs-color);
97
+ }
98
+
99
+ &-scroll-right @{pls}-cell-fixed-right-first {
100
+ box-shadow: -6px 0 6px -4px
101
+ var(--yee-table-fixed-right-after-bs-color);
102
+ }
87
103
  }
88
104
 
89
105
  > table {
@@ -101,10 +117,18 @@
101
117
  }
102
118
  }
103
119
 
120
+ &-row {
121
+ background-color: var(--yee-table-row-bg-color);
122
+ }
123
+
104
124
  &-even-row {
105
125
  background-color: var(--yee-table-even-row-bg-color);
106
126
  }
107
127
 
128
+ &-odd-row {
129
+ background-color: var(--yee-table-odd-row-bg-color);
130
+ }
131
+
108
132
  &-column-sorters {
109
133
  display: inline-flex;
110
134
  align-items: center;
@@ -137,7 +161,7 @@
137
161
  &-fixed-left,
138
162
  &-fixed-right {
139
163
  position: sticky;
140
- background-color: inherit;
164
+ background-color: var(--yee-table-fixed-column-bg-color);
141
165
  }
142
166
  }
143
167
 
@@ -163,17 +187,17 @@
163
187
  }
164
188
  }
165
189
 
166
- &-odd-row {
167
- > @{pls}-cell {
168
- background-color: var(--yee-table-odd-row-bg-color);
169
- }
170
- }
190
+ // &-odd-row {
191
+ // > @{pls}-cell {
192
+ // background-color: var(--yee-table-odd-row-bg-color);
193
+ // }
194
+ // }
171
195
 
172
- &-even-row {
173
- > @{pls}-cell {
174
- background-color: var(--yee-table-even-row-bg-color);
175
- }
176
- }
196
+ // &-even-row {
197
+ // > @{pls}-cell {
198
+ // background-color: var(--yee-table-even-row-bg-color);
199
+ // }
200
+ // }
177
201
 
178
202
  &-footer {
179
203
  display: flex;
@@ -126,6 +126,36 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
126
126
 
127
127
  // Track previous values to determine what action triggered onChange
128
128
  const prevSortersRef = useRef<typeof sorters>({});
129
+ const contentWrapperRef = useRef<HTMLDivElement>(null);
130
+ const contentRef = useRef<HTMLDivElement>(null);
131
+ const [horizontalScroll, setHorizontalScroll] = React.useState({
132
+ left: false,
133
+ right: false,
134
+ });
135
+
136
+ const updateHorizontalScroll = React.useCallback(() => {
137
+ const wrapper = contentWrapperRef.current;
138
+ const content = contentRef.current;
139
+ if (!wrapper || !content) return;
140
+
141
+ const scrollContainer =
142
+ content.scrollWidth - content.clientWidth > 1 ? content : wrapper;
143
+ const maxScrollLeft =
144
+ scrollContainer.scrollWidth - scrollContainer.clientWidth;
145
+ const hasHorizontalScroll = maxScrollLeft > 1;
146
+ const next = {
147
+ left: hasHorizontalScroll && scrollContainer.scrollLeft > 1,
148
+ right:
149
+ hasHorizontalScroll &&
150
+ scrollContainer.scrollLeft < maxScrollLeft - 1,
151
+ };
152
+
153
+ setHorizontalScroll((current) =>
154
+ current.left === next.left && current.right === next.right
155
+ ? current
156
+ : next,
157
+ );
158
+ }, []);
129
159
 
130
160
  // Wrap onFilter to trigger onChange
131
161
  const onFilter = React.useCallback(
@@ -173,6 +203,25 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
173
203
  }
174
204
  }, [sorters, current, pageSize, pageData, sortedData, onChange]);
175
205
 
206
+ useEffect(() => {
207
+ updateHorizontalScroll();
208
+
209
+ const wrapper = contentWrapperRef.current;
210
+ const content = contentRef.current;
211
+ if (!wrapper || !content) return undefined;
212
+
213
+ if (typeof ResizeObserver === 'undefined') {
214
+ window.addEventListener('resize', updateHorizontalScroll);
215
+ return () => window.removeEventListener('resize', updateHorizontalScroll);
216
+ }
217
+
218
+ const resizeObserver = new ResizeObserver(updateHorizontalScroll);
219
+ resizeObserver.observe(wrapper);
220
+ resizeObserver.observe(content);
221
+
222
+ return () => resizeObserver.disconnect();
223
+ }, [updateHorizontalScroll, wrapedColumns, pageData, scroll?.x]);
224
+
176
225
  const renderHeader = () => {
177
226
  return (
178
227
  <Header
@@ -259,13 +308,23 @@ const Table = React.forwardRef<HTMLDivElement, TableProps>((baseprops, ref) => {
259
308
  );
260
309
 
261
310
  return (
262
- <div className={`${prefixCls}-content-wrapper`}>
311
+ <div
312
+ ref={contentWrapperRef}
313
+ className={clsx(`${prefixCls}-content-wrapper`, {
314
+ [`${prefixCls}-content-wrapper-scroll-left`]: horizontalScroll.left,
315
+ [`${prefixCls}-content-wrapper-scroll-right`]:
316
+ horizontalScroll.right,
317
+ })}
318
+ onScroll={updateHorizontalScroll}
319
+ >
263
320
  <div
321
+ ref={contentRef}
264
322
  className={clsx(`${prefixCls}-content`, {
265
323
  [`${prefixCls}-${size}-size`]: size,
266
324
  [`${prefixCls}-fixed-header`]: scroll?.y,
267
325
  })}
268
326
  style={{ height: scroll?.y, width: scroll?.x }}
327
+ onScroll={updateHorizontalScroll}
269
328
  >
270
329
  {table}
271
330
  </div>
@@ -61,7 +61,7 @@ const TableCell: React.FC<BodyCellProps> = (props) => {
61
61
  {
62
62
  [`${prefixCls}-cell-fixed-${fixed}`]: fixed,
63
63
  [`${prefixCls}-cell-fixed-${fixed}-first`]: isFixedRightFirst,
64
- [`${prefixCls}-fixed-${fixed}-last`]: isFixedLeftLast,
64
+ [`${prefixCls}-cell-fixed-${fixed}-last`]: isFixedLeftLast,
65
65
  },
66
66
  className,
67
67
  );
@@ -32,13 +32,17 @@ export function flattenLeafColumns(
32
32
  *
33
33
  * - Pass 1 (DFS): place each column into the row matching its depth; a group's
34
34
  * colSpan equals the sum of its children's colSpans.
35
+ * - Fixed metadata from the resolved leaf columns is reused by leaf cells and
36
+ * aggregated for group cells whose descendants share the same fixed side.
35
37
  * - Pass 2: leaf cells (no sub-columns) get rowSpan = rowCount - rowIndex so
36
38
  * they stretch down to the last header row; group cells keep rowSpan = 1.
37
39
  */
38
40
  export function parseHeaderRows(
39
41
  rootColumns: WrapedColumnProps[],
42
+ resolvedLeafColumns: WrapedColumnProps[] = flattenLeafColumns(rootColumns),
40
43
  ): HeaderCell[][] {
41
44
  const rows: HeaderCell[][] = [];
45
+ let leafIndex = 0;
42
46
 
43
47
  const fillRowCells = (
44
48
  columns: WrapedColumnProps[],
@@ -46,17 +50,59 @@ export function parseHeaderRows(
46
50
  ): number => {
47
51
  rows[rowIndex] = rows[rowIndex] || [];
48
52
  let colSpanTotal = 0;
49
- columns.filter(Boolean).forEach((column) => {
53
+ columns.filter(Boolean).forEach((sourceColumn) => {
54
+ let column = sourceColumn;
55
+ const subColumns = sourceColumn.children as
56
+ | WrapedColumnProps[]
57
+ | undefined;
58
+ const leafStartIndex = leafIndex;
50
59
  const cell: HeaderCell = {
51
60
  column,
52
61
  colSpan: 1,
53
62
  rowSpan: 1,
54
63
  };
55
- const subColumns = column.children as WrapedColumnProps[] | undefined;
64
+
56
65
  if (subColumns && subColumns.length > 0) {
57
66
  cell.colSpan = fillRowCells(subColumns, rowIndex + 1);
58
67
  cell.hasSubColumns = true;
68
+
69
+ const groupLeaves = resolvedLeafColumns.slice(
70
+ leafStartIndex,
71
+ leafIndex,
72
+ );
73
+ const fixed = groupLeaves[0]?.fixed;
74
+ const isFixedGroup =
75
+ (fixed === 'left' || fixed === 'right') &&
76
+ groupLeaves.every((leaf) => leaf.fixed === fixed);
77
+
78
+ if (isFixedGroup) {
79
+ const offsetLeaf =
80
+ fixed === 'left'
81
+ ? groupLeaves[0]
82
+ : groupLeaves[groupLeaves.length - 1];
83
+ const boundaryLeaf =
84
+ fixed === 'left'
85
+ ? groupLeaves[groupLeaves.length - 1]
86
+ : groupLeaves[0];
87
+ column = {
88
+ ...sourceColumn,
89
+ fixed,
90
+ style: {
91
+ ...sourceColumn.style,
92
+ [fixed]: offsetLeaf.style?.[fixed],
93
+ },
94
+ isFixedLeftLast: fixed === 'left' && boundaryLeaf.isFixedLeftLast,
95
+ isFixedRightFirst:
96
+ fixed === 'right' && boundaryLeaf.isFixedRightFirst,
97
+ };
98
+ cell.column = column;
99
+ }
100
+ } else {
101
+ column = resolvedLeafColumns[leafIndex] || sourceColumn;
102
+ cell.column = column;
103
+ leafIndex += 1;
59
104
  }
105
+
60
106
  rows[rowIndex].push(cell);
61
107
  colSpanTotal += cell.colSpan;
62
108
  });
@@ -848,6 +848,7 @@
848
848
  --yee-table-thead-stripe-borderbottom-color: var(
849
849
  --yee-table-thead-borderbottom-color
850
850
  );
851
+ --yee-table-row-bg-color: var(--yee-color-bg-container);
851
852
  --yee-table-odd-row-bg-color: var(--yee-color-bg-container);
852
853
  --yee-table-even-row-bg-color: var(--yee-color-bg-head);
853
854
  --yee-table-elevated-thead-stripe-borderbottom-color: var(
@@ -870,6 +871,7 @@
870
871
  --yee-table-head-filter-active-color: var(--yee-primary-color);
871
872
  --yee-table-fixed-left-after-bs-color: var(--yee-box-shadow-color);
872
873
  --yee-table-fixed-right-after-bs-color: var(--yee-box-shadow-color);
874
+ --yee-table-fixed-column-bg-color: inherit;
873
875
  --yee-table-filter-list-bg-color: var(--yee-color-bg-elevated);
874
876
  --yee-table-tbody-tr-expanded-bg-color: var(--yee-color-bg-container);
875
877
  --yee-table-elevated-tbody-tr-expanded-bg-color: var(
package/src/version.ts CHANGED
@@ -4,7 +4,7 @@ declare global {
4
4
  }
5
5
  }
6
6
 
7
- export const version = "0.7.0";
7
+ export const version = "0.8.0";
8
8
 
9
9
  if (typeof window !== 'undefined') {
10
10
  window.__yee_c_version__ = version;