@livepreso/react-plugin-textfield 0.0.2

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 (176) hide show
  1. package/.lintstagedrc.js +6 -0
  2. package/.rush/temp/shrinkwrap-deps.json +311 -0
  3. package/.vscode/settings.json +22 -0
  4. package/CHANGELOG.json +17 -0
  5. package/CHANGELOG.md +9 -0
  6. package/components/BubbleMenu.js +181 -0
  7. package/components/BubbleMenu.module.scss +9 -0
  8. package/components/LinkEditDialog.js +105 -0
  9. package/components/LinkEditDialog.module.scss +171 -0
  10. package/components/Popover.js +43 -0
  11. package/components/Popover.module.scss +80 -0
  12. package/components/PrimaryToolbar.js +27 -0
  13. package/components/Select/Select.js +71 -0
  14. package/components/Select/Select.module.scss +100 -0
  15. package/components/Select/SelectGroup.js +37 -0
  16. package/components/Select/index.js +1 -0
  17. package/components/TableCellMenu.js +43 -0
  18. package/components/TableToolbar.js +41 -0
  19. package/components/Tooltip.js +34 -0
  20. package/components/Tooltip.module.scss +87 -0
  21. package/components/VerticalAlignToggle.js +65 -0
  22. package/components/color-picker/ColorPicker.js +28 -0
  23. package/components/color-picker/ColorPicker.module.scss +8 -0
  24. package/components/color-picker/ColorPickerChip.js +22 -0
  25. package/components/color-picker/ColorPickerChip.module.scss +25 -0
  26. package/components/color-picker/ColorPickerCombo.js +45 -0
  27. package/components/color-picker/ColorPickerCombo.module.scss +23 -0
  28. package/components/color-picker/TextColorIcon.js +18 -0
  29. package/components/editor-toolbars/EditorMenu.js +104 -0
  30. package/components/editor-toolbars/EditorMenu.module.scss +96 -0
  31. package/components/editor-toolbars/EditorToolbar.js +146 -0
  32. package/components/editor-toolbars/EditorToolbar.module.scss +75 -0
  33. package/components/editor-toolbars/MenuItem.js +24 -0
  34. package/components/editor-toolbars/SubMenu.js +50 -0
  35. package/components/editor-toolbars/ToolbarButton.js +26 -0
  36. package/components/editor-toolbars/ToolbarToggle.js +35 -0
  37. package/components/editor-toolbars/ToolbarToggleGroup.js +43 -0
  38. package/components/editor-toolbars/utils.js +7 -0
  39. package/components/hooks/use-presenter.js +5 -0
  40. package/components/style.module.scss +63 -0
  41. package/components/tiptap/ListItem.js +5 -0
  42. package/components/tiptap/Table.js +397 -0
  43. package/components/tiptap/TableCells.js +99 -0
  44. package/components/utils.js +84 -0
  45. package/configs/generate-toolbar-configuration.js +130 -0
  46. package/configs/generate-toolbar-options.js +96 -0
  47. package/configs/table-toolbar-configuration.js +187 -0
  48. package/configs/toolbar-configuration.js +330 -0
  49. package/constants.js +198 -0
  50. package/eslint.config.mjs +15 -0
  51. package/icons/AddColumnLeft.js +15 -0
  52. package/icons/AddColumnRight.js +15 -0
  53. package/icons/AddRowAbove.js +15 -0
  54. package/icons/AddRowBelow.js +15 -0
  55. package/icons/AlignHorizontalCenter.js +13 -0
  56. package/icons/AlignHorizontalLeft.js +13 -0
  57. package/icons/AlignHorizontalRight.js +13 -0
  58. package/icons/AlignVerticalBottom.js +13 -0
  59. package/icons/AlignVerticalCenter.js +13 -0
  60. package/icons/AlignVerticalTop.js +13 -0
  61. package/icons/Backspace.js +13 -0
  62. package/icons/Bold.js +14 -0
  63. package/icons/BorderAll.js +13 -0
  64. package/icons/BorderBottom.js +13 -0
  65. package/icons/BorderClear.js +13 -0
  66. package/icons/BorderHorizontal.js +13 -0
  67. package/icons/BorderInner.js +13 -0
  68. package/icons/BorderLeft.js +13 -0
  69. package/icons/BorderOuter.js +13 -0
  70. package/icons/BorderRight.js +13 -0
  71. package/icons/BorderTop.js +13 -0
  72. package/icons/BorderVertical.js +13 -0
  73. package/icons/Close.js +13 -0
  74. package/icons/Delete.js +13 -0
  75. package/icons/EvenlyDistribute.js +14 -0
  76. package/icons/FitWidth.js +13 -0
  77. package/icons/FitWidthArrows.js +21 -0
  78. package/icons/FormatAlignCenter.js +13 -0
  79. package/icons/FormatAlignJustify.js +13 -0
  80. package/icons/FormatAlignLeft.js +13 -0
  81. package/icons/FormatAlignRight.js +13 -0
  82. package/icons/FormatBold.js +13 -0
  83. package/icons/FormatClear.js +13 -0
  84. package/icons/FormatColorFill.js +13 -0
  85. package/icons/FormatColorText.js +13 -0
  86. package/icons/FormatItalic.js +13 -0
  87. package/icons/FormatLineSpacing.js +13 -0
  88. package/icons/FormatListBulleted.js +13 -0
  89. package/icons/FormatListNumbered.js +13 -0
  90. package/icons/FormatStrikethrough.js +13 -0
  91. package/icons/FormatUnderlined.js +13 -0
  92. package/icons/HorizontalRule.js +13 -0
  93. package/icons/Italic.js +14 -0
  94. package/icons/ItalicIcon.js +18 -0
  95. package/icons/Link.js +13 -0
  96. package/icons/LinkOff.js +13 -0
  97. package/icons/MergeCells.js +14 -0
  98. package/icons/Redo.js +13 -0
  99. package/icons/RemoveColumnOutline.js +28 -0
  100. package/icons/RemoveRowOutline.js +25 -0
  101. package/icons/SplitCells.js +14 -0
  102. package/icons/SplitScene.js +13 -0
  103. package/icons/Subscript.js +13 -0
  104. package/icons/Superscript.js +13 -0
  105. package/icons/Underline.js +14 -0
  106. package/icons/Undo.js +13 -0
  107. package/icons/VerticalAlignBottom.js +13 -0
  108. package/icons/VerticalAlignCenter.js +13 -0
  109. package/icons/VerticalAlignTop.js +13 -0
  110. package/icons/add_column_left.svg +6 -0
  111. package/icons/add_column_right.svg +6 -0
  112. package/icons/add_row_above.svg +6 -0
  113. package/icons/add_row_below.svg +6 -0
  114. package/icons/align_horizontal_center.svg +1 -0
  115. package/icons/align_horizontal_left.svg +1 -0
  116. package/icons/align_horizontal_right.svg +1 -0
  117. package/icons/align_vertical_bottom.svg +1 -0
  118. package/icons/align_vertical_center.svg +1 -0
  119. package/icons/align_vertical_top.svg +1 -0
  120. package/icons/backspace.svg +1 -0
  121. package/icons/bold.svg +1 -0
  122. package/icons/border_all.svg +1 -0
  123. package/icons/border_bottom.svg +1 -0
  124. package/icons/border_clear.svg +1 -0
  125. package/icons/border_horizontal.svg +1 -0
  126. package/icons/border_inner.svg +1 -0
  127. package/icons/border_left.svg +1 -0
  128. package/icons/border_outer.svg +1 -0
  129. package/icons/border_right.svg +1 -0
  130. package/icons/border_top.svg +1 -0
  131. package/icons/border_vertical.svg +1 -0
  132. package/icons/close.svg +1 -0
  133. package/icons/delete.svg +1 -0
  134. package/icons/evenly_distribute.svg +5 -0
  135. package/icons/fit_width.svg +1 -0
  136. package/icons/fit_width_arrows.svg +12 -0
  137. package/icons/format_align_center.svg +1 -0
  138. package/icons/format_align_justify.svg +1 -0
  139. package/icons/format_align_left.svg +1 -0
  140. package/icons/format_align_right.svg +1 -0
  141. package/icons/format_bold.svg +1 -0
  142. package/icons/format_clear.svg +1 -0
  143. package/icons/format_color_fill.svg +1 -0
  144. package/icons/format_color_text.svg +5 -0
  145. package/icons/format_color_text_ungrouped.svg +6 -0
  146. package/icons/format_italic.svg +1 -0
  147. package/icons/format_line_spacing.svg +1 -0
  148. package/icons/format_list_bulleted.svg +1 -0
  149. package/icons/format_list_numbered.svg +1 -0
  150. package/icons/format_strikethrough.svg +1 -0
  151. package/icons/format_underlined.svg +1 -0
  152. package/icons/horizontal_rule.svg +1 -0
  153. package/icons/index.js +191 -0
  154. package/icons/italic.svg +1 -0
  155. package/icons/link.svg +1 -0
  156. package/icons/link_off.svg +1 -0
  157. package/icons/merge_cells.svg +5 -0
  158. package/icons/redo.svg +1 -0
  159. package/icons/remove_column_outline.svg +20 -0
  160. package/icons/remove_row_outline.svg +17 -0
  161. package/icons/split_cells.svg +5 -0
  162. package/icons/split_scene.svg +1 -0
  163. package/icons/subscript.svg +1 -0
  164. package/icons/superscript.svg +1 -0
  165. package/icons/underline.svg +1 -0
  166. package/icons/undo.svg +1 -0
  167. package/icons/vertical_align_bottom.svg +1 -0
  168. package/icons/vertical_align_center.svg +1 -0
  169. package/icons/vertical_align_top.svg +1 -0
  170. package/index.js +334 -0
  171. package/index.module.scss +106 -0
  172. package/package.json +63 -0
  173. package/scripts/extract-svg.js +288 -0
  174. package/utils/color-utils.js +42 -0
  175. package/utils/generate-vertical-alignment-icon.js +22 -0
  176. package/utils/generateCustomExtensions.js +49 -0
