@sealcode/jdd-editor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/.arcconfig +12 -0
  2. package/.arclint +18 -0
  3. package/.eslintrc.js +37 -0
  4. package/.mocharc.js +6 -0
  5. package/.nycrc +6 -0
  6. package/.prettierrc +14 -0
  7. package/@types/component-preview-actions.d.ts +43 -0
  8. package/@types/components.sreact.d.ts +48 -0
  9. package/@types/controllers/autogrow-textarea.stimulus.d.ts +5 -0
  10. package/@types/controllers/component-debugger.stimulus.d.ts +28 -0
  11. package/@types/controllers/exportable-textarea.stimulus.d.ts +7 -0
  12. package/@types/controllers/input-image-preview.stimulus.d.ts +5 -0
  13. package/@types/controllers/jdd-table-paste.stimulus.d.ts +8 -0
  14. package/@types/controllers/json-editor.stimulus.d.ts +9 -0
  15. package/@types/controllers/markdown-textarea.stimulus.d.ts +20 -0
  16. package/@types/controllers/refresh-on-ts-changes.stimulus.d.ts +6 -0
  17. package/@types/controllers/refresh-styles.stimulus.d.ts +6 -0
  18. package/@types/controllers/submit-on-input.stimulus.d.ts +7 -0
  19. package/@types/controllers/toast.stimulus.d.ts +4 -0
  20. package/@types/edit-jdd-field.d.ts +22 -0
  21. package/@types/index.d.ts +2 -0
  22. package/@types/inputs/component-input-enum.d.ts +8 -0
  23. package/@types/inputs/component-input-image.d.ts +15 -0
  24. package/@types/inputs/component-input-list.d.ts +15 -0
  25. package/@types/inputs/component-input-single-reference.d.ts +11 -0
  26. package/@types/inputs/component-input-structured.d.ts +15 -0
  27. package/@types/inputs/component-input-table.d.ts +15 -0
  28. package/@types/inputs/component-input.d.ts +16 -0
  29. package/@types/inputs/print-arg-path.d.ts +1 -0
  30. package/@types/jdd-creator.d.ts +49 -0
  31. package/@types/jdd-page.d.ts +85 -0
  32. package/assets/icons/table-add-column-right.svg +1 -0
  33. package/assets/icons/table-add-row-below.svg +1 -0
  34. package/assets/icons/table-add-row-header-below.svg +1 -0
  35. package/assets/icons/table-delete-column.svg +1 -0
  36. package/assets/icons/table-delete-row.svg +1 -0
  37. package/assets/icons/table-move-column-right.svg +1 -0
  38. package/assets/icons/table-move-row-down.svg +1 -0
  39. package/assets/icons/table-move-row-up.svg +1 -0
  40. package/assets/styles/component-admin-table.jdd-page.css +11 -0
  41. package/assets/styles/component-debugger.jdd-page.css +71 -0
  42. package/assets/styles/components.jdd-page.css +286 -0
  43. package/assets/styles/grow-wrap.css +33 -0
  44. package/assets/styles/markdown-editor.css +42 -0
  45. package/dist/src/autogrow-textarea.stimulus.js +13 -0
  46. package/dist/src/autogrow-textarea.stimulus.js.map +7 -0
  47. package/dist/src/component-debugger.stimulus.js +190 -0
  48. package/dist/src/component-debugger.stimulus.js.map +7 -0
  49. package/dist/src/component-preview-actions.js +439 -0
  50. package/dist/src/component-preview-actions.js.map +7 -0
  51. package/dist/src/components.sreact.js +93 -0
  52. package/dist/src/components.sreact.js.map +7 -0
  53. package/dist/src/controllers/autogrow-textarea.stimulus.js +13 -0
  54. package/dist/src/controllers/autogrow-textarea.stimulus.js.map +7 -0
  55. package/dist/src/controllers/component-debugger.stimulus.js +193 -0
  56. package/dist/src/controllers/component-debugger.stimulus.js.map +7 -0
  57. package/dist/src/controllers/exportable-textarea.stimulus.js +71 -0
  58. package/dist/src/controllers/exportable-textarea.stimulus.js.map +7 -0
  59. package/dist/src/controllers/input-image-preview.stimulus.js +30 -0
  60. package/dist/src/controllers/input-image-preview.stimulus.js.map +7 -0
  61. package/dist/src/controllers/jdd-table-paste.stimulus.js +78 -0
  62. package/dist/src/controllers/jdd-table-paste.stimulus.js.map +7 -0
  63. package/dist/src/controllers/json-editor.stimulus.js +114 -0
  64. package/dist/src/controllers/json-editor.stimulus.js.map +7 -0
  65. package/dist/src/controllers/markdown-textarea.stimulus.js +174 -0
  66. package/dist/src/controllers/markdown-textarea.stimulus.js.map +7 -0
  67. package/dist/src/controllers/refresh-on-ts-changes.stimulus.js +90 -0
  68. package/dist/src/controllers/refresh-on-ts-changes.stimulus.js.map +7 -0
  69. package/dist/src/controllers/refresh-styles.stimulus.js +67 -0
  70. package/dist/src/controllers/refresh-styles.stimulus.js.map +7 -0
  71. package/dist/src/controllers/submit-on-input.stimulus.js +55 -0
  72. package/dist/src/controllers/submit-on-input.stimulus.js.map +7 -0
  73. package/dist/src/controllers/toast.stimulus.js +19 -0
  74. package/dist/src/controllers/toast.stimulus.js.map +7 -0
  75. package/dist/src/edit-jdd-field.js +94 -0
  76. package/dist/src/edit-jdd-field.js.map +7 -0
  77. package/dist/src/exportable-textarea.stimulus.js +71 -0
  78. package/dist/src/exportable-textarea.stimulus.js.map +7 -0
  79. package/dist/src/index.js +3 -0
  80. package/dist/src/index.js.map +7 -0
  81. package/dist/src/input-image-preview.stimulus.js +30 -0
  82. package/dist/src/input-image-preview.stimulus.js.map +7 -0
  83. package/dist/src/inputs/component-input-enum.js +30 -0
  84. package/dist/src/inputs/component-input-enum.js.map +7 -0
  85. package/dist/src/inputs/component-input-image.js +58 -0
  86. package/dist/src/inputs/component-input-image.js.map +7 -0
  87. package/dist/src/inputs/component-input-list.js +74 -0
  88. package/dist/src/inputs/component-input-list.js.map +7 -0
  89. package/dist/src/inputs/component-input-single-reference.js +31 -0
  90. package/dist/src/inputs/component-input-single-reference.js.map +7 -0
  91. package/dist/src/inputs/component-input-structured.js +36 -0
  92. package/dist/src/inputs/component-input-structured.js.map +7 -0
  93. package/dist/src/inputs/component-input-table.js +228 -0
  94. package/dist/src/inputs/component-input-table.js.map +7 -0
  95. package/dist/src/inputs/component-input.js +129 -0
  96. package/dist/src/inputs/component-input.js.map +7 -0
  97. package/dist/src/inputs/print-arg-path.js +7 -0
  98. package/dist/src/inputs/print-arg-path.js.map +7 -0
  99. package/dist/src/jdd-creator.js +131 -0
  100. package/dist/src/jdd-creator.js.map +7 -0
  101. package/dist/src/jdd-page.js +339 -0
  102. package/dist/src/jdd-page.js.map +7 -0
  103. package/dist/src/jdd-table-paste.stimulus.js +78 -0
  104. package/dist/src/jdd-table-paste.stimulus.js.map +7 -0
  105. package/dist/src/json-editor.stimulus.js +114 -0
  106. package/dist/src/json-editor.stimulus.js.map +7 -0
  107. package/dist/src/markdown-textarea.stimulus.js +174 -0
  108. package/dist/src/markdown-textarea.stimulus.js.map +7 -0
  109. package/dist/src/submit-on-input.stimulus.js +55 -0
  110. package/dist/src/submit-on-input.stimulus.js.map +7 -0
  111. package/dist/src/toast.stimulus.js +19 -0
  112. package/dist/src/toast.stimulus.js.map +7 -0
  113. package/esbuild.cjs +20 -0
  114. package/esbuild.js +23 -0
  115. package/jenkins.sanity.sh +3 -0
  116. package/lib/component-preview-actions.js +286 -0
  117. package/lib/component-preview-actions.js.map +1 -0
  118. package/lib/components.sreact.js +102 -0
  119. package/lib/components.sreact.js.map +1 -0
  120. package/lib/controllers/autogrow-textarea.stimulus.js +15 -0
  121. package/lib/controllers/autogrow-textarea.stimulus.js.map +1 -0
  122. package/lib/controllers/component-debugger.stimulus.js +188 -0
  123. package/lib/controllers/component-debugger.stimulus.js.map +1 -0
  124. package/lib/controllers/exportable-textarea.stimulus.js +79 -0
  125. package/lib/controllers/exportable-textarea.stimulus.js.map +1 -0
  126. package/lib/controllers/input-image-preview.stimulus.js +28 -0
  127. package/lib/controllers/input-image-preview.stimulus.js.map +1 -0
  128. package/lib/controllers/jdd-table-paste.stimulus.js +84 -0
  129. package/lib/controllers/jdd-table-paste.stimulus.js.map +1 -0
  130. package/lib/controllers/json-editor.stimulus.js +134 -0
  131. package/lib/controllers/json-editor.stimulus.js.map +1 -0
  132. package/lib/controllers/markdown-textarea.stimulus.js +186 -0
  133. package/lib/controllers/markdown-textarea.stimulus.js.map +1 -0
  134. package/lib/controllers/refresh-on-ts-changes.stimulus.js +123 -0
  135. package/lib/controllers/refresh-on-ts-changes.stimulus.js.map +1 -0
  136. package/lib/controllers/refresh-styles.stimulus.js +66 -0
  137. package/lib/controllers/refresh-styles.stimulus.js.map +1 -0
  138. package/lib/controllers/submit-on-input.stimulus.js +48 -0
  139. package/lib/controllers/submit-on-input.stimulus.js.map +1 -0
  140. package/lib/controllers/toast.stimulus.js +16 -0
  141. package/lib/controllers/toast.stimulus.js.map +1 -0
  142. package/lib/edit-jdd-field.js +102 -0
  143. package/lib/edit-jdd-field.js.map +1 -0
  144. package/lib/index.js +19 -0
  145. package/lib/index.js.map +1 -0
  146. package/lib/inputs/component-input-enum.js +25 -0
  147. package/lib/inputs/component-input-enum.js.map +1 -0
  148. package/lib/inputs/component-input-image.js +47 -0
  149. package/lib/inputs/component-input-image.js.map +1 -0
  150. package/lib/inputs/component-input-list.js +61 -0
  151. package/lib/inputs/component-input-list.js.map +1 -0
  152. package/lib/inputs/component-input-single-reference.js +36 -0
  153. package/lib/inputs/component-input-single-reference.js.map +1 -0
  154. package/lib/inputs/component-input-structured.js +42 -0
  155. package/lib/inputs/component-input-structured.js.map +1 -0
  156. package/lib/inputs/component-input-table.js +184 -0
  157. package/lib/inputs/component-input-table.js.map +1 -0
  158. package/lib/inputs/component-input.js +133 -0
  159. package/lib/inputs/component-input.js.map +1 -0
  160. package/lib/inputs/print-arg-path.js +7 -0
  161. package/lib/inputs/print-arg-path.js.map +1 -0
  162. package/lib/jdd-creator.js +113 -0
  163. package/lib/jdd-creator.js.map +1 -0
  164. package/lib/jdd-page.js +310 -0
  165. package/lib/jdd-page.js.map +1 -0
  166. package/package.json +61 -0
  167. package/src/component-preview-actions.ts +520 -0
  168. package/src/components.sreact.ts +100 -0
  169. package/src/controllers/autogrow-textarea.stimulus.ts +13 -0
  170. package/src/controllers/component-debugger.stimulus.ts +247 -0
  171. package/src/controllers/exportable-textarea.stimulus.ts +77 -0
  172. package/src/controllers/input-image-preview.stimulus.ts +29 -0
  173. package/src/controllers/jdd-table-paste.stimulus.ts +89 -0
  174. package/src/controllers/json-editor.stimulus.ts +127 -0
  175. package/src/controllers/markdown-textarea.stimulus.ts +198 -0
  176. package/src/controllers/refresh-on-ts-changes.stimulus.ts +112 -0
  177. package/src/controllers/refresh-styles.stimulus.ts +70 -0
  178. package/src/controllers/submit-on-input.stimulus.ts +66 -0
  179. package/src/controllers/toast.stimulus.ts +15 -0
  180. package/src/edit-jdd-field.ts +127 -0
  181. package/src/index.ts +2 -0
  182. package/src/inputs/component-input-enum.ts +36 -0
  183. package/src/inputs/component-input-image.ts +70 -0
  184. package/src/inputs/component-input-list.ts +91 -0
  185. package/src/inputs/component-input-single-reference.ts +45 -0
  186. package/src/inputs/component-input-structured.ts +51 -0
  187. package/src/inputs/component-input-table.ts +262 -0
  188. package/src/inputs/component-input.ts +158 -0
  189. package/src/inputs/print-arg-path.ts +3 -0
  190. package/src/jdd-creator.ts +151 -0
  191. package/src/jdd-page.ts +439 -0
  192. package/tsconfig.json +24 -0
