@peak-ai/canvas 1.4.21 → 1.4.22

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 (213) hide show
  1. package/.babelrc +14 -0
  2. package/.eslintcache +1 -0
  3. package/.eslintignore +5 -0
  4. package/.eslintrc.js +29 -0
  5. package/{GrapesjsCanvas.js → dist/GrapesjsCanvas.js} +3 -2
  6. package/dist/GrapesjsCanvas.js.map +1 -0
  7. package/dist/package.json +62 -0
  8. package/{plugins → dist/plugins}/helpers/render-components.js +1 -1
  9. package/dist/plugins/helpers/render-components.js.map +1 -0
  10. package/dist/shadcn/components/ui/error-wrapper.js +2 -0
  11. package/dist/shadcn/components/ui/error-wrapper.js.map +1 -0
  12. package/package.json +45 -7
  13. package/scripts/build.ts +120 -0
  14. package/src/GrapesjsCanvas.tsx +494 -0
  15. package/src/constants/index.ts +25 -0
  16. package/src/declaration.d.ts +1 -0
  17. package/src/helpers/compiled-table.css +2429 -0
  18. package/src/helpers/css.ts +2667 -0
  19. package/src/helpers/date-picker.ts +807 -0
  20. package/src/helpers/filter-placeholder.ts +18 -0
  21. package/src/helpers/index.ts +13 -0
  22. package/src/helpers/merge-json.ts +106 -0
  23. package/src/index.styles.ts +58 -0
  24. package/src/index.ts +9 -0
  25. package/src/plugins/grapejs-plugin.tsx +196 -0
  26. package/src/plugins/helpers/custom-modal.tsx +123 -0
  27. package/src/plugins/helpers/data-table.tsx +300 -0
  28. package/src/plugins/helpers/extra.tsx +164 -0
  29. package/src/plugins/helpers/query-cache-context.tsx +154 -0
  30. package/src/plugins/helpers/query-cache-singleton.ts +176 -0
  31. package/src/plugins/helpers/query-cache-utils.ts +226 -0
  32. package/src/plugins/helpers/query-details-modal.tsx +400 -0
  33. package/src/plugins/helpers/query-heading-formatter.ts +24 -0
  34. package/src/plugins/helpers/query-loading-modal.tsx +94 -0
  35. package/src/plugins/helpers/render-components.tsx +1450 -0
  36. package/src/plugins/helpers/styled-info-button.tsx +504 -0
  37. package/src/public/canvas.css +42 -0
  38. package/src/public/components-css/table/table-output.css +2436 -0
  39. package/src/public/components-css/table/table.css +30 -0
  40. package/src/public/output.css +2465 -0
  41. package/src/public/table.css +135 -0
  42. package/src/shadcn/components/icons/AiAvatarIcon.tsx +47 -0
  43. package/src/shadcn/components/icons/Co_driver Expanding button copy.svg +21 -0
  44. package/src/shadcn/components/icons/ai-avatar.svg +7 -0
  45. package/src/shadcn/components/icons/thinking.gif +0 -0
  46. package/src/shadcn/components/ui/button.tsx +132 -0
  47. package/src/shadcn/components/ui/card.tsx +92 -0
  48. package/src/shadcn/components/ui/chart.tsx +324 -0
  49. package/src/shadcn/components/ui/checkbox.tsx +27 -0
  50. package/src/shadcn/components/ui/component-wrapper.tsx +61 -0
  51. package/src/shadcn/components/ui/date-filter.tsx +816 -0
  52. package/src/shadcn/components/ui/error-container.tsx +125 -0
  53. package/src/shadcn/components/ui/error-wrapper.tsx +99 -0
  54. package/src/shadcn/components/ui/filter.tsx +368 -0
  55. package/src/shadcn/components/ui/hover-card.tsx +36 -0
  56. package/src/shadcn/components/ui/input.tsx +20 -0
  57. package/src/shadcn/components/ui/label.tsx +24 -0
  58. package/src/shadcn/components/ui/pagination.tsx +213 -0
  59. package/src/shadcn/components/ui/scroll-area.tsx +59 -0
  60. package/src/shadcn/components/ui/search.tsx +150 -0
  61. package/src/shadcn/components/ui/separator.tsx +26 -0
  62. package/src/shadcn/components/ui/skeleton.tsx +69 -0
  63. package/src/shadcn/components/ui/table.tsx +196 -0
  64. package/src/shadcn/components/ui/tabs.tsx +55 -0
  65. package/src/shadcn/components/ui/textarea.tsx +18 -0
  66. package/src/shadcn/components/ui/tooltip.tsx +87 -0
  67. package/src/shadcn/utils.ts +6 -0
  68. package/src/types/grapesjs-tailwind.d.ts +61 -0
  69. package/src/types/images.d.ts +1 -0
  70. package/tailwind.config.js +5 -0
  71. package/tooling/tailwind-compiler/index.js +99 -0
  72. package/tooling/tailwind-compiler/package.json +11 -0
  73. package/tooling/tailwind-compiler/yarn.lock +123 -0
  74. package/tsconfig.build.json +15 -0
  75. package/tsconfig.json +8 -0
  76. package/GrapesjsCanvas.js.map +0 -1
  77. package/plugins/helpers/render-components.js.map +0 -1
  78. package/shadcn/components/ui/error-wrapper.js +0 -2
  79. package/shadcn/components/ui/error-wrapper.js.map +0 -1
  80. /package/{GrapesjsCanvas.d.ts → dist/GrapesjsCanvas.d.ts} +0 -0
  81. /package/{constants → dist/constants}/index.d.ts +0 -0
  82. /package/{constants → dist/constants}/index.js +0 -0
  83. /package/{constants → dist/constants}/index.js.map +0 -0
  84. /package/{declaration.d.js → dist/declaration.d.js} +0 -0
  85. /package/{declaration.d.js.map → dist/declaration.d.js.map} +0 -0
  86. /package/{helpers → dist/helpers}/compiled-table.css +0 -0
  87. /package/{helpers → dist/helpers}/css.d.ts +0 -0
  88. /package/{helpers → dist/helpers}/css.js +0 -0
  89. /package/{helpers → dist/helpers}/css.js.map +0 -0
  90. /package/{helpers → dist/helpers}/date-picker.d.ts +0 -0
  91. /package/{helpers → dist/helpers}/date-picker.js +0 -0
  92. /package/{helpers → dist/helpers}/date-picker.js.map +0 -0
  93. /package/{helpers → dist/helpers}/filter-placeholder.d.ts +0 -0
  94. /package/{helpers → dist/helpers}/filter-placeholder.js +0 -0
  95. /package/{helpers → dist/helpers}/filter-placeholder.js.map +0 -0
  96. /package/{helpers → dist/helpers}/index.d.ts +0 -0
  97. /package/{helpers → dist/helpers}/index.js +0 -0
  98. /package/{helpers → dist/helpers}/index.js.map +0 -0
  99. /package/{helpers → dist/helpers}/merge-json.d.ts +0 -0
  100. /package/{helpers → dist/helpers}/merge-json.js +0 -0
  101. /package/{helpers → dist/helpers}/merge-json.js.map +0 -0
  102. /package/{index.d.ts → dist/index.d.ts} +0 -0
  103. /package/{index.js → dist/index.js} +0 -0
  104. /package/{index.js.map → dist/index.js.map} +0 -0
  105. /package/{index.styles.d.ts → dist/index.styles.d.ts} +0 -0
  106. /package/{index.styles.js → dist/index.styles.js} +0 -0
  107. /package/{index.styles.js.map → dist/index.styles.js.map} +0 -0
  108. /package/{plugins → dist/plugins}/grapejs-plugin.d.ts +0 -0
  109. /package/{plugins → dist/plugins}/grapejs-plugin.js +0 -0
  110. /package/{plugins → dist/plugins}/grapejs-plugin.js.map +0 -0
  111. /package/{plugins → dist/plugins}/helpers/custom-modal.d.ts +0 -0
  112. /package/{plugins → dist/plugins}/helpers/custom-modal.js +0 -0
  113. /package/{plugins → dist/plugins}/helpers/custom-modal.js.map +0 -0
  114. /package/{plugins → dist/plugins}/helpers/data-table.d.ts +0 -0
  115. /package/{plugins → dist/plugins}/helpers/data-table.js +0 -0
  116. /package/{plugins → dist/plugins}/helpers/data-table.js.map +0 -0
  117. /package/{plugins → dist/plugins}/helpers/extra.d.ts +0 -0
  118. /package/{plugins → dist/plugins}/helpers/extra.js +0 -0
  119. /package/{plugins → dist/plugins}/helpers/extra.js.map +0 -0
  120. /package/{plugins → dist/plugins}/helpers/query-cache-context.d.ts +0 -0
  121. /package/{plugins → dist/plugins}/helpers/query-cache-context.js +0 -0
  122. /package/{plugins → dist/plugins}/helpers/query-cache-context.js.map +0 -0
  123. /package/{plugins → dist/plugins}/helpers/query-cache-singleton.d.ts +0 -0
  124. /package/{plugins → dist/plugins}/helpers/query-cache-singleton.js +0 -0
  125. /package/{plugins → dist/plugins}/helpers/query-cache-singleton.js.map +0 -0
  126. /package/{plugins → dist/plugins}/helpers/query-cache-utils.d.ts +0 -0
  127. /package/{plugins → dist/plugins}/helpers/query-cache-utils.js +0 -0
  128. /package/{plugins → dist/plugins}/helpers/query-cache-utils.js.map +0 -0
  129. /package/{plugins → dist/plugins}/helpers/query-details-modal.d.ts +0 -0
  130. /package/{plugins → dist/plugins}/helpers/query-details-modal.js +0 -0
  131. /package/{plugins → dist/plugins}/helpers/query-details-modal.js.map +0 -0
  132. /package/{plugins → dist/plugins}/helpers/query-heading-formatter.d.ts +0 -0
  133. /package/{plugins → dist/plugins}/helpers/query-heading-formatter.js +0 -0
  134. /package/{plugins → dist/plugins}/helpers/query-heading-formatter.js.map +0 -0
  135. /package/{plugins → dist/plugins}/helpers/query-loading-modal.d.ts +0 -0
  136. /package/{plugins → dist/plugins}/helpers/query-loading-modal.js +0 -0
  137. /package/{plugins → dist/plugins}/helpers/query-loading-modal.js.map +0 -0
  138. /package/{plugins → dist/plugins}/helpers/render-components.d.ts +0 -0
  139. /package/{plugins → dist/plugins}/helpers/styled-info-button.d.ts +0 -0
  140. /package/{plugins → dist/plugins}/helpers/styled-info-button.js +0 -0
  141. /package/{plugins → dist/plugins}/helpers/styled-info-button.js.map +0 -0
  142. /package/{shadcn → dist/shadcn}/components/icons/AiAvatarIcon.d.ts +0 -0
  143. /package/{shadcn → dist/shadcn}/components/icons/AiAvatarIcon.js +0 -0
  144. /package/{shadcn → dist/shadcn}/components/icons/AiAvatarIcon.js.map +0 -0
  145. /package/{shadcn → dist/shadcn}/components/icons/thinking.gif +0 -0
  146. /package/{shadcn → dist/shadcn}/components/ui/button.d.ts +0 -0
  147. /package/{shadcn → dist/shadcn}/components/ui/button.js +0 -0
  148. /package/{shadcn → dist/shadcn}/components/ui/button.js.map +0 -0
  149. /package/{shadcn → dist/shadcn}/components/ui/card.d.ts +0 -0
  150. /package/{shadcn → dist/shadcn}/components/ui/card.js +0 -0
  151. /package/{shadcn → dist/shadcn}/components/ui/card.js.map +0 -0
  152. /package/{shadcn → dist/shadcn}/components/ui/chart.d.ts +0 -0
  153. /package/{shadcn → dist/shadcn}/components/ui/chart.js +0 -0
  154. /package/{shadcn → dist/shadcn}/components/ui/chart.js.map +0 -0
  155. /package/{shadcn → dist/shadcn}/components/ui/checkbox.d.ts +0 -0
  156. /package/{shadcn → dist/shadcn}/components/ui/checkbox.js +0 -0
  157. /package/{shadcn → dist/shadcn}/components/ui/checkbox.js.map +0 -0
  158. /package/{shadcn → dist/shadcn}/components/ui/component-wrapper.d.ts +0 -0
  159. /package/{shadcn → dist/shadcn}/components/ui/component-wrapper.js +0 -0
  160. /package/{shadcn → dist/shadcn}/components/ui/component-wrapper.js.map +0 -0
  161. /package/{shadcn → dist/shadcn}/components/ui/date-filter.d.ts +0 -0
  162. /package/{shadcn → dist/shadcn}/components/ui/date-filter.js +0 -0
  163. /package/{shadcn → dist/shadcn}/components/ui/date-filter.js.map +0 -0
  164. /package/{shadcn → dist/shadcn}/components/ui/error-container.d.ts +0 -0
  165. /package/{shadcn → dist/shadcn}/components/ui/error-container.js +0 -0
  166. /package/{shadcn → dist/shadcn}/components/ui/error-container.js.map +0 -0
  167. /package/{shadcn → dist/shadcn}/components/ui/error-wrapper.d.ts +0 -0
  168. /package/{shadcn → dist/shadcn}/components/ui/filter.d.ts +0 -0
  169. /package/{shadcn → dist/shadcn}/components/ui/filter.js +0 -0
  170. /package/{shadcn → dist/shadcn}/components/ui/filter.js.map +0 -0
  171. /package/{shadcn → dist/shadcn}/components/ui/hover-card.d.ts +0 -0
  172. /package/{shadcn → dist/shadcn}/components/ui/hover-card.js +0 -0
  173. /package/{shadcn → dist/shadcn}/components/ui/hover-card.js.map +0 -0
  174. /package/{shadcn → dist/shadcn}/components/ui/input.d.ts +0 -0
  175. /package/{shadcn → dist/shadcn}/components/ui/input.js +0 -0
  176. /package/{shadcn → dist/shadcn}/components/ui/input.js.map +0 -0
  177. /package/{shadcn → dist/shadcn}/components/ui/label.d.ts +0 -0
  178. /package/{shadcn → dist/shadcn}/components/ui/label.js +0 -0
  179. /package/{shadcn → dist/shadcn}/components/ui/label.js.map +0 -0
  180. /package/{shadcn → dist/shadcn}/components/ui/pagination.d.ts +0 -0
  181. /package/{shadcn → dist/shadcn}/components/ui/pagination.js +0 -0
  182. /package/{shadcn → dist/shadcn}/components/ui/pagination.js.map +0 -0
  183. /package/{shadcn → dist/shadcn}/components/ui/scroll-area.d.ts +0 -0
  184. /package/{shadcn → dist/shadcn}/components/ui/scroll-area.js +0 -0
  185. /package/{shadcn → dist/shadcn}/components/ui/scroll-area.js.map +0 -0
  186. /package/{shadcn → dist/shadcn}/components/ui/search.d.ts +0 -0
  187. /package/{shadcn → dist/shadcn}/components/ui/search.js +0 -0
  188. /package/{shadcn → dist/shadcn}/components/ui/search.js.map +0 -0
  189. /package/{shadcn → dist/shadcn}/components/ui/separator.d.ts +0 -0
  190. /package/{shadcn → dist/shadcn}/components/ui/separator.js +0 -0
  191. /package/{shadcn → dist/shadcn}/components/ui/separator.js.map +0 -0
  192. /package/{shadcn → dist/shadcn}/components/ui/skeleton.d.ts +0 -0
  193. /package/{shadcn → dist/shadcn}/components/ui/skeleton.js +0 -0
  194. /package/{shadcn → dist/shadcn}/components/ui/skeleton.js.map +0 -0
  195. /package/{shadcn → dist/shadcn}/components/ui/table.d.ts +0 -0
  196. /package/{shadcn → dist/shadcn}/components/ui/table.js +0 -0
  197. /package/{shadcn → dist/shadcn}/components/ui/table.js.map +0 -0
  198. /package/{shadcn → dist/shadcn}/components/ui/tabs.d.ts +0 -0
  199. /package/{shadcn → dist/shadcn}/components/ui/tabs.js +0 -0
  200. /package/{shadcn → dist/shadcn}/components/ui/tabs.js.map +0 -0
  201. /package/{shadcn → dist/shadcn}/components/ui/textarea.d.ts +0 -0
  202. /package/{shadcn → dist/shadcn}/components/ui/textarea.js +0 -0
  203. /package/{shadcn → dist/shadcn}/components/ui/textarea.js.map +0 -0
  204. /package/{shadcn → dist/shadcn}/components/ui/tooltip.d.ts +0 -0
  205. /package/{shadcn → dist/shadcn}/components/ui/tooltip.js +0 -0
  206. /package/{shadcn → dist/shadcn}/components/ui/tooltip.js.map +0 -0
  207. /package/{shadcn → dist/shadcn}/utils.d.ts +0 -0
  208. /package/{shadcn → dist/shadcn}/utils.js +0 -0
  209. /package/{shadcn → dist/shadcn}/utils.js.map +0 -0
  210. /package/{types → dist/types}/grapesjs-tailwind.d.js +0 -0
  211. /package/{types → dist/types}/grapesjs-tailwind.d.js.map +0 -0
  212. /package/{types → dist/types}/images.d.js +0 -0
  213. /package/{types → dist/types}/images.d.js.map +0 -0