@@ -0,0 +1,397 @@
1
+ import { CellSelection, TableMap } from "@tiptap/pm/tables";
2
+ import { Table as TableBase } from "@tiptap/extension-table";
3
+ import { getDomWidth } from "../utils";
4
+ import { CELL_MIN_WIDTH } from "../../constants";
5
+
6
+ export const CELL_VALIGN_TOP = "top";
7
+ export const CELL_VALIGN_MIDDLE = "middle";
8
+ export const CELL_VALIGN_BOTTOM = "bottom";
9
+ export const CELL_VALIGN_MIXED = "mixed";
10
+
11
+ /**
12
+ * Finds the table node and its position if the current selection is inside a table
13
+ * @param state The current editor state
14
+ * @returns An object containing the table node and its position, or null if not in a table
15
+ */
16
+ export function findTable(state) {
17
+ const { doc, selection } = state;
18
+ const { from } = selection;
19
+
20
+ // Find the node before the cursor/selection
21
+ let $pos = doc.resolve(from);
22
+
23
+ // Walk up the document tree to find a table node
24
+ for (let depth = $pos.depth; depth > 0; depth--) {
25
+ const node = $pos.node(depth);
26
+
27
+ if (node.type.spec.tableRole === "table") {
28
+ const pos = $pos.before(depth);
29
+ return { node, pos };
30
+ }
31
+ }
32
+
33
+ return null;
34
+ }
35
+
36
+ /**
37
+ * Finds all cells in the current selection
38
+ * @param state The current editor state
39
+ * @returns An array of objects containing cell nodes and their positions, or empty array if not in a table
40
+ */
41
+ export function findCellsInSelection(state) {
42
+ const { selection, doc } = state;
43
+ const result = [];
44
+
45
+ // Check if we have a cell selection (multiple cells selected)
46
+ if (selection instanceof CellSelection) {
47
+ const table = findTable(state);
48
+
49
+ if (!table) return result;
50
+ const cells = [];
51
+ selection.forEachCell((node, pos) => {
52
+ cells.push(node);
53
+ });
54
+ return cells;
55
+ } else {
56
+ // Regular selection - find the cell we're in
57
+ let $pos = doc.resolve(selection.from);
58
+
59
+ for (let depth = $pos.depth; depth > 0; depth--) {
60
+ const node = $pos.node(depth);
61
+
62
+ if (
63
+ node.type.spec.tableRole === "cell" ||
64
+ node.type.spec.tableRole === "header_cell"
65
+ ) {
66
+ result.push(node);
67
+ break;
68
+ }
69
+ }
70
+ }
71
+
72
+ return result;
73
+ }
74
+
75
+ function getColwidths({
76
+ tableNode,
77
+ cellMinWidth = CELL_MIN_WIDTH,
78
+ columnCount,
79
+ }) {
80
+ let hasColwidth = false;
81
+ // Get the first row to determine the number of columns
82
+ const firstRow = tableNode.firstChild;
83
+ if (!firstRow) return [];
84
+
85
+ let colwidths = [];
86
+ // Iterate through cells in the first row to calculate total width
87
+ firstRow.forEach((cell) => {
88
+ if (cell.attrs.colwidth && Array.isArray(cell.attrs.colwidth)) {
89
+ // Sum up the column widths for this cell
90
+ hasColwidth = true;
91
+ colwidths.push(
92
+ cell.attrs.colwidth.reduce((sum, width) => {
93
+ // colwidth 0, return min cell width
94
+ if (!width) return sum + cellMinWidth;
95
+ return sum + width;
96
+ }, 0),
97
+ );
98
+ } else {
99
+ // If no colwidth, add default width for each column this cell spans
100
+ const colspan = cell.attrs.colspan || 1;
101
+ colwidths.push(cellMinWidth * colspan);
102
+ }
103
+ });
104
+
105
+ const totalWidth = colwidths.reduce((sum, width) => sum + width, 0);
106
+
107
+ // If no colwidth attributes found or total is too small, use a default total
108
+ if (!hasColwidth || totalWidth < columnCount * cellMinWidth) {
109
+ const maxWidth = Math.max(totalWidth, columnCount * cellMinWidth);
110
+ return Array(columnCount).fill(maxWidth);
111
+ }
112
+
113
+ return colwidths;
114
+ }
115
+
116
+ /**
117
+ * Calculates total width of currently selected table
118
+ *
119
+ * @param tableNode The table node
120
+ * @param cellMinWidth The minimum width for each cell (default: 25)
121
+ * @returns Number (pixels) width of currently selected table
122
+ */
123
+ function getTableWidth({ tableNode, cellMinWidth = CELL_MIN_WIDTH }) {
124
+ // Create a table map to understand the table structure
125
+ const tableMap = TableMap.get(tableNode);
126
+ const columnCount = tableMap.width;
127
+
128
+ if (columnCount === 0) return 0;
129
+
130
+ // Calculate total width from existing column widths
131
+ const currColwidths = getColwidths({
132
+ tableNode,
133
+ cellMinWidth,
134
+ columnCount,
135
+ });
136
+
137
+ return currColwidths.reduce((sum, width) => sum + width, 0);
138
+ }
139
+
140
+ /**
141
+ * Updates widths for all columns in a table based on a supplied max width, or the current total width
142
+ *
143
+ * @param tr The current transaction
144
+ * @param tableNode The table node
145
+ * @param tablePos The position of the table in the document
146
+ * @param cellMinWidth The minimum width for each cell
147
+ * @param setEqualColumns If true, all columns will have the same width (default: false)
148
+ * @param maxWidth The maximum width for all columns, calculates current total width if maxWidth is not provided
149
+ * @returns The updated transaction
150
+ */
151
+ function setColumnWidths({
152
+ tr,
153
+ table,
154
+ cellMinWidth = CELL_MIN_WIDTH,
155
+ setEqualColumns = false,
156
+ useFullWidth = false,
157
+ maxWidth,
158
+ }) {
159
+ const tableNode = table.node;
160
+ const tablePos = table.pos;
161
+ // this avoids a no-param-reassign warning
162
+ let transaction = tr;
163
+ // Create a table map to understand the table structure
164
+ const tableMap = TableMap.get(tableNode);
165
+ const columnCount = tableMap.width;
166
+
167
+ if (columnCount === 0) return tr;
168
+
169
+ // Calculate total width from existing column widths
170
+ const currColwidths = getColwidths({
171
+ tableNode,
172
+ cellMinWidth,
173
+ columnCount,
174
+ });
175
+
176
+ if (!currColwidths.length) return tr;
177
+
178
+ const currWidth = currColwidths.reduce((sum, width) => sum + width, 0);
179
+
180
+ let totalWidth = currWidth;
181
+ if (useFullWidth || currWidth > maxWidth) {
182
+ totalWidth = maxWidth;
183
+ }
184
+
185
+ let colwidths = Array(columnCount).fill(0);
186
+
187
+ if (setEqualColumns) {
188
+ // Calculate equal width for each column
189
+ const equalColumnWidth = totalWidth / columnCount;
190
+ // Create an array of equal column widths
191
+ colwidths = colwidths.map(() => equalColumnWidth);
192
+ } else {
193
+ // Adjust column widths based on the new total width
194
+ colwidths = currColwidths.map((width) => (width / currWidth) * totalWidth);
195
+ }
196
+
197
+ // Now update all cells in the table to use the appropriate colwidth
198
+ // We need to traverse the entire table structure
199
+ let rowPos = tablePos + 1; // Start at the first row
200
+
201
+ // Process each row in the table
202
+ tableNode.forEach((row, rowOffset) => {
203
+ if (row.type.name !== "tableRow") return;
204
+
205
+ let cellPos = rowPos + 1; // Start at the first cell in this row
206
+ let colIndex = 0;
207
+
208
+ // Process each cell in the row
209
+ row.forEach((cell) => {
210
+ const colspan = cell.attrs.colspan || 1;
211
+
212
+ // For cells that span multiple columns, we need to sum the widths
213
+ const cellColwidths = colwidths.slice(colIndex, colIndex + colspan);
214
+
215
+ transaction = transaction.setNodeMarkup(cellPos, null, {
216
+ ...cell.attrs,
217
+ colwidth: cellColwidths,
218
+ });
219
+
220
+ // Move column index forward by the colspan
221
+ colIndex += colspan;
222
+
223
+ // Move to the next cell position
224
+ cellPos += cell.nodeSize;
225
+ });
226
+
227
+ // Move to the next row position
228
+ rowPos += row.nodeSize;
229
+ });
230
+
231
+ return transaction;
232
+ }
233
+
234
+ // helper that returns all found values of a given cell attribute
235
+ // this includes undefined values (these can be mapped to their defaults, ie vertical align: top)
236
+
237
+ function getCellAttributes(editor, attributeName) {
238
+ const selectedCells = findCellsInSelection(editor.state);
239
+ const foundValues = selectedCells.reduce((acc, cell) => {
240
+ const newVal = cell.attrs[attributeName];
241
+ if (acc.indexOf(newVal) > -1) return acc;
242
+ return [...acc, newVal];
243
+ }, []);
244
+ return foundValues;
245
+ }
246
+
247
+ /* summarises the alignments in the currently selected cells, accounts for unstyled cells */
248
+ export function getCellAlignment(editor) {
249
+ const alignments = getCellAttributes(editor, "verticalAlign");
250
+ // return value may contain falsey values, which equate to 'top'- map them, and rmeove any duplicates
251
+ const mappedAlignments = alignments
252
+ .map((v) => v || CELL_VALIGN_TOP)
253
+ .filter((v, index, all) => all.indexOf(v) === index);
254
+ if (mappedAlignments.length === 1) return mappedAlignments[0];
255
+ return CELL_VALIGN_MIXED;
256
+ }
257
+
258
+ /**
259
+ * Extension that adds equal column width functionality to tables
260
+ */
261
+ export const TableExtended = TableBase.extend({
262
+ addOptions() {
263
+ return {
264
+ ...this.parent?.(),
265
+ cellMinWidth: CELL_MIN_WIDTH,
266
+ lastColumnResizable: true,
267
+ resizable: true,
268
+ };
269
+ },
270
+ addCommands() {
271
+ return {
272
+ ...this.parent?.(),
273
+ /**
274
+ * Set equal widths for all columns in the current table based on the current total width
275
+ */
276
+ setTableEqualColumnWidths:
277
+ () =>
278
+ ({ state, tr, dispatch, editor }) => {
279
+ const table = findTable(state);
280
+
281
+ if (!table) return false;
282
+
283
+ const tableDom = editor.editorView.domAtPos(table.pos);
284
+
285
+ const newTr = setColumnWidths({
286
+ tr,
287
+ table,
288
+ setEqualColumns: true,
289
+ maxWidth: getDomWidth(tableDom.node),
290
+ cellMinWidth: this.options.cellMinWidth,
291
+ });
292
+
293
+ if (dispatch) {
294
+ dispatch(newTr);
295
+ }
296
+
297
+ return true;
298
+ },
299
+ setFullWidth:
300
+ () =>
301
+ ({ state, tr, dispatch, editor }) => {
302
+ const table = findTable(state);
303
+
304
+ if (!table) return false;
305
+
306
+ const tableDom = editor.editorView.domAtPos(table.pos);
307
+
308
+ const newTr = setColumnWidths({
309
+ tr,
310
+ table,
311
+ useFullWidth: true,
312
+ maxWidth: getDomWidth(tableDom.node),
313
+ cellMinWidth: this.options.cellMinWidth,
314
+ });
315
+
316
+ if (dispatch) {
317
+ dispatch(newTr);
318
+ }
319
+
320
+ return true;
321
+ },
322
+ cleanWidth:
323
+ () =>
324
+ ({ state, tr, dispatch, editor }) => {
325
+ const table = findTable(state);
326
+
327
+ if (!table) return false;
328
+
329
+ const tableDom = editor.editorView.domAtPos(table.pos);
330
+ const tableWidth = getTableWidth({
331
+ tableNode: table.node,
332
+ cellMinWidth: this.options.cellMinWidth,
333
+ });
334
+
335
+ const availableWidth = getDomWidth(tableDom.node);
336
+
337
+ if (tableWidth <= availableWidth) {
338
+ dispatch(tr);
339
+ return true;
340
+ }
341
+
342
+ const newTr = setColumnWidths({
343
+ tr,
344
+ table,
345
+ maxWidth: availableWidth,
346
+ cellMinWidth: this.options.cellMinWidth,
347
+ });
348
+
349
+ if (dispatch) {
350
+ dispatch(newTr);
351
+ }
352
+
353
+ return true;
354
+ },
355
+ applyStyleToCells:
356
+ (style) =>
357
+ ({ tr, state, dispatch }) => {
358
+ const cells = findCellsInSelection(state);
359
+
360
+ if (cells.length === 0) return false;
361
+
362
+ const newTr = tr.setNodes(
363
+ cells.map(({ node }) => ({
364
+ node,
365
+ attrs: {
366
+ ...node.attrs,
367
+ style: style,
368
+ },
369
+ })),
370
+ );
371
+
372
+ if (dispatch) {
373
+ dispatch(newTr);
374
+ }
375
+
376
+ return true;
377
+ },
378
+ /**
379
+ * Find the current table if the selection is inside one
380
+ */
381
+ findTable:
382
+ () =>
383
+ ({ state }) => {
384
+ return findTable(state);
385
+ },
386
+
387
+ /**
388
+ * Find all cells in the current selection
389
+ */
390
+ findCellsInSelection:
391
+ () =>
392
+ ({ state }) => {
393
+ return findCellsInSelection(state);
394
+ },
395
+ };
396
+ },
397
+ });
@@ -0,0 +1,99 @@
1
+ import { TableCell as TableCellBase } from "@tiptap/extension-table";
2
+ import { TableHeader as TableHeaderBase } from "@tiptap/extension-table";
3
+ import { hexIsDark } from "../../utils/color-utils";
4
+
5
+ const tableCellAttributes = {
6
+ colwidth: {
7
+ default: null,
8
+ parseHTML: (element) => {
9
+ const colwidth = element.getAttribute("colwidth");
10
+ const width = element.getAttribute("width");
11
+
12
+ if (!colwidth && width) {
13
+ return null;
14
+ }
15
+
16
+ const value = colwidth
17
+ ? colwidth.split(",").map((width) => parseInt(width, 10))
18
+ : null;
19
+
20
+ return value;
21
+ },
22
+ renderHTML: ({ width, colwidth }) => {
23
+ if (!colwidth && width) {
24
+ return { width, colwidth: null };
25
+ }
26
+
27
+ return {
28
+ colwidth: colwidth ? colwidth.join(",") : null,
29
+ width: null,
30
+ };
31
+ },
32
+ },
33
+ // Strategy Recs can receive tables, but the width might be percentages, and will
34
+ // be saved to width instead of colwidth. This allows it to be passed straight through.
35
+ width: {
36
+ default: null,
37
+ parseHTML: (element) => {
38
+ return element.getAttribute("width") || null;
39
+ },
40
+ renderHTML: ({ width, colwidth }) => {
41
+ if (colwidth) return { colwidth, width: null };
42
+
43
+ return {
44
+ width,
45
+ };
46
+ },
47
+ },
48
+ backgroundColor: {
49
+ default: null,
50
+ parseHTML: (element) => element.getAttribute("data-background-color"),
51
+ renderHTML: ({ backgroundColor }) => {
52
+ const attrs = {
53
+ "data-background-color": backgroundColor,
54
+ };
55
+
56
+ if (backgroundColor) {
57
+ attrs.style = `background-color: ${backgroundColor};`;
58
+ attrs.class = hexIsDark(backgroundColor)
59
+ ? "tiptap-table-cell-dark"
60
+ : "tiptap-table-cell-light";
61
+ }
62
+
63
+ return attrs;
64
+ },
65
+ },
66
+ verticalAlign: {
67
+ default: null,
68
+ parseHTML: (element) => element.getAttribute("data-vertical-align"),
69
+ renderHTML: ({ verticalAlign }) => {
70
+ const attrs = {
71
+ "data-vertical-align": verticalAlign,
72
+ };
73
+
74
+ if (verticalAlign) {
75
+ attrs.style = `vertical-align: ${verticalAlign};`;
76
+ }
77
+
78
+ return attrs;
79
+ },
80
+ },
81
+ };
82
+
83
+ export const TableCell = TableCellBase.extend({
84
+ addAttributes() {
85
+ return {
86
+ ...this.parent?.(),
87
+ ...tableCellAttributes,
88
+ };
89
+ },
90
+ });
91
+
92
+ export const TableHeader = TableHeaderBase.extend({
93
+ addAttributes() {
94
+ return {
95
+ ...this.parent?.(),
96
+ ...tableCellAttributes,
97
+ };
98
+ },
99
+ });
@@ -0,0 +1,84 @@
1
+ export function getContentActualDimensions() {
2
+ const slideshow = document.getElementById("slideshow");
3
+ const slideshowBounds = slideshow.getBoundingClientRect();
4
+ return { width: slideshowBounds.width, height: slideshowBounds.height };
5
+ }
6
+
7
+ export function getContentDimensions() {
8
+ const slideshow = document.getElementById("slideshow");
9
+ const slideshowStyle = slideshow.style;
10
+
11
+ const slideshowWidth = slideshowStyle
12
+ .getPropertyValue("width")
13
+ ?.replace("px", "");
14
+
15
+ const slideshowHeight = slideshowStyle
16
+ .getPropertyValue("height")
17
+ ?.replace("px", "");
18
+
19
+ return {
20
+ width: slideshowWidth ? parseInt(slideshowWidth, 10) : null,
21
+ height: slideshowHeight ? parseInt(slideshowHeight, 10) : null,
22
+ };
23
+ }
24
+
25
+ export function getContentRatio() {
26
+ const { width: actualWidth } = getContentActualDimensions();
27
+ const { width: rawWidth } = getContentDimensions();
28
+
29
+ return rawWidth && actualWidth ? actualWidth / rawWidth : 1;
30
+ }
31
+
32
+ export function getDomWidth(dom) {
33
+ const bounds = dom?.getBoundingClientRect() || { width: 0 };
34
+ return bounds.width ? bounds.width / getContentRatio() : null;
35
+ }
36
+
37
+ /**
38
+ * Tests if the element is contained within the bounds of a mask element
39
+ * and returns appropriate rect combining the element and mask rects.
40
+ *
41
+ * If the test is outside of the vertical limits of the mask, the mask's
42
+ * top, bottom and height will replace the testRect's, while textRect
43
+ * maintains its width, left and right.
44
+ *
45
+ * @param {Object} element - return of getBoundingClientRect()
46
+ * @param {Object} parent - return of getBoundingClientRect()
47
+ * @returns {Object}
48
+ */
49
+ export const getTestRect = (testRect, maskRect) => {
50
+ const newRect = {
51
+ y: testRect.y,
52
+ x: testRect.x,
53
+ width: testRect.width,
54
+ height: testRect.height,
55
+ top: testRect.top,
56
+ bottom: testRect.bottom,
57
+ left: testRect.left,
58
+ right: testRect.right,
59
+ };
60
+
61
+ if (testRect.top < maskRect.top) {
62
+ newRect.y = maskRect.y;
63
+ newRect.top = maskRect.top;
64
+ newRect.height = testRect.height - (maskRect.top - testRect.top);
65
+ }
66
+
67
+ if (testRect.bottom > maskRect.bottom) {
68
+ newRect.bottom = maskRect.bottom;
69
+ newRect.height = testRect.height - (testRect.bottom - maskRect.bottom);
70
+ }
71
+
72
+ if (testRect.left < maskRect.left) {
73
+ newRect.x = maskRect.x;
74
+ newRect.left = maskRect.left;
75
+ newRect.width = testRect.width - (maskRect.left - testRect.left);
76
+ }
77
+
78
+ if (testRect.right > maskRect.right) {
79
+ newRect.right = maskRect.right;
80
+ newRect.width = testRect.width - (testRect.right - maskRect.right);
81
+ }
82
+
83
+ return newRect;
84
+ };
@@ -0,0 +1,130 @@
1
+ import {
2
+ DEFAULT_TOOLBAR_GROUPS,
3
+ PRIMARY_TOOLBAR_IDS,
4
+ TABLE_TOOLBAR_IDS,
5
+ } from "../constants";
6
+
7
+ const generateGroup = (id, { label = null, items = [], options = {} } = {}) => {
8
+ if (items.length === 0) return null;
9
+
10
+ return {
11
+ id,
12
+ label,
13
+ items: items.map((generator) => generator(options)),
14
+ };
15
+ };
16
+
17
+ /**
18
+ * List of baked-in toolbar ids,
19
+ * these cannot be used when creating custom options.
20
+ */
21
+ const reservedIds = [
22
+ ...Object.values(PRIMARY_TOOLBAR_IDS),
23
+ ...Object.values(TABLE_TOOLBAR_IDS),
24
+ ];
25
+
26
+ /**
27
+ * Custom options (aka. items) can either be a function that returns an item object,
28
+ * of the final object. Supplying a function allows for options like 'textStyles' and 'colors'
29
+ * to be passed to the user when generating the option.
30
+ *
31
+ * This function tests the validity of the supplied item, and returns a function
32
+ * that can be used by the "generateGroup" function to create its items.
33
+ *
34
+ * @param {Object|Function} item
35
+ * @returns {Function} - function that returns an item object.
36
+ */
37
+ const getCustomGenerator = (item, options = {}) => {
38
+ if (typeof item !== "function" && !(item.constructor === Object)) {
39
+ throw new Error(
40
+ "An custom toolbar option must be a function or an object.",
41
+ );
42
+ }
43
+
44
+ let testItem = item;
45
+
46
+ if (typeof item === "function") {
47
+ testItem = item(options);
48
+ }
49
+
50
+ const { id, command, renderItem } = testItem;
51
+
52
+ if (!id) {
53
+ throw new Error("An id is required when creating a custom toolbar option.");
54
+ }
55
+
56
+ if (reservedIds.includes(id)) {
57
+ throw new Error(
58
+ `Custom id '${id}' is not allowed as it is used by the default toolbar.\nReserved ids: ${reservedIds.join(
59
+ ", ",
60
+ )}.`,
61
+ );
62
+ }
63
+
64
+ if (typeof command !== "function" && typeof renderItem !== "function") {
65
+ throw new Error(
66
+ "When creating a custom toolbar option, a command or renderItem function must be provided.",
67
+ );
68
+ }
69
+
70
+ return typeof item === "function" ? item : () => item;
71
+ };
72
+
73
+ export const generateToolbarConfiguration = ({
74
+ toolbar = [],
75
+ itemGenerators = {},
76
+ textStyles = [],
77
+ colors = [],
78
+ } = {}) => {
79
+ const options = {
80
+ textStyles,
81
+ colors,
82
+ };
83
+
84
+ const generateItem = (item) => {
85
+ if (typeof item === "string" && itemGenerators[item]) {
86
+ return itemGenerators[item];
87
+ }
88
+
89
+ // Assumes custom item has been supplied
90
+ return getCustomGenerator(item, options);
91
+ };
92
+
93
+ return toolbar
94
+ .map((group) => {
95
+ if (typeof group === "string") {
96
+ if (!DEFAULT_TOOLBAR_GROUPS[group]) {
97
+ throw new Error(
98
+ `Requested default group '${group}' is not found. Available groups: ${Object.keys(
99
+ DEFAULT_TOOLBAR_GROUPS,
100
+ ).join(", ")}`,
101
+ );
102
+ }
103
+
104
+ const { label, items = [] } = DEFAULT_TOOLBAR_GROUPS[group];
105
+ return generateGroup(group, {
106
+ label,
107
+ items: items.map(generateItem).filter(Boolean),
108
+ options,
109
+ });
110
+ }
111
+
112
+ // Is a list of items instead of a pre-defined group
113
+ if (Array.isArray(group)) {
114
+ const id = group.join("_");
115
+ return generateGroup(id, {
116
+ items: group.map(generateItem).filter(Boolean),
117
+ options,
118
+ });
119
+ }
120
+
121
+ throw new Error(
122
+ "Toolbar options must be either an existing id (supplied as string) or an array of items. Received: " +
123
+ JSON.stringify(group) +
124
+ " of type " +
125
+ typeof group +
126
+ ".",
127
+ );
128
+ })
129
+ .filter(Boolean);
130
+ };