@quadrats/react 1.0.0 → 1.1.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/card/components/Card.js +83 -75
- package/card/index.cjs.js +82 -74
- package/carousel/components/Carousel.js +32 -28
- package/carousel/index.cjs.js +32 -28
- package/components/Tooltip/index.js +5 -2
- package/components/index.cjs.js +5 -2
- package/embed/renderers/base/components/BaseEmbedElement.js +51 -43
- package/embed/renderers/base/index.cjs.js +50 -42
- package/image/components/Image.js +34 -26
- package/image/createReactImage.js +1 -1
- package/image/index.cjs.js +34 -26
- package/package.json +4 -4
- package/table/components/Table.d.ts +9 -0
- package/table/components/Table.js +231 -0
- package/table/components/TableBody.d.ts +5 -0
- package/table/components/TableBody.js +8 -0
- package/table/components/TableCell.d.ts +5 -0
- package/table/components/TableCell.js +191 -0
- package/table/components/TableHeader.d.ts +5 -0
- package/table/components/TableHeader.js +13 -0
- package/table/components/TableMain.d.ts +5 -0
- package/table/components/TableMain.js +225 -0
- package/table/components/TableRow.d.ts +5 -0
- package/table/components/TableRow.js +8 -0
- package/table/components/TableTitle.d.ts +5 -0
- package/table/components/TableTitle.js +18 -0
- package/table/contexts/TableActionsContext.d.ts +3 -0
- package/table/contexts/TableActionsContext.js +5 -0
- package/table/contexts/TableHeaderContext.d.ts +2 -0
- package/table/contexts/TableHeaderContext.js +7 -0
- package/table/contexts/TableMetadataContext.d.ts +3 -0
- package/table/contexts/TableMetadataContext.js +5 -0
- package/table/contexts/TableScrollContext.d.ts +2 -0
- package/table/contexts/TableScrollContext.js +9 -0
- package/table/contexts/TableStateContext.d.ts +3 -0
- package/table/contexts/TableStateContext.js +5 -0
- package/table/createReactTable.d.ts +4 -0
- package/table/createReactTable.js +297 -0
- package/table/defaultRenderTableElements.d.ts +2 -0
- package/table/defaultRenderTableElements.js +20 -0
- package/table/hooks/useColumnResize.d.ts +12 -0
- package/table/hooks/useColumnResize.js +139 -0
- package/table/hooks/useTableActions.d.ts +25 -0
- package/table/hooks/useTableActions.js +886 -0
- package/table/hooks/useTableActionsContext.d.ts +1 -0
- package/table/hooks/useTableActionsContext.js +12 -0
- package/table/hooks/useTableCell.d.ts +16 -0
- package/table/hooks/useTableCell.js +166 -0
- package/table/hooks/useTableCellToolbarActions.d.ts +34 -0
- package/table/hooks/useTableCellToolbarActions.js +404 -0
- package/table/hooks/useTableMetadata.d.ts +1 -0
- package/table/hooks/useTableMetadata.js +12 -0
- package/table/hooks/useTableStateContext.d.ts +1 -0
- package/table/hooks/useTableStateContext.js +12 -0
- package/table/hooks/useTableStates.d.ts +18 -0
- package/table/hooks/useTableStates.js +14 -0
- package/table/index.cjs.js +3254 -0
- package/table/index.d.ts +16 -0
- package/table/index.js +24 -0
- package/table/jsx-serializer/components/Table.d.ts +3 -0
- package/table/jsx-serializer/components/Table.js +7 -0
- package/table/jsx-serializer/components/TableBody.d.ts +3 -0
- package/table/jsx-serializer/components/TableBody.js +7 -0
- package/table/jsx-serializer/components/TableCell.d.ts +5 -0
- package/table/jsx-serializer/components/TableCell.js +33 -0
- package/table/jsx-serializer/components/TableHeader.d.ts +3 -0
- package/table/jsx-serializer/components/TableHeader.js +10 -0
- package/table/jsx-serializer/components/TableMain.d.ts +6 -0
- package/table/jsx-serializer/components/TableMain.js +18 -0
- package/table/jsx-serializer/components/TableRow.d.ts +3 -0
- package/table/jsx-serializer/components/TableRow.js +7 -0
- package/table/jsx-serializer/components/TableTitle.d.ts +3 -0
- package/table/jsx-serializer/components/TableTitle.js +7 -0
- package/table/jsx-serializer/contexts/TableHeaderContext.d.ts +1 -0
- package/table/jsx-serializer/contexts/TableHeaderContext.js +5 -0
- package/table/jsx-serializer/contexts/TableScrollContext.d.ts +2 -0
- package/table/jsx-serializer/contexts/TableScrollContext.js +7 -0
- package/table/jsx-serializer/createJsxSerializeTable.d.ts +5 -0
- package/table/jsx-serializer/createJsxSerializeTable.js +113 -0
- package/table/jsx-serializer/defaultRenderTableElements.d.ts +2 -0
- package/table/jsx-serializer/defaultRenderTableElements.js +20 -0
- package/table/jsx-serializer/index.cjs.js +195 -0
- package/table/jsx-serializer/index.d.ts +3 -0
- package/table/jsx-serializer/index.js +2 -0
- package/table/jsx-serializer/package.json +7 -0
- package/table/jsx-serializer/typings.d.ts +12 -0
- package/table/package.json +10 -0
- package/table/table.css +1 -0
- package/table/table.scss +393 -0
- package/table/toolbar/TableToolbarIcon.d.ts +8 -0
- package/table/toolbar/TableToolbarIcon.js +12 -0
- package/table/toolbar/index.cjs.js +24 -0
- package/table/toolbar/index.d.ts +2 -0
- package/table/toolbar/index.js +2 -0
- package/table/toolbar/package.json +7 -0
- package/table/toolbar/useTableTool.d.ts +4 -0
- package/table/toolbar/useTableTool.js +13 -0
- package/table/typings.d.ts +66 -0
- package/table/utils/helper.d.ts +160 -0
- package/table/utils/helper.js +693 -0
- package/toolbar/components/InlineToolbar.d.ts +12 -11
- package/toolbar/components/InlineToolbar.js +23 -19
- package/toolbar/components/Toolbar.js +2 -2
- package/toolbar/index.cjs.js +24 -21
- package/toolbar/toolbar.css +1 -1
- package/toolbar/toolbar.scss +4 -1
- package/utils/index.cjs.js +7 -1
- package/utils/removePreviousElement.js +7 -1
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { createTable } from '@quadrats/common/table';
|
|
2
|
+
import { createRenderElements } from '@quadrats/react';
|
|
3
|
+
import { Editor, Element, Transforms, PARAGRAPH_TYPE, Text } from '@quadrats/core';
|
|
4
|
+
import { defaultRenderTableElements } from './defaultRenderTableElements.js';
|
|
5
|
+
|
|
6
|
+
function createReactTable(options = {}) {
|
|
7
|
+
const core = createTable(options);
|
|
8
|
+
const { types } = core;
|
|
9
|
+
return Object.assign(Object.assign({}, core), { createHandlers: () => ({
|
|
10
|
+
onKeyDown(event, editor, next) {
|
|
11
|
+
if (event.nativeEvent.isComposing) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (core.isSelectionInTableCell(editor)) {
|
|
15
|
+
const checkCurrentCellHasContent = () => {
|
|
16
|
+
if (!editor.selection)
|
|
17
|
+
return false;
|
|
18
|
+
const [cellNode] = Editor.node(editor, editor.selection);
|
|
19
|
+
if (Text.isText(cellNode)) {
|
|
20
|
+
return cellNode.text.trim() !== '';
|
|
21
|
+
}
|
|
22
|
+
if (!Element.isElement(cellNode))
|
|
23
|
+
return false;
|
|
24
|
+
const hasContent = cellNode.children.some((child) => {
|
|
25
|
+
if (Text.isText(child)) {
|
|
26
|
+
return child.text.trim() !== '';
|
|
27
|
+
}
|
|
28
|
+
return Element.isElement(child);
|
|
29
|
+
});
|
|
30
|
+
return hasContent;
|
|
31
|
+
};
|
|
32
|
+
if (event.key === 'Enter') {
|
|
33
|
+
if (core.isSelectionInTableList(editor)) {
|
|
34
|
+
return next();
|
|
35
|
+
}
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
const currentCellHasContent = checkCurrentCellHasContent();
|
|
38
|
+
if (currentCellHasContent) {
|
|
39
|
+
// Insert soft break
|
|
40
|
+
Editor.insertText(editor, '\n');
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Move to next cell
|
|
44
|
+
core.moveToNextCell(editor, types);
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (event.key === 'Tab') {
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
// shift+tab
|
|
51
|
+
if (event.shiftKey)
|
|
52
|
+
return;
|
|
53
|
+
core.moveToNextCell(editor, types);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// 處理方向鍵上下移動
|
|
57
|
+
if (event.key === 'ArrowUp') {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
if (event.shiftKey) {
|
|
60
|
+
// Shift + 上:擴展選取範圍
|
|
61
|
+
core.extendSelectionUp(editor, types);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// 只按上:移動到上一行
|
|
65
|
+
core.moveToRowAbove(editor, types);
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (event.key === 'ArrowDown') {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
if (event.shiftKey) {
|
|
72
|
+
// Shift + 下:擴展選取範圍
|
|
73
|
+
core.extendSelectionDown(editor, types);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// 只按下:移動到下一行
|
|
77
|
+
core.moveToRowBelow(editor, types);
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (event.key === 'ArrowLeft') {
|
|
82
|
+
// Shift + 左:擴展選取範圍
|
|
83
|
+
if (event.shiftKey) {
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
core.extendSelectionLeft(editor, types);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (event.key === 'ArrowRight') {
|
|
90
|
+
// Shift + 右:擴展選取範圍
|
|
91
|
+
if (event.shiftKey) {
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
core.extendSelectionRight(editor, types);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
next();
|
|
99
|
+
},
|
|
100
|
+
}), createRenderElement: (options = {}) => {
|
|
101
|
+
const renderTable = options.table || defaultRenderTableElements.table;
|
|
102
|
+
const renderTableTitle = options.table_title || defaultRenderTableElements.table_title;
|
|
103
|
+
const renderTableMain = options.table_main || defaultRenderTableElements.table_main;
|
|
104
|
+
const renderTableHeader = options.table_header || defaultRenderTableElements.table_header;
|
|
105
|
+
const renderTableBody = options.table_body || defaultRenderTableElements.table_body;
|
|
106
|
+
const renderTableRow = options.table_row || defaultRenderTableElements.table_row;
|
|
107
|
+
const renderTableCell = options.table_cell || defaultRenderTableElements.table_cell;
|
|
108
|
+
return createRenderElements([
|
|
109
|
+
{
|
|
110
|
+
type: types.table,
|
|
111
|
+
render: (props) => {
|
|
112
|
+
const { attributes, children, element } = props;
|
|
113
|
+
return renderTable({
|
|
114
|
+
attributes,
|
|
115
|
+
element,
|
|
116
|
+
children,
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
type: types.table_title,
|
|
122
|
+
render: (props) => {
|
|
123
|
+
const { attributes, children, element } = props;
|
|
124
|
+
return renderTableTitle({
|
|
125
|
+
attributes,
|
|
126
|
+
element,
|
|
127
|
+
children,
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
type: types.table_main,
|
|
133
|
+
render: (props) => {
|
|
134
|
+
const { attributes, children, element } = props;
|
|
135
|
+
return renderTableMain({
|
|
136
|
+
attributes,
|
|
137
|
+
element,
|
|
138
|
+
children,
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
type: types.table_header,
|
|
144
|
+
render: (props) => {
|
|
145
|
+
const { attributes, children, element } = props;
|
|
146
|
+
return renderTableHeader({
|
|
147
|
+
attributes,
|
|
148
|
+
element,
|
|
149
|
+
children,
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
type: types.table_body,
|
|
155
|
+
render: (props) => {
|
|
156
|
+
const { attributes, children, element } = props;
|
|
157
|
+
return renderTableBody({
|
|
158
|
+
attributes,
|
|
159
|
+
element,
|
|
160
|
+
children,
|
|
161
|
+
});
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
type: types.table_row,
|
|
166
|
+
render: (props) => {
|
|
167
|
+
const { attributes, children, element } = props;
|
|
168
|
+
return renderTableRow({
|
|
169
|
+
attributes,
|
|
170
|
+
element,
|
|
171
|
+
children,
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: types.table_cell,
|
|
177
|
+
render: (props) => {
|
|
178
|
+
const { attributes, children, element } = props;
|
|
179
|
+
return renderTableCell({
|
|
180
|
+
attributes,
|
|
181
|
+
element,
|
|
182
|
+
children,
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
]);
|
|
187
|
+
}, with(editor) {
|
|
188
|
+
const { insertData } = editor;
|
|
189
|
+
/** 從他處複製文字貼過來時觸發 */
|
|
190
|
+
editor.insertData = (data) => {
|
|
191
|
+
const { selection } = editor;
|
|
192
|
+
if (!selection) {
|
|
193
|
+
insertData(data);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
// 先檢查選取的起點是否在 table cell 中
|
|
197
|
+
const cellEntry = Editor.above(editor, {
|
|
198
|
+
at: selection.anchor,
|
|
199
|
+
match: (n) => Element.isElement(n) && n.type === types.table_cell,
|
|
200
|
+
});
|
|
201
|
+
if (cellEntry) {
|
|
202
|
+
// 確認在 table 內,將選取範圍縮減到起點
|
|
203
|
+
Transforms.collapse(editor, { edge: 'start' });
|
|
204
|
+
// 重新取得 cell entry(使用當前游標位置)
|
|
205
|
+
const targetCellEntry = Editor.above(editor, {
|
|
206
|
+
match: (n) => Element.isElement(n) && n.type === types.table_cell,
|
|
207
|
+
});
|
|
208
|
+
if (!targetCellEntry) {
|
|
209
|
+
insertData(data);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const [, cellPath] = targetCellEntry;
|
|
213
|
+
// 從剪貼簿取得純文字
|
|
214
|
+
const text = data.getData('text/plain');
|
|
215
|
+
if (text) {
|
|
216
|
+
// 檢測是否為表格格式(包含 Tab 或換行)
|
|
217
|
+
const hasTableFormat = text.includes('\t') || text.includes('\n');
|
|
218
|
+
if (hasTableFormat) {
|
|
219
|
+
// 解析表格格式:行由 \n 分隔,欄位由 \t 分隔
|
|
220
|
+
const rows = text.split('\n').filter((row) => row.length > 0);
|
|
221
|
+
if (rows.length === 0) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
// 找到當前 cell 所在的 row 和 column index
|
|
225
|
+
const rowEntry = Editor.above(editor, {
|
|
226
|
+
at: cellPath,
|
|
227
|
+
match: (n) => Element.isElement(n) && n.type === types.table_row,
|
|
228
|
+
});
|
|
229
|
+
if (!rowEntry) {
|
|
230
|
+
// 如果找不到 row,降級為純文字插入
|
|
231
|
+
Transforms.insertText(editor, text.replace(/\t/g, ' '));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const [, rowPath] = rowEntry;
|
|
235
|
+
const currentColumnIndex = cellPath[cellPath.length - 1];
|
|
236
|
+
// 找到 table body 或 header
|
|
237
|
+
const containerEntry = Editor.above(editor, {
|
|
238
|
+
at: rowPath,
|
|
239
|
+
match: (n) => Element.isElement(n) &&
|
|
240
|
+
(n.type === types.table_body || n.type === types.table_header),
|
|
241
|
+
});
|
|
242
|
+
if (!containerEntry) {
|
|
243
|
+
// 降級為純文字插入
|
|
244
|
+
Transforms.insertText(editor, text.replace(/\t/g, ' '));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const [container, containerPath] = containerEntry;
|
|
248
|
+
const currentRowIndex = rowPath[rowPath.length - 1];
|
|
249
|
+
// 使用 Editor.withoutNormalizing 批次處理
|
|
250
|
+
Editor.withoutNormalizing(editor, () => {
|
|
251
|
+
rows.forEach((rowText, rowOffset) => {
|
|
252
|
+
const columns = rowText.split('\t');
|
|
253
|
+
const targetRowIndex = currentRowIndex + rowOffset;
|
|
254
|
+
// 檢查目標 row 是否存在
|
|
255
|
+
if (targetRowIndex >= container.children.length) {
|
|
256
|
+
return; // 超出範圍,跳過此行
|
|
257
|
+
}
|
|
258
|
+
const targetRow = container.children[targetRowIndex];
|
|
259
|
+
if (!Element.isElement(targetRow) || targetRow.type !== types.table_row) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
// 貼上到各個 cell
|
|
263
|
+
columns.forEach((columnText, columnOffset) => {
|
|
264
|
+
const targetColumnIndex = currentColumnIndex + columnOffset;
|
|
265
|
+
// 檢查目標 cell 是否存在
|
|
266
|
+
if (targetColumnIndex >= targetRow.children.length) {
|
|
267
|
+
return; // 超出範圍,跳過此欄
|
|
268
|
+
}
|
|
269
|
+
const targetCellPath = [...containerPath, targetRowIndex, targetColumnIndex];
|
|
270
|
+
// 清空目標 cell 的內容
|
|
271
|
+
const targetCell = targetRow.children[targetColumnIndex];
|
|
272
|
+
if (Element.isElement(targetCell) && targetCell.type === types.table_cell) {
|
|
273
|
+
// 移除所有子節點
|
|
274
|
+
for (let i = targetCell.children.length - 1; i >= 0; i--) {
|
|
275
|
+
Transforms.removeNodes(editor, { at: [...targetCellPath, i] });
|
|
276
|
+
}
|
|
277
|
+
// 插入新內容
|
|
278
|
+
Transforms.insertNodes(editor, {
|
|
279
|
+
type: PARAGRAPH_TYPE,
|
|
280
|
+
children: [{ text: columnText }],
|
|
281
|
+
}, { at: [...targetCellPath, 0] });
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// 預設行為
|
|
291
|
+
insertData(data);
|
|
292
|
+
};
|
|
293
|
+
return core.with(editor);
|
|
294
|
+
} });
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export { createReactTable };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Table from './components/Table.js';
|
|
3
|
+
import TableTitle from './components/TableTitle.js';
|
|
4
|
+
import TableMain from './components/TableMain.js';
|
|
5
|
+
import TableHeader from './components/TableHeader.js';
|
|
6
|
+
import TableRow from './components/TableRow.js';
|
|
7
|
+
import TableCell from './components/TableCell.js';
|
|
8
|
+
import TableBody from './components/TableBody.js';
|
|
9
|
+
|
|
10
|
+
const defaultRenderTableElements = {
|
|
11
|
+
table: (props) => React.createElement(Table, Object.assign({}, props)),
|
|
12
|
+
table_title: (props) => React.createElement(TableTitle, Object.assign({}, props)),
|
|
13
|
+
table_main: (props) => React.createElement(TableMain, Object.assign({}, props)),
|
|
14
|
+
table_header: (props) => React.createElement(TableHeader, Object.assign({}, props)),
|
|
15
|
+
table_body: (props) => React.createElement(TableBody, Object.assign({}, props)),
|
|
16
|
+
table_row: (props) => React.createElement(TableRow, Object.assign({}, props)),
|
|
17
|
+
table_cell: (props) => React.createElement(TableCell, Object.assign({}, props)),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { defaultRenderTableElements };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TableElement } from '@quadrats/common/table';
|
|
2
|
+
interface UseColumnResizeParams {
|
|
3
|
+
tableElement: TableElement;
|
|
4
|
+
columnIndex: number;
|
|
5
|
+
cellRef: React.RefObject<HTMLTableCellElement | null>;
|
|
6
|
+
}
|
|
7
|
+
interface UseColumnResizeReturn {
|
|
8
|
+
isResizing: boolean;
|
|
9
|
+
handleResizeStart: (e: React.MouseEvent) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function useColumnResize({ tableElement, columnIndex, cellRef }: UseColumnResizeParams): UseColumnResizeReturn;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { useState, useRef, useCallback } from 'react';
|
|
2
|
+
import { useSlateStatic } from 'slate-react';
|
|
3
|
+
import { Transforms, Editor } from '@quadrats/core';
|
|
4
|
+
import { calculateTableMinWidth } from '@quadrats/common/table';
|
|
5
|
+
import { getPinnedColumnsInfo, getColumnWidths, calculateResizedColumnWidths, setColumnWidths } from '../utils/helper.js';
|
|
6
|
+
import { useTableStateContext } from './useTableStateContext.js';
|
|
7
|
+
|
|
8
|
+
function useColumnResize({ tableElement, columnIndex, cellRef }) {
|
|
9
|
+
const editor = useSlateStatic();
|
|
10
|
+
const { setTableSelectedOn } = useTableStateContext();
|
|
11
|
+
const [isResizing, setIsResizing] = useState(false);
|
|
12
|
+
const resizeDataRef = useRef(null);
|
|
13
|
+
const currentWidthsRef = useRef(null);
|
|
14
|
+
const handleResizeStart = useCallback((e) => {
|
|
15
|
+
e.preventDefault();
|
|
16
|
+
e.stopPropagation();
|
|
17
|
+
// 清除當前的 focus 狀態和 table selection
|
|
18
|
+
Transforms.deselect(editor);
|
|
19
|
+
setTableSelectedOn(undefined);
|
|
20
|
+
const cell = cellRef.current;
|
|
21
|
+
if (!cell)
|
|
22
|
+
return;
|
|
23
|
+
// 找到 table DOM 元素和 scroll container
|
|
24
|
+
const tableDOMElement = cell.closest('table');
|
|
25
|
+
const scrollContainer = tableDOMElement === null || tableDOMElement === void 0 ? void 0 : tableDOMElement.closest('.qdr-table__scrollContainer');
|
|
26
|
+
if (!tableDOMElement || !scrollContainer)
|
|
27
|
+
return;
|
|
28
|
+
const tableRect = tableDOMElement.getBoundingClientRect();
|
|
29
|
+
const containerRect = scrollContainer.getBoundingClientRect();
|
|
30
|
+
// 獲取釘選欄位資訊
|
|
31
|
+
const { pinnedColumnIndices } = getPinnedColumnsInfo(tableElement);
|
|
32
|
+
// 儲存初始狀態
|
|
33
|
+
resizeDataRef.current = {
|
|
34
|
+
startX: e.clientX,
|
|
35
|
+
startWidths: getColumnWidths(tableElement, tableRect.width),
|
|
36
|
+
tableWidth: tableRect.width,
|
|
37
|
+
containerWidth: containerRect.width,
|
|
38
|
+
pinnedColumnIndices,
|
|
39
|
+
tableDOMElement,
|
|
40
|
+
};
|
|
41
|
+
setIsResizing(true);
|
|
42
|
+
// 顯示 size indicators 容器
|
|
43
|
+
const mainWrapper = tableDOMElement.closest('.qdr-table__mainWrapper');
|
|
44
|
+
const sizeIndicatorsContainer = mainWrapper === null || mainWrapper === void 0 ? void 0 : mainWrapper.querySelector('.qdr-table__size-indicators');
|
|
45
|
+
if (sizeIndicatorsContainer) {
|
|
46
|
+
sizeIndicatorsContainer.style.display = 'flex';
|
|
47
|
+
}
|
|
48
|
+
// 為當前 column 的所有 cell 添加 resizing class
|
|
49
|
+
const allRows = tableDOMElement.querySelectorAll('tr');
|
|
50
|
+
allRows.forEach((row) => {
|
|
51
|
+
const cells = row.querySelectorAll('td, th');
|
|
52
|
+
const targetCell = cells[columnIndex];
|
|
53
|
+
if (targetCell) {
|
|
54
|
+
targetCell.classList.add('qdr-table__cell--resizing');
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const handleMouseMove = (moveEvent) => {
|
|
58
|
+
if (!resizeDataRef.current)
|
|
59
|
+
return;
|
|
60
|
+
const { startX, startWidths, tableWidth, pinnedColumnIndices, tableDOMElement } = resizeDataRef.current;
|
|
61
|
+
const deltaX = moveEvent.clientX - startX;
|
|
62
|
+
// 將位移轉換為百分比
|
|
63
|
+
const deltaPercentage = (deltaX / tableWidth) * 100;
|
|
64
|
+
// 計算新的欄位寬度
|
|
65
|
+
const newWidths = calculateResizedColumnWidths(startWidths, columnIndex, deltaPercentage, deltaX, pinnedColumnIndices);
|
|
66
|
+
// 儲存計算結果,但不更新 Slate
|
|
67
|
+
currentWidthsRef.current = newWidths;
|
|
68
|
+
// 更新 table 的最小寬度
|
|
69
|
+
tableDOMElement.style.minWidth = calculateTableMinWidth(newWidths);
|
|
70
|
+
// 直接更新 DOM 的 <col> 元素
|
|
71
|
+
const colgroup = tableDOMElement.querySelector('colgroup');
|
|
72
|
+
if (colgroup) {
|
|
73
|
+
const cols = colgroup.querySelectorAll('col');
|
|
74
|
+
newWidths.forEach((width, index) => {
|
|
75
|
+
const col = cols[index];
|
|
76
|
+
if (col) {
|
|
77
|
+
const cssWidth = width.type === 'percentage' ? `${width.value.toFixed(1)}%` : `${width.value}px`;
|
|
78
|
+
col.style.width = cssWidth;
|
|
79
|
+
col.style.minWidth = cssWidth;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// 更新 size indicators
|
|
84
|
+
const sizeIndicatorsContainer = mainWrapper === null || mainWrapper === void 0 ? void 0 : mainWrapper.querySelector('.qdr-table__size-indicators');
|
|
85
|
+
if (sizeIndicatorsContainer) {
|
|
86
|
+
const indicators = sizeIndicatorsContainer.querySelectorAll('.qdr-table__size-indicator');
|
|
87
|
+
newWidths.forEach((width, index) => {
|
|
88
|
+
const indicator = indicators[index];
|
|
89
|
+
if (indicator) {
|
|
90
|
+
const cssWidth = width.type === 'percentage' ? `${width.value.toFixed(1)}%` : `${width.value}px`;
|
|
91
|
+
const displayWidth = width.type === 'percentage' ? `${width.value.toFixed(1)}%` : `${width.value}px`;
|
|
92
|
+
indicator.style.width = cssWidth;
|
|
93
|
+
indicator.style.minWidth = cssWidth;
|
|
94
|
+
// 更新顯示文字
|
|
95
|
+
const sizeText = indicator.querySelector('.qdr-table__size');
|
|
96
|
+
if (sizeText) {
|
|
97
|
+
sizeText.textContent = displayWidth;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const handleMouseUp = () => {
|
|
104
|
+
const allRows = tableDOMElement.querySelectorAll('tr');
|
|
105
|
+
allRows.forEach((row) => {
|
|
106
|
+
const cells = row.querySelectorAll('td, th');
|
|
107
|
+
const targetCell = cells[columnIndex];
|
|
108
|
+
if (targetCell) {
|
|
109
|
+
targetCell.classList.remove('qdr-table__cell--resizing');
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
// 隱藏 size indicators 容器
|
|
113
|
+
const mainWrapper = tableDOMElement.closest('.qdr-table__mainWrapper');
|
|
114
|
+
const sizeIndicatorsContainer = mainWrapper === null || mainWrapper === void 0 ? void 0 : mainWrapper.querySelector('.qdr-table__size-indicators');
|
|
115
|
+
if (sizeIndicatorsContainer) {
|
|
116
|
+
sizeIndicatorsContainer.style.display = 'none';
|
|
117
|
+
}
|
|
118
|
+
// 將最終寬度寫入 Slate
|
|
119
|
+
if (currentWidthsRef.current) {
|
|
120
|
+
Editor.withoutNormalizing(editor, () => {
|
|
121
|
+
setColumnWidths(editor, tableElement, currentWidthsRef.current);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
setIsResizing(false);
|
|
125
|
+
resizeDataRef.current = null;
|
|
126
|
+
currentWidthsRef.current = null;
|
|
127
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
128
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
129
|
+
};
|
|
130
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
131
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
132
|
+
}, [editor, columnIndex, cellRef, tableElement, setTableSelectedOn]);
|
|
133
|
+
return {
|
|
134
|
+
isResizing,
|
|
135
|
+
handleResizeStart,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export { useColumnResize };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { RenderTableElementProps } from '../typings';
|
|
2
|
+
import { TableElement } from '@quadrats/common/table';
|
|
3
|
+
export declare function useTableActions(element: RenderTableElementProps['element']): {
|
|
4
|
+
addColumn: (options?: {
|
|
5
|
+
position?: "left" | "right";
|
|
6
|
+
columnIndex?: number;
|
|
7
|
+
}) => void;
|
|
8
|
+
addRow: (options?: {
|
|
9
|
+
position?: "top" | "bottom";
|
|
10
|
+
rowIndex?: number;
|
|
11
|
+
}) => void;
|
|
12
|
+
addColumnAndRow: VoidFunction;
|
|
13
|
+
deleteRow: (rowIndex: number) => void;
|
|
14
|
+
deleteColumn: (columnIndex: number) => void;
|
|
15
|
+
moveRowToBody: (rowIndex: number) => void;
|
|
16
|
+
moveRowToHeader: (rowIndex: number, customProps?: Pick<TableElement, "pinned">) => void;
|
|
17
|
+
unsetColumnAsTitle: (columnIndex: number) => void;
|
|
18
|
+
setColumnAsTitle: (columnIndex: number, customProps?: Pick<TableElement, "pinned">) => void;
|
|
19
|
+
pinColumn: (columnIndex: number) => void;
|
|
20
|
+
unpinColumn: () => void;
|
|
21
|
+
pinRow: (rowIndex: number) => void;
|
|
22
|
+
unpinRow: () => void;
|
|
23
|
+
isColumnPinned: (columnIndex: number) => boolean;
|
|
24
|
+
isRowPinned: (rowIndex: number) => boolean;
|
|
25
|
+
};
|