@@ -0,0 +1,494 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React, { useEffect, useRef } from 'react';
3
+ // eslint-disable-next-line import/no-named-as-default
4
+ import grapesjs, { Component, Editor } from 'grapesjs';
5
+ import 'grapesjs/dist/css/grapes.min.css';
6
+ import tailwind from 'grapesjs-tailwind';
7
+ import { TypesToRegister } from './constants';
8
+ import { toKebabCase } from './helpers';
9
+ import { createGrapesjsShadcnGenericPlugin } from './plugins/grapejs-plugin';
10
+ import { StyledEditor } from './index.styles';
11
+ import { tailwindCompiledCss } from './helpers/css';
12
+ import { datePickerCss } from './helpers/date-picker';
13
+
14
+ type NotificationHandler = (newData: {
15
+ componentId: string;
16
+ newProps: Record<string, any>;
17
+ newAttributes: Record<string, any>;
18
+ }) => boolean;
19
+
20
+ type GrapesjsCanvasProps = {
21
+ json: any;
22
+ mode?: 'editor' | 'preview';
23
+ setEditor: (editor: Editor) => void;
24
+ performInteraction: (payload: Record<string, any>) => Promise<Record<string, any> | undefined>;
25
+ newDataNotifier: (notifHandler: NotificationHandler) => void;
26
+ setHasChanged: (hasChanged: boolean) => void;
27
+ isStreaming: boolean;
28
+ };
29
+
30
+ function setComponentProperties(editor: Editor, isPreview: boolean) {
31
+ function setPropertiesRecursively(comp: Component) {
32
+ comp.set({
33
+ editable: !isPreview,
34
+ draggable: !isPreview,
35
+ droppable: !isPreview && comp.getName() !== 'Shadcn-generic',
36
+ selectable: !isPreview,
37
+ hoverable: !isPreview,
38
+ highlightable: !isPreview,
39
+ copyable: false,
40
+ resizable: false,
41
+ removable: !isPreview,
42
+ badgable: false,
43
+ });
44
+
45
+ if (!isPreview) {
46
+ comp.set('toolbar', [
47
+ { attributes: { class: 'fa fa-arrows' }, command: 'tlb-move' },
48
+ { attributes: { class: 'fa fa-trash' }, command: 'tlb-delete' },
49
+ ]);
50
+ }
51
+
52
+ comp.components().forEach(setPropertiesRecursively);
53
+ }
54
+
55
+ if (isPreview) {
56
+ editor.runCommand('core:preview');
57
+ } else {
58
+ editor.stopCommand('core:preview');
59
+ }
60
+
61
+ const wrapper = editor.getWrapper();
62
+ wrapper?.find('*').forEach(setPropertiesRecursively);
63
+ }
64
+
65
+ function convertStyles(stylesArray: any[]): string {
66
+ return stylesArray
67
+ .map((block) => {
68
+ const selectors = Array.isArray(block.selectors)
69
+ ? block.selectors.join(', ')
70
+ : block.selectors;
71
+ const declarations = Object.entries(block.style)
72
+ .map(([prop, value]) => `${toKebabCase(prop)}: ${value};`)
73
+ .join(' ');
74
+
75
+ return `${selectors} { ${declarations} }`;
76
+ })
77
+ .join('\n');
78
+ }
79
+
80
+ function createActionCardConfig(action: any, index?: number): any {
81
+ const { icon, title, description } = action;
82
+
83
+ return {
84
+ type: 'shadcn-generic',
85
+ componentName: 'ActionCard',
86
+ badgable: false,
87
+ copyable: false,
88
+ removable: true,
89
+ draggable: true,
90
+ componentProps: {
91
+ icon,
92
+ headerContent: title,
93
+ bodyContent: description,
94
+ },
95
+ };
96
+ }
97
+
98
+ function expandActions(node: any): any {
99
+ if (
100
+ node.type === 'shadcn-generic' &&
101
+ node.componentName === 'Actions'
102
+ ) {
103
+ const actions = node.componentProps?.actions;
104
+
105
+ if (Array.isArray(actions) && actions.length > 0) {
106
+ return {
107
+ ...node,
108
+ type: 'div',
109
+ classes: ['dashboard-cards', ...(node.classes || [])],
110
+ componentName: undefined,
111
+ componentProps: undefined,
112
+ components: actions.map((action: any, i: number) => ({
113
+ ...createActionCardConfig(action, i),
114
+ id: `${node.id}--action-card-${i}`,
115
+ })),
116
+ };
117
+ }
118
+
119
+ return node;
120
+ }
121
+
122
+ if (Array.isArray(node.components)) {
123
+ return {
124
+ ...node,
125
+ components: node.components.map(expandActions),
126
+ };
127
+ }
128
+
129
+ return node;
130
+ }
131
+
132
+ function setInteracting(
133
+ renderedComponents: Record<string, Component>,
134
+ components: { componentId: string; showLoader: boolean }[],
135
+ inProgress: boolean,
136
+ ) {
137
+ components.forEach((component) => {
138
+ const { componentId, showLoader } = component;
139
+
140
+ if (!showLoader) {
141
+ return;
142
+ }
143
+
144
+ const gjsModel = renderedComponents[componentId];
145
+
146
+ if (gjsModel) {
147
+ gjsModel.set('attributes', {
148
+ ...gjsModel.get('attributes'),
149
+ interactionApiInProgress: inProgress,
150
+ });
151
+ }
152
+ });
153
+ }
154
+
155
+ function expandActionsComponent(model: Component, actions: any[]): void {
156
+ model.components('');
157
+
158
+ actions.forEach((action: any) => {
159
+ model.append(createActionCardConfig(action));
160
+ });
161
+
162
+ model.set('type', 'div');
163
+ model.addClass('dashboard-cards');
164
+ model.set('componentName', undefined);
165
+ model.set('componentProps', {});
166
+ }
167
+
168
+ function GrapesjsCanvas({
169
+ json,
170
+ mode = 'preview',
171
+ setEditor,
172
+ performInteraction,
173
+ newDataNotifier = () => {
174
+ // no op
175
+ },
176
+ setHasChanged,
177
+ isStreaming,
178
+ }: GrapesjsCanvasProps) {
179
+ const editorRef = useRef<HTMLDivElement>(null);
180
+ const editorInstance = useRef<Editor | null>(null);
181
+ const renderedComponents = useRef<Record<string, Component>>({});
182
+ const isStreamingRef = useRef(isStreaming);
183
+ const isSystemUpdateRef = useRef(false);
184
+ const lastJsonRef = useRef<string>('');
185
+ const isEditorAliveRef = useRef(false);
186
+
187
+ useEffect(() => {
188
+ isStreamingRef.current = isStreaming;
189
+ }, [isStreaming]);
190
+
191
+ function notificationHandler(
192
+ props: Parameters<NotificationHandler>[0],
193
+ ): ReturnType<NotificationHandler> {
194
+ if (!props) {
195
+ return true;
196
+ }
197
+
198
+ const { componentId, newProps = {}, newAttributes = {} } = props;
199
+
200
+ const model = renderedComponents.current[componentId];
201
+
202
+ if (!model) {
203
+ return false;
204
+ }
205
+
206
+ // Filter out complex placeholder objects that shouldn't be stored in the model, done intentionally to keep grapejs out of errors
207
+ const filteredProps = { ...newProps };
208
+
209
+ Object.keys(filteredProps).forEach((key) => {
210
+ const value = filteredProps[key];
211
+
212
+ if (value && typeof value === 'object' && value.name === '__peak_placeholder') {
213
+ delete filteredProps[key];
214
+ }
215
+ });
216
+
217
+ const parsedNewProps = {
218
+ ...filteredProps,
219
+ };
220
+
221
+ if (filteredProps.pagination) {
222
+ parsedNewProps.pagination = {
223
+ ...(model.get('componentProps').pagination || {}),
224
+ ...filteredProps.pagination,
225
+ };
226
+ }
227
+
228
+ // Handle Actions component: expand into ActionCards when data arrives via streaming
229
+ const isActionsComponent = model.get('componentName') === 'Actions';
230
+ const hasValidActionsData = parsedNewProps.actions &&
231
+ parsedNewProps.actions !== null &&
232
+ Array.isArray(parsedNewProps.actions) &&
233
+ parsedNewProps.actions.length > 0;
234
+
235
+ if (isActionsComponent && hasValidActionsData) {
236
+ const isAlreadyExpanded = model.components().length > 0 &&
237
+ model.components().at(0)?.get('componentName') === 'ActionCard';
238
+
239
+ if (!isAlreadyExpanded) {
240
+ expandActionsComponent(model, parsedNewProps.actions);
241
+
242
+ return true;
243
+ }
244
+ }
245
+
246
+ model.set('componentProps', {
247
+ ...model.get('componentProps'),
248
+ ...parsedNewProps,
249
+ });
250
+
251
+ model.set('attributes', {
252
+ ...model.get('attributes'),
253
+ ...newAttributes,
254
+ });
255
+
256
+ return true;
257
+ }
258
+
259
+ useEffect(() => {
260
+ newDataNotifier(notificationHandler);
261
+ }, []);
262
+
263
+ async function performInteractionWrapper(payload: Record<string, any>) {
264
+ const updatedJson = await editorInstance.current?.store();
265
+ setInteracting(renderedComponents.current, payload.affectedComponents, true);
266
+
267
+ try {
268
+ const apiResponse = await performInteraction({
269
+ ...payload,
270
+ json: updatedJson,
271
+ });
272
+
273
+ const componentUpdates = apiResponse || {};
274
+
275
+ Object.entries(componentUpdates).forEach(([key, value]) => {
276
+ const model = renderedComponents.current[key];
277
+
278
+ if (model) {
279
+ model.set('attributes', {
280
+ ...model.get('attributes'),
281
+ error: undefined,
282
+ });
283
+
284
+ model.set('componentProps', {
285
+ ...model.get('componentProps'),
286
+ ...value,
287
+ error: undefined,
288
+ isMissing: undefined,
289
+ });
290
+ }
291
+ });
292
+
293
+ return componentUpdates;
294
+ } catch (error) {
295
+ payload.affectedComponents.forEach((componentId: string) => {
296
+ const model = renderedComponents.current[componentId];
297
+
298
+ if (model) {
299
+ const errorMessage =
300
+ error instanceof Error ? error.message : 'Something went wrong. Please try again.';
301
+
302
+ model.set('attributes', {
303
+ ...model.get('attributes'),
304
+ error: errorMessage,
305
+ });
306
+
307
+ model.set('componentProps', {
308
+ ...model.get('componentProps'),
309
+ error: errorMessage,
310
+ isMissing: undefined,
311
+ });
312
+ }
313
+ });
314
+
315
+ throw error;
316
+ } finally {
317
+ setInteracting(renderedComponents.current, payload.affectedComponents, false);
318
+ }
319
+ }
320
+
321
+ useEffect(() => {
322
+ if (!editorRef.current) {
323
+ return () => {
324
+ // no op
325
+ };
326
+ }
327
+
328
+ if (editorInstance.current) {
329
+ editorInstance.current.destroy();
330
+ editorInstance.current = null;
331
+ isEditorAliveRef.current = false;
332
+ }
333
+
334
+ const currentMode = mode;
335
+
336
+ const editor = grapesjs.init({
337
+ container: editorRef.current,
338
+ height: '100%',
339
+ plugins: [
340
+ tailwind,
341
+ createGrapesjsShadcnGenericPlugin(
342
+ currentMode,
343
+ performInteractionWrapper,
344
+ renderedComponents.current,
345
+ setHasChanged,
346
+ ),
347
+ ],
348
+ storageManager: { type: 'none' },
349
+ richTextEditor: {
350
+ actions: ['bold', 'italic', 'underline', 'strikethrough'],
351
+ },
352
+ canvas: {
353
+ customBadgeLabel: (component) => {
354
+ const tagName = component.get('tagName');
355
+
356
+ return tagName === 'body' ? '' : tagName || '';
357
+ },
358
+ },
359
+ });
360
+
361
+ isEditorAliveRef.current = true;
362
+
363
+ async function checkDirty() {
364
+ if (isStreamingRef.current || isSystemUpdateRef.current) {
365
+ return;
366
+ }
367
+
368
+ if (!isEditorAliveRef.current || !editorInstance.current) {
369
+ return;
370
+ }
371
+
372
+ let stored = null;
373
+
374
+ try {
375
+ stored = await editorInstance.current.store();
376
+ } catch (err) {
377
+ return;
378
+ }
379
+
380
+ if (!stored) {
381
+ return;
382
+ }
383
+
384
+ const newJson = JSON.stringify(stored);
385
+
386
+ if (newJson !== lastJsonRef.current) {
387
+ setHasChanged(true);
388
+ lastJsonRef.current = newJson;
389
+ }
390
+ }
391
+
392
+ const debouncedCheckDirty = (() => {
393
+ let timeout: NodeJS.Timeout | null = null;
394
+
395
+ return () => {
396
+ if (timeout) {
397
+ clearTimeout(timeout);
398
+ }
399
+
400
+ timeout = setTimeout(checkDirty, 200);
401
+ };
402
+ })();
403
+
404
+ [
405
+ 'component:add',
406
+ 'component:remove',
407
+ 'component:update:content',
408
+ 'style:update',
409
+ 'component:drag:end',
410
+ 'rte:change',
411
+ ].forEach((evt) => editor.on(evt, debouncedCheckDirty));
412
+
413
+ editor.on('component:update', (model, prop) => {
414
+ if (['attributes', 'componentProps', 'content'].includes(prop)) {
415
+ debouncedCheckDirty();
416
+ }
417
+ });
418
+
419
+ // Tailwind CSS rules aren't fully added to our component
420
+ // So, adding them from here!
421
+ editor.on('load', async () => {
422
+ const canvasHead = editor.Canvas.getDocument().head;
423
+ const style = document.createElement('style');
424
+ const fullCss = `
425
+ ${tailwindCompiledCss}
426
+ ${datePickerCss}
427
+ ::-webkit-scrollbar {
428
+ width: 0px;
429
+ height: 0px;
430
+ }
431
+ html, body {
432
+ overflow: auto !important;
433
+ -ms-overflow-style: none; /* IE and Edge */
434
+ scrollbar-width: none; /* Firefox */
435
+ }
436
+
437
+ `;
438
+ style.innerHTML = fullCss;
439
+ style.setAttribute('type', 'text/css');
440
+ canvasHead.appendChild(style);
441
+
442
+ isSystemUpdateRef.current = true;
443
+ setComponentProperties(editor, mode === 'preview');
444
+ lastJsonRef.current = JSON.stringify(await editor.store());
445
+ isSystemUpdateRef.current = false;
446
+ });
447
+
448
+ editorInstance.current = editor;
449
+
450
+ if (setEditor) {
451
+ setEditor(editor);
452
+ }
453
+
454
+ TypesToRegister.forEach(({ type, tagName }) => {
455
+ if (!editor.DomComponents.getType(type)) {
456
+ editor.DomComponents.addType(type, {
457
+ model: {
458
+ defaults: { tagName },
459
+ },
460
+ view: {},
461
+ });
462
+ }
463
+ });
464
+
465
+ if (json && json.pages?.length > 0 && json.pages[0].frames?.length > 0) {
466
+ isSystemUpdateRef.current = true;
467
+ const raw = json.pages[0].frames[0].component;
468
+ const processed = expandActions(raw);
469
+ editor.setComponents(processed);
470
+
471
+ const cssText = convertStyles(json.styles || []);
472
+ editor.setStyle(cssText);
473
+
474
+ editor.on('load', async () => {
475
+ lastJsonRef.current = JSON.stringify(await editor.store());
476
+ isSystemUpdateRef.current = false;
477
+ });
478
+ }
479
+
480
+ return function cleanup() {
481
+ editor.destroy();
482
+ isEditorAliveRef.current = false;
483
+ };
484
+ }, [json, mode]);
485
+
486
+ return (
487
+ <React.Fragment>
488
+ <StyledEditor />
489
+ <div ref={editorRef} id="grapesjs-editor" />
490
+ </React.Fragment>
491
+ );
492
+ }
493
+
494
+ export default GrapesjsCanvas;
@@ -0,0 +1,25 @@
1
+ export const TypesToRegister = [
2
+ { tagName: 'div', type: 'div' },
3
+ { tagName: 'h1', type: 'heading' },
4
+ { tagName: 'p', type: 'text' },
5
+ { tagName: 'div', type: 'container' },
6
+ { tagName: 'div', type: 'grid' },
7
+ { tagName: 'table', type: 'table' },
8
+ { tagName: 'thead', type: 'thead' },
9
+ { tagName: 'tbody', type: 'tbody' },
10
+ { tagName: 'tr', type: 'tr' },
11
+ { tagName: 'th', type: 'th' },
12
+ { tagName: 'td', type: 'td' },
13
+ { tagName: 'button', type: 'button' },
14
+ { tagName: 'section', type: 'section' },
15
+ { tagName: 'a', type: 'link' },
16
+ { tagName: 'form', type: 'form' },
17
+ { tagName: 'input', type: 'input' },
18
+ { tagName: 'textarea', type: 'textarea' },
19
+ { tagName: 'select', type: 'select' },
20
+ { tagName: 'ul', type: 'ul' },
21
+ { tagName: 'ol', type: 'ol' },
22
+ { tagName: 'li', type: 'li' },
23
+ { tagName: 'header', type: 'header' },
24
+ { tagName: 'footer', type: 'footer' },
25
+ ];
@@ -0,0 +1 @@
1
+ declare module '*.gif';