@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,198 @@
1
+ /* eslint-disable @typescript-eslint/no-misused-promises */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
4
+ /* eslint-disable @typescript-eslint/no-explicit-any */
5
+ import { Controller } from "stimulus";
6
+ import type { default as simplemde } from "simplemde";
7
+
8
+ const CSS_ID = "simplemde-css";
9
+ const JS_ID = "simplemde-js";
10
+
11
+ declare const SimpleMDE: simplemde;
12
+
13
+ export default class MarkdownTextarea extends Controller<HTMLTextAreaElement> {
14
+ sm: simplemde;
15
+ checkboxHandler: (this: HTMLElement, ev: Event) => any;
16
+ resizeObserver: ResizeObserver;
17
+ intersectionObserver: IntersectionObserver;
18
+ mdeStarted = false;
19
+
20
+ addCSS() {
21
+ const tag = document.querySelector(`head #${CSS_ID}`);
22
+ if (!tag) {
23
+ const link = document.createElement("link");
24
+ link.setAttribute("rel", "stylesheet");
25
+ link.setAttribute("type", "text/css");
26
+ link.setAttribute("id", CSS_ID);
27
+ link.setAttribute("href", "/dist/simplemde.min.css");
28
+ document.head.appendChild(link);
29
+ }
30
+ }
31
+
32
+ async addJS() {
33
+ return new Promise<void>((resolve, reject) => {
34
+ const once_loaded = (e: MouseEvent) => {
35
+ (e.target as HTMLScriptElement).setAttribute("loaded", "true");
36
+ resolve();
37
+ };
38
+ try {
39
+ const tag = document.querySelector(`head #${JS_ID}`);
40
+ if (!tag) {
41
+ const script = document.createElement("script");
42
+ script.setAttribute("id", JS_ID);
43
+ script.setAttribute("src", "/dist/simplemde.min.js");
44
+ script.addEventListener("load", once_loaded);
45
+ document.head.appendChild(script);
46
+ } else {
47
+ if (tag.getAttribute("loaded") == "true") {
48
+ resolve();
49
+ } else {
50
+ tag.addEventListener("load", once_loaded);
51
+ }
52
+ }
53
+ } catch (e) {
54
+ reject(e);
55
+ }
56
+ });
57
+ }
58
+
59
+ handleResize() {
60
+ this.sm.codemirror.refresh();
61
+ }
62
+
63
+ async connect() {
64
+ console.log("Markdown connect!", this.element);
65
+ if (this.element.parentNode?.querySelector(".editor-toolbar")) {
66
+ //already loaded, quit;
67
+ return;
68
+ }
69
+ this.addCSS();
70
+ await this.addJS();
71
+ const component_block = this.isInsideComponentBlock();
72
+
73
+ // some offloading of starting the MDE, because it is slow on Chrome
74
+ if (component_block) {
75
+ if (this.isHiddenBlock()) {
76
+ const handler = () => {
77
+ this.getCheckboxThatShowsBlock()?.removeEventListener(
78
+ "change",
79
+ handler
80
+ );
81
+ setTimeout(() => this.init(), 1);
82
+ };
83
+ this.getCheckboxThatShowsBlock()?.addEventListener(
84
+ "change",
85
+ handler
86
+ );
87
+ } else {
88
+ await this.init();
89
+ }
90
+ } else {
91
+ await this.init();
92
+ }
93
+ }
94
+
95
+ async startMDE() {
96
+ if (this.mdeStarted) {
97
+ return;
98
+ }
99
+ this.sm = new (SimpleMDE as any)({
100
+ element: this.element,
101
+ autoDownloadFontAwesome: false,
102
+ spellChecker: false,
103
+ hideIcons: ["image", "preview", "side-by-side"],
104
+ status: ["words"],
105
+ autosave: { enabled: false },
106
+ forceSync: true, // for autosubmit to work
107
+ initialValue: this.element.value,
108
+ }) as simplemde;
109
+ // this.element.closest(".grow-wrap").setAttribute("data-turbo-permanent", "");
110
+ this.sm.codemirror.on("change", () => {
111
+ this.element.dispatchEvent(new Event("input"));
112
+ });
113
+ this.setupRefreshOnShow();
114
+ this.resizeObserver = new ResizeObserver(() => {
115
+ this.sm.codemirror.refresh();
116
+ });
117
+ const wrapper = (this.sm as any).element.closest(
118
+ ".grow-wrap"
119
+ ) as HTMLDivElement;
120
+ this.resizeObserver.observe(wrapper);
121
+ document.addEventListener(
122
+ "turbo:before-morph-element",
123
+ function (event: BeforeUnloadEvent) {
124
+ const target = event.target as HTMLDivElement;
125
+ // disallow morphing, but allow removing
126
+ if (
127
+ target == wrapper &&
128
+ (event as any).detail.newElement !== undefined
129
+ ) {
130
+ event.preventDefault();
131
+ }
132
+ }
133
+ );
134
+ this.mdeStarted = true;
135
+ }
136
+
137
+ async init() {
138
+ this.intersectionObserver = new IntersectionObserver(
139
+ (entries) => {
140
+ entries.forEach((entry) => {
141
+ if (entry.intersectionRatio > 0.2) {
142
+ this.startMDE();
143
+ }
144
+ });
145
+ },
146
+ {
147
+ root: this.element.closest(".component-arguments"),
148
+ rootMargin: "0px",
149
+ threshold: 0.25,
150
+ }
151
+ );
152
+ this.intersectionObserver.observe(this.element);
153
+ }
154
+
155
+ isHiddenBlock() {
156
+ return !this.getCheckboxThatShowsBlock()?.checked;
157
+ }
158
+
159
+ isInsideComponentBlock(): false | HTMLDivElement {
160
+ return this.element.closest(".jdd-editor__component-block") as
161
+ | HTMLDivElement
162
+ | false;
163
+ }
164
+
165
+ getCheckboxThatShowsBlock(): HTMLInputElement | null {
166
+ const block = this.isInsideComponentBlock();
167
+ if (!block) {
168
+ return null;
169
+ }
170
+ return block.querySelector(".component-collapse-toggle");
171
+ }
172
+
173
+ setupRefreshOnShow() {
174
+ this.checkboxHandler = (e) => {
175
+ const target = e.target as HTMLInputElement;
176
+ if (target.checked) {
177
+ this.sm.codemirror.refresh();
178
+ }
179
+ };
180
+ this.getCheckboxThatShowsBlock()?.addEventListener(
181
+ "change",
182
+ this.checkboxHandler
183
+ );
184
+ }
185
+
186
+ disconnect() {
187
+ console.log("disconnecting", this.element);
188
+ this.getCheckboxThatShowsBlock()?.removeEventListener(
189
+ "change",
190
+ this.checkboxHandler
191
+ );
192
+ this.element
193
+ .closest(".grow-wrap")
194
+ ?.removeAttribute("data-turbo-permanent");
195
+ this.resizeObserver?.unobserve((this.sm as any).element as HTMLElement);
196
+ this.sm.toTextArea();
197
+ }
198
+ }
@@ -0,0 +1,112 @@
1
+ import { Controller } from "stimulus";
2
+
3
+ const APP_DOWN_ERROR_MESSAGE = "App is currently down";
4
+
5
+ const sleep = (time: number) =>
6
+ new Promise((resolve) => {
7
+ setTimeout(resolve, time);
8
+ });
9
+
10
+ async function get_status(): Promise<{ started_at: number; status: string }> {
11
+ const r = await fetch("/status.json");
12
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13
+ return (await r.json()) as { started_at: number; status: string };
14
+ }
15
+
16
+ async function wait_for_run_id_to_change() {
17
+ let first_timestamp: number;
18
+ try {
19
+ const { started_at } = await get_status();
20
+ first_timestamp = started_at;
21
+ } catch (e) {
22
+ await wait_for_app_to_be_stable();
23
+ return;
24
+ }
25
+
26
+ if (!first_timestamp) {
27
+ throw new Error(APP_DOWN_ERROR_MESSAGE);
28
+ }
29
+
30
+ // eslint-disable-next-line no-constant-condition
31
+ while (true) {
32
+ // eslint-disable-next-line no-await-in-loop
33
+ const { started_at } = await get_status().catch(() => ({
34
+ started_at: first_timestamp,
35
+ }));
36
+ if (started_at !== first_timestamp) {
37
+ return;
38
+ }
39
+ // eslint-disable-next-line no-await-in-loop
40
+ await sleep(100);
41
+ }
42
+ }
43
+
44
+ async function wait_for_app_to_be_stable(n = 3) {
45
+ // eslint-disable-next-line no-console
46
+ console.log("Waiting for app to be stable....");
47
+ let counter = 0;
48
+ // eslint-disable-next-line no-constant-condition
49
+ while (true) {
50
+ // eslint-disable-next-line no-await-in-loop
51
+ const { status } = await get_status().catch(() => ({
52
+ status: "down",
53
+ }));
54
+ if (status == "running") {
55
+ // eslint-disable-next-line no-console
56
+ console.log(counter);
57
+ counter++;
58
+ } else {
59
+ counter = 0;
60
+ }
61
+ if (counter == n) {
62
+ return;
63
+ }
64
+ // eslint-disable-next-line no-await-in-loop
65
+ await sleep(100);
66
+ }
67
+ }
68
+
69
+ async function wait_for_app_restart() {
70
+ try {
71
+ await wait_for_run_id_to_change();
72
+ } catch (e) {
73
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
74
+ if (e.message !== APP_DOWN_ERROR_MESSAGE) {
75
+ throw e;
76
+ }
77
+ }
78
+ await wait_for_app_to_be_stable();
79
+ }
80
+
81
+ export default class RefreshOnTSChanges extends Controller {
82
+ socket: WebSocket;
83
+
84
+ async connect() {
85
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
86
+ const { port, watch } = (await fetch("/dist/notifier.json").then((r) =>
87
+ r.json()
88
+ )) as { port: number; watch: boolean };
89
+ if (!watch) {
90
+ // eslint-disable-next-line no-console
91
+ console.warn(
92
+ "Not running auto refresh on watch because the build process is not running in watch mode"
93
+ );
94
+ return;
95
+ }
96
+ const socket = new WebSocket(`ws://localhost:${port}`);
97
+ socket.onmessage = async (message) => {
98
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
99
+ const data = message.data as unknown;
100
+ if (typeof data === "string" && data.endsWith("-ts")) {
101
+ document.documentElement.classList.add("restarting");
102
+ await wait_for_app_restart();
103
+ document.documentElement.dispatchEvent(new Event("ts-rebuilt"));
104
+ document.documentElement.classList.remove("restarting");
105
+ }
106
+ };
107
+ }
108
+
109
+ async disconnect() {
110
+ this.socket.close();
111
+ }
112
+ }
@@ -0,0 +1,70 @@
1
+ import { Controller } from "stimulus";
2
+
3
+ function make_new_link(href: string) {
4
+ const new_link = document.createElement("link");
5
+ new_link.rel = "stylesheet";
6
+ new_link.href = href.split("?")[0] + `?${Math.random()}+${Math.random()}`;
7
+ new_link.type = "text/css";
8
+ return new_link;
9
+ }
10
+
11
+ function refresh_css() {
12
+ const actual_hrefs = new Set(
13
+ Array.from(document.querySelectorAll("head link[rel=stylesheet]")).map(
14
+ (e) => e?.getAttribute("href")?.split("?")[0]
15
+ )
16
+ );
17
+ return actual_hrefs.forEach((href: string) => {
18
+ const new_link = make_new_link(href);
19
+ const to_delete_after_load = Array.from(
20
+ document.querySelectorAll(
21
+ `head link[rel=stylesheet][href^="${href}"]`
22
+ )
23
+ );
24
+ new_link.onload = function () {
25
+ // delay to prevent flicker
26
+ setTimeout(() => {
27
+ to_delete_after_load.forEach((to_remove) => {
28
+ to_remove.remove();
29
+ });
30
+ }, 100);
31
+ };
32
+ document.querySelector("head")?.appendChild(new_link);
33
+ });
34
+ }
35
+
36
+ export default class RefreshStyles extends Controller {
37
+ socket: WebSocket;
38
+
39
+ async connect() {
40
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
41
+ const { port } = await fetch("/dist/notifier.json").then((r) =>
42
+ r.json()
43
+ );
44
+ try {
45
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
46
+
47
+ this.socket = new WebSocket(`ws://localhost:${port as number}`);
48
+ } catch (e) {
49
+ console.error(e);
50
+ // eslint-disable-next-line no-console
51
+ console.warn(
52
+ "Not enabling auto style refresh due to the above error"
53
+ );
54
+ }
55
+ this.socket.onmessage = async (message) => {
56
+ if (message.data === "css") {
57
+ refresh_css();
58
+ }
59
+ };
60
+ document.documentElement.addEventListener("turbo:morph", refresh_css);
61
+ }
62
+
63
+ async disconnect() {
64
+ this.socket.close();
65
+ document.documentElement.removeEventListener(
66
+ "turbo:morph",
67
+ refresh_css
68
+ );
69
+ }
70
+ }
@@ -0,0 +1,66 @@
1
+ /* eslint-disable @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-assignment */
2
+ import { Controller } from "stimulus";
3
+ import { throttle } from "throttle-debounce";
4
+
5
+ export default class SubmitOnInput extends Controller<HTMLTextAreaElement> {
6
+ sendValues: () => void;
7
+
8
+ connect() {
9
+ this.sendValues = throttle(
10
+ 500,
11
+ () => {
12
+ const component_index =
13
+ this.element
14
+ .closest(".jdd-editor__component-block")
15
+ ?.getAttribute("data-component-index") || null;
16
+ if (component_index == null) {
17
+ // not an input within a JDD component editor
18
+ this.element.closest("form")?.requestSubmit();
19
+ return;
20
+ }
21
+
22
+ // During this automatic submit, we force idiomorph to only update the components edited by this input.
23
+ const updateOnlyPreview = (event: BeforeUnloadEvent) => {
24
+ const target = event.target as HTMLDivElement;
25
+ if (
26
+ [
27
+ "component-arguments",
28
+ "component-preview__header",
29
+ ].includes(target.id) ||
30
+ (target.parentElement?.classList.contains(
31
+ "jdd-container"
32
+ ) &&
33
+ !target.classList.contains(
34
+ "component-number-" + component_index
35
+ ))
36
+ ) {
37
+ event.preventDefault();
38
+ }
39
+ };
40
+ document.addEventListener(
41
+ "turbo:before-morph-element",
42
+ updateOnlyPreview
43
+ );
44
+ this.element.closest("form")?.requestSubmit();
45
+ const after_submit = () => {
46
+ document.removeEventListener(
47
+ "turbo:before-morph-element",
48
+ updateOnlyPreview
49
+ );
50
+ document.removeEventListener("turbo:morph", after_submit);
51
+ };
52
+ document.addEventListener("turbo:morph", after_submit);
53
+ },
54
+ { noTrailing: false }
55
+ );
56
+ }
57
+
58
+ makePermanent() {
59
+ // this prevents morphing from overwriting the input value with previous half-dane values - https://github.com/hotwired/turbo/issues/1199
60
+ this.element.setAttribute("data-turbo-permanent", "");
61
+ }
62
+
63
+ makeNotPermanent() {
64
+ this.element.removeAttribute("data-turbo-permanent");
65
+ }
66
+ }
@@ -0,0 +1,15 @@
1
+ import { Controller } from "stimulus";
2
+
3
+ export default class Toast extends Controller {
4
+ connect() {
5
+ console.log("connect");
6
+ this.element.addEventListener(
7
+ "animationend",
8
+ ({ animationName }: AnimationEvent) => {
9
+ if (animationName == "fade-out") {
10
+ this.element.parentElement?.removeChild(this.element);
11
+ }
12
+ }
13
+ );
14
+ }
15
+ }
@@ -0,0 +1,127 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
+ /* eslint-disable @typescript-eslint/consistent-type-assertions */
4
+ /* eslint-disable @typescript-eslint/no-unused-vars */
5
+ import type Router from "@koa/router";
6
+ import type { JDDocumentContainer, RawJDDocument } from "@sealcode/jdd";
7
+ import {
8
+ documentContainerFromStorage,
9
+ documentToParsed,
10
+ documentToStorage,
11
+ } from "@sealcode/jdd";
12
+ import type { Context } from "koa";
13
+ import type { Collection, CollectionItem, FieldNames } from "sealious";
14
+ import JDDCreator from "./jdd-creator.js";
15
+ import type { JDDPageState } from "./jdd-page.js";
16
+ import { tempstream } from "tempstream";
17
+
18
+ export abstract class EditJDDField<C extends Collection> extends JDDCreator {
19
+ async getID(ctx: Context): Promise<string> {
20
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-member-access
21
+ const id = ctx.params["id"] as string;
22
+ if (!id) {
23
+ throw new Error("Missing URL parameter: " + "id");
24
+ }
25
+ return id;
26
+ }
27
+
28
+ abstract getCollection(ctx: Context): C;
29
+
30
+ async getItem(ctx: Context): Promise<CollectionItem<C>> {
31
+ const {
32
+ items: [item],
33
+ } = await this.getCollection(ctx)
34
+ .list(ctx.$context)
35
+ .ids([await this.getID(ctx)])
36
+ .fetch();
37
+ if (!item) {
38
+ throw new Error(
39
+ "Couldn't get item of id " + (await this.getID(ctx))
40
+ );
41
+ }
42
+ return item;
43
+ }
44
+
45
+ abstract getJDDFieldName(): FieldNames<C["fields"]>;
46
+
47
+ mount(router: Router, path: string) {
48
+ super.mount(router, path);
49
+
50
+ router.post(path + "save/", async (ctx) => {
51
+ const { state } = await this.extractState(ctx);
52
+ const item = await this.getItem(ctx);
53
+
54
+ item.set(
55
+ this.getJDDFieldName(),
56
+ (
57
+ await documentToStorage(
58
+ this.registry,
59
+ this.makeJDDContext(ctx),
60
+ {
61
+ value: state.components,
62
+ } as unknown as JDDocumentContainer<"parsed">
63
+ )
64
+ ).value as any
65
+ );
66
+ await item.save(ctx.$context);
67
+ ctx.type = "html";
68
+ ctx.status = 422;
69
+ if (!state.messages) {
70
+ state.messages = [];
71
+ }
72
+ state.messages.push("Saved!");
73
+ ctx.body = this.render(ctx, state);
74
+ });
75
+ }
76
+
77
+ async renderHeader(_ctx: Context, _item: CollectionItem<C>) {
78
+ return /* HTML */ `<h1>Edit JDD</h1>`;
79
+ }
80
+
81
+ async renderPreParameterButtons(ctx: Context) {
82
+ const item = await this.getItem(ctx);
83
+ return tempstream`<div>${this.renderHeader(ctx, item)}</div>`;
84
+ }
85
+
86
+ renderParameterButtons(state: JDDPageState) {
87
+ {
88
+ /*The below button has to be here in order for it to be the default behavior */
89
+ }
90
+ return `<div class="jdd-editor__toolbar">
91
+ <input type="submit" value="Preview" />
92
+ <select name="component">
93
+ ${Object.keys(this.getRegistryComponents())
94
+ .map((cmp) => `<option value="${cmp}">${cmp}</option>`)
95
+ .join("")}
96
+ </select>
97
+ ${this.makeActionButton(state, {
98
+ action: "add_component",
99
+ label: "Add component",
100
+ })}
101
+ <input type="submit" formaction="./save/" value="zapisz" />
102
+ </div>`;
103
+ }
104
+
105
+ async getInitialState(ctx: Context) {
106
+ const article = await this.getItem(ctx);
107
+ const parsed_document = await documentToParsed(
108
+ this.registry,
109
+ this.makeJDDContext(ctx),
110
+ documentContainerFromStorage(
111
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
112
+ (article.get(this.getJDDFieldName()) as RawJDDocument) || []
113
+ )
114
+ );
115
+ return {
116
+ components: parsed_document.value.map((e) => ({
117
+ ...e,
118
+ open: true,
119
+ })),
120
+ };
121
+ }
122
+
123
+ // uncomment to create whitelist of allowed components
124
+ // getAllowedComponents() {
125
+ // return ["nice-box"];
126
+ // }
127
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./edit-jdd-field.js";
2
+ export * from "./components.sreact.js";
@@ -0,0 +1,36 @@
1
+ import type { Enum } from "@sealcode/jdd";
2
+ import { printArgPath } from "./print-arg-path.js";
3
+
4
+ export function ComponentInputEnum<State, S extends string, T extends Enum<S>>({
5
+ arg_path,
6
+ arg,
7
+ value,
8
+ onchange,
9
+ }: {
10
+ state: State;
11
+ arg_path: string[];
12
+ arg: T;
13
+ value: string;
14
+ onchange?: string;
15
+ }) {
16
+ return /* HTML */ `<div id=${`component-input-enum-${arg_path.join("-")}`}>
17
+ <label>
18
+ ${arg_path.at(-1) || ""}
19
+ <select
20
+ name="${`$${printArgPath(arg_path)}`}"
21
+ onchange="${onchange || ""}"
22
+ >
23
+ ${arg.values
24
+ .map(
25
+ (v: S) => /* HTML */ `<option
26
+ value="${v}"
27
+ ${value == v ? "selected" : ""}
28
+ >
29
+ ${v}
30
+ </option>`
31
+ )
32
+ .join("")}
33
+ </select>
34
+ </label>
35
+ </div>`;
36
+ }
@@ -0,0 +1,70 @@
1
+ import type { Context } from "koa";
2
+ import type { FilePointer } from "@sealcode/file-manager";
3
+ import type { Image, JDDContext } from "@sealcode/jdd";
4
+ import type { StatefulPage } from "@sealcode/sealgen";
5
+ import type { ComponentPreviewActions } from "../component-preview-actions.js";
6
+ import type { JDDPageState } from "../jdd-page.js";
7
+ import { tempstream } from "tempstream";
8
+ import { printArgPath } from "./print-arg-path.js";
9
+
10
+ export function ComponentInputImage<State extends JDDPageState>({
11
+ arg_path,
12
+ arg,
13
+ value,
14
+ ctx,
15
+ page,
16
+ state,
17
+ makeJDDContext,
18
+ }: {
19
+ state: State;
20
+ arg_path: string[];
21
+ arg: Image;
22
+ value: FilePointer | null;
23
+ page: StatefulPage<JDDPageState, typeof ComponentPreviewActions>;
24
+ ctx: Context;
25
+ makeJDDContext: (ctx: Context) => JDDContext;
26
+ }): JSX.Element {
27
+ const jdd_context = makeJDDContext(ctx);
28
+ return tempstream/* HTML */ `<div style="margin-bottom: 10px">
29
+ <label
30
+ style="display: flex; align-items: center; column-gap: 10px;"
31
+ data-controller="input-image-preview"
32
+ >
33
+ ${arg_path.at(-1) || ""}
34
+ <div class="image-preview-container">
35
+ ${value &&
36
+ jdd_context.render_image(value, {
37
+ container: {
38
+ width: 40,
39
+ height: 40,
40
+ objectFit: "cover",
41
+ },
42
+ crop: { width: 40, height: 40 },
43
+ style: "height: 40px; width: 40px;",
44
+ alt: "",
45
+ })}
46
+ </div>
47
+
48
+ <input
49
+ type="file"
50
+ name="${`$${printArgPath(arg_path)}.new`}"
51
+ value=""
52
+ autocomplete="off"
53
+ data-action="change->input-image-preview#handleChange"
54
+ ${arg.hasParent("list") ? "multiple" : ""}
55
+ />
56
+ </label>
57
+ <div>
58
+ <input type="hidden" name="${`$${printArgPath(arg_path)}.old`}"
59
+ value={value?.token || ""} autocomplete="off" />
60
+ </div>
61
+ ${page.makeActionButton(
62
+ state,
63
+ {
64
+ action: "remove_file",
65
+ label: "❌",
66
+ },
67
+ arg_path
68
+ )}
69
+ </div>`;
70
+ }