@canlooks/can-ui 0.0.203 → 0.0.205

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 (45) hide show
  1. package/dist/cjs/components/autocomplete/autocomplete.js +6 -1
  2. package/dist/cjs/components/curd/curd.js +1 -0
  3. package/dist/cjs/components/curd/curd.style.d.ts +1 -0
  4. package/dist/cjs/components/curd/curd.style.js +2 -1
  5. package/dist/cjs/components/curd/curdColumnConfig.js +3 -2
  6. package/dist/cjs/components/curd/curdColumnConfig.style.js +3 -12
  7. package/dist/cjs/components/dataGrid/dataGrid.d.ts +2 -1
  8. package/dist/cjs/components/dataGrid/dataGrid.js +7 -2
  9. package/dist/cjs/components/dataGrid/dataGrid.style.js +27 -16
  10. package/dist/cjs/components/dataGrid/dataGridHead.d.ts +2 -1
  11. package/dist/cjs/components/dataGrid/dataGridHead.js +2 -2
  12. package/dist/cjs/components/dataGrid/dataGridRows.js +5 -3
  13. package/dist/cjs/components/dataGrid/stickyRow.d.ts +5 -0
  14. package/dist/cjs/components/dataGrid/stickyRow.js +28 -0
  15. package/dist/cjs/components/popper/popper.js +67 -67
  16. package/dist/cjs/components/select/select.js +10 -1
  17. package/dist/cjs/components/selectedList/selectedList.style.js +1 -1
  18. package/dist/cjs/components/selectionContext/selectionHook.js +6 -1
  19. package/dist/cjs/components/table/table.style.js +1 -1
  20. package/dist/cjs/components/table/tableSticky.js +3 -1
  21. package/dist/cjs/components/table/tableSticky.style.js +1 -1
  22. package/dist/cjs/utils/dataGrid.js +6 -1
  23. package/dist/esm/components/autocomplete/autocomplete.js +6 -1
  24. package/dist/esm/components/curd/curd.js +1 -0
  25. package/dist/esm/components/curd/curd.style.d.ts +1 -0
  26. package/dist/esm/components/curd/curd.style.js +2 -1
  27. package/dist/esm/components/curd/curdColumnConfig.js +3 -2
  28. package/dist/esm/components/curd/curdColumnConfig.style.js +3 -12
  29. package/dist/esm/components/dataGrid/dataGrid.d.ts +2 -1
  30. package/dist/esm/components/dataGrid/dataGrid.js +8 -3
  31. package/dist/esm/components/dataGrid/dataGrid.style.js +27 -16
  32. package/dist/esm/components/dataGrid/dataGridHead.d.ts +2 -1
  33. package/dist/esm/components/dataGrid/dataGridHead.js +2 -2
  34. package/dist/esm/components/dataGrid/dataGridRows.js +5 -3
  35. package/dist/esm/components/dataGrid/stickyRow.d.ts +5 -0
  36. package/dist/esm/components/dataGrid/stickyRow.js +25 -0
  37. package/dist/esm/components/popper/popper.js +67 -67
  38. package/dist/esm/components/select/select.js +10 -1
  39. package/dist/esm/components/selectedList/selectedList.style.js +1 -1
  40. package/dist/esm/components/selectionContext/selectionHook.js +6 -1
  41. package/dist/esm/components/table/table.style.js +1 -1
  42. package/dist/esm/components/table/tableSticky.js +3 -1
  43. package/dist/esm/components/table/tableSticky.style.js +1 -1
  44. package/dist/esm/utils/dataGrid.js +6 -1
  45. package/package.json +1 -1
