@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.
Files changed (108) hide show
  1. package/card/components/Card.js +83 -75
  2. package/card/index.cjs.js +82 -74
  3. package/carousel/components/Carousel.js +32 -28
  4. package/carousel/index.cjs.js +32 -28
  5. package/components/Tooltip/index.js +5 -2
  6. package/components/index.cjs.js +5 -2
  7. package/embed/renderers/base/components/BaseEmbedElement.js +51 -43
  8. package/embed/renderers/base/index.cjs.js +50 -42
  9. package/image/components/Image.js +34 -26
  10. package/image/createReactImage.js +1 -1
  11. package/image/index.cjs.js +34 -26
  12. package/package.json +4 -4
  13. package/table/components/Table.d.ts +9 -0
  14. package/table/components/Table.js +231 -0
  15. package/table/components/TableBody.d.ts +5 -0
  16. package/table/components/TableBody.js +8 -0
  17. package/table/components/TableCell.d.ts +5 -0
  18. package/table/components/TableCell.js +191 -0
  19. package/table/components/TableHeader.d.ts +5 -0
  20. package/table/components/TableHeader.js +13 -0
  21. package/table/components/TableMain.d.ts +5 -0
  22. package/table/components/TableMain.js +225 -0
  23. package/table/components/TableRow.d.ts +5 -0
  24. package/table/components/TableRow.js +8 -0
  25. package/table/components/TableTitle.d.ts +5 -0
  26. package/table/components/TableTitle.js +18 -0
  27. package/table/contexts/TableActionsContext.d.ts +3 -0
  28. package/table/contexts/TableActionsContext.js +5 -0
  29. package/table/contexts/TableHeaderContext.d.ts +2 -0
  30. package/table/contexts/TableHeaderContext.js +7 -0
  31. package/table/contexts/TableMetadataContext.d.ts +3 -0
  32. package/table/contexts/TableMetadataContext.js +5 -0
  33. package/table/contexts/TableScrollContext.d.ts +2 -0
  34. package/table/contexts/TableScrollContext.js +9 -0
  35. package/table/contexts/TableStateContext.d.ts +3 -0
  36. package/table/contexts/TableStateContext.js +5 -0
  37. package/table/createReactTable.d.ts +4 -0
  38. package/table/createReactTable.js +297 -0
  39. package/table/defaultRenderTableElements.d.ts +2 -0
  40. package/table/defaultRenderTableElements.js +20 -0
  41. package/table/hooks/useColumnResize.d.ts +12 -0
  42. package/table/hooks/useColumnResize.js +139 -0
  43. package/table/hooks/useTableActions.d.ts +25 -0
  44. package/table/hooks/useTableActions.js +886 -0
  45. package/table/hooks/useTableActionsContext.d.ts +1 -0
  46. package/table/hooks/useTableActionsContext.js +12 -0
  47. package/table/hooks/useTableCell.d.ts +16 -0
  48. package/table/hooks/useTableCell.js +166 -0
  49. package/table/hooks/useTableCellToolbarActions.d.ts +34 -0
  50. package/table/hooks/useTableCellToolbarActions.js +404 -0
  51. package/table/hooks/useTableMetadata.d.ts +1 -0
  52. package/table/hooks/useTableMetadata.js +12 -0
  53. package/table/hooks/useTableStateContext.d.ts +1 -0
  54. package/table/hooks/useTableStateContext.js +12 -0
  55. package/table/hooks/useTableStates.d.ts +18 -0
  56. package/table/hooks/useTableStates.js +14 -0
  57. package/table/index.cjs.js +3254 -0
  58. package/table/index.d.ts +16 -0
  59. package/table/index.js +24 -0
  60. package/table/jsx-serializer/components/Table.d.ts +3 -0
  61. package/table/jsx-serializer/components/Table.js +7 -0
  62. package/table/jsx-serializer/components/TableBody.d.ts +3 -0
  63. package/table/jsx-serializer/components/TableBody.js +7 -0
  64. package/table/jsx-serializer/components/TableCell.d.ts +5 -0
  65. package/table/jsx-serializer/components/TableCell.js +33 -0
  66. package/table/jsx-serializer/components/TableHeader.d.ts +3 -0
  67. package/table/jsx-serializer/components/TableHeader.js +10 -0
  68. package/table/jsx-serializer/components/TableMain.d.ts +6 -0
  69. package/table/jsx-serializer/components/TableMain.js +18 -0
  70. package/table/jsx-serializer/components/TableRow.d.ts +3 -0
  71. package/table/jsx-serializer/components/TableRow.js +7 -0
  72. package/table/jsx-serializer/components/TableTitle.d.ts +3 -0
  73. package/table/jsx-serializer/components/TableTitle.js +7 -0
  74. package/table/jsx-serializer/contexts/TableHeaderContext.d.ts +1 -0
  75. package/table/jsx-serializer/contexts/TableHeaderContext.js +5 -0
  76. package/table/jsx-serializer/contexts/TableScrollContext.d.ts +2 -0
  77. package/table/jsx-serializer/contexts/TableScrollContext.js +7 -0
  78. package/table/jsx-serializer/createJsxSerializeTable.d.ts +5 -0
  79. package/table/jsx-serializer/createJsxSerializeTable.js +113 -0
  80. package/table/jsx-serializer/defaultRenderTableElements.d.ts +2 -0
  81. package/table/jsx-serializer/defaultRenderTableElements.js +20 -0
  82. package/table/jsx-serializer/index.cjs.js +195 -0
  83. package/table/jsx-serializer/index.d.ts +3 -0
  84. package/table/jsx-serializer/index.js +2 -0
  85. package/table/jsx-serializer/package.json +7 -0
  86. package/table/jsx-serializer/typings.d.ts +12 -0
  87. package/table/package.json +10 -0
  88. package/table/table.css +1 -0
  89. package/table/table.scss +393 -0
  90. package/table/toolbar/TableToolbarIcon.d.ts +8 -0
  91. package/table/toolbar/TableToolbarIcon.js +12 -0
  92. package/table/toolbar/index.cjs.js +24 -0
  93. package/table/toolbar/index.d.ts +2 -0
  94. package/table/toolbar/index.js +2 -0
  95. package/table/toolbar/package.json +7 -0
  96. package/table/toolbar/useTableTool.d.ts +4 -0
  97. package/table/toolbar/useTableTool.js +13 -0
  98. package/table/typings.d.ts +66 -0
  99. package/table/utils/helper.d.ts +160 -0
  100. package/table/utils/helper.js +693 -0
  101. package/toolbar/components/InlineToolbar.d.ts +12 -11
  102. package/toolbar/components/InlineToolbar.js +23 -19
  103. package/toolbar/components/Toolbar.js +2 -2
  104. package/toolbar/index.cjs.js +24 -21
  105. package/toolbar/toolbar.css +1 -1
  106. package/toolbar/toolbar.scss +4 -1
  107. package/utils/index.cjs.js +7 -1
  108. 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 };