@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,18 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ export function filterPlaceholders(obj: any) {
3
+ if (!obj || typeof obj !== 'object') {
4
+ return obj;
5
+ }
6
+
7
+ const filtered = { ...obj };
8
+
9
+ Object.keys(filtered).forEach((key) => {
10
+ const value = filtered[key];
11
+
12
+ if (value && typeof value === 'object' && value.name === '__peak_placeholder') {
13
+ delete filtered[key];
14
+ }
15
+ });
16
+
17
+ return filtered;
18
+ }
@@ -0,0 +1,13 @@
1
+ export function toKebabCase(str: string): string {
2
+ return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
3
+ }
4
+
5
+ export function getAffectedComponentsWithLoader(
6
+ affectedComponents: Array<string>,
7
+ showLoader = true,
8
+ ) {
9
+ return affectedComponents.map((component) => ({
10
+ componentId: component,
11
+ showLoader,
12
+ }));
13
+ }
@@ -0,0 +1,106 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ function isPlaceholder(obj: Record<string, any>) {
3
+ return typeof obj === 'object' && obj !== null && obj.name === '__peak_placeholder';
4
+ }
5
+
6
+ function extractPlaceholders(
7
+ json: Record<string, any>,
8
+ mapping: Record<string, any>,
9
+ parentId: string | undefined = undefined,
10
+ ) {
11
+ let updatedParentId = parentId;
12
+
13
+ if (json === null) {
14
+ return;
15
+ } else if (Array.isArray(json)) {
16
+ json.forEach((item) => extractPlaceholders(item, mapping, updatedParentId));
17
+
18
+ return;
19
+ } else if (typeof json === 'object') {
20
+ if (json.id) {
21
+ updatedParentId = json.id;
22
+ }
23
+
24
+ for (const [key, value] of Object.entries(json)) {
25
+ if (isPlaceholder(value)) {
26
+ if (updatedParentId) {
27
+ mapping[updatedParentId] = mapping[updatedParentId] || {};
28
+ mapping[updatedParentId][key] = value;
29
+ }
30
+ } else if (typeof value === 'object') {
31
+ extractPlaceholders(value, mapping, updatedParentId);
32
+ }
33
+ }
34
+ }
35
+ }
36
+
37
+ function replacePlaceholders(
38
+ json: Record<string, any>,
39
+ mapping: Record<string, any>,
40
+ ): Record<string, any> {
41
+ if (json === null) {
42
+ return json;
43
+ }
44
+
45
+ if (Array.isArray(json)) {
46
+ return json.map((item) => replacePlaceholders(item, mapping));
47
+ }
48
+
49
+ if (typeof json === 'object') {
50
+ const newJson = { ...json };
51
+
52
+ if (newJson.id && mapping[newJson.id]) {
53
+ for (const [key, placeholderValue] of Object.entries(mapping[newJson.id])) {
54
+ if (key in newJson) {
55
+ newJson[key] = placeholderValue;
56
+ }
57
+ }
58
+ }
59
+
60
+ if (newJson.componentName?.toLowerCase() === 'filter') {
61
+ newJson.componentProps = {
62
+ ...newJson.componentProps,
63
+ value: null,
64
+ };
65
+ }
66
+
67
+ if (newJson.componentName?.toLowerCase() === 'search' && newJson.componentProps?.searchText) {
68
+ newJson.componentProps = {
69
+ ...newJson.componentProps,
70
+ searchText: null,
71
+ };
72
+ }
73
+
74
+ if (
75
+ newJson.componentName?.toLowerCase() === 'table' &&
76
+ (newJson.componentProps?.sortColumn || newJson.componentProps?.sortDirection)
77
+ ) {
78
+ newJson.componentProps = {
79
+ ...newJson.componentProps,
80
+ sortColumn: null,
81
+ sortDirection: null,
82
+ };
83
+ }
84
+
85
+ for (const [key, value] of Object.entries(newJson)) {
86
+ newJson[key] = replacePlaceholders(value, mapping);
87
+ }
88
+
89
+ return newJson;
90
+ }
91
+
92
+ return json;
93
+ }
94
+
95
+ export function getUpdatedUiJson(uiJson: Record<string, any>, finalJson: Record<string, any>) {
96
+ const placeholderMapping = {};
97
+ extractPlaceholders(uiJson, placeholderMapping);
98
+
99
+ const cloneJson = JSON.parse(JSON.stringify(finalJson));
100
+ const newFinalJson = replacePlaceholders(cloneJson, placeholderMapping);
101
+
102
+ return {
103
+ ...newFinalJson,
104
+ styles: uiJson.styles || [],
105
+ };
106
+ }
@@ -0,0 +1,58 @@
1
+ import { createGlobalStyle } from 'styled-components';
2
+
3
+ export const StyledEditor = createGlobalStyle`
4
+ /* Hide GrapesJS panels */
5
+ .gs-sidebar-left,
6
+ .gs-sidebar-top,
7
+ .gs-sidebar-right,
8
+ .gjs-pn-panels,
9
+ .gjs-pn-views,
10
+ .gjs-off-prv,
11
+ .gjs-pn-devices {
12
+ display: none !important;
13
+ }
14
+
15
+ .gjs-badge-label {
16
+ display: none !important;
17
+ }
18
+
19
+ .gjs-cv-canvas {
20
+ height: 100% !important;
21
+ width: 100% !important;
22
+ top: 0 !important;
23
+ }
24
+
25
+ .gjs-hovered,
26
+ .gjs-selected,
27
+ [data-gjs-type],
28
+ [data-highlightable] {
29
+ outline: none !important;
30
+ }
31
+
32
+ .gjs-highlighter,
33
+ .gjs-highlighter-sel,
34
+ .gjs-selected-parent,
35
+ .gjs-toolbar {
36
+ outline: none !important;
37
+ border: none !important;
38
+ box-shadow: none !important;
39
+ }
40
+
41
+ .gjs-frame {
42
+ outline: none !important;
43
+ border: none !important;
44
+ }
45
+
46
+ .gjs-selected::before,
47
+ .gjs-selected::after,
48
+ .gjs-hovered::before,
49
+ .gjs-hovered::after {
50
+ display: none !important;
51
+ background: none !important;
52
+ }
53
+
54
+ .gjs-com-badge,
55
+ .gjs-badge {
56
+ display: none !important;
57
+ }
58
+ `;
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ export { default as GrapejsCanvas } from './GrapesjsCanvas';
2
+ export * from './index.styles';
3
+ export { getUpdatedUiJson } from './helpers/merge-json';
4
+ export { DataTable } from './plugins/helpers/data-table';
5
+ export { StyledInfoButton } from './plugins/helpers/styled-info-button';
6
+ export { AiAvatarIcon } from './shadcn/components/icons/AiAvatarIcon';
7
+ export { CustomModal } from './plugins/helpers/custom-modal';
8
+ export { QueryDetailsModal } from './plugins/helpers/query-details-modal';
9
+ export { QueryLoadingModal } from './plugins/helpers/query-loading-modal';
@@ -0,0 +1,196 @@
1
+ /* eslint-disable import/no-named-as-default-member */
2
+ /* eslint-disable func-names */
3
+ /* eslint-disable @typescript-eslint/no-explicit-any */
4
+ import ReactDOM from 'react-dom';
5
+ import { Editor, Component } from 'grapesjs';
6
+ import { getRenderers } from './helpers/render-components';
7
+ import { QueryCacheProvider } from './helpers/query-cache-context';
8
+
9
+ export function createGrapesjsShadcnGenericPlugin(
10
+ mode: 'editor' | 'preview',
11
+ performInteraction: (payload: Record<string, any>) => void,
12
+ renderedComponents: Record<string, Component> = {},
13
+ setHasChanged: (hasChanged: boolean) => void,
14
+ ) {
15
+ return function grapesjsShadcnGenericPlugin(editor: Editor): void {
16
+ editor.DomComponents.addType('shadcn-generic', {
17
+ model: {
18
+ defaults: {
19
+ tagName: 'div',
20
+ droppable: false,
21
+ componentName: 'Card',
22
+ componentProps: {},
23
+ traits: [],
24
+ },
25
+ },
26
+ view: {
27
+ events: {
28
+ 'input [data-slot]': 'handleSlotInput',
29
+ } as any,
30
+ handleSlotInput(e: Event) {
31
+ const target = e.target as HTMLElement;
32
+ const slot = target.dataset.slot;
33
+ const text = target.innerText;
34
+
35
+ const props = this.model.get('componentProps') || {};
36
+
37
+ if (!slot) {
38
+ return;
39
+ }
40
+
41
+ if (slot.startsWith('table-header-')) {
42
+ const idx = parseInt(slot.split('-')[2], 10);
43
+ const dataKeys = Object.keys(props.data?.[0] || {});
44
+
45
+ if (!props.headerMapper) {
46
+ props.headerMapper = {};
47
+ }
48
+
49
+ const key = dataKeys[idx];
50
+
51
+ if (key) {
52
+ props.headerMapper[key] = text;
53
+ }
54
+ } else {
55
+ props[slot] = text;
56
+ }
57
+
58
+ this.model.set('componentProps', {
59
+ ...props,
60
+ });
61
+
62
+ if (!['search-input'].includes(slot)) {
63
+ setHasChanged(true);
64
+ }
65
+ },
66
+ render: function () {
67
+ const compName: string = this.model.get('componentName');
68
+ let compProps: any = this.model.get('componentProps');
69
+
70
+ if (typeof compProps === 'string') {
71
+ try {
72
+ compProps = JSON.parse(compProps);
73
+ } catch (err) {
74
+ // eslint-disable-next-line no-console
75
+ console.error('Invalid JSON in componentProps', err);
76
+ compProps = {};
77
+ }
78
+ }
79
+
80
+ compProps.isEditable = mode === 'editor';
81
+ compProps.performInteraction = performInteraction;
82
+
83
+ compProps.onChange = (data: Record<string, any>) => {
84
+ this.model.set('componentProps', {
85
+ ...this.model.get('componentProps'),
86
+ ...data,
87
+ });
88
+ };
89
+
90
+ const renderers = getRenderers();
91
+ const Renderer = renderers[compName];
92
+
93
+ if (Renderer) {
94
+ if (compName === 'Tabs') {
95
+ ReactDOM.render(
96
+ <QueryCacheProvider maxCacheSize={100} maxCacheAge={5 * 60 * 1000}>
97
+ <Renderer {...compProps} gjsModel={this.model} />
98
+ </QueryCacheProvider>,
99
+ this.el
100
+ );
101
+
102
+ const children = this.model.components();
103
+ const tabsContent = this.el.querySelectorAll('.tabs-content');
104
+
105
+ const tabAttributes = this.model.get('attributes') || {};
106
+ const hasTabsError = tabAttributes.error || compProps.error;
107
+ const hasTabsMissing = tabAttributes.isMissing || compProps.isMissing;
108
+ const isFallback = Array.from(tabsContent).some(
109
+ (el: Element) => (el as HTMLElement).dataset.fallback === 'true',
110
+ );
111
+
112
+ if (!isFallback && tabsContent.length > 0) {
113
+ // Helper function to sync child attributes
114
+ // eslint-disable-next-line no-inner-declarations
115
+ function syncChildAttributes(childModel: any) {
116
+ const childAttributes = childModel.get('attributes') || {};
117
+ const childProps = childModel.get('componentProps') || {};
118
+
119
+ const hasError = hasTabsError || childAttributes.error || childProps.error;
120
+ const hasMissing =
121
+ hasTabsMissing || childAttributes.isMissing || childProps.isMissing;
122
+
123
+ if (hasError || hasMissing) {
124
+ const newAttributes = {
125
+ ...childAttributes,
126
+ loading: false,
127
+ error: hasError,
128
+ isMissing: hasMissing,
129
+ inheritedFromParent: !!(hasTabsError || hasTabsMissing),
130
+ };
131
+
132
+ childModel.set('attributes', newAttributes, { silent: false });
133
+ }
134
+ }
135
+
136
+ children.each((childModel, i) => {
137
+ const childView = this.createView(childModel);
138
+
139
+ if (childView && tabsContent[i]) {
140
+ childView.render();
141
+ tabsContent[i].appendChild(childView.el);
142
+
143
+ setTimeout(() => {
144
+ syncChildAttributes(childModel);
145
+ }, 100);
146
+
147
+ childModel.on('change:attributes', () => {
148
+ syncChildAttributes(childModel);
149
+ });
150
+ }
151
+ });
152
+ }
153
+ } else {
154
+ ReactDOM.render(
155
+ <QueryCacheProvider maxCacheSize={100} maxCacheAge={5 * 60 * 1000}>
156
+ <Renderer {...compProps} gjsModel={this.model} />
157
+ </QueryCacheProvider>,
158
+ this.el
159
+ );
160
+ }
161
+ } else {
162
+ this.el.innerHTML = `<div>Unsupported component: ${compName}</div>`;
163
+ }
164
+
165
+ renderedComponents[this.model.get('id')] = this.model;
166
+
167
+ return this;
168
+ },
169
+ createView(model: any) {
170
+ const ComponentView = this.em.get('DomComponents').getType(model.get('type'))?.view;
171
+
172
+ if (!ComponentView) {
173
+ // eslint-disable-next-line no-console
174
+ console.warn('No view found for component type:', model.get('type'));
175
+
176
+ return null;
177
+ }
178
+
179
+ const view = new ComponentView({
180
+ model,
181
+ config: this.config,
182
+ componentTypes: this.componentTypes,
183
+ });
184
+
185
+ return view;
186
+ },
187
+ remove: function () {
188
+ // eslint-disable-next-line import/no-named-as-default-member
189
+ ReactDOM.unmountComponentAtNode(this.el);
190
+
191
+ return this;
192
+ },
193
+ },
194
+ });
195
+ };
196
+ }
@@ -0,0 +1,123 @@
1
+ import React, { useEffect } from 'react';
2
+ import { X } from 'lucide-react';
3
+
4
+ type CustomModalProps = {
5
+ isOpen: boolean;
6
+ onClose: () => void;
7
+ title: string;
8
+ children: React.ReactNode;
9
+ className?: string;
10
+ titleIcon?: React.ReactNode;
11
+ }
12
+
13
+ export function CustomModal({
14
+ isOpen,
15
+ onClose,
16
+ title,
17
+ children,
18
+ className = '',
19
+ titleIcon
20
+ }: CustomModalProps) {
21
+
22
+ useEffect(() => {
23
+ function handleEscapeKey(event: KeyboardEvent) {
24
+ if (event.key === 'Escape' && isOpen) {
25
+ onClose();
26
+ }
27
+ }
28
+
29
+ if (isOpen) {
30
+ document.addEventListener('keydown', handleEscapeKey);
31
+ const iframe = document.querySelector('.gjs-frame') as HTMLIFrameElement;
32
+ const iframeDocument = iframe?.contentDocument || iframe?.contentWindow?.document;
33
+
34
+ if (iframeDocument) {
35
+ iframeDocument.documentElement?.style.setProperty('overflow', 'hidden', 'important');
36
+ }
37
+
38
+ return () => {
39
+ document.removeEventListener('keydown', handleEscapeKey);
40
+
41
+ if (iframeDocument) {
42
+ iframeDocument.documentElement?.style.removeProperty('overflow');
43
+ }
44
+ };
45
+ }
46
+
47
+ return () => {
48
+ document.removeEventListener('keydown', handleEscapeKey);
49
+ };
50
+ }, [isOpen, onClose]);
51
+
52
+ function handleBackdropClick(event: React.MouseEvent<HTMLDivElement>) {
53
+ if (event.target === event.currentTarget) {
54
+ onClose();
55
+ }
56
+ }
57
+
58
+ if (!isOpen) {
59
+ return null;
60
+ }
61
+
62
+ return (
63
+ <div
64
+ className={`fixed inset-0 z-[9999] flex items-center justify-center p-4 bg-black bg-opacity-30 ${className}`}
65
+ onClick={handleBackdropClick}
66
+ >
67
+ <div
68
+ className="relative w-full max-w-2xl max-h-[90vh] min-h-[45vh] bg-white rounded-lg shadow-xl overflow-hidden animate-modal-fade-in"
69
+ onClick={(e) => e.stopPropagation()}
70
+ >
71
+ <div className="p-6 border-b border-gray-200 bg-gray-50 relative flex items-center justify-between">
72
+ <h2 className="flex items-center text-lg font-semibold text-gray-900">
73
+ {titleIcon && <span className="mr-2">{titleIcon}</span>}
74
+ {title}
75
+ </h2>
76
+
77
+ <button
78
+ onClick={onClose}
79
+ className="transition-colors duration-200 hover:bg-gray-100 rounded p-1 flex items-center justify-center"
80
+ type="button"
81
+ aria-label="Close modal"
82
+ style={{
83
+ width: '24px',
84
+ height: '24px'
85
+ }}
86
+ >
87
+ <X
88
+ size={12}
89
+ className="text-gray-600"
90
+ style={{
91
+ opacity: 1,
92
+ transform: 'rotate(0deg)'
93
+ }}
94
+ />
95
+ </button>
96
+ </div>
97
+
98
+ <div className="overflow-y-auto max-h-[calc(90vh-160px)]">
99
+ {children}
100
+ </div>
101
+ </div>
102
+
103
+ <style>{`
104
+ .animate-modal-fade-in {
105
+ animation: modalFadeIn 0.2s ease-out;
106
+ }
107
+
108
+ @keyframes modalFadeIn {
109
+ from {
110
+ opacity: 0;
111
+ transform: scale(0.95) translateY(-10px);
112
+ }
113
+ to {
114
+ opacity: 1;
115
+ transform: scale(1) translateY(0);
116
+ }
117
+ }
118
+ `}</style>
119
+ </div>
120
+ );
121
+ }
122
+
123
+ export default CustomModal;