@@ -48,7 +48,12 @@ exports.Autocomplete = (0, react_1.memo)(({ children, loading, options, loadOpti
48
48
  actualOptions?.forEach(opt => {
49
49
  if (opt && typeof opt === 'object') {
50
50
  const key = opt[primaryKey];
51
- !(0, utils_1.isUnset)(key) && map.set(key, opt);
51
+ if (!(0, utils_1.isUnset)(key)) {
52
+ if (map.has(key)) {
53
+ console.warn(`[@canlooks/can-ui/<Autocomple/>] option key "${key}" was duplicated`);
54
+ }
55
+ map.set(key, opt);
56
+ }
52
57
  }
53
58
  });
54
59
  return map;
@@ -97,6 +97,7 @@ exports.Curd = (0, react_1.memo)((props) => {
97
97
  const controlColumn = {
98
98
  key: utils_1.CONTROL_COLUMN_KEY,
99
99
  title: controlColumnTitle,
100
+ className: curd_style_1.classes.controlColumn,
100
101
  render(row) {
101
102
  const _updatable = typeof updatable === 'function' ? updatable(row) : updatable;
102
103
  const _deletable = typeof deletable === 'function' ? deletable(row) : deletable;
@@ -14,6 +14,7 @@ export declare const classes: {
14
14
  toolbarLeft: string;
15
15
  toolbarRight: string;
16
16
  card: string;
17
+ controlColumn: string;
17
18
  dialogTitle: string;
18
19
  copyButton: string;
19
20
  } & {
@@ -20,6 +20,7 @@ exports.classes = (0, utils_1.defineInnerClasses)('curd', [
20
20
  'toolbarRight',
21
21
  'divider',
22
22
  'card',
23
+ 'controlColumn',
23
24
  'control',
24
25
  'dialogTitle',
25
26
  'copyButton'
@@ -32,7 +33,7 @@ exports.style = (0, utils_1.defineCss)(({ spacing, text, background, borderRadiu
32
33
  min-height: 0;
33
34
  display: flex;
34
35
  flex-direction: column;
35
-
36
+
36
37
  .${exports.classes.filterForm} {
37
38
  min-height: 0;
38
39
  display: flex;
@@ -13,6 +13,7 @@ const utils_1 = require("../../utils");
13
13
  const icon_1 = require("../icon");
14
14
  const faGear_1 = require("@fortawesome/free-solid-svg-icons/faGear");
15
15
  const react_2 = require("@dnd-kit/react");
16
+ const tooltip_1 = require("../tooltip");
16
17
  exports.CurdColumnConfig = (0, react_1.memo)((props) => {
17
18
  const dragEndHandler = e => {
18
19
  const newColumns = (0, utils_1.onDndDragEnd)(e, props.columns || [], '_key');
@@ -41,12 +42,12 @@ const CurdColumnConfigContent = (0, react_1.memo)(({ columns, innerVisible, setI
41
42
  ? [...o, key]
42
43
  : o.filter(k => k !== key));
43
44
  };
44
- return ((0, jsx_runtime_1.jsx)(bubble_1.Bubble, { placement: "bottomRight", trigger: ['hover', 'click'], ...columnConfigBubbleProps, css: curdColumnConfig_style_1.style, open: open, onOpenChange: openChangeHandler, content: (0, jsx_runtime_1.jsxs)("div", { className: curdColumnConfig_style_1.classes.content, children: [(0, jsx_runtime_1.jsxs)("div", { className: curdColumnConfig_style_1.classes.title, children: [(0, jsx_runtime_1.jsx)("div", { className: curdColumnConfig_style_1.classes.titleText, children: "\u5217\u8BBE\u7F6E" }), (0, jsx_runtime_1.jsx)("div", { className: curdColumnConfig_style_1.classes.description, children: "\u62D6\u62FD\u8C03\u6574\u987A\u5E8F" })] }), columns?.map((col, i) => {
45
+ return ((0, jsx_runtime_1.jsx)(bubble_1.Bubble, { placement: "bottomRight", trigger: "click", ...columnConfigBubbleProps, css: curdColumnConfig_style_1.style, open: open, onOpenChange: openChangeHandler, content: (0, jsx_runtime_1.jsxs)("div", { className: curdColumnConfig_style_1.classes.content, children: [(0, jsx_runtime_1.jsxs)("div", { className: curdColumnConfig_style_1.classes.title, children: [(0, jsx_runtime_1.jsx)("div", { className: curdColumnConfig_style_1.classes.titleText, children: "\u5217\u8BBE\u7F6E" }), (0, jsx_runtime_1.jsx)("div", { className: curdColumnConfig_style_1.classes.description, children: "\u62D6\u62FD\u8C03\u6574\u987A\u5E8F" })] }), columns?.map((col, i) => {
45
46
  const id = col._key;
46
47
  const checked = !(0, utils_1.isUnset)(id) && (!visibleSet || visibleSet.has(id));
47
48
  return ((0, jsx_runtime_1.jsx)(sortableItem_1.SortableItem, { id: id ?? i, index: i, component: menuItem_1.MenuItem, className: curdColumnConfig_style_1.classes.item, prefix: (0, jsx_runtime_1.jsx)(checkbox_1.Checkbox, { className: curdColumnConfig_style_1.classes.checkbox, checked: checked, onChange: e => {
48
49
  e.stopPropagation();
49
50
  toggleVisible(id, e.target.checked);
50
51
  } }), onClick: () => toggleVisible(id, !checked), label: col.titleText ?? col.title, noStyle: true }, id ?? i));
51
- })] }), autoClose: false, children: (0, jsx_runtime_1.jsx)(button_1.Button, { shape: "circular", variant: "text", color: "text.secondary", children: (0, jsx_runtime_1.jsx)(icon_1.Icon, { icon: faGear_1.faGear }) }) }));
52
+ })] }), autoClose: false, children: (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { title: "\u5217\u8BBE\u7F6E", clickToClose: true, children: (0, jsx_runtime_1.jsx)(button_1.Button, { shape: "circular", variant: "text", color: "text.secondary", children: (0, jsx_runtime_1.jsx)(icon_1.Icon, { icon: faGear_1.faGear }) }) }) }));
52
53
  });
@@ -21,25 +21,16 @@ exports.style = (0, utils_1.defineCss)(({ spacing, text }) => (0, react_1.css) `
21
21
  align-items: center;
22
22
  justify-content: space-between;
23
23
  padding: ${spacing[3]}px;
24
-
24
+
25
25
  .${exports.classes.titleText} {
26
26
  font-weight: bold;
27
27
  }
28
-
28
+
29
29
  .${exports.classes.description} {
30
30
  color: ${text.disabled};
31
31
  }
32
32
  }
33
-
34
- .${exports.classes.item} {
35
- cursor: pointer;
36
-
37
- &[data-dragging=true] {
38
- position: relative;
39
- z-index: 1;
40
- }
41
- }
42
-
33
+
43
34
  .${exports.classes.checkbox} {
44
35
  display: flex;
45
36
  margin-right: ${spacing[1]}px;
@@ -1,4 +1,4 @@
1
- import React, { ComponentProps, ReactElement, ReactNode } from 'react';
1
+ import React, { ComponentProps, ReactElement, ReactNode, RefObject } from 'react';
2
2
  import { DivProps, Id, Obj, SlotsAndProps, ToRequired } from '../../types';
3
3
  import { SelectionContextProps } from '../selectionContext';
4
4
  import { PaginationProps } from '../pagination';
@@ -136,6 +136,7 @@ interface IDataGridContext<R extends RowType = RowType> extends ToRequired<DataG
136
136
  expandedSet: Set<Id>;
137
137
  flattedColumns: (symbol | ColumnType<R>)[] | undefined;
138
138
  toggleExpanded(key: Id): void;
139
+ theadRef: RefObject<HTMLTableSectionElement | null>;
139
140
  }
140
141
  export declare function useDataGridContext<R extends RowType>(): IDataGridContext<R>;
141
142
  export declare const DataGrid: {
@@ -130,13 +130,18 @@ exports.DataGrid = (0, react_1.memo)(({ slots, slotProps, columns, rows, rowProp
130
130
  const { page, pageSize } = _paginationProps;
131
131
  return orderedRows?.slice((page - 1) * pageSize, page * pageSize);
132
132
  }, [orderedRows, _paginationProps.page, _paginationProps.pageSize, paginatable]);
133
+ /**
134
+ * ---------------------------------------------------------------
135
+ * 渲染
136
+ */
137
+ const theadRef = (0, react_1.useRef)(null);
133
138
  const { container: Container = table_1.TableContainer } = slots || {};
134
139
  const { container: containerProps } = slotProps || {};
135
140
  const renderedContent = ((0, jsx_runtime_1.jsx)(columnResize_1.ColumnResizeContext, { columnResizable: columnResizable, children: ({ scrollerRef, tableRef }) => (0, jsx_runtime_1.jsxs)(Container, { ...(0, utils_1.mergeComponentProps)(containerProps, {
136
141
  ref: scrollerRef,
137
142
  className: dataGrid_style_1.classes.container
138
- }), children: [(0, jsx_runtime_1.jsx)(table_1.Table, { size: size, bordered: bordered, striped: striped, ...tableProps, ref: (0, utils_1.cloneRef)(tableProps?.ref, tableRef), children: (0, jsx_runtime_1.jsxs)(selectionContext_1.SelectionContext, { options: rows, primaryKey: primaryKey, childrenKey: childrenKey !== null ? childrenKey : void 0, relation: relation, integration: integration, disabled: !selectable, multiple: multiple, defaultValue: defaultValue, value: value, onChange: onChange, onToggle: onToggle, children: [(0, jsx_runtime_1.jsx)(dataGridHead_1.DataGridHead, { rows: rows, flattedColumns: flattedColumns, completedColumns: completedColumns, primaryKey: primaryKey, allowSelectAll: allowSelectAll, columnResizable: columnResizable, orderColumn: innerOrderColumn.current, orderType: innerOrderType.current, onOrderChange: orderChangeHandler, filterBubbleProps: filterBubbleProps, onFilterClick: onFilterClick }), (0, jsx_runtime_1.jsx)("tbody", { children: (0, jsx_runtime_1.jsx)(DataGridContext, { value: (0, react_1.useMemo)(() => ({
139
- slots, slotProps,
143
+ }), children: [(0, jsx_runtime_1.jsx)(table_1.Table, { size: size, bordered: bordered, striped: striped, ...tableProps, ref: (0, utils_1.cloneRef)(tableProps?.ref, tableRef), children: (0, jsx_runtime_1.jsxs)(selectionContext_1.SelectionContext, { options: rows, primaryKey: primaryKey, childrenKey: childrenKey !== null ? childrenKey : void 0, relation: relation, integration: integration, disabled: !selectable, multiple: multiple, defaultValue: defaultValue, value: value, onChange: onChange, onToggle: onToggle, children: [(0, jsx_runtime_1.jsx)(dataGridHead_1.DataGridHead, { ref: theadRef, rows: rows, flattedColumns: flattedColumns, completedColumns: completedColumns, primaryKey: primaryKey, allowSelectAll: allowSelectAll, columnResizable: columnResizable, orderColumn: innerOrderColumn.current, orderType: innerOrderType.current, onOrderChange: orderChangeHandler, filterBubbleProps: filterBubbleProps, onFilterClick: onFilterClick }), (0, jsx_runtime_1.jsx)("tbody", { children: (0, jsx_runtime_1.jsx)(DataGridContext, { value: (0, react_1.useMemo)(() => ({
144
+ slots, slotProps, theadRef,
140
145
  rowProps, primaryKey, childrenKey, clickRowToSelect, indent, renderExpandIcon,
141
146
  expandedSet, flattedColumns, toggleExpanded
142
147
  }), [
@@ -92,25 +92,36 @@ exports.style = (0, utils_1.defineCss)(({ spacing, mode, gray, text, colors, eas
92
92
  }
93
93
  }
94
94
  }
95
+
96
+ tr {
97
+ position: relative;
98
+ z-index: 2;
99
+
100
+ &[data-expanded=true] {
101
+ position: sticky;
102
+ z-index: 1;
103
+ }
104
+
105
+ &.${exports.classes.sub} {
106
+ z-index: 0;
107
+ background-color: ${gray(mode === 'light' ? .02 : .22)};
95
108
 
96
- tr.${exports.classes.sub} {
97
- background-color: ${gray(mode === 'light' ? .02 : .22)};
98
-
99
- td.${exports.classes.subTd} {
100
- padding: 0;
101
- border: none;
109
+ td.${exports.classes.subTd} {
110
+ padding: 0;
111
+ border: none;
102
112
 
103
- .${exports.classes.children} {
104
- padding: ${spacing[4]}px ${spacing[5]}px;
105
- border-bottom: 1px solid ${gray(mode === 'light' ? .12 : .32)};
113
+ .${exports.classes.children} {
114
+ padding: ${spacing[4]}px ${spacing[5]}px;
115
+ border-bottom: 1px solid ${gray(mode === 'light' ? .12 : .32)};
106
116
 
107
- thead, tfoot {
108
- z-index: 3;
109
- }
117
+ thead, tfoot {
118
+ z-index: 5;
119
+ }
110
120
 
111
- th, td {
112
- &[data-sticky=left], &[data-sticky=right] {
113
- z-index: 2;
121
+ th, td {
122
+ &[data-sticky=left], &[data-sticky=right] {
123
+ z-index: 4;
124
+ }
114
125
  }
115
126
  }
116
127
  }
@@ -157,7 +168,7 @@ exports.style = (0, utils_1.defineCss)(({ spacing, mode, gray, text, colors, eas
157
168
  position: absolute;
158
169
  top: 0;
159
170
  right: -4px;
160
- z-index: 1;
171
+ z-index: 3;
161
172
  }
162
173
 
163
174
  th:last-of-type {
@@ -1,7 +1,8 @@
1
- import { ReactElement } from 'react';
1
+ import { ReactElement, RefObject } from 'react';
2
2
  import { ColumnType, DataGridProps, RowType } from './dataGrid';
3
3
  import { Id } from '../../types';
4
4
  interface DataGridHeadProps<R extends RowType, V extends Id = Id> extends Required<Pick<DataGridProps<R, V>, 'primaryKey' | 'orderType' | 'onOrderChange' | 'allowSelectAll' | 'columnResizable'>> {
5
+ ref: RefObject<HTMLTableSectionElement | null>;
5
6
  rows: R[] | undefined;
6
7
  orderColumn: Id | undefined;
7
8
  flattedColumns: (symbol | ColumnType<R>)[] | undefined;
@@ -18,7 +18,7 @@ const bubble_1 = require("../bubble");
18
18
  const filterBubbleContent_1 = require("./filterBubbleContent");
19
19
  const form_1 = require("../form");
20
20
  const utils_1 = require("../../utils");
21
- exports.DataGridHead = (0, react_2.memo)(({ allowSelectAll, columnResizable, flattedColumns, completedColumns, rows, primaryKey, orderColumn, orderType, onOrderChange, filterBubbleProps, onFilterClick }) => {
21
+ exports.DataGridHead = (0, react_2.memo)(({ ref, allowSelectAll, columnResizable, flattedColumns, completedColumns, rows, primaryKey, orderColumn, orderType, onOrderChange, filterBubbleProps, onFilterClick }) => {
22
22
  const { multiple, setValue, selectionStatus } = (0, selectionContext_1.useSelectionContext)();
23
23
  const allSelectionStatus = (0, react_2.useMemo)(() => {
24
24
  if (multiple && allowSelectAll) {
@@ -116,5 +116,5 @@ exports.DataGridHead = (0, react_2.memo)(({ allowSelectAll, columnResizable, fla
116
116
  ];
117
117
  }) }, i));
118
118
  });
119
- return (0, jsx_runtime_1.jsx)("thead", { children: renderedHead });
119
+ return (0, jsx_runtime_1.jsx)("thead", { ref: ref, children: renderedHead });
120
120
  });
@@ -16,10 +16,11 @@ const icon_1 = require("../icon");
16
16
  const faMinusSquare_1 = require("@fortawesome/free-regular-svg-icons/faMinusSquare");
17
17
  const faPlusSquare_1 = require("@fortawesome/free-regular-svg-icons/faPlusSquare");
18
18
  const dataGrid_style_1 = require("./dataGrid.style");
19
+ const stickyRow_1 = require("./stickyRow");
19
20
  exports.DataGridRows = (0, react_2.memo)(({ rows, _level = 0 }) => {
20
21
  const { multiple, toggleSelected, selectionStatus } = (0, selectionContext_1.useSelectionContext)();
21
22
  const { slots, slotProps, rowProps, primaryKey, childrenKey, clickRowToSelect, indent, renderExpandIcon, expandedSet, flattedColumns, toggleExpanded } = (0, dataGrid_1.useDataGridContext)();
22
- const { tr: Tr = 'tr' } = slots || {};
23
+ const { tr: Tr = stickyRow_1.StickyRow } = slots || {};
23
24
  const { tr: TrProps } = slotProps || {};
24
25
  return rows?.flatMap((row, i, arr) => {
25
26
  const trKey = row[primaryKey];
@@ -37,8 +38,9 @@ exports.DataGridRows = (0, react_2.memo)(({ rows, _level = 0 }) => {
37
38
  className: (0, utils_1.clsx)(_rowProps, _level > 0 && dataGrid_style_1.classes.sub),
38
39
  onClick() {
39
40
  clickRowToSelect && toggleSelected(trKey);
40
- }
41
- }), key: trKey, "data-selected": status === 2 }, flattedColumns?.flatMap((col, j) => {
41
+ },
42
+ sticky: currentExpanded
43
+ }), key: trKey, "data-selected": status === 2, "data-expanded": currentExpanded }, flattedColumns?.flatMap((col, j) => {
42
44
  if (typeof col === 'symbol') {
43
45
  if (col === dataGrid_1.DataGrid.EXPAND_COLUMN) {
44
46
  expandableIndex = j;
@@ -0,0 +1,5 @@
1
+ import { ComponentProps } from 'react';
2
+ export interface StickyRowProps extends ComponentProps<'tr'> {
3
+ sticky?: boolean;
4
+ }
5
+ export declare function StickyRow({ sticky, ...props }: StickyRowProps): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StickyRow = StickyRow;
4
+ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const dataGrid_1 = require("./dataGrid");
7
+ const utils_1 = require("../../utils");
8
+ function StickyRow({ sticky, ...props }) {
9
+ const [offset, setOffset] = (0, react_1.useState)(0);
10
+ const { theadRef } = (0, dataGrid_1.useDataGridContext)();
11
+ (0, react_1.useEffect)(() => {
12
+ if (!sticky || !theadRef.current) {
13
+ return;
14
+ }
15
+ const resizeObserver = new ResizeObserver(() => {
16
+ setOffset(theadRef.current.offsetHeight);
17
+ });
18
+ resizeObserver.observe(theadRef.current);
19
+ return () => {
20
+ resizeObserver.disconnect();
21
+ };
22
+ }, [sticky]);
23
+ return ((0, jsx_runtime_1.jsx)("tr", { ...(0, utils_1.mergeComponentProps)(props, {
24
+ ...sticky && {
25
+ style: { top: offset }
26
+ }
27
+ }) }));
28
+ }
@@ -9,18 +9,56 @@ const clickAway_1 = require("../clickAway");
9
9
  const popper_style_1 = require("./popper.style");
10
10
  const popperContext_1 = require("./popperContext");
11
11
  const theme_1 = require("../theme");
12
- const getAttemptOrder = (placement) => {
13
- const order = ['top', 'bottom', 'left', 'right', 'topLeft', 'topRight', 'rightTop', 'rightBottom', 'bottomRight', 'bottomLeft', 'leftBottom', 'leftTop'];
14
- const index = order.indexOf(placement);
15
- if (index > -1) {
16
- return [
17
- placement,
18
- ...order.slice(index + 1),
19
- ...order.slice(0, index),
20
- placement
21
- ];
12
+ const defaultAttemptOrder = ['top', 'bottom', 'left', 'right', 'topLeft', 'topRight', 'rightTop', 'rightBottom', 'bottomRight', 'bottomLeft', 'leftBottom', 'leftTop'];
13
+ const contextMenuOrder = [
14
+ 'bottomRight',
15
+ 'rightBottom',
16
+ 'bottomLeft',
17
+ 'leftBottom',
18
+ 'topLeft',
19
+ 'leftTop',
20
+ 'topRight',
21
+ 'rightTop'
22
+ ];
23
+ const getAttemptOrder = (startPlacement, contextMenu, sizeAdaptable, callback) => {
24
+ if (contextMenu) {
25
+ const startIndex = Math.max(contextMenuOrder.indexOf(startPlacement), 0);
26
+ for (let i = 0; i <= contextMenuOrder.length + 1; i += 2) {
27
+ const index = (startIndex + i) % contextMenuOrder.length;
28
+ if (callback(contextMenuOrder[index]) === false) {
29
+ break;
30
+ }
31
+ }
32
+ }
33
+ else {
34
+ let order = defaultAttemptOrder;
35
+ let startIndex;
36
+ if (sizeAdaptable) {
37
+ switch (startPlacement) {
38
+ case 'top':
39
+ order = [startPlacement, 'bottom', startPlacement];
40
+ break;
41
+ case 'bottom':
42
+ order = [startPlacement, 'top', startPlacement];
43
+ break;
44
+ case 'left':
45
+ order = [startPlacement, 'right', startPlacement];
46
+ break;
47
+ case 'right':
48
+ order = [startPlacement, 'left', startPlacement];
49
+ }
50
+ startIndex = 0;
51
+ }
52
+ else {
53
+ startIndex = Math.max(order.indexOf(startPlacement), 0);
54
+ }
55
+ for (let i = 0; i <= order.length; i++) {
56
+ const index = (startIndex + i) % order.length;
57
+ if (callback(order[index]) === false) {
58
+ break;
59
+ }
60
+ }
22
61
  }
23
- return order;
24
62
  };
25
63
  const splitPlacement = {
26
64
  top: ['top'],
@@ -141,33 +179,6 @@ function Popper({ ref, popperRef, anchorElement, container, effectContainer, con
141
179
  const mouseY = contextMenuEvent.current.clientY - containerRect.top;
142
180
  attempt = placement => {
143
181
  switch (placement) {
144
- case 'topLeft':
145
- case 'leftTop':
146
- top = void 0;
147
- bottom = -mouseY;
148
- left = void 0;
149
- right = containerRect.width - mouseX;
150
- originX = '100%';
151
- originY = '100%';
152
- break;
153
- case 'topRight':
154
- case 'rightTop':
155
- top = void 0;
156
- bottom = -mouseY;
157
- left = mouseX;
158
- right = void 0;
159
- originX = '0%';
160
- originY = '100%';
161
- break;
162
- case 'bottomLeft':
163
- case 'leftBottom':
164
- top = mouseY;
165
- bottom = void 0;
166
- left = void 0;
167
- right = containerRect.width - mouseX;
168
- originX = '100%';
169
- originY = '0%';
170
- break;
171
182
  case 'bottomRight':
172
183
  case 'rightBottom':
173
184
  top = mouseY;
@@ -177,37 +188,32 @@ function Popper({ ref, popperRef, anchorElement, container, effectContainer, con
177
188
  originX = '0%';
178
189
  originY = '0%';
179
190
  break;
180
- case 'top':
181
- top = mouseY - popperHeight;
182
- bottom = void 0;
183
- left = mouseX - popperWidth / 2;
184
- right = void 0;
185
- originX = '50%';
186
- originY = '100%';
187
- break;
188
- case 'bottom':
191
+ case 'bottomLeft':
192
+ case 'leftBottom':
189
193
  top = mouseY;
190
194
  bottom = void 0;
191
- left = mouseX - popperWidth / 2;
192
- right = void 0;
193
- originX = '50%';
195
+ left = void 0;
196
+ right = containerRect.width - mouseX;
197
+ originX = '100%';
194
198
  originY = '0%';
195
199
  break;
196
- case 'left':
197
- top = mouseY - popperHeight / 2;
198
- bottom = void 0;
199
- left = mouseX - popperWidth;
200
- right = void 0;
200
+ case 'topLeft':
201
+ case 'leftTop':
202
+ top = void 0;
203
+ bottom = -mouseY;
204
+ left = void 0;
205
+ right = containerRect.width - mouseX;
201
206
  originX = '100%';
202
- originY = '50%';
207
+ originY = '100%';
203
208
  break;
204
- case 'right':
205
- top = mouseY - popperHeight / 2;
206
- bottom = void 0;
209
+ case 'topRight':
210
+ case 'rightTop':
211
+ top = void 0;
212
+ bottom = -mouseY;
207
213
  left = mouseX;
208
214
  right = void 0;
209
215
  originX = '0%';
210
- originY = '50%';
216
+ originY = '100%';
211
217
  }
212
218
  popperEl.style.top = typeof top === 'undefined' ? '' : top + 'px';
213
219
  popperEl.style.bottom = typeof bottom === 'undefined' ? '' : bottom + 'px';
@@ -311,13 +317,7 @@ function Popper({ ref, popperRef, anchorElement, container, effectContainer, con
311
317
  else {
312
318
  popperEl.style.transition = 'none';
313
319
  popperEl.style.transform = 'scale(1)';
314
- const attemptOrder = getAttemptOrder(placement);
315
- for (let i = 0; i < attemptOrder.length; i++) {
316
- const t = attempt(attemptOrder[i]);
317
- if (t === false) {
318
- break;
319
- }
320
- }
320
+ getAttemptOrder(placement, !!contextMenuEvent.current, sizeAdaptable, attempt);
321
321
  }
322
322
  beforeOpen?.();
323
323
  placeA.current = pA;
@@ -7,6 +7,7 @@ const selectBase_1 = require("../selectBase");
7
7
  const optionsBase_1 = require("../optionsBase");
8
8
  const menuItem_1 = require("../menuItem");
9
9
  const selectionContext_1 = require("../selectionContext");
10
+ const utils_1 = require("../../utils");
10
11
  exports.Select = (0, react_1.memo)(({ children,
11
12
  // 从SelectableProps继承
12
13
  multiple = false, // 同时转发至<SelectBase/>
@@ -19,7 +20,15 @@ showCheckbox = multiple, loading = false, options, labelKey = 'label', primaryKe
19
20
  const optionsMap = (0, react_1.useMemo)(() => {
20
21
  const map = new Map();
21
22
  optionsArr?.forEach(opt => {
22
- opt && typeof opt === 'object' && map.set(opt[primaryKey], opt);
23
+ if (opt && typeof opt === 'object') {
24
+ const key = opt[primaryKey];
25
+ if (!(0, utils_1.isUnset)(key)) {
26
+ if (map.has(key)) {
27
+ console.warn(`[@canlooks/can-ui/<Select/>] option key "${key}" was duplicated`);
28
+ }
29
+ map.set(opt[primaryKey], opt);
30
+ }
31
+ }
23
32
  });
24
33
  return map;
25
34
  }, [optionsArr, primaryKey]);
@@ -9,7 +9,7 @@ exports.classes = (0, utils_1.defineInnerClasses)('selected-list', [
9
9
  'optionWrap'
10
10
  ]);
11
11
  exports.style = (0, utils_1.defineCss)(({ spacing }) => (0, react_1.css) `
12
- @layer reset {
12
+ @layer override {
13
13
  .${exports.classes.optionWrap} {
14
14
  margin-bottom: ${spacing[3]}px;
15
15
  }
@@ -32,7 +32,12 @@ function useSelection({ ...props }) {
32
32
  item._parentId = parentId;
33
33
  item._isLast = i === arr.length - 1;
34
34
  const id = item[props.primaryKey];
35
- !(0, utils_1.isUnset)(id) && map.set(id, item);
35
+ if (!(0, utils_1.isUnset)(id)) {
36
+ if (map.has(id)) {
37
+ console.warn(`[@canlooks/can-ui/<SelectionContext/>] option key "${id}" was duplicated`);
38
+ }
39
+ map.set(id, item);
40
+ }
36
41
  fn(item[props.childrenKey], id);
37
42
  });
38
43
  };
@@ -20,7 +20,7 @@ exports.style = (0, utils_1.defineCss)(({ mode, spacing, gray, divider, backgrou
20
20
 
21
21
  thead, tfoot {
22
22
  position: sticky;
23
- z-index: 5;
23
+ z-index: 7;
24
24
 
25
25
  th, td {
26
26
  background-color: ${headerBg};
@@ -73,7 +73,9 @@ function useStickyCellProps({ sticky, ...props }) {
73
73
  ref: innerRef,
74
74
  css: tableSticky_style_1.style,
75
75
  className: table_style_1.classes.cell,
76
- style: sticky && { [sticky]: offset },
76
+ ...sticky && {
77
+ style: { [sticky]: offset }
78
+ },
77
79
  'data-sticky': sticky
78
80
  });
79
81
  }
@@ -11,7 +11,7 @@ exports.style = (0, utils_1.defineCss)(({ easing }) => (0, react_1.css) `
11
11
  &[data-sticky=left], &[data-sticky=right] {
12
12
  overflow: visible;
13
13
  position: sticky;
14
- z-index: 4;
14
+ z-index: 6;
15
15
 
16
16
  &::after {
17
17
  content: '';
@@ -26,7 +26,12 @@ function useColumnMap(columns) {
26
26
  columns?.forEach((col, i) => {
27
27
  if (typeof col !== 'symbol') {
28
28
  const _key = setDefaultColumnKey(col, i);
29
- !(0, utils_1.isUnset)(_key) && map.set(_key, col);
29
+ if (!(0, utils_1.isUnset)(_key)) {
30
+ if (map.has(_key)) {
31
+ console.warn(`[@canlooks/can-ui/<DataGrid/>] column key "${_key}" was duplicated`);
32
+ }
33
+ map.set(_key, col);
34
+ }
30
35
  }
31
36
  });
32
37
  return map;
@@ -45,7 +45,12 @@ export const Autocomplete = memo(({ children, loading, options, loadOptions, pri
45
45
  actualOptions?.forEach(opt => {
46
46
  if (opt && typeof opt === 'object') {
47
47
  const key = opt[primaryKey];
48
- !isUnset(key) && map.set(key, opt);
48
+ if (!isUnset(key)) {
49
+ if (map.has(key)) {
50
+ console.warn(`[@canlooks/can-ui/<Autocomple/>] option key "${key}" was duplicated`);
51
+ }
52
+ map.set(key, opt);
53
+ }
49
54
  }
50
55
  });
51
56
  return map;
@@ -94,6 +94,7 @@ export const Curd = memo((props) => {
94
94
  const controlColumn = {
95
95
  key: CONTROL_COLUMN_KEY,
96
96
  title: controlColumnTitle,
97
+ className: classes.controlColumn,
97
98
  render(row) {
98
99
  const _updatable = typeof updatable === 'function' ? updatable(row) : updatable;
99
100
  const _deletable = typeof deletable === 'function' ? deletable(row) : deletable;
@@ -14,6 +14,7 @@ export declare const classes: {
14
14
  toolbarLeft: string;
15
15
  toolbarRight: string;
16
16
  card: string;
17
+ controlColumn: string;
17
18
  dialogTitle: string;
18
19
  copyButton: string;
19
20
  } & {
@@ -17,6 +17,7 @@ export const classes = defineInnerClasses('curd', [
17
17
  'toolbarRight',
18
18
  'divider',
19
19
  'card',
20
+ 'controlColumn',
20
21
  'control',
21
22
  'dialogTitle',
22
23
  'copyButton'
@@ -29,7 +30,7 @@ export const style = defineCss(({ spacing, text, background, borderRadius }) =>
29
30
  min-height: 0;
30
31
  display: flex;
31
32
  flex-direction: column;
32
-
33
+
33
34
  .${classes.filterForm} {
34
35
  min-height: 0;
35
36
  display: flex;
@@ -10,6 +10,7 @@ import { defaultSensors, isUnset, onDndDragEnd } from '../../utils/index.js';
10
10
  import { Icon } from '../icon/index.js';
11
11
  import { faGear } from '@fortawesome/free-solid-svg-icons/faGear';
12
12
  import { DragDropProvider, useDragDropMonitor } from '@dnd-kit/react';
13
+ import { Tooltip } from '../tooltip/index.js';
13
14
  export const CurdColumnConfig = memo((props) => {
14
15
  const dragEndHandler = e => {
15
16
  const newColumns = onDndDragEnd(e, props.columns || [], '_key');
@@ -38,12 +39,12 @@ const CurdColumnConfigContent = memo(({ columns, innerVisible, setInnerVisible,
38
39
  ? [...o, key]
39
40
  : o.filter(k => k !== key));
40
41
  };
41
- return (_jsx(Bubble, { placement: "bottomRight", trigger: ['hover', 'click'], ...columnConfigBubbleProps, css: style, open: open, onOpenChange: openChangeHandler, content: _jsxs("div", { className: classes.content, children: [_jsxs("div", { className: classes.title, children: [_jsx("div", { className: classes.titleText, children: "\u5217\u8BBE\u7F6E" }), _jsx("div", { className: classes.description, children: "\u62D6\u62FD\u8C03\u6574\u987A\u5E8F" })] }), columns?.map((col, i) => {
42
+ return (_jsx(Bubble, { placement: "bottomRight", trigger: "click", ...columnConfigBubbleProps, css: style, open: open, onOpenChange: openChangeHandler, content: _jsxs("div", { className: classes.content, children: [_jsxs("div", { className: classes.title, children: [_jsx("div", { className: classes.titleText, children: "\u5217\u8BBE\u7F6E" }), _jsx("div", { className: classes.description, children: "\u62D6\u62FD\u8C03\u6574\u987A\u5E8F" })] }), columns?.map((col, i) => {
42
43
  const id = col._key;
43
44
  const checked = !isUnset(id) && (!visibleSet || visibleSet.has(id));
44
45
  return (_jsx(SortableItem, { id: id ?? i, index: i, component: MenuItem, className: classes.item, prefix: _jsx(Checkbox, { className: classes.checkbox, checked: checked, onChange: e => {
45
46
  e.stopPropagation();
46
47
  toggleVisible(id, e.target.checked);
47
48
  } }), onClick: () => toggleVisible(id, !checked), label: col.titleText ?? col.title, noStyle: true }, id ?? i));
48
- })] }), autoClose: false, children: _jsx(Button, { shape: "circular", variant: "text", color: "text.secondary", children: _jsx(Icon, { icon: faGear }) }) }));
49
+ })] }), autoClose: false, children: _jsx(Tooltip, { title: "\u5217\u8BBE\u7F6E", clickToClose: true, children: _jsx(Button, { shape: "circular", variant: "text", color: "text.secondary", children: _jsx(Icon, { icon: faGear }) }) }) }));
49
50
  });
@@ -18,25 +18,16 @@ export const style = defineCss(({ spacing, text }) => css `
18
18
  align-items: center;
19
19
  justify-content: space-between;
20
20
  padding: ${spacing[3]}px;
21
-
21
+
22
22
  .${classes.titleText} {
23
23
  font-weight: bold;
24
24
  }
25
-
25
+
26
26
  .${classes.description} {
27
27
  color: ${text.disabled};
28
28
  }
29
29
  }
30
-
31
- .${classes.item} {
32
- cursor: pointer;
33
-
34
- &[data-dragging=true] {
35
- position: relative;
36
- z-index: 1;
37
- }
38
- }
39
-
30
+
40
31
  .${classes.checkbox} {
41
32
  display: flex;
42
33
  margin-right: ${spacing[1]}px;
@@ -1,4 +1,4 @@
1
- import React, { ComponentProps, ReactElement, ReactNode } from 'react';
1
+ import React, { ComponentProps, ReactElement, ReactNode, RefObject } from 'react';
2
2
  import { DivProps, Id, Obj, SlotsAndProps, ToRequired } from '../../types.js';
3
3
  import { SelectionContextProps } from '../selectionContext/index.js';
4
4
  import { PaginationProps } from '../pagination/index.js';
@@ -136,6 +136,7 @@ interface IDataGridContext<R extends RowType = RowType> extends ToRequired<DataG
136
136
  expandedSet: Set<Id>;
137
137
  flattedColumns: (symbol | ColumnType<R>)[] | undefined;
138
138
  toggleExpanded(key: Id): void;
139
+ theadRef: RefObject<HTMLTableSectionElement | null>;
139
140
  }
140
141
  export declare function useDataGridContext<R extends RowType>(): IDataGridContext<R>;
141
142
  export declare const DataGrid: {
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
- import { createContext, memo, useCallback, useContext, useMemo, useState } from 'react';
2
+ import { createContext, memo, useCallback, useContext, useMemo, useRef, useState } from 'react';
3
3
  import { classes, style } from './dataGrid.style.js';
4
4
  import { SelectionContext, useSelectionContext } from '../selectionContext/index.js';
5
5
  import { Pagination } from '../pagination/index.js';
@@ -126,13 +126,18 @@ export const DataGrid = memo(({ slots, slotProps, columns, rows, rowProps, prima
126
126
  const { page, pageSize } = _paginationProps;
127
127
  return orderedRows?.slice((page - 1) * pageSize, page * pageSize);
128
128
  }, [orderedRows, _paginationProps.page, _paginationProps.pageSize, paginatable]);
129
+ /**
130
+ * ---------------------------------------------------------------
131
+ * 渲染
132
+ */
133
+ const theadRef = useRef(null);
129
134
  const { container: Container = TableContainer } = slots || {};
130
135
  const { container: containerProps } = slotProps || {};
131
136
  const renderedContent = (_jsx(ColumnResizeContext, { columnResizable: columnResizable, children: ({ scrollerRef, tableRef }) => _jsxs(Container, { ...mergeComponentProps(containerProps, {
132
137
  ref: scrollerRef,
133
138
  className: classes.container
134
- }), children: [_jsx(Table, { size: size, bordered: bordered, striped: striped, ...tableProps, ref: cloneRef(tableProps?.ref, tableRef), children: _jsxs(SelectionContext, { options: rows, primaryKey: primaryKey, childrenKey: childrenKey !== null ? childrenKey : void 0, relation: relation, integration: integration, disabled: !selectable, multiple: multiple, defaultValue: defaultValue, value: value, onChange: onChange, onToggle: onToggle, children: [_jsx(DataGridHead, { rows: rows, flattedColumns: flattedColumns, completedColumns: completedColumns, primaryKey: primaryKey, allowSelectAll: allowSelectAll, columnResizable: columnResizable, orderColumn: innerOrderColumn.current, orderType: innerOrderType.current, onOrderChange: orderChangeHandler, filterBubbleProps: filterBubbleProps, onFilterClick: onFilterClick }), _jsx("tbody", { children: _jsx(DataGridContext, { value: useMemo(() => ({
135
- slots, slotProps,
139
+ }), children: [_jsx(Table, { size: size, bordered: bordered, striped: striped, ...tableProps, ref: cloneRef(tableProps?.ref, tableRef), children: _jsxs(SelectionContext, { options: rows, primaryKey: primaryKey, childrenKey: childrenKey !== null ? childrenKey : void 0, relation: relation, integration: integration, disabled: !selectable, multiple: multiple, defaultValue: defaultValue, value: value, onChange: onChange, onToggle: onToggle, children: [_jsx(DataGridHead, { ref: theadRef, rows: rows, flattedColumns: flattedColumns, completedColumns: completedColumns, primaryKey: primaryKey, allowSelectAll: allowSelectAll, columnResizable: columnResizable, orderColumn: innerOrderColumn.current, orderType: innerOrderType.current, onOrderChange: orderChangeHandler, filterBubbleProps: filterBubbleProps, onFilterClick: onFilterClick }), _jsx("tbody", { children: _jsx(DataGridContext, { value: useMemo(() => ({
140
+ slots, slotProps, theadRef,
136
141
  rowProps, primaryKey, childrenKey, clickRowToSelect, indent, renderExpandIcon,
137
142
  expandedSet, flattedColumns, toggleExpanded
138
143
  }), [
@@ -89,25 +89,36 @@ export const style = defineCss(({ spacing, mode, gray, text, colors, easing }) =
89
89
  }
90
90
  }
91
91
  }
92
+
93
+ tr {
94
+ position: relative;
95
+ z-index: 2;
96
+
97
+ &[data-expanded=true] {
98
+ position: sticky;
99
+ z-index: 1;
100
+ }
101
+
102
+ &.${classes.sub} {
103
+ z-index: 0;
104
+ background-color: ${gray(mode === 'light' ? .02 : .22)};
92
105
 
93
- tr.${classes.sub} {
94
- background-color: ${gray(mode === 'light' ? .02 : .22)};
95
-
96
- td.${classes.subTd} {
97
- padding: 0;
98
- border: none;
106
+ td.${classes.subTd} {
107
+ padding: 0;
108
+ border: none;
99
109
 
100
- .${classes.children} {
101
- padding: ${spacing[4]}px ${spacing[5]}px;
102
- border-bottom: 1px solid ${gray(mode === 'light' ? .12 : .32)};
110
+ .${classes.children} {
111
+ padding: ${spacing[4]}px ${spacing[5]}px;
112
+ border-bottom: 1px solid ${gray(mode === 'light' ? .12 : .32)};
103
113
 
104
- thead, tfoot {
105
- z-index: 3;
106
- }
114
+ thead, tfoot {
115
+ z-index: 5;
116
+ }
107
117
 
108
- th, td {
109
- &[data-sticky=left], &[data-sticky=right] {
110
- z-index: 2;
118
+ th, td {
119
+ &[data-sticky=left], &[data-sticky=right] {
120
+ z-index: 4;
121
+ }
111
122
  }
112
123
  }
113
124
  }
@@ -154,7 +165,7 @@ export const style = defineCss(({ spacing, mode, gray, text, colors, easing }) =
154
165
  position: absolute;
155
166
  top: 0;
156
167
  right: -4px;
157
- z-index: 1;
168
+ z-index: 3;
158
169
  }
159
170
 
160
171
  th:last-of-type {
@@ -1,7 +1,8 @@
1
- import { ReactElement } from 'react';
1
+ import { ReactElement, RefObject } from 'react';
2
2
  import { ColumnType, DataGridProps, RowType } from './dataGrid.js';
3
3
  import { Id } from '../../types.js';
4
4
  interface DataGridHeadProps<R extends RowType, V extends Id = Id> extends Required<Pick<DataGridProps<R, V>, 'primaryKey' | 'orderType' | 'onOrderChange' | 'allowSelectAll' | 'columnResizable'>> {
5
+ ref: RefObject<HTMLTableSectionElement | null>;
5
6
  rows: R[] | undefined;
6
7
  orderColumn: Id | undefined;
7
8
  flattedColumns: (symbol | ColumnType<R>)[] | undefined;
@@ -15,7 +15,7 @@ import { Bubble } from '../bubble/index.js';
15
15
  import { FilterBubbleContent } from './filterBubbleContent.js';
16
16
  import { useFormValueContext } from '../form/index.js';
17
17
  import { isUnset, mergeComponentProps } from '../../utils/index.js';
18
- export const DataGridHead = memo(({ allowSelectAll, columnResizable, flattedColumns, completedColumns, rows, primaryKey, orderColumn, orderType, onOrderChange, filterBubbleProps, onFilterClick }) => {
18
+ export const DataGridHead = memo(({ ref, allowSelectAll, columnResizable, flattedColumns, completedColumns, rows, primaryKey, orderColumn, orderType, onOrderChange, filterBubbleProps, onFilterClick }) => {
19
19
  const { multiple, setValue, selectionStatus } = useSelectionContext();
20
20
  const allSelectionStatus = useMemo(() => {
21
21
  if (multiple && allowSelectAll) {
@@ -113,5 +113,5 @@ export const DataGridHead = memo(({ allowSelectAll, columnResizable, flattedColu
113
113
  ];
114
114
  }) }, i));
115
115
  });
116
- return _jsx("thead", { children: renderedHead });
116
+ return _jsx("thead", { ref: ref, children: renderedHead });
117
117
  });
@@ -13,10 +13,11 @@ import { Icon } from '../icon/index.js';
13
13
  import { faMinusSquare } from '@fortawesome/free-regular-svg-icons/faMinusSquare';
14
14
  import { faPlusSquare } from '@fortawesome/free-regular-svg-icons/faPlusSquare';
15
15
  import { classes } from './dataGrid.style.js';
16
+ import { StickyRow } from './stickyRow.js';
16
17
  export const DataGridRows = memo(({ rows, _level = 0 }) => {
17
18
  const { multiple, toggleSelected, selectionStatus } = useSelectionContext();
18
19
  const { slots, slotProps, rowProps, primaryKey, childrenKey, clickRowToSelect, indent, renderExpandIcon, expandedSet, flattedColumns, toggleExpanded } = useDataGridContext();
19
- const { tr: Tr = 'tr' } = slots || {};
20
+ const { tr: Tr = StickyRow } = slots || {};
20
21
  const { tr: TrProps } = slotProps || {};
21
22
  return rows?.flatMap((row, i, arr) => {
22
23
  const trKey = row[primaryKey];
@@ -34,8 +35,9 @@ export const DataGridRows = memo(({ rows, _level = 0 }) => {
34
35
  className: clsx(_rowProps, _level > 0 && classes.sub),
35
36
  onClick() {
36
37
  clickRowToSelect && toggleSelected(trKey);
37
- }
38
- }), key: trKey, "data-selected": status === 2 }, flattedColumns?.flatMap((col, j) => {
38
+ },
39
+ sticky: currentExpanded
40
+ }), key: trKey, "data-selected": status === 2, "data-expanded": currentExpanded }, flattedColumns?.flatMap((col, j) => {
39
41
  if (typeof col === 'symbol') {
40
42
  if (col === DataGrid.EXPAND_COLUMN) {
41
43
  expandableIndex = j;
@@ -0,0 +1,5 @@
1
+ import { ComponentProps } from 'react';
2
+ export interface StickyRowProps extends ComponentProps<'tr'> {
3
+ sticky?: boolean;
4
+ }
5
+ export declare function StickyRow({ sticky, ...props }: StickyRowProps): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "@emotion/react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { useDataGridContext } from './dataGrid.js';
4
+ import { mergeComponentProps } from '../../utils/index.js';
5
+ export function StickyRow({ sticky, ...props }) {
6
+ const [offset, setOffset] = useState(0);
7
+ const { theadRef } = useDataGridContext();
8
+ useEffect(() => {
9
+ if (!sticky || !theadRef.current) {
10
+ return;
11
+ }
12
+ const resizeObserver = new ResizeObserver(() => {
13
+ setOffset(theadRef.current.offsetHeight);
14
+ });
15
+ resizeObserver.observe(theadRef.current);
16
+ return () => {
17
+ resizeObserver.disconnect();
18
+ };
19
+ }, [sticky]);
20
+ return (_jsx("tr", { ...mergeComponentProps(props, {
21
+ ...sticky && {
22
+ style: { top: offset }
23
+ }
24
+ }) }));
25
+ }
@@ -6,18 +6,56 @@ import { ClickAway } from '../clickAway/index.js';
6
6
  import { classes, style } from './popper.style.js';
7
7
  import { PopperContext, usePopperContext } from './popperContext.js';
8
8
  import { useTheme } from '../theme/index.js';
9
- const getAttemptOrder = (placement) => {
10
- const order = ['top', 'bottom', 'left', 'right', 'topLeft', 'topRight', 'rightTop', 'rightBottom', 'bottomRight', 'bottomLeft', 'leftBottom', 'leftTop'];
11
- const index = order.indexOf(placement);
12
- if (index > -1) {
13
- return [
14
- placement,
15
- ...order.slice(index + 1),
16
- ...order.slice(0, index),
17
- placement
18
- ];
9
+ const defaultAttemptOrder = ['top', 'bottom', 'left', 'right', 'topLeft', 'topRight', 'rightTop', 'rightBottom', 'bottomRight', 'bottomLeft', 'leftBottom', 'leftTop'];
10
+ const contextMenuOrder = [
11
+ 'bottomRight',
12
+ 'rightBottom',
13
+ 'bottomLeft',
14
+ 'leftBottom',
15
+ 'topLeft',
16
+ 'leftTop',
17
+ 'topRight',
18
+ 'rightTop'
19
+ ];
20
+ const getAttemptOrder = (startPlacement, contextMenu, sizeAdaptable, callback) => {
21
+ if (contextMenu) {
22
+ const startIndex = Math.max(contextMenuOrder.indexOf(startPlacement), 0);
23
+ for (let i = 0; i <= contextMenuOrder.length + 1; i += 2) {
24
+ const index = (startIndex + i) % contextMenuOrder.length;
25
+ if (callback(contextMenuOrder[index]) === false) {
26
+ break;
27
+ }
28
+ }
29
+ }
30
+ else {
31
+ let order = defaultAttemptOrder;
32
+ let startIndex;
33
+ if (sizeAdaptable) {
34
+ switch (startPlacement) {
35
+ case 'top':
36
+ order = [startPlacement, 'bottom', startPlacement];
37
+ break;
38
+ case 'bottom':
39
+ order = [startPlacement, 'top', startPlacement];
40
+ break;
41
+ case 'left':
42
+ order = [startPlacement, 'right', startPlacement];
43
+ break;
44
+ case 'right':
45
+ order = [startPlacement, 'left', startPlacement];
46
+ }
47
+ startIndex = 0;
48
+ }
49
+ else {
50
+ startIndex = Math.max(order.indexOf(startPlacement), 0);
51
+ }
52
+ for (let i = 0; i <= order.length; i++) {
53
+ const index = (startIndex + i) % order.length;
54
+ if (callback(order[index]) === false) {
55
+ break;
56
+ }
57
+ }
19
58
  }
20
- return order;
21
59
  };
22
60
  const splitPlacement = {
23
61
  top: ['top'],
@@ -138,33 +176,6 @@ export function Popper({ ref, popperRef, anchorElement, container, effectContain
138
176
  const mouseY = contextMenuEvent.current.clientY - containerRect.top;
139
177
  attempt = placement => {
140
178
  switch (placement) {
141
- case 'topLeft':
142
- case 'leftTop':
143
- top = void 0;
144
- bottom = -mouseY;
145
- left = void 0;
146
- right = containerRect.width - mouseX;
147
- originX = '100%';
148
- originY = '100%';
149
- break;
150
- case 'topRight':
151
- case 'rightTop':
152
- top = void 0;
153
- bottom = -mouseY;
154
- left = mouseX;
155
- right = void 0;
156
- originX = '0%';
157
- originY = '100%';
158
- break;
159
- case 'bottomLeft':
160
- case 'leftBottom':
161
- top = mouseY;
162
- bottom = void 0;
163
- left = void 0;
164
- right = containerRect.width - mouseX;
165
- originX = '100%';
166
- originY = '0%';
167
- break;
168
179
  case 'bottomRight':
169
180
  case 'rightBottom':
170
181
  top = mouseY;
@@ -174,37 +185,32 @@ export function Popper({ ref, popperRef, anchorElement, container, effectContain
174
185
  originX = '0%';
175
186
  originY = '0%';
176
187
  break;
177
- case 'top':
178
- top = mouseY - popperHeight;
179
- bottom = void 0;
180
- left = mouseX - popperWidth / 2;
181
- right = void 0;
182
- originX = '50%';
183
- originY = '100%';
184
- break;
185
- case 'bottom':
188
+ case 'bottomLeft':
189
+ case 'leftBottom':
186
190
  top = mouseY;
187
191
  bottom = void 0;
188
- left = mouseX - popperWidth / 2;
189
- right = void 0;
190
- originX = '50%';
192
+ left = void 0;
193
+ right = containerRect.width - mouseX;
194
+ originX = '100%';
191
195
  originY = '0%';
192
196
  break;
193
- case 'left':
194
- top = mouseY - popperHeight / 2;
195
- bottom = void 0;
196
- left = mouseX - popperWidth;
197
- right = void 0;
197
+ case 'topLeft':
198
+ case 'leftTop':
199
+ top = void 0;
200
+ bottom = -mouseY;
201
+ left = void 0;
202
+ right = containerRect.width - mouseX;
198
203
  originX = '100%';
199
- originY = '50%';
204
+ originY = '100%';
200
205
  break;
201
- case 'right':
202
- top = mouseY - popperHeight / 2;
203
- bottom = void 0;
206
+ case 'topRight':
207
+ case 'rightTop':
208
+ top = void 0;
209
+ bottom = -mouseY;
204
210
  left = mouseX;
205
211
  right = void 0;
206
212
  originX = '0%';
207
- originY = '50%';
213
+ originY = '100%';
208
214
  }
209
215
  popperEl.style.top = typeof top === 'undefined' ? '' : top + 'px';
210
216
  popperEl.style.bottom = typeof bottom === 'undefined' ? '' : bottom + 'px';
@@ -308,13 +314,7 @@ export function Popper({ ref, popperRef, anchorElement, container, effectContain
308
314
  else {
309
315
  popperEl.style.transition = 'none';
310
316
  popperEl.style.transform = 'scale(1)';
311
- const attemptOrder = getAttemptOrder(placement);
312
- for (let i = 0; i < attemptOrder.length; i++) {
313
- const t = attempt(attemptOrder[i]);
314
- if (t === false) {
315
- break;
316
- }
317
- }
317
+ getAttemptOrder(placement, !!contextMenuEvent.current, sizeAdaptable, attempt);
318
318
  }
319
319
  beforeOpen?.();
320
320
  placeA.current = pA;
@@ -4,6 +4,7 @@ import { SelectBase } from '../selectBase/index.js';
4
4
  import { OptionsBase } from '../optionsBase/index.js';
5
5
  import { MenuItem } from '../menuItem/index.js';
6
6
  import { useFlatSelection } from '../selectionContext/index.js';
7
+ import { isUnset } from '../../utils/index.js';
7
8
  export const Select = memo(({ children,
8
9
  // 从SelectableProps继承
9
10
  multiple = false, // 同时转发至<SelectBase/>
@@ -16,7 +17,15 @@ showCheckbox = multiple, loading = false, options, labelKey = 'label', primaryKe
16
17
  const optionsMap = useMemo(() => {
17
18
  const map = new Map();
18
19
  optionsArr?.forEach(opt => {
19
- opt && typeof opt === 'object' && map.set(opt[primaryKey], opt);
20
+ if (opt && typeof opt === 'object') {
21
+ const key = opt[primaryKey];
22
+ if (!isUnset(key)) {
23
+ if (map.has(key)) {
24
+ console.warn(`[@canlooks/can-ui/<Select/>] option key "${key}" was duplicated`);
25
+ }
26
+ map.set(opt[primaryKey], opt);
27
+ }
28
+ }
20
29
  });
21
30
  return map;
22
31
  }, [optionsArr, primaryKey]);
@@ -6,7 +6,7 @@ export const classes = defineInnerClasses('selected-list', [
6
6
  'optionWrap'
7
7
  ]);
8
8
  export const style = defineCss(({ spacing }) => css `
9
- @layer reset {
9
+ @layer override {
10
10
  .${classes.optionWrap} {
11
11
  margin-bottom: ${spacing[3]}px;
12
12
  }
@@ -28,7 +28,12 @@ export function useSelection({ ...props }) {
28
28
  item._parentId = parentId;
29
29
  item._isLast = i === arr.length - 1;
30
30
  const id = item[props.primaryKey];
31
- !isUnset(id) && map.set(id, item);
31
+ if (!isUnset(id)) {
32
+ if (map.has(id)) {
33
+ console.warn(`[@canlooks/can-ui/<SelectionContext/>] option key "${id}" was duplicated`);
34
+ }
35
+ map.set(id, item);
36
+ }
32
37
  fn(item[props.childrenKey], id);
33
38
  });
34
39
  };
@@ -16,7 +16,7 @@ export const style = defineCss(({ mode, spacing, gray, divider, background, easi
16
16
 
17
17
  thead, tfoot {
18
18
  position: sticky;
19
- z-index: 5;
19
+ z-index: 7;
20
20
 
21
21
  th, td {
22
22
  background-color: ${headerBg};
@@ -68,7 +68,9 @@ function useStickyCellProps({ sticky, ...props }) {
68
68
  ref: innerRef,
69
69
  css: style,
70
70
  className: classes.cell,
71
- style: sticky && { [sticky]: offset },
71
+ ...sticky && {
72
+ style: { [sticky]: offset }
73
+ },
72
74
  'data-sticky': sticky
73
75
  });
74
76
  }
@@ -8,7 +8,7 @@ export const style = defineCss(({ easing }) => css `
8
8
  &[data-sticky=left], &[data-sticky=right] {
9
9
  overflow: visible;
10
10
  position: sticky;
11
- z-index: 4;
11
+ z-index: 6;
12
12
 
13
13
  &::after {
14
14
  content: '';
@@ -20,7 +20,12 @@ export function useColumnMap(columns) {
20
20
  columns?.forEach((col, i) => {
21
21
  if (typeof col !== 'symbol') {
22
22
  const _key = setDefaultColumnKey(col, i);
23
- !isUnset(_key) && map.set(_key, col);
23
+ if (!isUnset(_key)) {
24
+ if (map.has(_key)) {
25
+ console.warn(`[@canlooks/can-ui/<DataGrid/>] column key "${_key}" was duplicated`);
26
+ }
27
+ map.set(_key, col);
28
+ }
24
29
  }
25
30
  });
26
31
  return map;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canlooks/can-ui",
3
- "version": "0.0.203",
3
+ "version": "0.0.205",
4
4
  "author": "C.CanLiang <canlooks@gmail.com>",
5
5
  "description": "My ui framework",
6
6
  "license": "MIT",