@@ -0,0 +1,247 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+ /* eslint-disable @typescript-eslint/consistent-type-assertions */
3
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
4
+ import { Controller } from "stimulus";
5
+
6
+ export default class ComponentDebugger extends Controller {
7
+ declare sizeSelectTarget: HTMLSelectElement;
8
+ declare gutterTarget: HTMLDivElement;
9
+ declare checkboxTarget: HTMLInputElement;
10
+ declare checkboxTargets: HTMLInputElement[];
11
+ declare previewTarget: HTMLDivElement;
12
+ declare componentBlockTargets: HTMLDivElement[];
13
+ static targets = [
14
+ "gutter",
15
+ "componentBlock",
16
+ "checkbox",
17
+ "preview",
18
+ "sizeSelect",
19
+ ];
20
+
21
+ id: string;
22
+ main_form: HTMLFormElement;
23
+ origin_x: number;
24
+ origin_width: number;
25
+
26
+ connect() {
27
+ const main_form = document
28
+ .querySelector("#component-debugger")
29
+ ?.closest("form");
30
+ if (!main_form) {
31
+ throw new Error("No main form");
32
+ }
33
+ this.main_form = main_form;
34
+ document.documentElement.addEventListener("ts-rebuilt", () => {
35
+ this.main_form.requestSubmit();
36
+ });
37
+ this.main_form.addEventListener("turbo:submit-end", () => {
38
+ // this clears the values of file inputs, so they don't get unecessarily
39
+ // re-uploaded on future submissions - the file is alreade there on the server
40
+ this.main_form
41
+ .querySelectorAll("input[type=file]")
42
+ .forEach((input: HTMLInputElement) => (input.value = ""));
43
+ });
44
+
45
+ window.addEventListener("load", () => {
46
+ this.update_width_display();
47
+ });
48
+ document.addEventListener("turbo:render", () => {
49
+ // not calling that to see if that improves performance
50
+ // console.log("UWD because of render event");
51
+ // this.update_width_display();
52
+ });
53
+
54
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
55
+ const gutter = this.gutterTarget;
56
+ gutter.addEventListener("mousedown", (e) => {
57
+ this.origin_x = e.clientX;
58
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
59
+ const resizable = this.targets.find("preview") as HTMLSpanElement;
60
+ this.origin_width = resizable.getBoundingClientRect().width;
61
+ const handler = (e: MouseEvent) => this.resizeHandler(e);
62
+ document.addEventListener("mousemove", handler);
63
+ const remove_move_listener = () => {
64
+ document.removeEventListener("mousemove", handler);
65
+ document.removeEventListener("mouseup", remove_move_listener);
66
+ document.dispatchEvent(
67
+ new Event("component-debugger--resize-done")
68
+ );
69
+ };
70
+ document.addEventListener("mouseup", remove_move_listener);
71
+ e.preventDefault();
72
+ });
73
+ }
74
+
75
+ update_width_display() {
76
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
77
+ console.log("uwd");
78
+ const preview = this.targets.find("preview") as HTMLSpanElement;
79
+ const component_width = preview.offsetWidth;
80
+ this.sizeSelectTarget
81
+ .querySelectorAll("option")
82
+ .forEach((e) => e.removeAttribute("selected"));
83
+ let option: HTMLOptionElement | null =
84
+ this.sizeSelectTarget.querySelector("option.dynamic");
85
+ if (!option) {
86
+ option = document.createElement("option");
87
+ option.classList.add("dynamic");
88
+ option.setAttribute("selected", "");
89
+ this.sizeSelectTarget.insertBefore(
90
+ option,
91
+ this.sizeSelectTarget.childNodes[0]!
92
+ );
93
+ }
94
+ option.setAttribute("selected", "");
95
+ option.innerHTML = `${component_width} px`;
96
+ option.value = String(component_width);
97
+ this.sizeSelectTarget.value = String(component_width);
98
+ }
99
+
100
+ resizeHandler(e: MouseEvent) {
101
+ const width_offset = this.origin_x - e.clientX;
102
+ const new_width = Math.max(this.origin_width + width_offset, 1);
103
+ this.setPreviewWidth(new_width);
104
+ this.update_width_display();
105
+ document.dispatchEvent(new Event("component-debugger--resize"));
106
+ }
107
+
108
+ setPreviewWidth(width: number) {
109
+ document
110
+ .getElementById("component-debugger")
111
+ ?.style.setProperty(
112
+ "--resizable-column-width",
113
+ width.toString() + "px"
114
+ );
115
+ this.update_width_display();
116
+ }
117
+
118
+ handleWidthDropdown() {
119
+ const value = this.sizeSelectTarget.value;
120
+ this.setPreviewWidth(parseInt(value));
121
+ }
122
+
123
+ handleBlockHover(e: MouseEvent) {
124
+ const index = parseInt(
125
+ (e.target as HTMLDivElement)
126
+ .closest(".jdd-editor__component-block")
127
+ ?.getAttribute("data-component-index") || "0"
128
+ );
129
+ this.element
130
+ .querySelector(`.component-number-${index}`)
131
+ ?.classList.add("highlighted");
132
+ }
133
+
134
+ handleBlockUnhover(e: MouseEvent) {
135
+ const index = parseInt(
136
+ (e.target as HTMLDivElement)
137
+ .closest(".jdd-editor__component-block")
138
+ ?.getAttribute("data-component-index") || ""
139
+ );
140
+ this.element
141
+ .querySelectorAll(`.component-number-${index}.highlighted`)
142
+ .forEach((e) => e.classList.remove("highlighted"));
143
+ }
144
+
145
+ componentBlockTargetConnected(block_element: HTMLDivElement) {
146
+ const index = parseInt(
147
+ block_element.getAttribute("data-component-index") || "0"
148
+ );
149
+ block_element.addEventListener("focusin", () => {
150
+ this.scrollToComponentPreview(index);
151
+ });
152
+
153
+ const summary = block_element.querySelector("summary");
154
+ if (summary) {
155
+ summary.addEventListener(
156
+ "mouseenter",
157
+ this.handleBlockHover.bind(this)
158
+ );
159
+ summary.addEventListener(
160
+ "mouseleave",
161
+ this.handleBlockUnhover.bind(this)
162
+ );
163
+ }
164
+ }
165
+
166
+ previewTargetConnected(preview_element: HTMLDivElement) {
167
+ preview_element.addEventListener("click", ({ target }) => {
168
+ if (!(target instanceof HTMLElement)) {
169
+ return;
170
+ }
171
+ const closest = target.closest(".jdd-component");
172
+ if (!closest) {
173
+ return;
174
+ }
175
+ const index = parseInt(
176
+ String(
177
+ Array.from(closest.classList)
178
+ .find((c) => c.startsWith("component-number-"))
179
+ ?.replace("component-number-", "")
180
+ )
181
+ );
182
+ if (isNaN(index)) {
183
+ return;
184
+ }
185
+ this.focusComponentBlock(index);
186
+ });
187
+ }
188
+
189
+ focusComponentBlock(index: number) {
190
+ const block = this.componentBlockTargets[index];
191
+ if (!block) {
192
+ return;
193
+ }
194
+ this.checkboxTargets[index]!.checked = true;
195
+ this.checkboxTargets[index]!.dispatchEvent(new Event("change")); // to help with refreshing markdown editor
196
+ block.scrollIntoView({ behavior: "smooth" });
197
+ (
198
+ block.querySelector(
199
+ ".component-preview-parameters input"
200
+ ) as HTMLInputElement
201
+ )?.focus();
202
+ }
203
+
204
+ getIndex(block_element: HTMLDivElement) {
205
+ const index = parseInt(
206
+ String(block_element.getAttribute("data-component-index"))
207
+ );
208
+ return index;
209
+ }
210
+
211
+ labelClicked(element: MouseEvent) {
212
+ const block_element = (element.target as HTMLDivElement).closest(
213
+ `[data-component-debugger-target="componentBlock"]`
214
+ ) as HTMLDivElement;
215
+ const index = this.getIndex(block_element);
216
+ if (!this.checkboxTargets?.[index]?.checked) {
217
+ this.scrollToComponentPreview(index);
218
+ }
219
+ }
220
+
221
+ getPreviewElementForComponentIndex(index: number) {
222
+ const element = this.element.querySelector(
223
+ `.component-number-${index}`
224
+ ) as HTMLDialogElement;
225
+ return element;
226
+ }
227
+
228
+ scrollToComponentPreview(index: number) {
229
+ const element = this.getPreviewElementForComponentIndex(index);
230
+ if (!element) {
231
+ return;
232
+ }
233
+ const preview_element =
234
+ this.element.querySelector(".component-preview");
235
+ if (!preview_element) {
236
+ throw new Error("Missing preview element!");
237
+ }
238
+ if (element.clientHeight > preview_element.clientHeight) {
239
+ preview_element.scrollTop = element.offsetTop - 44;
240
+ } else {
241
+ preview_element.scrollTop =
242
+ element.offsetTop -
243
+ (preview_element.clientHeight - element.clientHeight) / 2 -
244
+ 44;
245
+ }
246
+ }
247
+ }
@@ -0,0 +1,77 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+ import { Controller } from "stimulus";
3
+
4
+ function download(data: string, filename: string, type: string) {
5
+ const file = new Blob([data], { type: type });
6
+ const a = document.createElement("a"),
7
+ url = URL.createObjectURL(file);
8
+ a.href = url;
9
+ a.download = filename;
10
+ document.body.appendChild(a);
11
+ a.click();
12
+ setTimeout(function () {
13
+ document.body.removeChild(a);
14
+ window.URL.revokeObjectURL(url);
15
+ }, 0);
16
+ }
17
+
18
+ export default class ExportableTextarea extends Controller<HTMLDetailsElement> {
19
+ connect() {
20
+ console.log("exportable", this.element);
21
+ this.element.removeAttribute("open");
22
+ this.element.addEventListener(
23
+ "turbo:before-morph-attribute",
24
+ (event: CustomEvent<{ attributeName: string }>) => {
25
+ if (
26
+ event.target == this.element &&
27
+ event.detail.attributeName == "open"
28
+ ) {
29
+ event.preventDefault();
30
+ }
31
+ }
32
+ );
33
+ }
34
+
35
+ async copy(e: MouseEvent) {
36
+ e.preventDefault();
37
+ const textarea = this.element.querySelector("textarea");
38
+ if (!textarea) {
39
+ throw new Error("Couldn't find the textarea");
40
+ }
41
+ await navigator.clipboard.writeText(textarea.value);
42
+ (e.target as HTMLButtonElement).textContent = "Copied";
43
+ await sleep(1000);
44
+ (e.target as HTMLButtonElement).textContent = "Copy";
45
+ }
46
+
47
+ async download(e: MouseEvent) {
48
+ e.preventDefault();
49
+ const textarea = this.element.querySelector("textarea");
50
+ if (!textarea) {
51
+ throw new Error("Couldn't find the textarea");
52
+ }
53
+ download(
54
+ textarea.value,
55
+ String(Date.now()) + ".json",
56
+ "application/json"
57
+ );
58
+ }
59
+
60
+ async import(e: MouseEvent) {
61
+ e.preventDefault();
62
+ const file_input = this.element.querySelector(
63
+ "input[type='file']"
64
+ ) as HTMLInputElement;
65
+ const file = file_input.files?.[0];
66
+ if (file) {
67
+ const text = await file.text();
68
+ const textarea = this.element.querySelector("textarea");
69
+ textarea!.innerHTML = text.replaceAll("<", "&lt;");
70
+ textarea!.dispatchEvent(new Event("change"));
71
+ }
72
+ }
73
+ }
74
+
75
+ function sleep(n: number) {
76
+ return new Promise((resolve) => setTimeout(resolve, n));
77
+ }
@@ -0,0 +1,29 @@
1
+ import { Controller } from "stimulus";
2
+
3
+ export default class InputImagePreview extends Controller {
4
+ id: string;
5
+
6
+ handleChange() {
7
+ let img = this.element.querySelector("img");
8
+ if (!img) {
9
+ img = document.createElement("img");
10
+ img.setAttribute("style", "height: 40px; width: 40px");
11
+ this.element
12
+ .querySelector(".image-preview-container")
13
+ ?.appendChild(img);
14
+ }
15
+ window.URL.revokeObjectURL(img.src);
16
+ const file = this.element.querySelector("input")?.files?.[0];
17
+ if (!file) {
18
+ console.warn("No file");
19
+ return;
20
+ }
21
+ const new_url = window.URL.createObjectURL(file);
22
+ img.src = new_url;
23
+ img.parentNode;
24
+ img.parentElement
25
+ ?.querySelectorAll("source")
26
+ .forEach((e) => e.parentNode?.removeChild(e));
27
+ img.style.aspectRatio = "1";
28
+ }
29
+ }
@@ -0,0 +1,89 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+ /* eslint-disable @typescript-eslint/consistent-type-assertions */
3
+ /* eslint-disable no-await-in-loop */
4
+ /* eslint-disable @typescript-eslint/no-misused-promises */
5
+ import { Controller } from "stimulus";
6
+ import TurndownService from "turndown";
7
+ import objectPath from "object-path";
8
+
9
+ const turndownService = new TurndownService();
10
+
11
+ export default class extends Controller<HTMLButtonElement> {
12
+ declare argpathValue: string;
13
+ static values = {
14
+ argpath: String,
15
+ };
16
+
17
+ connect() {
18
+ this.element.addEventListener("click", async () => {
19
+ const clipboardContents = await navigator.clipboard.read();
20
+ for (const item of clipboardContents) {
21
+ if (!item.types.includes("text/html")) {
22
+ alert(
23
+ "Nie można skopiować tabeli - nie znaleziono danych HTML"
24
+ );
25
+ return;
26
+ }
27
+ const blob = await item.getType("text/html");
28
+ const html = await blob.text();
29
+ const div = document.createElement("div");
30
+ div.innerHTML = html;
31
+ const table = div.querySelector("table");
32
+ if (!table) {
33
+ alert("No table in clipboard");
34
+ }
35
+ const rows = Array.from(
36
+ table?.querySelectorAll("tr") || []
37
+ ).map((tr) => {
38
+ const cells = Array.from(tr.querySelectorAll("td"));
39
+ return {
40
+ type: "row",
41
+ cells: cells.map((cell) =>
42
+ turndownService.turndown(cell)
43
+ ),
44
+ };
45
+ });
46
+
47
+ const form = this.element.closest("form");
48
+ const json_textarea = document.getElementById(
49
+ "component-debugger-json-textarea"
50
+ ) as HTMLTextAreaElement;
51
+ if (!json_textarea) {
52
+ console.error(
53
+ "Could not find json textarea with the jdd state"
54
+ );
55
+ return;
56
+ }
57
+ if (!form) {
58
+ throw new Error("Not inside a form");
59
+ }
60
+ const handler = () => {
61
+ document.removeEventListener("turbo:morph", handler);
62
+ const state = JSON.parse(json_textarea.value) as unknown;
63
+ objectPath.set(state as object, this.argpathValue, {
64
+ rows,
65
+ });
66
+
67
+ json_textarea.childNodes[0]!.textContent =
68
+ JSON.stringify(state);
69
+ setTimeout(() => {
70
+ // async in order to give a chance for UI state to update after changing the iframe value
71
+ const submitter_button =
72
+ document.createElement("button");
73
+ submitter_button.setAttribute(
74
+ "formaction",
75
+ "./?action=replace_state&action_args=W10="
76
+ );
77
+ form.appendChild(submitter_button);
78
+ form.requestSubmit(submitter_button);
79
+ submitter_button.remove();
80
+ }, 0);
81
+ };
82
+
83
+ document.addEventListener("turbo:morph", handler);
84
+ form.requestSubmit(); // to get the newest json first, there may be some
85
+ // unsent changes
86
+ }
87
+ });
88
+ }
89
+ }
@@ -0,0 +1,127 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
4
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
5
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
6
+ import { Controller } from "stimulus";
7
+
8
+ const JS_ID = "codemirror-js";
9
+ const JS_PATH = "/dist/codemirror.js";
10
+
11
+ const JS_MODE_ID = "codemirror-js-mode-js";
12
+ const JS_MODE_PATH = "/dist/codemirror-javascript-mode.js";
13
+
14
+ const CSS_ID = "codemirror-css";
15
+ const CSS_PATH = "/dist/codemirror.css";
16
+
17
+ declare const CodeMirror: any;
18
+
19
+ export default class JSONEditor extends Controller<HTMLTextAreaElement> {
20
+ cm: any;
21
+ saving = false;
22
+
23
+ addCSS() {
24
+ const tag = document.querySelector(`head #${CSS_ID}`);
25
+ if (!tag) {
26
+ const link = document.createElement("link");
27
+ link.setAttribute("rel", "stylesheet");
28
+ link.setAttribute("type", "text/css");
29
+ link.setAttribute("id", CSS_ID);
30
+ link.setAttribute("href", CSS_PATH);
31
+ document.head.appendChild(link);
32
+ }
33
+ }
34
+
35
+ async addJS(js_id: string, js_path: string) {
36
+ return new Promise<void>((resolve, reject) => {
37
+ const once_loaded = (e: MouseEvent) => {
38
+ (e.target as HTMLScriptElement).setAttribute("loaded", "true");
39
+ resolve();
40
+ };
41
+ try {
42
+ const tag = document.querySelector(`head #${js_id}`);
43
+ if (!tag) {
44
+ const script = document.createElement("script");
45
+ script.setAttribute("id", js_id);
46
+ script.setAttribute("src", js_path);
47
+ script.addEventListener("load", once_loaded);
48
+ document.head.appendChild(script);
49
+ } else {
50
+ if (tag.getAttribute("loaded") == "true") {
51
+ resolve();
52
+ } else {
53
+ tag.addEventListener("load", once_loaded);
54
+ }
55
+ }
56
+ } catch (e) {
57
+ reject(e);
58
+ }
59
+ });
60
+ }
61
+
62
+ async connect() {
63
+ if (this.element.parentNode?.querySelector(".CodeMirror")) {
64
+ //already loaded, quit;
65
+ return;
66
+ }
67
+ this.addCSS();
68
+ await this.addJS(JS_ID, JS_PATH);
69
+ await this.addJS(JS_MODE_ID, JS_MODE_PATH);
70
+ this.cm = CodeMirror.fromTextArea(this.element, {
71
+ mode: {
72
+ name: "javascript",
73
+ json: true,
74
+ },
75
+ theme: "default",
76
+ addModeClass: true,
77
+ lineWrapping: true,
78
+ });
79
+ this.cm.on("change", () => {
80
+ this.saving = true;
81
+ this.element.innerHTML = this.cm.getValue().replace("<", "&lt;");
82
+ this.saving = false;
83
+ });
84
+ // to prevent morhping from removing the element
85
+ this.cm.getWrapperElement().setAttribute("data-turbo-permanent", "");
86
+ const observer = new MutationObserver((mutation) => {
87
+ console.log("DETECTED TEXTAREA MUTATION CHANGE!", mutation);
88
+ if (!this.saving) {
89
+ if (this.cm.getValue() != this.element.innerHTML) {
90
+ console.log(
91
+ "SETTING CM VALUE TO",
92
+ this.element.innerHTML.slice(0, 100)
93
+ );
94
+ this.cm.setValue(this.element.innerHTML);
95
+ }
96
+ }
97
+ });
98
+ observer.observe(this.element, {
99
+ characterData: true,
100
+ subtree: true,
101
+ });
102
+
103
+ this.element.addEventListener("change", () => {
104
+ console.log("DETECTED TEXTAREA CHANGE!");
105
+ if (!this.saving) {
106
+ if (this.cm.getValue() != this.element.innerHTML) {
107
+ console.log(
108
+ "SETTING CM VALUE TO",
109
+ this.element.innerHTML.slice(0, 100)
110
+ );
111
+ this.cm.setValue(this.element.innerHTML);
112
+ }
113
+ }
114
+ });
115
+ this.element
116
+ .closest("details")
117
+ ?.addEventListener("toggle", (event: MouseEvent) => {
118
+ if ((event.target as HTMLDetailsElement).open) {
119
+ this.cm.refresh();
120
+ }
121
+ });
122
+ }
123
+
124
+ async disconnect() {
125
+ console.log("DISCONNECTING JSON EDITOR");
126
+ }
127
+ }