@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,96 @@
1
+ import { tableItemGenerators } from "./table-toolbar-configuration";
2
+ import { generateToolbarConfiguration } from "./generate-toolbar-configuration";
3
+ import { TABLE, TOOLBAR_SCOPES } from "../constants";
4
+ import { primaryItemGenerators } from "./toolbar-configuration";
5
+
6
+ const testUniqueIds = (options) => {
7
+ const allIds = options.flatMap(({ items = [] } = {}) =>
8
+ items.map(({ id }) => id),
9
+ );
10
+
11
+ const groupedIds = allIds.reduce((acc, id) => {
12
+ acc[id] = acc[id] || [];
13
+ acc[id].push(id);
14
+ return acc;
15
+ }, {});
16
+
17
+ for (const ids of Object.values(groupedIds)) {
18
+ if (ids.length > 1) {
19
+ throw new Error(`Toolbar IDs must be unique, duplicate found: ${ids[0]}`);
20
+ }
21
+ }
22
+ };
23
+
24
+ export const generateToolbarOptions = ({
25
+ toolbarConfig = [],
26
+ tableToolbarConfig = [],
27
+ textStyles = [],
28
+ colors = [],
29
+ } = {}) => {
30
+ const includeTableEditor = toolbarConfig.includes(TABLE);
31
+ const options = { textStyles, colors };
32
+
33
+ const primaryOptions = generateToolbarConfiguration({
34
+ toolbar: toolbarConfig,
35
+ itemGenerators: primaryItemGenerators,
36
+ ...options,
37
+ });
38
+
39
+ const allTableOptions = generateToolbarConfiguration({
40
+ toolbar: tableToolbarConfig,
41
+ itemGenerators: tableItemGenerators,
42
+ ...options,
43
+ });
44
+
45
+ // Split table options into table-wide vs. cell based on scope
46
+ let tableOptions = [];
47
+ let tableCellOptions = [];
48
+
49
+ if (includeTableEditor) {
50
+ for (const {
51
+ id: groupId,
52
+ items = [],
53
+ label: groupLabel = null,
54
+ } of allTableOptions) {
55
+ for (const { scope, ...item } of items) {
56
+ // Target defaults to table-wide if not supplied
57
+ const target =
58
+ scope === TOOLBAR_SCOPES.TABLE_CELL ? tableCellOptions : tableOptions;
59
+ const existingGroup = target.find((g) => g.id === groupId);
60
+
61
+ if (existingGroup) {
62
+ existingGroup.items.push(item);
63
+ continue;
64
+ }
65
+
66
+ target.push({ id: groupId, label: groupLabel, items: [item] });
67
+ }
68
+ }
69
+ }
70
+
71
+ const allOptions = [...primaryOptions, ...tableOptions, ...tableCellOptions];
72
+ testUniqueIds(allOptions);
73
+
74
+ // Remove duplicate extensions
75
+ const extensions = allOptions
76
+ .flatMap((group) => {
77
+ const groupExtensions = group.extensions || [];
78
+ const itemExtensions = group.items.flatMap(
79
+ (item) => item.extensions || [],
80
+ );
81
+ return [...groupExtensions, ...itemExtensions].reduce(
82
+ (extensions, extension) => {
83
+ // Remove duplicate extensions
84
+ if (extensions.some(({ name }) => extension.name === name)) {
85
+ return extensions;
86
+ }
87
+
88
+ return [...extensions, extension];
89
+ },
90
+ [],
91
+ );
92
+ })
93
+ .filter(Boolean);
94
+
95
+ return { primaryOptions, tableOptions, tableCellOptions, extensions };
96
+ };
@@ -0,0 +1,187 @@
1
+ import React from "react";
2
+ import icons from "../icons";
3
+ import {
4
+ TOOLBAR_COMPONENT_TYPES,
5
+ TABLE_TOOLBAR_IDS,
6
+ TOOLBAR_SCOPES,
7
+ } from "../constants";
8
+ import { ColorPicker } from "../components/color-picker/ColorPicker";
9
+ import { VerticalAlignToggle } from "../components/VerticalAlignToggle";
10
+ import { findCellsInSelection } from "../components/tiptap/Table";
11
+ import { generateVerticalAlignmentIcon } from "../utils/generate-vertical-alignment-icon";
12
+
13
+ // Table-wide options
14
+ const fullWidthToggle = () => ({
15
+ id: TABLE_TOOLBAR_IDS.FULL_WIDTH,
16
+ label: "Fit width",
17
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
18
+ command: (editor) => editor.chain().focus().setFullWidth().run(),
19
+ icon: <icons.FitWidthArrows />,
20
+ scope: TOOLBAR_SCOPES.TABLE,
21
+ });
22
+
23
+ const distributeColumnsButton = () => ({
24
+ id: TABLE_TOOLBAR_IDS.EQUAL_WIDTH_COLUMNS,
25
+ label: "Distribute columns",
26
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
27
+ command: (editor) => editor.chain().focus().setTableEqualColumnWidths().run(),
28
+ icon: <icons.EvenlyDistribute />,
29
+ scope: TOOLBAR_SCOPES.TABLE,
30
+ });
31
+
32
+ const deleteTableButton = () => ({
33
+ id: TABLE_TOOLBAR_IDS.DELETE_TABLE,
34
+ label: "Delete table",
35
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
36
+ command: (editor) => editor.chain().focus().deleteTable().run(),
37
+ icon: <icons.Delete />,
38
+ scope: TOOLBAR_SCOPES.TABLE,
39
+ });
40
+
41
+ // Internal table options (eg. cell, row and column)
42
+ const addColumnLeftButton = () => ({
43
+ id: TABLE_TOOLBAR_IDS.ADD_COLUMN_LEFT,
44
+ label: "Add column left",
45
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
46
+ command: (editor) =>
47
+ editor.chain().focus().addColumnBefore().cleanWidth().run(),
48
+ icon: <icons.AddColumnLeft />,
49
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
50
+ });
51
+
52
+ const addColumnRightButton = () => ({
53
+ id: TABLE_TOOLBAR_IDS.ADD_COLUMN_RIGHT,
54
+ label: "Add column right",
55
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
56
+ command: (editor) =>
57
+ editor.chain().focus().addColumnAfter().cleanWidth().run(),
58
+ icon: <icons.AddColumnRight />,
59
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
60
+ });
61
+
62
+ const removeColumnButton = () => ({
63
+ id: TABLE_TOOLBAR_IDS.REMOVE_COLUMN,
64
+ label: "Remove column",
65
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
66
+ command: (editor) => editor.chain().focus().deleteColumn().run(),
67
+ icon: <icons.RemoveColumnOutline />,
68
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
69
+ });
70
+
71
+ const addRowAboveButton = () => ({
72
+ id: TABLE_TOOLBAR_IDS.ADD_ROW_ABOVE,
73
+ label: "Add row above",
74
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
75
+ command: (editor) => editor.chain().focus().addRowBefore().run(),
76
+ icon: <icons.AddRowAbove />,
77
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
78
+ });
79
+
80
+ const addRowBelowButton = () => ({
81
+ id: TABLE_TOOLBAR_IDS.ADD_ROW_BELOW,
82
+ label: "Add row below",
83
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
84
+ command: (editor) => editor.chain().focus().addRowAfter().run(),
85
+ icon: <icons.AddRowBelow />,
86
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
87
+ });
88
+
89
+ const removeRowButton = () => ({
90
+ id: TABLE_TOOLBAR_IDS.REMOVE_ROW,
91
+ label: "Remove row",
92
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
93
+ command: (editor) => editor.chain().focus().deleteRow().run(),
94
+ icon: <icons.RemoveRowOutline />,
95
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
96
+ });
97
+
98
+ const distributeSelectedColumnsButton = () => ({
99
+ id: TABLE_TOOLBAR_IDS.EQUAL_WIDTH_SELECTION,
100
+ label: "Set equal column widths",
101
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
102
+ command: (editor) => editor.chain().focus().setTableEqualColumnWidths().run(),
103
+ icon: <icons.EvenlyDistribute />,
104
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
105
+ });
106
+
107
+ const mergeCellsButton = () => ({
108
+ id: TABLE_TOOLBAR_IDS.MERGE_CELLS,
109
+ label: "Merge cells",
110
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
111
+ enabled: (editor) => editor.can().mergeCells(),
112
+ command: (editor) => editor.chain().focus().mergeCells().run(),
113
+ icon: <icons.MergeCells />,
114
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
115
+ });
116
+
117
+ const splitCellsButton = () => ({
118
+ id: TABLE_TOOLBAR_IDS.SPLIT_CELLS,
119
+ label: "Split cell",
120
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
121
+ enabled: (editor) => editor.can().splitCell(),
122
+ command: (editor) => editor.chain().focus().splitCell().run(),
123
+ icon: <icons.SplitCells />,
124
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
125
+ });
126
+
127
+ const cellBgColorPicker = ({ colors }) => ({
128
+ id: TABLE_TOOLBAR_IDS.CELL_BG_COLOR,
129
+ label: "Cell colour",
130
+ icon: <icons.FormatColorFill />,
131
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
132
+ type: TOOLBAR_COMPONENT_TYPES.SUB,
133
+ items: [{ id: "color-picker" }],
134
+ renderItem: (item, { editor } = {}) => (
135
+ <ColorPicker
136
+ colors={colors}
137
+ onChange={(color) =>
138
+ editor.chain().focus().setCellAttribute("backgroundColor", color).run()
139
+ }
140
+ />
141
+ ),
142
+ });
143
+
144
+ const cellValignPicker = () => ({
145
+ id: TABLE_TOOLBAR_IDS.CELL_VALIGN,
146
+ label: "Cell alignment",
147
+ renderIcon: generateVerticalAlignmentIcon,
148
+ scope: TOOLBAR_SCOPES.TABLE_CELL,
149
+ type: TOOLBAR_COMPONENT_TYPES.SUB,
150
+ items: [{ id: "vertical-align" }],
151
+ value: (editor) => {
152
+ const cells = findCellsInSelection(editor.state);
153
+ if (!cells) return null;
154
+ return cells[0]?.attrs?.verticalAlign;
155
+ },
156
+ renderItem: (item, { editor, editorState, value } = {}) => (
157
+ <VerticalAlignToggle
158
+ id={TABLE_TOOLBAR_IDS.CELL_VALIGN}
159
+ value={value}
160
+ onChange={(alignment) =>
161
+ editor
162
+ .chain()
163
+ .focus()
164
+ .setCellAttribute("verticalAlign", alignment)
165
+ .run()
166
+ }
167
+ />
168
+ ),
169
+ });
170
+ export const tableItemGenerators = {
171
+ // Table-wide options
172
+ [TABLE_TOOLBAR_IDS.FULL_WIDTH]: fullWidthToggle,
173
+ [TABLE_TOOLBAR_IDS.EQUAL_WIDTH_COLUMNS]: distributeColumnsButton,
174
+ [TABLE_TOOLBAR_IDS.DELETE_TABLE]: deleteTableButton,
175
+ // Internal table options
176
+ [TABLE_TOOLBAR_IDS.ADD_COLUMN_LEFT]: addColumnLeftButton,
177
+ [TABLE_TOOLBAR_IDS.ADD_COLUMN_RIGHT]: addColumnRightButton,
178
+ [TABLE_TOOLBAR_IDS.REMOVE_COLUMN]: removeColumnButton,
179
+ [TABLE_TOOLBAR_IDS.ADD_ROW_ABOVE]: addRowAboveButton,
180
+ [TABLE_TOOLBAR_IDS.ADD_ROW_BELOW]: addRowBelowButton,
181
+ [TABLE_TOOLBAR_IDS.REMOVE_ROW]: removeRowButton,
182
+ [TABLE_TOOLBAR_IDS.EQUAL_WIDTH_SELECTION]: distributeSelectedColumnsButton,
183
+ [TABLE_TOOLBAR_IDS.MERGE_CELLS]: mergeCellsButton,
184
+ [TABLE_TOOLBAR_IDS.SPLIT_CELLS]: splitCellsButton,
185
+ [TABLE_TOOLBAR_IDS.CELL_BG_COLOR]: cellBgColorPicker,
186
+ [TABLE_TOOLBAR_IDS.CELL_VALIGN]: cellValignPicker,
187
+ };
@@ -0,0 +1,330 @@
1
+ import React from "react";
2
+
3
+ import Bold from "@tiptap/extension-bold";
4
+ import Italic from "@tiptap/extension-italic";
5
+ import Link from "@tiptap/extension-link";
6
+ import { BulletList, OrderedList } from "@tiptap/extension-list";
7
+ import { ListItem } from "../components/tiptap/ListItem";
8
+ import Subscript from "@tiptap/extension-subscript";
9
+ import Superscript from "@tiptap/extension-superscript";
10
+ import TextAlign from "@tiptap/extension-text-align";
11
+ import { Heading } from "@tiptap/extension-heading";
12
+ import Underline from "@tiptap/extension-underline";
13
+ import { UndoRedo } from "@tiptap/extensions";
14
+
15
+ import icons from "../icons";
16
+ import { TableRow } from "@tiptap/extension-table";
17
+ import { TextStyle, Color } from "@tiptap/extension-text-style";
18
+ import { TableCell, TableHeader } from "../components/tiptap/TableCells";
19
+ import { TableExtended } from "../components/tiptap/Table";
20
+ import { Select } from "../components/Select/Select";
21
+ import { PRIMARY_TOOLBAR_IDS, TOOLBAR_COMPONENT_TYPES } from "../constants";
22
+ import LinkEditDialog from "../components/LinkEditDialog";
23
+ import { Gapcursor } from "@tiptap/extensions";
24
+ import { ColorPickerCombo } from "../components/color-picker/ColorPickerCombo";
25
+
26
+ function generateTextStyleTest(textStyles) {
27
+ return (editor) => {
28
+ // test for level zero aka paragraph last
29
+ const sortedTextStyles = [...textStyles].sort(
30
+ (a, b) => Number(b.block) - Number(a.block),
31
+ );
32
+ const match = sortedTextStyles.find((ts) => {
33
+ const level = Number(ts.block);
34
+ if (level > 0) return editor.isActive("heading", { level });
35
+ return editor.isActive("paragraph");
36
+ });
37
+ return match?.block;
38
+ };
39
+ }
40
+
41
+ const undoButton = () => ({
42
+ id: PRIMARY_TOOLBAR_IDS.UNDO,
43
+ label: "Undo",
44
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
45
+ extensions: [UndoRedo],
46
+ enabled: (editor) => editor.can().chain().focus().undo().run(),
47
+ command: (editor) => editor.chain().focus().undo().run(),
48
+ icon: <icons.Undo />,
49
+ });
50
+
51
+ const redoButton = () => ({
52
+ id: PRIMARY_TOOLBAR_IDS.REDO,
53
+ label: "Redo",
54
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
55
+ extensions: [UndoRedo],
56
+ enabled: (editor) => editor.can().chain().focus().redo().run(),
57
+ command: (editor) => editor.chain().focus().redo().run(),
58
+ icon: <icons.Redo />,
59
+ });
60
+
61
+ const textStyleSelect = ({ textStyles = [] } = {}) => ({
62
+ id: PRIMARY_TOOLBAR_IDS.HEADING,
63
+ label: "Text style",
64
+ type: TOOLBAR_COMPONENT_TYPES.CUSTOM,
65
+ extensions: [Heading],
66
+ value: generateTextStyleTest(textStyles),
67
+ renderContent: ({ editor, value }) => {
68
+ const options = textStyles.map(({ block: value, title: label }) => ({
69
+ value,
70
+ label,
71
+ }));
72
+
73
+ const onChange = (value) => {
74
+ const level = Number(value);
75
+ if (!value || level === 0)
76
+ return editor.chain().focus().setParagraph().run();
77
+
78
+ return editor
79
+ .chain()
80
+ .focus()
81
+ .toggleHeading({ level: Number(value) })
82
+ .run();
83
+ };
84
+
85
+ return (
86
+ <Select
87
+ label="Text style"
88
+ placeholder="Text style"
89
+ options={[options]}
90
+ value={value}
91
+ onValueChange={onChange}
92
+ />
93
+ );
94
+ },
95
+ });
96
+
97
+ const CustomLinkExtension = Link.configure({
98
+ linkOnPaste: true,
99
+ enableClickSelection: true,
100
+ openOnClick: false,
101
+ protocols: ["http", "https", "slide"],
102
+ });
103
+
104
+ const boldButton = () => ({
105
+ id: PRIMARY_TOOLBAR_IDS.BOLD,
106
+ label: "Bold",
107
+ type: TOOLBAR_COMPONENT_TYPES.TOGGLE,
108
+ extensions: [Bold],
109
+ icon: <icons.FormatBold />,
110
+ active: (editor) => editor.isActive("bold"),
111
+ command: (editor) => editor.chain().focus().toggleBold().run(),
112
+ });
113
+
114
+ const italicButton = () => ({
115
+ id: PRIMARY_TOOLBAR_IDS.ITALIC,
116
+ label: "Italic",
117
+ type: TOOLBAR_COMPONENT_TYPES.TOGGLE,
118
+ extensions: [Italic],
119
+ icon: <icons.FormatItalic />,
120
+ active: (editor) => editor.isActive("italic"),
121
+ command: (editor) => editor.chain().focus().toggleItalic().run(),
122
+ });
123
+
124
+ const underlineButton = () => ({
125
+ id: PRIMARY_TOOLBAR_IDS.UNDERLINE,
126
+ label: "Underline",
127
+ type: TOOLBAR_COMPONENT_TYPES.TOGGLE,
128
+ extensions: [Underline],
129
+ icon: <icons.FormatUnderlined />,
130
+ active: (editor) => editor.isActive("underline"),
131
+ command: (editor) => editor.chain().focus().toggleUnderline().run(),
132
+ });
133
+
134
+ const superscriptButton = () => ({
135
+ id: PRIMARY_TOOLBAR_IDS.SUPERSCRIPT,
136
+ label: "Superscript",
137
+ type: TOOLBAR_COMPONENT_TYPES.TOGGLE,
138
+ extensions: [Superscript],
139
+ icon: <icons.Superscript />,
140
+ active: (editor) => editor.isActive("superscript"),
141
+ command: (editor) => editor.chain().focus().toggleSuperscript().run(),
142
+ });
143
+
144
+ const subscriptButton = () => ({
145
+ id: PRIMARY_TOOLBAR_IDS.SUBSCRIPT,
146
+ label: "Subscript",
147
+ type: TOOLBAR_COMPONENT_TYPES.TOGGLE,
148
+ extensions: [Subscript],
149
+ icon: <icons.Subscript />,
150
+ active: (editor) => editor.isActive("subscript"),
151
+ command: (editor) => editor.chain().focus().toggleSubscript().run(),
152
+ });
153
+
154
+ const textColorPicker = ({ colors } = {}) => ({
155
+ id: PRIMARY_TOOLBAR_IDS.TEXT_COLOR,
156
+ label: "Text colour",
157
+ type: TOOLBAR_COMPONENT_TYPES.CUSTOM,
158
+ extensions: [TextStyle, Color],
159
+ enabled: (editor) => editor.can().chain().focus().setColor("#000000").run(),
160
+ value: (editor) => editor.getAttributes("textStyle").color,
161
+ renderContent: ({ editor, value, isEnabled, userSelections = {} } = {}) => {
162
+ const { textColor = {} } = userSelections;
163
+ const { value: savedValue, setValue: setSavedValue } = textColor;
164
+
165
+ return (
166
+ <ColorPickerCombo
167
+ colors={colors}
168
+ value={savedValue}
169
+ pickerValue={value}
170
+ disabled={!isEnabled}
171
+ onChange={(color) => {
172
+ setSavedValue(color);
173
+ return editor.chain().focus().setColor(color).run();
174
+ }}
175
+ />
176
+ );
177
+ },
178
+ });
179
+
180
+ const alignmentToggleGroup = () => ({
181
+ id: PRIMARY_TOOLBAR_IDS.TEXT_ALIGN,
182
+ type: TOOLBAR_COMPONENT_TYPES.TOGGLE_GROUP,
183
+ extensions: [
184
+ TextAlign.configure({
185
+ types: ["heading", "paragraph"],
186
+ }),
187
+ ],
188
+ label: "Text alignment",
189
+ command: (editor, { value }) =>
190
+ editor.chain().focus().setTextAlign(value).run(),
191
+ value: (editor) => editor.isActive("textAlign"),
192
+ defaultValue: "left",
193
+ options: [
194
+ {
195
+ id: "align-left",
196
+ tooltip: "Left",
197
+ label: "Left align",
198
+ value: "left",
199
+ icon: <icons.FormatAlignLeft />,
200
+ },
201
+ {
202
+ id: "align-center",
203
+ tooltip: "Center",
204
+ label: "Center align",
205
+ value: "center",
206
+ icon: <icons.FormatAlignCenter />,
207
+ },
208
+ {
209
+ id: "align-right",
210
+ tooltip: "Right",
211
+ label: "Right align",
212
+ value: "right",
213
+ icon: <icons.FormatAlignRight />,
214
+ },
215
+ {
216
+ id: "align-justify",
217
+ tooltip: "Justify",
218
+ label: "Justify align",
219
+ value: "justify",
220
+ icon: <icons.FormatAlignJustify />,
221
+ },
222
+ ],
223
+ });
224
+
225
+ const bulletListButton = () => ({
226
+ id: PRIMARY_TOOLBAR_IDS.UNORDERED_LIST,
227
+ label: "Bullet List",
228
+ type: TOOLBAR_COMPONENT_TYPES.TOGGLE,
229
+ extensions: [ListItem, BulletList],
230
+ command: (editor) => editor.chain().focus().toggleBulletList().run(),
231
+ icon: <icons.FormatListBulleted />,
232
+ active: (editor) => editor.isActive("bulletList"),
233
+ });
234
+
235
+ const numberedListButton = () => ({
236
+ id: PRIMARY_TOOLBAR_IDS.ORDERED_LIST,
237
+ label: "Ordered List",
238
+ type: TOOLBAR_COMPONENT_TYPES.TOGGLE,
239
+ extensions: [ListItem, OrderedList],
240
+ command: (editor) => editor.chain().focus().toggleOrderedList().run(),
241
+ icon: <icons.FormatListNumbered />,
242
+ active: (editor) => editor.isActive("orderedList"),
243
+ });
244
+
245
+ const addLinkButton = () => {
246
+ const label = "Add / edit Link";
247
+
248
+ return {
249
+ id: PRIMARY_TOOLBAR_IDS.INSERT_LINK,
250
+ label,
251
+ type: TOOLBAR_COMPONENT_TYPES.CUSTOM,
252
+ extensions: [CustomLinkExtension],
253
+ value: (editor) => editor.getAttributes("link").href,
254
+ active: (editor) => editor.isActive("link"),
255
+ renderContent({ editor, isActive, value }) {
256
+ return (
257
+ <LinkEditDialog
258
+ icon={<icons.Link />}
259
+ label={label}
260
+ active={isActive}
261
+ value={value}
262
+ onChange={(url) =>
263
+ editor
264
+ .chain()
265
+ .focus()
266
+ .extendMarkRange("link")
267
+ .setLink({ href: url, target: "_blank" })
268
+ .run()
269
+ }
270
+ />
271
+ );
272
+ },
273
+ };
274
+ };
275
+
276
+ const removeLinkButton = () => ({
277
+ id: PRIMARY_TOOLBAR_IDS.REMOVE_LINK,
278
+ label: "Remove link",
279
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
280
+ extensions: [CustomLinkExtension],
281
+ command: (editor) => editor.chain().focus().unsetLink().run(),
282
+ enabled: (editor) => editor.isActive("link"),
283
+ icon: <icons.LinkOff />,
284
+ });
285
+
286
+ const clearFormattingButton = () => ({
287
+ id: PRIMARY_TOOLBAR_IDS.CLEAR_FORMATTING,
288
+ label: "Clear Formatting",
289
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
290
+ command: (editor) => editor.chain().focus().unsetAllMarks().run(),
291
+ icon: <icons.FormatClear />,
292
+ });
293
+
294
+ const insertTableButton = () => ({
295
+ id: PRIMARY_TOOLBAR_IDS.INSERT_TABLE,
296
+ label: "Insert Table",
297
+ type: TOOLBAR_COMPONENT_TYPES.BUTTON,
298
+ extensions: [TableExtended, TableCell, TableHeader, TableRow, Gapcursor],
299
+ command: (editor) =>
300
+ editor
301
+ .chain()
302
+ .focus()
303
+ .insertTable({
304
+ rows: 3,
305
+ cols: 3,
306
+ withHeaderRow: true,
307
+ })
308
+ .setFullWidth()
309
+ .run(),
310
+ icon: <icons.BorderAll />,
311
+ });
312
+
313
+ export const primaryItemGenerators = {
314
+ [PRIMARY_TOOLBAR_IDS.UNDO]: undoButton,
315
+ [PRIMARY_TOOLBAR_IDS.REDO]: redoButton,
316
+ [PRIMARY_TOOLBAR_IDS.HEADING]: textStyleSelect,
317
+ [PRIMARY_TOOLBAR_IDS.BOLD]: boldButton,
318
+ [PRIMARY_TOOLBAR_IDS.ITALIC]: italicButton,
319
+ [PRIMARY_TOOLBAR_IDS.UNDERLINE]: underlineButton,
320
+ [PRIMARY_TOOLBAR_IDS.SUPERSCRIPT]: superscriptButton,
321
+ [PRIMARY_TOOLBAR_IDS.SUBSCRIPT]: subscriptButton,
322
+ [PRIMARY_TOOLBAR_IDS.TEXT_COLOR]: textColorPicker,
323
+ [PRIMARY_TOOLBAR_IDS.TEXT_ALIGN]: alignmentToggleGroup,
324
+ [PRIMARY_TOOLBAR_IDS.UNORDERED_LIST]: bulletListButton,
325
+ [PRIMARY_TOOLBAR_IDS.ORDERED_LIST]: numberedListButton,
326
+ [PRIMARY_TOOLBAR_IDS.INSERT_LINK]: addLinkButton,
327
+ [PRIMARY_TOOLBAR_IDS.REMOVE_LINK]: removeLinkButton,
328
+ [PRIMARY_TOOLBAR_IDS.CLEAR_FORMATTING]: clearFormattingButton,
329
+ [PRIMARY_TOOLBAR_IDS.INSERT_TABLE]: insertTableButton,
330
+ };