resizable-pro-table 1.0.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.
- package/README.md +54 -0
- package/dist/index.d.mts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +228 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +205 -0
- package/dist/index.mjs.map +1 -0
- package/dist/style.css +36 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# resizable-pro-table
|
|
2
|
+
|
|
3
|
+
基于 [Ant Design ProComponents ProTable](https://procomponents.ant.design/components/table) 的可拖拽调整列宽表格组件。
|
|
4
|
+
|
|
5
|
+
## 依赖
|
|
6
|
+
|
|
7
|
+
- React 17+
|
|
8
|
+
- antd 5+
|
|
9
|
+
- @ant-design/pro-components 2+
|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install resizable-pro-table
|
|
15
|
+
# 或
|
|
16
|
+
yarn add resizable-pro-table
|
|
17
|
+
# 或
|
|
18
|
+
pnpm add resizable-pro-table
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 使用
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import ResizableProTable from 'resizable-pro-table';
|
|
25
|
+
import 'resizable-pro-table/dist/style.css';
|
|
26
|
+
|
|
27
|
+
<ResizableProTable<YourDataType>
|
|
28
|
+
columns={columns}
|
|
29
|
+
request={async () => ({ data: [], success: true })}
|
|
30
|
+
columnWidthPersistenceKey="my-table-columns"
|
|
31
|
+
minColumnWidth={60}
|
|
32
|
+
defaultColumnWidth={120}
|
|
33
|
+
/>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Props(在 ProTable 基础上新增)
|
|
37
|
+
|
|
38
|
+
| 属性 | 说明 | 类型 | 默认值 |
|
|
39
|
+
|------|------|------|--------|
|
|
40
|
+
| columnWidthPersistenceKey | 用于在 localStorage 持久化列宽的 key,不传则不持久化 | string | - |
|
|
41
|
+
| minColumnWidth | 列最小宽度(px) | number | 60 |
|
|
42
|
+
| defaultColumnWidth | 未设置 width 的列使用的默认宽度(px),用于显示拖拽把手 | number | 120 |
|
|
43
|
+
|
|
44
|
+
## 开发与构建
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cd resizable-pro-table
|
|
48
|
+
npm install
|
|
49
|
+
npm run build
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ProTableProps } from '@ant-design/pro-components';
|
|
3
|
+
|
|
4
|
+
interface ResizableProTableProps<T extends Record<string, any>> extends Omit<ProTableProps<T, any>, 'components'> {
|
|
5
|
+
/** localStorage key for persisting column widths across page refreshes */
|
|
6
|
+
columnWidthPersistenceKey?: string;
|
|
7
|
+
/** Minimum column width in pixels (default: 60) */
|
|
8
|
+
minColumnWidth?: number;
|
|
9
|
+
/** Default width for columns without width (default: 120), used for drag handle */
|
|
10
|
+
defaultColumnWidth?: number;
|
|
11
|
+
}
|
|
12
|
+
declare function ResizableProTable<T extends Record<string, any>>(props: ResizableProTableProps<T>): react_jsx_runtime.JSX.Element;
|
|
13
|
+
|
|
14
|
+
export { type ResizableProTableProps, ResizableProTable as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ProTableProps } from '@ant-design/pro-components';
|
|
3
|
+
|
|
4
|
+
interface ResizableProTableProps<T extends Record<string, any>> extends Omit<ProTableProps<T, any>, 'components'> {
|
|
5
|
+
/** localStorage key for persisting column widths across page refreshes */
|
|
6
|
+
columnWidthPersistenceKey?: string;
|
|
7
|
+
/** Minimum column width in pixels (default: 60) */
|
|
8
|
+
minColumnWidth?: number;
|
|
9
|
+
/** Default width for columns without width (default: 120), used for drag handle */
|
|
10
|
+
defaultColumnWidth?: number;
|
|
11
|
+
}
|
|
12
|
+
declare function ResizableProTable<T extends Record<string, any>>(props: ResizableProTableProps<T>): react_jsx_runtime.JSX.Element;
|
|
13
|
+
|
|
14
|
+
export { type ResizableProTableProps, ResizableProTable as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
default: () => ResizableProTable_default
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/ResizableProTable.tsx
|
|
28
|
+
var import_pro_components = require("@ant-design/pro-components");
|
|
29
|
+
|
|
30
|
+
// src/useResizableColumns.ts
|
|
31
|
+
var import_react2 = require("react");
|
|
32
|
+
|
|
33
|
+
// src/ResizableTitle.tsx
|
|
34
|
+
var import_react = require("react");
|
|
35
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
|
+
function findCorrespondingCols(th) {
|
|
37
|
+
const cellIndex = th.cellIndex;
|
|
38
|
+
const wrapper = th.closest(".ant-table-wrapper");
|
|
39
|
+
if (!wrapper) {
|
|
40
|
+
const table = th.closest("table");
|
|
41
|
+
if (!table) return [];
|
|
42
|
+
const col = table.querySelectorAll("colgroup > col")[cellIndex];
|
|
43
|
+
return col ? [col] : [];
|
|
44
|
+
}
|
|
45
|
+
const cols = [];
|
|
46
|
+
wrapper.querySelectorAll("colgroup").forEach((cg) => {
|
|
47
|
+
const col = cg.children[cellIndex];
|
|
48
|
+
if (col) cols.push(col);
|
|
49
|
+
});
|
|
50
|
+
return cols;
|
|
51
|
+
}
|
|
52
|
+
var ResizableTitle = ({
|
|
53
|
+
onResizeEnd,
|
|
54
|
+
width,
|
|
55
|
+
minWidth = 60,
|
|
56
|
+
children,
|
|
57
|
+
...restProps
|
|
58
|
+
}) => {
|
|
59
|
+
const thRef = (0, import_react.useRef)(null);
|
|
60
|
+
const widthRef = (0, import_react.useRef)(0);
|
|
61
|
+
const handleMouseDown = (0, import_react.useCallback)(
|
|
62
|
+
(e) => {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
e.stopPropagation();
|
|
65
|
+
const th = thRef.current;
|
|
66
|
+
if (!th) return;
|
|
67
|
+
const startX = e.clientX;
|
|
68
|
+
const startWidth = th.getBoundingClientRect().width;
|
|
69
|
+
const cols = findCorrespondingCols(th);
|
|
70
|
+
widthRef.current = startWidth;
|
|
71
|
+
const onMouseMove = (ev) => {
|
|
72
|
+
const newWidth = Math.max(minWidth, startWidth + (ev.clientX - startX));
|
|
73
|
+
th.style.width = `${newWidth}px`;
|
|
74
|
+
cols.forEach((col) => {
|
|
75
|
+
col.style.width = `${newWidth}px`;
|
|
76
|
+
col.style.minWidth = `${newWidth}px`;
|
|
77
|
+
});
|
|
78
|
+
widthRef.current = newWidth;
|
|
79
|
+
};
|
|
80
|
+
const onMouseUp = () => {
|
|
81
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
82
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
83
|
+
document.body.style.userSelect = "";
|
|
84
|
+
document.body.style.cursor = "";
|
|
85
|
+
document.body.classList.remove("resizing-active");
|
|
86
|
+
onResizeEnd?.(widthRef.current);
|
|
87
|
+
const suppressClick = (ev) => {
|
|
88
|
+
ev.stopPropagation();
|
|
89
|
+
ev.preventDefault();
|
|
90
|
+
};
|
|
91
|
+
document.addEventListener("click", suppressClick, true);
|
|
92
|
+
requestAnimationFrame(() => {
|
|
93
|
+
document.removeEventListener("click", suppressClick, true);
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
document.body.style.userSelect = "none";
|
|
97
|
+
document.body.style.cursor = "col-resize";
|
|
98
|
+
document.body.classList.add("resizing-active");
|
|
99
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
100
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
101
|
+
},
|
|
102
|
+
[minWidth, onResizeEnd]
|
|
103
|
+
);
|
|
104
|
+
if (!width || !onResizeEnd) {
|
|
105
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { ...restProps, children });
|
|
106
|
+
}
|
|
107
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
108
|
+
"th",
|
|
109
|
+
{
|
|
110
|
+
ref: thRef,
|
|
111
|
+
...restProps,
|
|
112
|
+
style: { position: "relative", ...restProps.style },
|
|
113
|
+
children: [
|
|
114
|
+
children,
|
|
115
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
116
|
+
"span",
|
|
117
|
+
{
|
|
118
|
+
className: "resizable-handle",
|
|
119
|
+
style: {
|
|
120
|
+
position: "absolute",
|
|
121
|
+
right: -6,
|
|
122
|
+
top: 0,
|
|
123
|
+
bottom: 0,
|
|
124
|
+
width: 12,
|
|
125
|
+
cursor: "col-resize",
|
|
126
|
+
zIndex: 10
|
|
127
|
+
},
|
|
128
|
+
onMouseDown: handleMouseDown,
|
|
129
|
+
onClick: (e) => e.stopPropagation()
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
var ResizableTitle_default = ResizableTitle;
|
|
137
|
+
|
|
138
|
+
// src/useResizableColumns.ts
|
|
139
|
+
function getColumnKey(col) {
|
|
140
|
+
if (col.dataIndex !== void 0) {
|
|
141
|
+
return [].concat(col.dataIndex).join(".");
|
|
142
|
+
}
|
|
143
|
+
if (col.key !== void 0) {
|
|
144
|
+
return String(col.key);
|
|
145
|
+
}
|
|
146
|
+
if (typeof col.title === "string") {
|
|
147
|
+
return col.title;
|
|
148
|
+
}
|
|
149
|
+
return void 0;
|
|
150
|
+
}
|
|
151
|
+
function safeReadStorage(key) {
|
|
152
|
+
try {
|
|
153
|
+
const raw = localStorage.getItem(key);
|
|
154
|
+
return raw ? JSON.parse(raw) : {};
|
|
155
|
+
} catch {
|
|
156
|
+
return {};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function safeWriteStorage(key, value) {
|
|
160
|
+
try {
|
|
161
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function useResizableColumns(columns, options) {
|
|
166
|
+
const { columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth = 120 } = options ?? {};
|
|
167
|
+
const [columnWidths, setColumnWidths] = (0, import_react2.useState)(
|
|
168
|
+
() => columnWidthPersistenceKey ? safeReadStorage(columnWidthPersistenceKey) : {}
|
|
169
|
+
);
|
|
170
|
+
const mergedColumns = (0, import_react2.useMemo)(
|
|
171
|
+
() => columns.map((col) => {
|
|
172
|
+
const colKey = getColumnKey(col);
|
|
173
|
+
const effectiveWidth = colKey !== void 0 ? columnWidths[colKey] ?? col.width ?? defaultColumnWidth : col.width;
|
|
174
|
+
if (colKey === void 0) {
|
|
175
|
+
return col;
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
...col,
|
|
179
|
+
width: effectiveWidth,
|
|
180
|
+
onHeaderCell: () => ({
|
|
181
|
+
width: effectiveWidth,
|
|
182
|
+
minWidth: minColumnWidth,
|
|
183
|
+
style: { width: effectiveWidth, minWidth: minColumnWidth ?? 60 },
|
|
184
|
+
onResizeEnd: (finalWidth) => {
|
|
185
|
+
setColumnWidths((prev) => {
|
|
186
|
+
const next = { ...prev, [colKey]: finalWidth };
|
|
187
|
+
if (columnWidthPersistenceKey) {
|
|
188
|
+
safeWriteStorage(columnWidthPersistenceKey, next);
|
|
189
|
+
}
|
|
190
|
+
return next;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
};
|
|
195
|
+
}),
|
|
196
|
+
[columns, columnWidths, minColumnWidth, columnWidthPersistenceKey, defaultColumnWidth]
|
|
197
|
+
);
|
|
198
|
+
const components = (0, import_react2.useMemo)(
|
|
199
|
+
() => ({
|
|
200
|
+
header: {
|
|
201
|
+
cell: ResizableTitle_default
|
|
202
|
+
}
|
|
203
|
+
}),
|
|
204
|
+
[]
|
|
205
|
+
);
|
|
206
|
+
return { mergedColumns, components };
|
|
207
|
+
}
|
|
208
|
+
var useResizableColumns_default = useResizableColumns;
|
|
209
|
+
|
|
210
|
+
// src/ResizableProTable.tsx
|
|
211
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
212
|
+
function ResizableProTable(props) {
|
|
213
|
+
const { columns = [], columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth, ...rest } = props;
|
|
214
|
+
const { mergedColumns, components } = useResizableColumns_default(
|
|
215
|
+
columns,
|
|
216
|
+
{ columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth }
|
|
217
|
+
);
|
|
218
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
219
|
+
import_pro_components.ProTable,
|
|
220
|
+
{
|
|
221
|
+
...rest,
|
|
222
|
+
columns: mergedColumns,
|
|
223
|
+
components
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
var ResizableProTable_default = ResizableProTable;
|
|
228
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/ResizableProTable.tsx","../src/useResizableColumns.ts","../src/ResizableTitle.tsx"],"sourcesContent":["export { default } from './ResizableProTable';\nexport type { ResizableProTableProps } from './ResizableProTable';\n","import React from 'react';\nimport { ProTable } from '@ant-design/pro-components';\nimport type { ProTableProps, ProColumns } from '@ant-design/pro-components';\nimport useResizableColumns from './useResizableColumns';\n\nexport interface ResizableProTableProps<T extends Record<string, any>>\n extends Omit<ProTableProps<T, any>, 'components'> {\n /** localStorage key for persisting column widths across page refreshes */\n columnWidthPersistenceKey?: string;\n /** Minimum column width in pixels (default: 60) */\n minColumnWidth?: number;\n /** Default width for columns without width (default: 120), used for drag handle */\n defaultColumnWidth?: number;\n}\n\nfunction ResizableProTable<T extends Record<string, any>>(\n props: ResizableProTableProps<T>,\n) {\n const { columns = [], columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth, ...rest } = props;\n\n const { mergedColumns, components } = useResizableColumns<T>(\n columns as ProColumns<T>[],\n { columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth },\n );\n\n return (\n <ProTable<T>\n {...rest}\n columns={mergedColumns}\n components={components}\n />\n );\n}\n\nexport default ResizableProTable;\n","import { useMemo, useState } from 'react';\nimport type { ProColumns } from '@ant-design/pro-components';\nimport ResizableTitle from './ResizableTitle';\nimport type { ColumnWidthMap } from './types';\n\ninterface UseResizableColumnsOptions {\n columnWidthPersistenceKey?: string;\n minColumnWidth?: number;\n defaultColumnWidth?: number;\n}\n\nfunction getColumnKey<T>(col: ProColumns<T>): string | undefined {\n if (col.dataIndex !== undefined) {\n return ([] as string[]).concat(col.dataIndex as string | string[]).join('.');\n }\n if (col.key !== undefined) {\n return String(col.key);\n }\n if (typeof col.title === 'string') {\n return col.title;\n }\n return undefined;\n}\n\nfunction safeReadStorage(key: string): ColumnWidthMap {\n try {\n const raw = localStorage.getItem(key);\n return raw ? (JSON.parse(raw) as ColumnWidthMap) : {};\n } catch {\n return {};\n }\n}\n\nfunction safeWriteStorage(key: string, value: ColumnWidthMap): void {\n try {\n localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // quota exceeded or private mode\n }\n}\n\nfunction useResizableColumns<T>(\n columns: ProColumns<T>[],\n options?: UseResizableColumnsOptions,\n) {\n const { columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth = 120 } = options ?? {};\n\n const [columnWidths, setColumnWidths] = useState<ColumnWidthMap>(() =>\n columnWidthPersistenceKey ? safeReadStorage(columnWidthPersistenceKey) : {},\n );\n\n const mergedColumns = useMemo<ProColumns<T>[]>(\n () =>\n columns.map((col) => {\n const colKey = getColumnKey(col);\n const effectiveWidth =\n colKey !== undefined\n ? (columnWidths[colKey] ?? (col.width as number | undefined) ?? defaultColumnWidth)\n : (col.width as number | undefined);\n\n if (colKey === undefined) {\n return col;\n }\n\n return {\n ...col,\n width: effectiveWidth,\n onHeaderCell: () => ({\n width: effectiveWidth,\n minWidth: minColumnWidth,\n style: { width: effectiveWidth, minWidth: minColumnWidth ?? 60 },\n onResizeEnd: (finalWidth: number) => {\n setColumnWidths((prev) => {\n const next = { ...prev, [colKey]: finalWidth };\n if (columnWidthPersistenceKey) {\n safeWriteStorage(columnWidthPersistenceKey, next);\n }\n return next;\n });\n },\n }),\n };\n }),\n [columns, columnWidths, minColumnWidth, columnWidthPersistenceKey, defaultColumnWidth],\n );\n\n const components = useMemo(\n () => ({\n header: {\n cell: ResizableTitle,\n },\n }),\n [],\n );\n\n return { mergedColumns, components };\n}\n\nexport default useResizableColumns;\n","import React, { useCallback, useRef } from 'react';\nimport type { ResizableTitleProps } from './types';\n\n/**\n * Find all <col> elements corresponding to the given <th> by matching cellIndex\n * across all <colgroup>s within the same .ant-table-wrapper.\n * Ant Design may split header and body into separate <table> elements when\n * scroll is enabled, each with its own <colgroup>.\n */\nfunction findCorrespondingCols(th: HTMLTableCellElement): HTMLElement[] {\n const cellIndex = th.cellIndex;\n const wrapper = th.closest('.ant-table-wrapper');\n if (!wrapper) {\n const table = th.closest('table');\n if (!table) return [];\n const col = table.querySelectorAll('colgroup > col')[cellIndex];\n return col ? [col as HTMLElement] : [];\n }\n const cols: HTMLElement[] = [];\n wrapper.querySelectorAll('colgroup').forEach((cg) => {\n const col = cg.children[cellIndex] as HTMLElement | undefined;\n if (col) cols.push(col);\n });\n return cols;\n}\n\nconst ResizableTitle: React.FC<ResizableTitleProps> = ({\n onResizeEnd,\n width,\n minWidth = 60,\n children,\n ...restProps\n}) => {\n const thRef = useRef<HTMLTableCellElement>(null);\n const widthRef = useRef<number>(0);\n\n const handleMouseDown = useCallback(\n (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const th = thRef.current;\n if (!th) return;\n\n const startX = e.clientX;\n const startWidth = th.getBoundingClientRect().width;\n const cols = findCorrespondingCols(th);\n\n widthRef.current = startWidth;\n\n const onMouseMove = (ev: MouseEvent) => {\n const newWidth = Math.max(minWidth, startWidth + (ev.clientX - startX));\n th.style.width = `${newWidth}px`;\n cols.forEach((col) => {\n col.style.width = `${newWidth}px`;\n col.style.minWidth = `${newWidth}px`;\n });\n widthRef.current = newWidth;\n };\n\n const onMouseUp = () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n document.body.style.userSelect = '';\n document.body.style.cursor = '';\n document.body.classList.remove('resizing-active');\n onResizeEnd?.(widthRef.current);\n\n const suppressClick = (ev: MouseEvent) => {\n ev.stopPropagation();\n ev.preventDefault();\n };\n document.addEventListener('click', suppressClick, true);\n requestAnimationFrame(() => {\n document.removeEventListener('click', suppressClick, true);\n });\n };\n\n document.body.style.userSelect = 'none';\n document.body.style.cursor = 'col-resize';\n document.body.classList.add('resizing-active');\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n },\n [minWidth, onResizeEnd],\n );\n\n if (!width || !onResizeEnd) {\n return <th {...restProps}>{children}</th>;\n }\n\n return (\n <th\n ref={thRef}\n {...restProps}\n style={{ position: 'relative', ...restProps.style }}\n >\n {children}\n <span\n className=\"resizable-handle\"\n style={{\n position: 'absolute',\n right: -6,\n top: 0,\n bottom: 0,\n width: 12,\n cursor: 'col-resize',\n zIndex: 10,\n }}\n onMouseDown={handleMouseDown}\n onClick={(e) => e.stopPropagation()}\n />\n </th>\n );\n};\n\nexport default ResizableTitle;\n"],"mappings":"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","names":["import_react","import_jsx_runtime"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// src/ResizableProTable.tsx
|
|
2
|
+
import { ProTable } from "@ant-design/pro-components";
|
|
3
|
+
|
|
4
|
+
// src/useResizableColumns.ts
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
|
|
7
|
+
// src/ResizableTitle.tsx
|
|
8
|
+
import { useCallback, useRef } from "react";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
function findCorrespondingCols(th) {
|
|
11
|
+
const cellIndex = th.cellIndex;
|
|
12
|
+
const wrapper = th.closest(".ant-table-wrapper");
|
|
13
|
+
if (!wrapper) {
|
|
14
|
+
const table = th.closest("table");
|
|
15
|
+
if (!table) return [];
|
|
16
|
+
const col = table.querySelectorAll("colgroup > col")[cellIndex];
|
|
17
|
+
return col ? [col] : [];
|
|
18
|
+
}
|
|
19
|
+
const cols = [];
|
|
20
|
+
wrapper.querySelectorAll("colgroup").forEach((cg) => {
|
|
21
|
+
const col = cg.children[cellIndex];
|
|
22
|
+
if (col) cols.push(col);
|
|
23
|
+
});
|
|
24
|
+
return cols;
|
|
25
|
+
}
|
|
26
|
+
var ResizableTitle = ({
|
|
27
|
+
onResizeEnd,
|
|
28
|
+
width,
|
|
29
|
+
minWidth = 60,
|
|
30
|
+
children,
|
|
31
|
+
...restProps
|
|
32
|
+
}) => {
|
|
33
|
+
const thRef = useRef(null);
|
|
34
|
+
const widthRef = useRef(0);
|
|
35
|
+
const handleMouseDown = useCallback(
|
|
36
|
+
(e) => {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
e.stopPropagation();
|
|
39
|
+
const th = thRef.current;
|
|
40
|
+
if (!th) return;
|
|
41
|
+
const startX = e.clientX;
|
|
42
|
+
const startWidth = th.getBoundingClientRect().width;
|
|
43
|
+
const cols = findCorrespondingCols(th);
|
|
44
|
+
widthRef.current = startWidth;
|
|
45
|
+
const onMouseMove = (ev) => {
|
|
46
|
+
const newWidth = Math.max(minWidth, startWidth + (ev.clientX - startX));
|
|
47
|
+
th.style.width = `${newWidth}px`;
|
|
48
|
+
cols.forEach((col) => {
|
|
49
|
+
col.style.width = `${newWidth}px`;
|
|
50
|
+
col.style.minWidth = `${newWidth}px`;
|
|
51
|
+
});
|
|
52
|
+
widthRef.current = newWidth;
|
|
53
|
+
};
|
|
54
|
+
const onMouseUp = () => {
|
|
55
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
56
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
57
|
+
document.body.style.userSelect = "";
|
|
58
|
+
document.body.style.cursor = "";
|
|
59
|
+
document.body.classList.remove("resizing-active");
|
|
60
|
+
onResizeEnd?.(widthRef.current);
|
|
61
|
+
const suppressClick = (ev) => {
|
|
62
|
+
ev.stopPropagation();
|
|
63
|
+
ev.preventDefault();
|
|
64
|
+
};
|
|
65
|
+
document.addEventListener("click", suppressClick, true);
|
|
66
|
+
requestAnimationFrame(() => {
|
|
67
|
+
document.removeEventListener("click", suppressClick, true);
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
document.body.style.userSelect = "none";
|
|
71
|
+
document.body.style.cursor = "col-resize";
|
|
72
|
+
document.body.classList.add("resizing-active");
|
|
73
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
74
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
75
|
+
},
|
|
76
|
+
[minWidth, onResizeEnd]
|
|
77
|
+
);
|
|
78
|
+
if (!width || !onResizeEnd) {
|
|
79
|
+
return /* @__PURE__ */ jsx("th", { ...restProps, children });
|
|
80
|
+
}
|
|
81
|
+
return /* @__PURE__ */ jsxs(
|
|
82
|
+
"th",
|
|
83
|
+
{
|
|
84
|
+
ref: thRef,
|
|
85
|
+
...restProps,
|
|
86
|
+
style: { position: "relative", ...restProps.style },
|
|
87
|
+
children: [
|
|
88
|
+
children,
|
|
89
|
+
/* @__PURE__ */ jsx(
|
|
90
|
+
"span",
|
|
91
|
+
{
|
|
92
|
+
className: "resizable-handle",
|
|
93
|
+
style: {
|
|
94
|
+
position: "absolute",
|
|
95
|
+
right: -6,
|
|
96
|
+
top: 0,
|
|
97
|
+
bottom: 0,
|
|
98
|
+
width: 12,
|
|
99
|
+
cursor: "col-resize",
|
|
100
|
+
zIndex: 10
|
|
101
|
+
},
|
|
102
|
+
onMouseDown: handleMouseDown,
|
|
103
|
+
onClick: (e) => e.stopPropagation()
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
var ResizableTitle_default = ResizableTitle;
|
|
111
|
+
|
|
112
|
+
// src/useResizableColumns.ts
|
|
113
|
+
function getColumnKey(col) {
|
|
114
|
+
if (col.dataIndex !== void 0) {
|
|
115
|
+
return [].concat(col.dataIndex).join(".");
|
|
116
|
+
}
|
|
117
|
+
if (col.key !== void 0) {
|
|
118
|
+
return String(col.key);
|
|
119
|
+
}
|
|
120
|
+
if (typeof col.title === "string") {
|
|
121
|
+
return col.title;
|
|
122
|
+
}
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
function safeReadStorage(key) {
|
|
126
|
+
try {
|
|
127
|
+
const raw = localStorage.getItem(key);
|
|
128
|
+
return raw ? JSON.parse(raw) : {};
|
|
129
|
+
} catch {
|
|
130
|
+
return {};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function safeWriteStorage(key, value) {
|
|
134
|
+
try {
|
|
135
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function useResizableColumns(columns, options) {
|
|
140
|
+
const { columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth = 120 } = options ?? {};
|
|
141
|
+
const [columnWidths, setColumnWidths] = useState(
|
|
142
|
+
() => columnWidthPersistenceKey ? safeReadStorage(columnWidthPersistenceKey) : {}
|
|
143
|
+
);
|
|
144
|
+
const mergedColumns = useMemo(
|
|
145
|
+
() => columns.map((col) => {
|
|
146
|
+
const colKey = getColumnKey(col);
|
|
147
|
+
const effectiveWidth = colKey !== void 0 ? columnWidths[colKey] ?? col.width ?? defaultColumnWidth : col.width;
|
|
148
|
+
if (colKey === void 0) {
|
|
149
|
+
return col;
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
...col,
|
|
153
|
+
width: effectiveWidth,
|
|
154
|
+
onHeaderCell: () => ({
|
|
155
|
+
width: effectiveWidth,
|
|
156
|
+
minWidth: minColumnWidth,
|
|
157
|
+
style: { width: effectiveWidth, minWidth: minColumnWidth ?? 60 },
|
|
158
|
+
onResizeEnd: (finalWidth) => {
|
|
159
|
+
setColumnWidths((prev) => {
|
|
160
|
+
const next = { ...prev, [colKey]: finalWidth };
|
|
161
|
+
if (columnWidthPersistenceKey) {
|
|
162
|
+
safeWriteStorage(columnWidthPersistenceKey, next);
|
|
163
|
+
}
|
|
164
|
+
return next;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
};
|
|
169
|
+
}),
|
|
170
|
+
[columns, columnWidths, minColumnWidth, columnWidthPersistenceKey, defaultColumnWidth]
|
|
171
|
+
);
|
|
172
|
+
const components = useMemo(
|
|
173
|
+
() => ({
|
|
174
|
+
header: {
|
|
175
|
+
cell: ResizableTitle_default
|
|
176
|
+
}
|
|
177
|
+
}),
|
|
178
|
+
[]
|
|
179
|
+
);
|
|
180
|
+
return { mergedColumns, components };
|
|
181
|
+
}
|
|
182
|
+
var useResizableColumns_default = useResizableColumns;
|
|
183
|
+
|
|
184
|
+
// src/ResizableProTable.tsx
|
|
185
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
186
|
+
function ResizableProTable(props) {
|
|
187
|
+
const { columns = [], columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth, ...rest } = props;
|
|
188
|
+
const { mergedColumns, components } = useResizableColumns_default(
|
|
189
|
+
columns,
|
|
190
|
+
{ columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth }
|
|
191
|
+
);
|
|
192
|
+
return /* @__PURE__ */ jsx2(
|
|
193
|
+
ProTable,
|
|
194
|
+
{
|
|
195
|
+
...rest,
|
|
196
|
+
columns: mergedColumns,
|
|
197
|
+
components
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
var ResizableProTable_default = ResizableProTable;
|
|
202
|
+
export {
|
|
203
|
+
ResizableProTable_default as default
|
|
204
|
+
};
|
|
205
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ResizableProTable.tsx","../src/useResizableColumns.ts","../src/ResizableTitle.tsx"],"sourcesContent":["import React from 'react';\nimport { ProTable } from '@ant-design/pro-components';\nimport type { ProTableProps, ProColumns } from '@ant-design/pro-components';\nimport useResizableColumns from './useResizableColumns';\n\nexport interface ResizableProTableProps<T extends Record<string, any>>\n extends Omit<ProTableProps<T, any>, 'components'> {\n /** localStorage key for persisting column widths across page refreshes */\n columnWidthPersistenceKey?: string;\n /** Minimum column width in pixels (default: 60) */\n minColumnWidth?: number;\n /** Default width for columns without width (default: 120), used for drag handle */\n defaultColumnWidth?: number;\n}\n\nfunction ResizableProTable<T extends Record<string, any>>(\n props: ResizableProTableProps<T>,\n) {\n const { columns = [], columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth, ...rest } = props;\n\n const { mergedColumns, components } = useResizableColumns<T>(\n columns as ProColumns<T>[],\n { columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth },\n );\n\n return (\n <ProTable<T>\n {...rest}\n columns={mergedColumns}\n components={components}\n />\n );\n}\n\nexport default ResizableProTable;\n","import { useMemo, useState } from 'react';\nimport type { ProColumns } from '@ant-design/pro-components';\nimport ResizableTitle from './ResizableTitle';\nimport type { ColumnWidthMap } from './types';\n\ninterface UseResizableColumnsOptions {\n columnWidthPersistenceKey?: string;\n minColumnWidth?: number;\n defaultColumnWidth?: number;\n}\n\nfunction getColumnKey<T>(col: ProColumns<T>): string | undefined {\n if (col.dataIndex !== undefined) {\n return ([] as string[]).concat(col.dataIndex as string | string[]).join('.');\n }\n if (col.key !== undefined) {\n return String(col.key);\n }\n if (typeof col.title === 'string') {\n return col.title;\n }\n return undefined;\n}\n\nfunction safeReadStorage(key: string): ColumnWidthMap {\n try {\n const raw = localStorage.getItem(key);\n return raw ? (JSON.parse(raw) as ColumnWidthMap) : {};\n } catch {\n return {};\n }\n}\n\nfunction safeWriteStorage(key: string, value: ColumnWidthMap): void {\n try {\n localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // quota exceeded or private mode\n }\n}\n\nfunction useResizableColumns<T>(\n columns: ProColumns<T>[],\n options?: UseResizableColumnsOptions,\n) {\n const { columnWidthPersistenceKey, minColumnWidth, defaultColumnWidth = 120 } = options ?? {};\n\n const [columnWidths, setColumnWidths] = useState<ColumnWidthMap>(() =>\n columnWidthPersistenceKey ? safeReadStorage(columnWidthPersistenceKey) : {},\n );\n\n const mergedColumns = useMemo<ProColumns<T>[]>(\n () =>\n columns.map((col) => {\n const colKey = getColumnKey(col);\n const effectiveWidth =\n colKey !== undefined\n ? (columnWidths[colKey] ?? (col.width as number | undefined) ?? defaultColumnWidth)\n : (col.width as number | undefined);\n\n if (colKey === undefined) {\n return col;\n }\n\n return {\n ...col,\n width: effectiveWidth,\n onHeaderCell: () => ({\n width: effectiveWidth,\n minWidth: minColumnWidth,\n style: { width: effectiveWidth, minWidth: minColumnWidth ?? 60 },\n onResizeEnd: (finalWidth: number) => {\n setColumnWidths((prev) => {\n const next = { ...prev, [colKey]: finalWidth };\n if (columnWidthPersistenceKey) {\n safeWriteStorage(columnWidthPersistenceKey, next);\n }\n return next;\n });\n },\n }),\n };\n }),\n [columns, columnWidths, minColumnWidth, columnWidthPersistenceKey, defaultColumnWidth],\n );\n\n const components = useMemo(\n () => ({\n header: {\n cell: ResizableTitle,\n },\n }),\n [],\n );\n\n return { mergedColumns, components };\n}\n\nexport default useResizableColumns;\n","import React, { useCallback, useRef } from 'react';\nimport type { ResizableTitleProps } from './types';\n\n/**\n * Find all <col> elements corresponding to the given <th> by matching cellIndex\n * across all <colgroup>s within the same .ant-table-wrapper.\n * Ant Design may split header and body into separate <table> elements when\n * scroll is enabled, each with its own <colgroup>.\n */\nfunction findCorrespondingCols(th: HTMLTableCellElement): HTMLElement[] {\n const cellIndex = th.cellIndex;\n const wrapper = th.closest('.ant-table-wrapper');\n if (!wrapper) {\n const table = th.closest('table');\n if (!table) return [];\n const col = table.querySelectorAll('colgroup > col')[cellIndex];\n return col ? [col as HTMLElement] : [];\n }\n const cols: HTMLElement[] = [];\n wrapper.querySelectorAll('colgroup').forEach((cg) => {\n const col = cg.children[cellIndex] as HTMLElement | undefined;\n if (col) cols.push(col);\n });\n return cols;\n}\n\nconst ResizableTitle: React.FC<ResizableTitleProps> = ({\n onResizeEnd,\n width,\n minWidth = 60,\n children,\n ...restProps\n}) => {\n const thRef = useRef<HTMLTableCellElement>(null);\n const widthRef = useRef<number>(0);\n\n const handleMouseDown = useCallback(\n (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const th = thRef.current;\n if (!th) return;\n\n const startX = e.clientX;\n const startWidth = th.getBoundingClientRect().width;\n const cols = findCorrespondingCols(th);\n\n widthRef.current = startWidth;\n\n const onMouseMove = (ev: MouseEvent) => {\n const newWidth = Math.max(minWidth, startWidth + (ev.clientX - startX));\n th.style.width = `${newWidth}px`;\n cols.forEach((col) => {\n col.style.width = `${newWidth}px`;\n col.style.minWidth = `${newWidth}px`;\n });\n widthRef.current = newWidth;\n };\n\n const onMouseUp = () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n document.body.style.userSelect = '';\n document.body.style.cursor = '';\n document.body.classList.remove('resizing-active');\n onResizeEnd?.(widthRef.current);\n\n const suppressClick = (ev: MouseEvent) => {\n ev.stopPropagation();\n ev.preventDefault();\n };\n document.addEventListener('click', suppressClick, true);\n requestAnimationFrame(() => {\n document.removeEventListener('click', suppressClick, true);\n });\n };\n\n document.body.style.userSelect = 'none';\n document.body.style.cursor = 'col-resize';\n document.body.classList.add('resizing-active');\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n },\n [minWidth, onResizeEnd],\n );\n\n if (!width || !onResizeEnd) {\n return <th {...restProps}>{children}</th>;\n }\n\n return (\n <th\n ref={thRef}\n {...restProps}\n style={{ position: 'relative', ...restProps.style }}\n >\n {children}\n <span\n className=\"resizable-handle\"\n style={{\n position: 'absolute',\n right: -6,\n top: 0,\n bottom: 0,\n width: 12,\n cursor: 'col-resize',\n zIndex: 10,\n }}\n onMouseDown={handleMouseDown}\n onClick={(e) => e.stopPropagation()}\n />\n </th>\n );\n};\n\nexport default ResizableTitle;\n"],"mappings":"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","names":["jsx"]}
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* Make header cells the positioning parent for the handle */
|
|
2
|
+
.ant-table-thead > tr > th {
|
|
3
|
+
position: relative;
|
|
4
|
+
overflow: visible !important;
|
|
5
|
+
}
|
|
6
|
+
.ant-table-wrapper .ant-table-thead,
|
|
7
|
+
.ant-table .ant-table-thead {
|
|
8
|
+
overflow: visible !important;
|
|
9
|
+
}
|
|
10
|
+
/* Custom drag handle */
|
|
11
|
+
.resizable-handle {
|
|
12
|
+
position: absolute;
|
|
13
|
+
right: -6px;
|
|
14
|
+
top: 0;
|
|
15
|
+
bottom: 0;
|
|
16
|
+
width: 12px;
|
|
17
|
+
cursor: col-resize;
|
|
18
|
+
z-index: 10;
|
|
19
|
+
pointer-events: auto;
|
|
20
|
+
}
|
|
21
|
+
.resizable-handle::after {
|
|
22
|
+
content: '';
|
|
23
|
+
position: absolute;
|
|
24
|
+
left: 5px;
|
|
25
|
+
top: 15%;
|
|
26
|
+
height: 70%;
|
|
27
|
+
width: 2px;
|
|
28
|
+
background: rgba(0, 0, 0, 0.08);
|
|
29
|
+
transition: background 0.2s;
|
|
30
|
+
}
|
|
31
|
+
.resizable-handle:hover::after {
|
|
32
|
+
background: #1677ff;
|
|
33
|
+
}
|
|
34
|
+
.resizing-active .resizable-handle::after {
|
|
35
|
+
background: #1677ff;
|
|
36
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "resizable-pro-table",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ProTable with resizable columns for Ant Design",
|
|
5
|
+
"author": "yinzhimin",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./dist/style.css": "./dist/style.css"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup && lessc src/ResizableProTable.less dist/style.css",
|
|
24
|
+
"dev": "tsup --watch"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"react": ">=17.0.0",
|
|
28
|
+
"react-dom": ">=17.0.0",
|
|
29
|
+
"antd": ">=5.0.0",
|
|
30
|
+
"@ant-design/pro-components": ">=2.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@ant-design/pro-components": "^2.4.4",
|
|
34
|
+
"@types/react": "^18.0.0",
|
|
35
|
+
"@types/react-dom": "^18.0.0",
|
|
36
|
+
"antd": "^5.4.0",
|
|
37
|
+
"less": "^4.2.0",
|
|
38
|
+
"react": "^18.2.0",
|
|
39
|
+
"react-dom": "^18.2.0",
|
|
40
|
+
"tsup": "^8.0.0",
|
|
41
|
+
"typescript": "^5.0.0"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"ant-design",
|
|
45
|
+
"pro-table",
|
|
46
|
+
"resizable",
|
|
47
|
+
"column-resize",
|
|
48
|
+
"table"
|
|
49
|
+
]
|
|
50
|
+
}
|