@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 @@
|
|
|
1
|
+
export declare function useTableActionsContext(): import("../contexts/TableActionsContext").TableActionsContextType;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { TableActionsContext } from '../contexts/TableActionsContext.js';
|
|
3
|
+
|
|
4
|
+
function useTableActionsContext() {
|
|
5
|
+
const context = useContext(TableActionsContext);
|
|
6
|
+
if (!context) {
|
|
7
|
+
throw new Error('useTableActionsContext must be used within a TableActionsContext.Provider');
|
|
8
|
+
}
|
|
9
|
+
return context;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { useTableActionsContext };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TableElement } from '@quadrats/common/table';
|
|
2
|
+
import { QuadratsReactEditor } from '@quadrats/react';
|
|
3
|
+
import { AlignValue } from '@quadrats/common/align';
|
|
4
|
+
/** 檢查 table cell 是否在 focused 狀態 */
|
|
5
|
+
export declare function useTableCellFocused(element: TableElement, editor: QuadratsReactEditor): boolean;
|
|
6
|
+
/** 取得 table cell 在 table 裡的 columnIndex 及 rowIndex */
|
|
7
|
+
export declare function useTableCellPosition(element: TableElement, _editor: QuadratsReactEditor): {
|
|
8
|
+
columnIndex: number;
|
|
9
|
+
rowIndex: number;
|
|
10
|
+
};
|
|
11
|
+
/** 轉換 paragraph, order list, un-order list */
|
|
12
|
+
export declare function useTableCellTransformContent(element: TableElement, editor: QuadratsReactEditor): (elementType: string) => void;
|
|
13
|
+
/** 設定 column 或 table 的 align */
|
|
14
|
+
export declare function useTableCellAlign(tableElement: TableElement, editor: QuadratsReactEditor): (alignValue: AlignValue, scope: "table" | "column", columnIndex?: number) => void;
|
|
15
|
+
/** 獲取 column 或 table 的 align 狀態 */
|
|
16
|
+
export declare function useTableCellAlignStatus(tableElement: TableElement, editor: QuadratsReactEditor): (scope: "table" | "column", columnIndex?: number) => AlignValue;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { usePreviousValue } from '@quadrats/react/utils';
|
|
2
|
+
import { Editor, Range, Node, Element, Transforms, PARAGRAPH_TYPE } from '@quadrats/core';
|
|
3
|
+
import { useCallback, useMemo, useEffect } from 'react';
|
|
4
|
+
import { ReactEditor, useFocused } from 'slate-react';
|
|
5
|
+
import { useTableStateContext } from './useTableStateContext.js';
|
|
6
|
+
import { useTableMetadata } from './useTableMetadata.js';
|
|
7
|
+
import { createList, LIST_TYPES } from '@quadrats/common/list';
|
|
8
|
+
import { getTableStructure, collectCells, setAlignForCells, getAlignFromCells } from '../utils/helper.js';
|
|
9
|
+
|
|
10
|
+
/** 檢查 table cell 是否在 focused 狀態 */
|
|
11
|
+
function useTableCellFocused(element, editor) {
|
|
12
|
+
const { tableSelectedOn, setTableHoveredOn, setTableSelectedOn } = useTableStateContext();
|
|
13
|
+
const cellPath = ReactEditor.findPath(editor, element);
|
|
14
|
+
const focused = useFocused();
|
|
15
|
+
const isCellFocused = useMemo(() => {
|
|
16
|
+
if (!focused || !editor.selection || (tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region))
|
|
17
|
+
return false;
|
|
18
|
+
const isSelectionCollapsed = Range.isCollapsed(editor.selection);
|
|
19
|
+
if (!isSelectionCollapsed)
|
|
20
|
+
return false;
|
|
21
|
+
const selectionPath = editor.selection.anchor.path;
|
|
22
|
+
return selectionPath.slice(0, cellPath.length).every((segment, index) => segment === cellPath[index]);
|
|
23
|
+
}, [focused, editor.selection, cellPath, tableSelectedOn]);
|
|
24
|
+
const isPreviousCellFocused = usePreviousValue(isCellFocused);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (isCellFocused) {
|
|
27
|
+
setTableHoveredOn(undefined);
|
|
28
|
+
}
|
|
29
|
+
if (isCellFocused && !isPreviousCellFocused) {
|
|
30
|
+
setTableSelectedOn(undefined);
|
|
31
|
+
}
|
|
32
|
+
}, [isCellFocused, isPreviousCellFocused, setTableSelectedOn, setTableHoveredOn]);
|
|
33
|
+
return isCellFocused;
|
|
34
|
+
}
|
|
35
|
+
/** 取得 table cell 在 table 裡的 columnIndex 及 rowIndex */
|
|
36
|
+
function useTableCellPosition(element, _editor) {
|
|
37
|
+
const { cellPositions } = useTableMetadata();
|
|
38
|
+
const cellPosition = useMemo(() => {
|
|
39
|
+
const position = cellPositions.get(element);
|
|
40
|
+
if (position) {
|
|
41
|
+
return position;
|
|
42
|
+
}
|
|
43
|
+
return { columnIndex: -1, rowIndex: -1 };
|
|
44
|
+
}, [cellPositions, element]);
|
|
45
|
+
return cellPosition;
|
|
46
|
+
}
|
|
47
|
+
/** 轉換 paragraph, order list, un-order list */
|
|
48
|
+
function useTableCellTransformContent(element, editor) {
|
|
49
|
+
const splitContentByNewlines = useCallback((cellPath) => {
|
|
50
|
+
try {
|
|
51
|
+
const cellNode = Node.get(editor, cellPath);
|
|
52
|
+
if (!Element.isElement(cellNode))
|
|
53
|
+
return false;
|
|
54
|
+
let hasChanges = false;
|
|
55
|
+
const nodesToProcess = [...cellNode.children];
|
|
56
|
+
// Process each content node in reverse order to maintain indices
|
|
57
|
+
for (let contentIndex = nodesToProcess.length - 1; contentIndex >= 0; contentIndex--) {
|
|
58
|
+
const contentNode = nodesToProcess[contentIndex];
|
|
59
|
+
if (Element.isElement(contentNode)) {
|
|
60
|
+
const textContent = Node.string(contentNode);
|
|
61
|
+
if (textContent.includes('\n')) {
|
|
62
|
+
const contentPath = [...cellPath, contentIndex];
|
|
63
|
+
// Verify the path is still valid
|
|
64
|
+
try {
|
|
65
|
+
const currentNode = Node.get(editor, contentPath);
|
|
66
|
+
if (!currentNode)
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
catch (_a) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
// Split by newlines and filter out empty strings
|
|
73
|
+
const lines = textContent.split('\n').filter((line) => line.trim() !== '');
|
|
74
|
+
if (lines.length > 1) {
|
|
75
|
+
hasChanges = true;
|
|
76
|
+
// Use Editor.withoutNormalizing to batch operations
|
|
77
|
+
Editor.withoutNormalizing(editor, () => {
|
|
78
|
+
Transforms.removeNodes(editor, { at: contentPath });
|
|
79
|
+
lines.forEach((line, lineIndex) => {
|
|
80
|
+
const newParagraph = {
|
|
81
|
+
type: PARAGRAPH_TYPE,
|
|
82
|
+
children: [{ text: line.trim() }],
|
|
83
|
+
};
|
|
84
|
+
Transforms.insertNodes(editor, newParagraph, {
|
|
85
|
+
at: [...cellPath, contentIndex + lineIndex],
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return hasChanges;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.warn('Failed to split content by newlines:', error);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}, [editor]);
|
|
100
|
+
const transformCellContent = useCallback((elementType) => {
|
|
101
|
+
try {
|
|
102
|
+
const cellPath = ReactEditor.findPath(editor, element);
|
|
103
|
+
const listHelper = createList();
|
|
104
|
+
if (elementType === LIST_TYPES.ol || elementType === LIST_TYPES.ul) {
|
|
105
|
+
/** 將 paragraph 的換行符號轉換成 list item */
|
|
106
|
+
const wasSplit = splitContentByNewlines(cellPath);
|
|
107
|
+
if (wasSplit) {
|
|
108
|
+
Editor.normalize(editor, { force: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// 選擇整個儲存格內容
|
|
112
|
+
const cellStartPoint = Editor.start(editor, cellPath);
|
|
113
|
+
const cellEndPoint = Editor.end(editor, cellPath);
|
|
114
|
+
Transforms.select(editor, { anchor: cellStartPoint, focus: cellEndPoint });
|
|
115
|
+
if (elementType === LIST_TYPES.ol || elementType === LIST_TYPES.ul) {
|
|
116
|
+
listHelper.toggleList(editor, elementType, PARAGRAPH_TYPE);
|
|
117
|
+
}
|
|
118
|
+
else if (elementType === PARAGRAPH_TYPE) {
|
|
119
|
+
const cellNode = Node.get(editor, cellPath);
|
|
120
|
+
if (Element.isElement(cellNode)) {
|
|
121
|
+
const currentListElement = cellNode.children.find((child) => Element.isElement(child) && [LIST_TYPES.ol, LIST_TYPES.ul].includes(child.type));
|
|
122
|
+
if (currentListElement && Element.isElement(currentListElement)) {
|
|
123
|
+
listHelper.toggleList(editor, currentListElement.type, PARAGRAPH_TYPE);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// 因為底層結構改變了,取消選取以避免錯誤
|
|
128
|
+
Transforms.deselect(editor);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.warn('Failed to transform cell content:', error);
|
|
132
|
+
}
|
|
133
|
+
}, [editor, element, splitContentByNewlines]);
|
|
134
|
+
return transformCellContent;
|
|
135
|
+
}
|
|
136
|
+
/** 設定 column 或 table 的 align */
|
|
137
|
+
function useTableCellAlign(tableElement, editor) {
|
|
138
|
+
const setAlign = useCallback((alignValue, scope, columnIndex) => {
|
|
139
|
+
const tableStructure = getTableStructure(editor, tableElement);
|
|
140
|
+
if (!tableStructure) {
|
|
141
|
+
console.warn('Failed to get table structure');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// 使用 helper 函數收集 cells
|
|
145
|
+
const cells = collectCells(tableStructure, scope, columnIndex);
|
|
146
|
+
// 使用 helper 函數設定 align
|
|
147
|
+
Editor.withoutNormalizing(editor, () => {
|
|
148
|
+
setAlignForCells(editor, cells, alignValue);
|
|
149
|
+
});
|
|
150
|
+
}, [editor, tableElement]);
|
|
151
|
+
return setAlign;
|
|
152
|
+
}
|
|
153
|
+
/** 獲取 column 或 table 的 align 狀態 */
|
|
154
|
+
function useTableCellAlignStatus(tableElement, editor) {
|
|
155
|
+
const getAlign = useCallback((scope, columnIndex) => {
|
|
156
|
+
const tableStructure = getTableStructure(editor, tableElement);
|
|
157
|
+
if (!tableStructure) {
|
|
158
|
+
return 'left';
|
|
159
|
+
}
|
|
160
|
+
const cells = collectCells(tableStructure, scope, columnIndex);
|
|
161
|
+
return getAlignFromCells(cells);
|
|
162
|
+
}, [editor, tableElement]);
|
|
163
|
+
return getAlign;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export { useTableCellAlign, useTableCellAlignStatus, useTableCellFocused, useTableCellPosition, useTableCellTransformContent };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TableElement } from '@quadrats/common/table';
|
|
3
|
+
interface Position {
|
|
4
|
+
columnIndex: number;
|
|
5
|
+
rowIndex: number;
|
|
6
|
+
}
|
|
7
|
+
interface UseTableCellToolbarActionsParams {
|
|
8
|
+
element: TableElement;
|
|
9
|
+
cellPosition: Position;
|
|
10
|
+
isHeader: boolean;
|
|
11
|
+
transformCellContent: (type: string) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 將所有與 toolbar icons 相關的邏輯集中管理
|
|
15
|
+
*/
|
|
16
|
+
export declare function useTableCellToolbarActions({ element, cellPosition, isHeader, transformCellContent, }: UseTableCellToolbarActionsParams): {
|
|
17
|
+
focusToolbarIconGroups: ({
|
|
18
|
+
icons: React.JSX.Element[];
|
|
19
|
+
} | {
|
|
20
|
+
icons: {
|
|
21
|
+
icon: import("@quadrats/icons").IconDefinition;
|
|
22
|
+
onClick: () => void;
|
|
23
|
+
}[];
|
|
24
|
+
})[];
|
|
25
|
+
inlineToolbarIconGroups: ({
|
|
26
|
+
icons: {
|
|
27
|
+
icon: import("@quadrats/icons").IconDefinition;
|
|
28
|
+
onClick: () => void;
|
|
29
|
+
}[];
|
|
30
|
+
} | {
|
|
31
|
+
icons: React.JSX.Element[];
|
|
32
|
+
})[];
|
|
33
|
+
};
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { UnorderedList, OrderedList, Paragraph, AddRowAtBottom, AddRowAtTop, AddColumnAtLeft, AddColumnAtRight, Unpinned, Pinned, TableRemoveTitle, TableSetColumnTitle, TableSetRowTitle, AlignLeft, AlignCenter, AlignRight, 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, 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, } = 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
|
+
// Delete actions
|
|
363
|
+
const deleteActions = useMemo(() => {
|
|
364
|
+
return [
|
|
365
|
+
{
|
|
366
|
+
icon: Trash,
|
|
367
|
+
className: 'qdr-table__delete',
|
|
368
|
+
onClick: () => {
|
|
369
|
+
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'row' && typeof tableSelectedOn.index === 'number') {
|
|
370
|
+
deleteRow(tableSelectedOn.index);
|
|
371
|
+
setTableSelectedOn(undefined);
|
|
372
|
+
}
|
|
373
|
+
else if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'column' && typeof tableSelectedOn.index === 'number') {
|
|
374
|
+
deleteColumn(tableSelectedOn.index);
|
|
375
|
+
setTableSelectedOn(undefined);
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
];
|
|
380
|
+
}, [tableSelectedOn, deleteRow, deleteColumn, setTableSelectedOn]);
|
|
381
|
+
// Inline toolbar icon groups - 當選中行/列時顯示的完整工具列
|
|
382
|
+
const inlineToolbarIconGroups = useMemo(() => {
|
|
383
|
+
return [
|
|
384
|
+
{
|
|
385
|
+
icons: rowActions || columnActions || [],
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
icons: columnAlignActions || [],
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
icons: rowAddActions || columnAddActions || [],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
icons: deleteActions,
|
|
395
|
+
},
|
|
396
|
+
];
|
|
397
|
+
}, [rowActions, columnActions, columnAlignActions, rowAddActions, columnAddActions, deleteActions]);
|
|
398
|
+
return {
|
|
399
|
+
focusToolbarIconGroups,
|
|
400
|
+
inlineToolbarIconGroups,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
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 };
|