@quadrats/react 1.0.0 → 1.1.1
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/core/components/Quadrats.js +5 -2
- package/core/contexts/modal/CarouselModal/CarouselModal.js +14 -17
- 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/index.cjs.js +19 -19
- package/package.json +4 -4
- package/table/components/ColumnDragButton.d.ts +10 -0
- package/table/components/ColumnDragButton.js +41 -0
- package/table/components/RowDragButton.d.ts +10 -0
- package/table/components/RowDragButton.js +42 -0
- package/table/components/Table.d.ts +9 -0
- package/table/components/Table.js +236 -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 +297 -0
- package/table/components/TableDragLayer.d.ts +6 -0
- package/table/components/TableDragLayer.js +89 -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 +233 -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/TableDragContext.d.ts +26 -0
- package/table/contexts/TableDragContext.js +26 -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 +168 -0
- package/table/hooks/useTableActions.d.ts +27 -0
- package/table/hooks/useTableActions.js +1092 -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 +526 -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 +4002 -0
- package/table/index.d.ts +16 -0
- package/table/index.js +27 -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 +428 -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 +68 -0
- package/table/utils/helper.d.ts +186 -0
- package/table/utils/helper.js +799 -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,526 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { UnorderedList, OrderedList, Paragraph, AddRowAtBottom, AddRowAtTop, AddColumnAtLeft, AddColumnAtRight, Unpinned, Pinned, TableRemoveTitle, TableSetColumnTitle, TableSetRowTitle, AlignLeft, AlignCenter, AlignRight, TableMoveDown, TableMoveUp, TableMoveRight, TableMoveLeft, Trash } from '@quadrats/icons';
|
|
3
|
+
import { Element, PARAGRAPH_TYPE } from '@quadrats/core';
|
|
4
|
+
import { LIST_TYPES } from '@quadrats/common/list';
|
|
5
|
+
import { useTableActionsContext } from './useTableActionsContext.js';
|
|
6
|
+
import { useTableMetadata } from './useTableMetadata.js';
|
|
7
|
+
import { useTableStateContext } from './useTableStateContext.js';
|
|
8
|
+
import { ToolbarGroupIcon, ToolbarIcon } from '@quadrats/react/toolbar';
|
|
9
|
+
import { useTableCellAlign, useTableCellAlignStatus } from './useTableCell.js';
|
|
10
|
+
import { useSlateStatic } from 'slate-react';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 將所有與 toolbar icons 相關的邏輯集中管理
|
|
14
|
+
*/
|
|
15
|
+
function useTableCellToolbarActions({ element, cellPosition, isHeader, transformCellContent, }) {
|
|
16
|
+
const editor = useSlateStatic();
|
|
17
|
+
const { tableSelectedOn, setTableSelectedOn } = useTableStateContext();
|
|
18
|
+
const { tableElement, columnCount, rowCount, isReachMaximumColumns, isReachMinimumNormalColumns, isReachMinimumBodyRows, isColumnPinned, isRowPinned, } = useTableMetadata();
|
|
19
|
+
const setAlign = useTableCellAlign(tableElement, editor);
|
|
20
|
+
const getAlign = useTableCellAlignStatus(tableElement, editor);
|
|
21
|
+
const { deleteRow, deleteColumn, addRow, addColumn, moveRowToBody, moveRowToHeader, unsetColumnAsTitle, setColumnAsTitle, pinColumn, unpinColumn, pinRow, unpinRow, moveOrSwapRow, moveOrSwapColumn, } = useTableActionsContext();
|
|
22
|
+
// 獲取當前 cell 內容的類型(用於顯示對應的 icon)
|
|
23
|
+
const getCurrentContentType = useMemo(() => {
|
|
24
|
+
// 檢查 cell 的第一個 child 的類型
|
|
25
|
+
if (element.children && element.children.length > 0) {
|
|
26
|
+
const firstChild = element.children[0];
|
|
27
|
+
if (Element.isElement(firstChild)) {
|
|
28
|
+
const childType = firstChild.type;
|
|
29
|
+
// 檢查是否為 list 類型
|
|
30
|
+
if (childType === LIST_TYPES.ul) {
|
|
31
|
+
return LIST_TYPES.ul;
|
|
32
|
+
}
|
|
33
|
+
if (childType === LIST_TYPES.ol) {
|
|
34
|
+
return LIST_TYPES.ol;
|
|
35
|
+
}
|
|
36
|
+
// 檢查是否為 paragraph(預設)
|
|
37
|
+
if (childType === PARAGRAPH_TYPE) {
|
|
38
|
+
return PARAGRAPH_TYPE;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// 預設返回 paragraph
|
|
43
|
+
return PARAGRAPH_TYPE;
|
|
44
|
+
}, [element.children]);
|
|
45
|
+
// 根據當前內容類型選擇對應的 icon
|
|
46
|
+
const getCurrentIcon = useMemo(() => {
|
|
47
|
+
if (getCurrentContentType === LIST_TYPES.ul) {
|
|
48
|
+
return UnorderedList;
|
|
49
|
+
}
|
|
50
|
+
if (getCurrentContentType === LIST_TYPES.ol) {
|
|
51
|
+
return OrderedList;
|
|
52
|
+
}
|
|
53
|
+
return Paragraph;
|
|
54
|
+
}, [getCurrentContentType]);
|
|
55
|
+
// Focus toolbar - 當 cell 被 focus 時顯示的工具列
|
|
56
|
+
const focusToolbarIconGroups = useMemo(() => {
|
|
57
|
+
return [
|
|
58
|
+
{
|
|
59
|
+
icons: [
|
|
60
|
+
React.createElement(ToolbarGroupIcon, { key: "typography-change", icon: getCurrentIcon },
|
|
61
|
+
React.createElement(ToolbarIcon, { icon: Paragraph, onClick: () => {
|
|
62
|
+
transformCellContent(PARAGRAPH_TYPE);
|
|
63
|
+
} }),
|
|
64
|
+
React.createElement(ToolbarIcon, { icon: UnorderedList, onClick: () => {
|
|
65
|
+
transformCellContent(LIST_TYPES.ul);
|
|
66
|
+
} }),
|
|
67
|
+
React.createElement(ToolbarIcon, { icon: OrderedList, onClick: () => {
|
|
68
|
+
transformCellContent(LIST_TYPES.ol);
|
|
69
|
+
} })),
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
icons: [
|
|
74
|
+
{
|
|
75
|
+
icon: AddRowAtBottom,
|
|
76
|
+
onClick: () => {
|
|
77
|
+
addRow({ position: 'bottom', rowIndex: cellPosition.rowIndex });
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
icon: AddRowAtTop,
|
|
82
|
+
onClick: () => {
|
|
83
|
+
addRow({ position: 'top', rowIndex: cellPosition.rowIndex });
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
isReachMaximumColumns
|
|
87
|
+
? undefined
|
|
88
|
+
: {
|
|
89
|
+
icon: AddColumnAtLeft,
|
|
90
|
+
onClick: () => {
|
|
91
|
+
addColumn({
|
|
92
|
+
position: 'left',
|
|
93
|
+
columnIndex: cellPosition.columnIndex,
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
isReachMaximumColumns
|
|
98
|
+
? undefined
|
|
99
|
+
: {
|
|
100
|
+
icon: AddColumnAtRight,
|
|
101
|
+
onClick: () => {
|
|
102
|
+
addColumn({
|
|
103
|
+
position: 'right',
|
|
104
|
+
columnIndex: cellPosition.columnIndex,
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
].filter((i) => i !== undefined),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
}, [transformCellContent, addRow, addColumn, cellPosition, isReachMaximumColumns, getCurrentIcon]);
|
|
112
|
+
// Row actions
|
|
113
|
+
const rowActions = useMemo(() => {
|
|
114
|
+
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'row' || typeof tableSelectedOn.index !== 'number') {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const removeTitleAction = {
|
|
118
|
+
icon: TableRemoveTitle,
|
|
119
|
+
onClick: () => {
|
|
120
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
121
|
+
moveRowToBody(tableSelectedOn.index);
|
|
122
|
+
setTableSelectedOn(undefined);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
const setColumnTitleAction = isReachMinimumBodyRows
|
|
127
|
+
? undefined
|
|
128
|
+
: {
|
|
129
|
+
icon: TableSetColumnTitle,
|
|
130
|
+
onClick: () => {
|
|
131
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
132
|
+
moveRowToHeader(tableSelectedOn.index);
|
|
133
|
+
setTableSelectedOn(undefined);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
const unpinnedAction = {
|
|
138
|
+
icon: Unpinned,
|
|
139
|
+
onClick: () => {
|
|
140
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
141
|
+
unpinRow();
|
|
142
|
+
setTableSelectedOn(undefined);
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
const pinnedAction = isReachMinimumBodyRows && !isHeader
|
|
147
|
+
? undefined
|
|
148
|
+
: {
|
|
149
|
+
icon: Pinned,
|
|
150
|
+
onClick: () => {
|
|
151
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
152
|
+
pinRow(tableSelectedOn.index);
|
|
153
|
+
setTableSelectedOn(undefined);
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
const actions = [];
|
|
158
|
+
const rowIsPinned = isRowPinned(tableSelectedOn.index);
|
|
159
|
+
if (rowIsPinned) {
|
|
160
|
+
actions.push(unpinnedAction);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
if (pinnedAction)
|
|
164
|
+
actions.push(pinnedAction);
|
|
165
|
+
}
|
|
166
|
+
if (isHeader) {
|
|
167
|
+
actions.push(removeTitleAction);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
if (setColumnTitleAction)
|
|
171
|
+
actions.push(setColumnTitleAction);
|
|
172
|
+
}
|
|
173
|
+
return actions;
|
|
174
|
+
}, [
|
|
175
|
+
tableSelectedOn,
|
|
176
|
+
moveRowToBody,
|
|
177
|
+
moveRowToHeader,
|
|
178
|
+
unpinRow,
|
|
179
|
+
pinRow,
|
|
180
|
+
setTableSelectedOn,
|
|
181
|
+
isReachMinimumBodyRows,
|
|
182
|
+
isHeader,
|
|
183
|
+
isRowPinned,
|
|
184
|
+
]);
|
|
185
|
+
// Column actions
|
|
186
|
+
const columnActions = useMemo(() => {
|
|
187
|
+
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'column' || typeof tableSelectedOn.index !== 'number') {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const removeTitleAction = {
|
|
191
|
+
icon: TableRemoveTitle,
|
|
192
|
+
onClick: () => {
|
|
193
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
194
|
+
unsetColumnAsTitle(tableSelectedOn.index);
|
|
195
|
+
setTableSelectedOn(undefined);
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
const setRowTitleAction = isReachMinimumNormalColumns
|
|
200
|
+
? undefined
|
|
201
|
+
: {
|
|
202
|
+
icon: TableSetRowTitle,
|
|
203
|
+
onClick: () => {
|
|
204
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
205
|
+
setColumnAsTitle(tableSelectedOn.index);
|
|
206
|
+
setTableSelectedOn(undefined);
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
const unpinnedAction = {
|
|
211
|
+
icon: Unpinned,
|
|
212
|
+
onClick: () => {
|
|
213
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
214
|
+
unpinColumn();
|
|
215
|
+
setTableSelectedOn(undefined);
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
const pinnedAction = isReachMinimumNormalColumns && !element.treatAsTitle
|
|
220
|
+
? undefined
|
|
221
|
+
: {
|
|
222
|
+
icon: Pinned,
|
|
223
|
+
onClick: () => {
|
|
224
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
225
|
+
pinColumn(tableSelectedOn.index);
|
|
226
|
+
setTableSelectedOn(undefined);
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
const actions = [];
|
|
231
|
+
const columnIsPinned = isColumnPinned(tableSelectedOn.index);
|
|
232
|
+
if (columnIsPinned) {
|
|
233
|
+
actions.push(unpinnedAction);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
if (pinnedAction)
|
|
237
|
+
actions.push(pinnedAction);
|
|
238
|
+
}
|
|
239
|
+
if (element.treatAsTitle) {
|
|
240
|
+
actions.push(removeTitleAction);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
if (setRowTitleAction)
|
|
244
|
+
actions.push(setRowTitleAction);
|
|
245
|
+
}
|
|
246
|
+
return actions;
|
|
247
|
+
}, [
|
|
248
|
+
tableSelectedOn,
|
|
249
|
+
unsetColumnAsTitle,
|
|
250
|
+
setColumnAsTitle,
|
|
251
|
+
unpinColumn,
|
|
252
|
+
pinColumn,
|
|
253
|
+
setTableSelectedOn,
|
|
254
|
+
isReachMinimumNormalColumns,
|
|
255
|
+
element.treatAsTitle,
|
|
256
|
+
isColumnPinned,
|
|
257
|
+
]);
|
|
258
|
+
// Column align actions
|
|
259
|
+
const columnAlignActions = useMemo(() => {
|
|
260
|
+
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'column' || typeof tableSelectedOn.index !== 'number') {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
// 獲取當前 column 的 align 狀態
|
|
264
|
+
const currentAlign = getAlign('column', tableSelectedOn.index);
|
|
265
|
+
// 根據當前 align 狀態選擇對應的 icon
|
|
266
|
+
const getCurrentAlignIcon = () => {
|
|
267
|
+
switch (currentAlign) {
|
|
268
|
+
case 'left':
|
|
269
|
+
return AlignLeft;
|
|
270
|
+
case 'center':
|
|
271
|
+
return AlignCenter;
|
|
272
|
+
case 'right':
|
|
273
|
+
return AlignRight;
|
|
274
|
+
default:
|
|
275
|
+
return AlignLeft;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
return [
|
|
279
|
+
React.createElement(ToolbarGroupIcon, { key: "align-change", icon: getCurrentAlignIcon() },
|
|
280
|
+
React.createElement(ToolbarIcon, { icon: AlignLeft, onClick: () => {
|
|
281
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
282
|
+
setAlign('left', 'column', tableSelectedOn.index);
|
|
283
|
+
setTableSelectedOn(undefined);
|
|
284
|
+
}
|
|
285
|
+
} }),
|
|
286
|
+
React.createElement(ToolbarIcon, { icon: AlignCenter, onClick: () => {
|
|
287
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
288
|
+
setAlign('center', 'column', tableSelectedOn.index);
|
|
289
|
+
setTableSelectedOn(undefined);
|
|
290
|
+
}
|
|
291
|
+
} }),
|
|
292
|
+
React.createElement(ToolbarIcon, { icon: AlignRight, onClick: () => {
|
|
293
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
294
|
+
setAlign('right', 'column', tableSelectedOn.index);
|
|
295
|
+
setTableSelectedOn(undefined);
|
|
296
|
+
}
|
|
297
|
+
} })),
|
|
298
|
+
];
|
|
299
|
+
}, [tableSelectedOn, setAlign, setTableSelectedOn, getAlign]);
|
|
300
|
+
// Row add actions
|
|
301
|
+
const rowAddActions = useMemo(() => {
|
|
302
|
+
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'row' || typeof tableSelectedOn.index !== 'number') {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
return [
|
|
306
|
+
{
|
|
307
|
+
icon: AddRowAtBottom,
|
|
308
|
+
onClick: () => {
|
|
309
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
310
|
+
addRow({ position: 'bottom', rowIndex: tableSelectedOn.index });
|
|
311
|
+
setTableSelectedOn(undefined);
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
icon: AddRowAtTop,
|
|
317
|
+
onClick: () => {
|
|
318
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
319
|
+
addRow({ position: 'top', rowIndex: tableSelectedOn.index });
|
|
320
|
+
setTableSelectedOn(undefined);
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
];
|
|
325
|
+
}, [tableSelectedOn, addRow, setTableSelectedOn]);
|
|
326
|
+
// Column add actions
|
|
327
|
+
const columnAddActions = useMemo(() => {
|
|
328
|
+
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'column' || typeof tableSelectedOn.index !== 'number') {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
return [
|
|
332
|
+
isReachMaximumColumns
|
|
333
|
+
? undefined
|
|
334
|
+
: {
|
|
335
|
+
icon: AddColumnAtLeft,
|
|
336
|
+
onClick: () => {
|
|
337
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
338
|
+
addColumn({
|
|
339
|
+
position: 'left',
|
|
340
|
+
columnIndex: tableSelectedOn.index,
|
|
341
|
+
});
|
|
342
|
+
setTableSelectedOn(undefined);
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
isReachMaximumColumns
|
|
347
|
+
? undefined
|
|
348
|
+
: {
|
|
349
|
+
icon: AddColumnAtRight,
|
|
350
|
+
onClick: () => {
|
|
351
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
352
|
+
addColumn({
|
|
353
|
+
position: 'right',
|
|
354
|
+
columnIndex: tableSelectedOn.index,
|
|
355
|
+
});
|
|
356
|
+
setTableSelectedOn(undefined);
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
].filter((i) => i !== undefined);
|
|
361
|
+
}, [tableSelectedOn, addColumn, setTableSelectedOn, isReachMaximumColumns]);
|
|
362
|
+
// Row move actions
|
|
363
|
+
const rowMoveActions = useMemo(() => {
|
|
364
|
+
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'row' || typeof tableSelectedOn.index !== 'number') {
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
const rowIndex = tableSelectedOn.index;
|
|
368
|
+
// 從 tableElement 獲取 header 和 body 的資訊
|
|
369
|
+
const tableMainElement = tableElement.children.find((child) => Element.isElement(child) && child.type.includes('table_main'));
|
|
370
|
+
if (!tableMainElement || !Element.isElement(tableMainElement)) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
const tableHeaderElement = tableMainElement.children.find((child) => Element.isElement(child) && child.type.includes('table_header'));
|
|
374
|
+
const headerRowCount = tableHeaderElement && Element.isElement(tableHeaderElement) ? tableHeaderElement.children.length : 0;
|
|
375
|
+
// 判斷當前列是在 header 還是 body 中
|
|
376
|
+
const currentInHeader = rowIndex < headerRowCount;
|
|
377
|
+
// 判斷上方和下方是否可以互換
|
|
378
|
+
const canMoveUp = rowIndex > 0; // 不在第一列
|
|
379
|
+
const canMoveDown = rowIndex < rowCount - 1; // 不在最後一列
|
|
380
|
+
// 檢查目標位置是否在相同的容器中(header 或 body)
|
|
381
|
+
const targetUpInHeader = rowIndex - 1 < headerRowCount;
|
|
382
|
+
const targetDownInHeader = rowIndex + 1 < headerRowCount;
|
|
383
|
+
// 標題列只能與標題列互換,一般列只能與一般列互換
|
|
384
|
+
const canSwapUp = canMoveUp && currentInHeader === targetUpInHeader;
|
|
385
|
+
const canSwapDown = canMoveDown && currentInHeader === targetDownInHeader;
|
|
386
|
+
return [
|
|
387
|
+
canSwapDown
|
|
388
|
+
? {
|
|
389
|
+
icon: TableMoveDown,
|
|
390
|
+
onClick: () => {
|
|
391
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
392
|
+
moveOrSwapRow(tableSelectedOn.index, tableSelectedOn.index + 1, 'swap');
|
|
393
|
+
setTableSelectedOn(undefined);
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
}
|
|
397
|
+
: undefined,
|
|
398
|
+
canSwapUp
|
|
399
|
+
? {
|
|
400
|
+
icon: TableMoveUp,
|
|
401
|
+
onClick: () => {
|
|
402
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
403
|
+
moveOrSwapRow(tableSelectedOn.index, tableSelectedOn.index - 1, 'swap');
|
|
404
|
+
setTableSelectedOn(undefined);
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
}
|
|
408
|
+
: undefined,
|
|
409
|
+
].filter((i) => i !== undefined);
|
|
410
|
+
}, [tableSelectedOn, setTableSelectedOn, rowCount, tableElement, moveOrSwapRow]);
|
|
411
|
+
// Column move actions
|
|
412
|
+
const columnMoveActions = useMemo(() => {
|
|
413
|
+
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'column' || typeof tableSelectedOn.index !== 'number') {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
const columnIndex = tableSelectedOn.index;
|
|
417
|
+
// 檢查當前行是否為標題行
|
|
418
|
+
const currentIsTitle = !!element.treatAsTitle;
|
|
419
|
+
// 判斷左右是否可以移動
|
|
420
|
+
const canMoveLeft = columnIndex > 0;
|
|
421
|
+
const canMoveRight = columnIndex < columnCount - 1;
|
|
422
|
+
// 檢查相鄰行是否為相同類型(都是標題行或都不是標題行)
|
|
423
|
+
const checkAdjacentColumnType = (adjacentIndex) => {
|
|
424
|
+
// 從 tableElement 中獲取相鄰行的 treatAsTitle 狀態
|
|
425
|
+
const tableMainElement = tableElement.children.find((child) => Element.isElement(child) && child.type.includes('table_main'));
|
|
426
|
+
if (!tableMainElement || !Element.isElement(tableMainElement)) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
const tableBodyElement = tableMainElement.children.find((child) => Element.isElement(child) && child.type.includes('table_body'));
|
|
430
|
+
if (!tableBodyElement || !Element.isElement(tableBodyElement)) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
// 從 body 的第一列獲取相鄰 cell 的 treatAsTitle 屬性
|
|
434
|
+
const firstRow = tableBodyElement.children[0];
|
|
435
|
+
if (!Element.isElement(firstRow)) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
const adjacentCell = firstRow.children[adjacentIndex];
|
|
439
|
+
if (!Element.isElement(adjacentCell)) {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
return !!adjacentCell.treatAsTitle;
|
|
443
|
+
};
|
|
444
|
+
// 標題行只能與標題行互換,一般行只能與一般行互換
|
|
445
|
+
const canSwapLeft = canMoveLeft && currentIsTitle === checkAdjacentColumnType(columnIndex - 1);
|
|
446
|
+
const canSwapRight = canMoveRight && currentIsTitle === checkAdjacentColumnType(columnIndex + 1);
|
|
447
|
+
return [
|
|
448
|
+
canSwapRight
|
|
449
|
+
? {
|
|
450
|
+
icon: TableMoveRight,
|
|
451
|
+
onClick: () => {
|
|
452
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
453
|
+
moveOrSwapColumn(tableSelectedOn.index, tableSelectedOn.index + 1, 'swap');
|
|
454
|
+
setTableSelectedOn(undefined);
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
}
|
|
458
|
+
: undefined,
|
|
459
|
+
canSwapLeft
|
|
460
|
+
? {
|
|
461
|
+
icon: TableMoveLeft,
|
|
462
|
+
onClick: () => {
|
|
463
|
+
if (typeof tableSelectedOn.index === 'number') {
|
|
464
|
+
moveOrSwapColumn(tableSelectedOn.index, tableSelectedOn.index - 1, 'swap');
|
|
465
|
+
setTableSelectedOn(undefined);
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
}
|
|
469
|
+
: undefined,
|
|
470
|
+
].filter((i) => i !== undefined);
|
|
471
|
+
}, [tableSelectedOn, setTableSelectedOn, columnCount, element.treatAsTitle, tableElement, moveOrSwapColumn]);
|
|
472
|
+
// Delete actions
|
|
473
|
+
const deleteActions = useMemo(() => {
|
|
474
|
+
return [
|
|
475
|
+
{
|
|
476
|
+
icon: Trash,
|
|
477
|
+
className: 'qdr-table__delete',
|
|
478
|
+
onClick: () => {
|
|
479
|
+
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'row' && typeof tableSelectedOn.index === 'number') {
|
|
480
|
+
deleteRow(tableSelectedOn.index);
|
|
481
|
+
setTableSelectedOn(undefined);
|
|
482
|
+
}
|
|
483
|
+
else if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'column' && typeof tableSelectedOn.index === 'number') {
|
|
484
|
+
deleteColumn(tableSelectedOn.index);
|
|
485
|
+
setTableSelectedOn(undefined);
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
];
|
|
490
|
+
}, [tableSelectedOn, deleteRow, deleteColumn, setTableSelectedOn]);
|
|
491
|
+
// Inline toolbar icon groups - 當選中行/列時顯示的完整工具列
|
|
492
|
+
const inlineToolbarIconGroups = useMemo(() => {
|
|
493
|
+
return [
|
|
494
|
+
{
|
|
495
|
+
icons: rowActions || columnActions || [],
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
icons: columnAlignActions || [],
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
icons: rowAddActions || columnAddActions || [],
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
icons: rowMoveActions || columnMoveActions || [],
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
icons: deleteActions,
|
|
508
|
+
},
|
|
509
|
+
];
|
|
510
|
+
}, [
|
|
511
|
+
rowActions,
|
|
512
|
+
columnActions,
|
|
513
|
+
columnAlignActions,
|
|
514
|
+
rowAddActions,
|
|
515
|
+
columnAddActions,
|
|
516
|
+
rowMoveActions,
|
|
517
|
+
columnMoveActions,
|
|
518
|
+
deleteActions,
|
|
519
|
+
]);
|
|
520
|
+
return {
|
|
521
|
+
focusToolbarIconGroups,
|
|
522
|
+
inlineToolbarIconGroups,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export { useTableCellToolbarActions };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useTableMetadata(): import("../contexts/TableMetadataContext").TableMetadataContextType;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { TableMetadataContext } from '../contexts/TableMetadataContext.js';
|
|
3
|
+
|
|
4
|
+
function useTableMetadata() {
|
|
5
|
+
const context = useContext(TableMetadataContext);
|
|
6
|
+
if (!context) {
|
|
7
|
+
throw new Error('useTableMetadata must be used within a TableMetadataContext.Provider');
|
|
8
|
+
}
|
|
9
|
+
return context;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { useTableMetadata };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useTableStateContext(): import("../contexts/TableStateContext").TableStateContextType;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { TableStateContext } from '../contexts/TableStateContext.js';
|
|
3
|
+
|
|
4
|
+
function useTableStateContext() {
|
|
5
|
+
const context = useContext(TableStateContext);
|
|
6
|
+
if (!context) {
|
|
7
|
+
throw new Error('useTableState must be used within a TableStateContext.Provider');
|
|
8
|
+
}
|
|
9
|
+
return context;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { useTableStateContext };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare function useTableStates(): {
|
|
2
|
+
tableSelectedOn: {
|
|
3
|
+
region: "table" | "header" | "row" | "column";
|
|
4
|
+
index?: number;
|
|
5
|
+
} | undefined;
|
|
6
|
+
setTableSelectedOn: import("react").Dispatch<import("react").SetStateAction<{
|
|
7
|
+
region: "table" | "header" | "row" | "column";
|
|
8
|
+
index?: number;
|
|
9
|
+
} | undefined>>;
|
|
10
|
+
tableHoveredOn: {
|
|
11
|
+
columnIndex: number;
|
|
12
|
+
rowIndex: number;
|
|
13
|
+
} | undefined;
|
|
14
|
+
setTableHoveredOn: import("react").Dispatch<import("react").SetStateAction<{
|
|
15
|
+
columnIndex: number;
|
|
16
|
+
rowIndex: number;
|
|
17
|
+
} | undefined>>;
|
|
18
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
function useTableStates() {
|
|
4
|
+
const [tableSelectedOn, setTableSelectedOn] = useState();
|
|
5
|
+
const [tableHoveredOn, setTableHoveredOn] = useState();
|
|
6
|
+
return {
|
|
7
|
+
tableSelectedOn,
|
|
8
|
+
setTableSelectedOn,
|
|
9
|
+
tableHoveredOn,
|
|
10
|
+
setTableHoveredOn,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { useTableStates };
|