@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.
- package/.arcconfig +12 -0
- package/.arclint +18 -0
- package/.eslintrc.js +37 -0
- package/.mocharc.js +6 -0
- package/.nycrc +6 -0
- package/.prettierrc +14 -0
- package/@types/component-preview-actions.d.ts +43 -0
- package/@types/components.sreact.d.ts +48 -0
- package/@types/controllers/autogrow-textarea.stimulus.d.ts +5 -0
- package/@types/controllers/component-debugger.stimulus.d.ts +28 -0
- package/@types/controllers/exportable-textarea.stimulus.d.ts +7 -0
- package/@types/controllers/input-image-preview.stimulus.d.ts +5 -0
- package/@types/controllers/jdd-table-paste.stimulus.d.ts +8 -0
- package/@types/controllers/json-editor.stimulus.d.ts +9 -0
- package/@types/controllers/markdown-textarea.stimulus.d.ts +20 -0
- package/@types/controllers/refresh-on-ts-changes.stimulus.d.ts +6 -0
- package/@types/controllers/refresh-styles.stimulus.d.ts +6 -0
- package/@types/controllers/submit-on-input.stimulus.d.ts +7 -0
- package/@types/controllers/toast.stimulus.d.ts +4 -0
- package/@types/edit-jdd-field.d.ts +22 -0
- package/@types/index.d.ts +2 -0
- package/@types/inputs/component-input-enum.d.ts +8 -0
- package/@types/inputs/component-input-image.d.ts +15 -0
- package/@types/inputs/component-input-list.d.ts +15 -0
- package/@types/inputs/component-input-single-reference.d.ts +11 -0
- package/@types/inputs/component-input-structured.d.ts +15 -0
- package/@types/inputs/component-input-table.d.ts +15 -0
- package/@types/inputs/component-input.d.ts +16 -0
- package/@types/inputs/print-arg-path.d.ts +1 -0
- package/@types/jdd-creator.d.ts +49 -0
- package/@types/jdd-page.d.ts +85 -0
- package/assets/icons/table-add-column-right.svg +1 -0
- package/assets/icons/table-add-row-below.svg +1 -0
- package/assets/icons/table-add-row-header-below.svg +1 -0
- package/assets/icons/table-delete-column.svg +1 -0
- package/assets/icons/table-delete-row.svg +1 -0
- package/assets/icons/table-move-column-right.svg +1 -0
- package/assets/icons/table-move-row-down.svg +1 -0
- package/assets/icons/table-move-row-up.svg +1 -0
- package/assets/styles/component-admin-table.jdd-page.css +11 -0
- package/assets/styles/component-debugger.jdd-page.css +71 -0
- package/assets/styles/components.jdd-page.css +286 -0
- package/assets/styles/grow-wrap.css +33 -0
- package/assets/styles/markdown-editor.css +42 -0
- package/dist/src/autogrow-textarea.stimulus.js +13 -0
- package/dist/src/autogrow-textarea.stimulus.js.map +7 -0
- package/dist/src/component-debugger.stimulus.js +190 -0
- package/dist/src/component-debugger.stimulus.js.map +7 -0
- package/dist/src/component-preview-actions.js +439 -0
- package/dist/src/component-preview-actions.js.map +7 -0
- package/dist/src/components.sreact.js +93 -0
- package/dist/src/components.sreact.js.map +7 -0
- package/dist/src/controllers/autogrow-textarea.stimulus.js +13 -0
- package/dist/src/controllers/autogrow-textarea.stimulus.js.map +7 -0
- package/dist/src/controllers/component-debugger.stimulus.js +193 -0
- package/dist/src/controllers/component-debugger.stimulus.js.map +7 -0
- package/dist/src/controllers/exportable-textarea.stimulus.js +71 -0
- package/dist/src/controllers/exportable-textarea.stimulus.js.map +7 -0
- package/dist/src/controllers/input-image-preview.stimulus.js +30 -0
- package/dist/src/controllers/input-image-preview.stimulus.js.map +7 -0
- package/dist/src/controllers/jdd-table-paste.stimulus.js +78 -0
- package/dist/src/controllers/jdd-table-paste.stimulus.js.map +7 -0
- package/dist/src/controllers/json-editor.stimulus.js +114 -0
- package/dist/src/controllers/json-editor.stimulus.js.map +7 -0
- package/dist/src/controllers/markdown-textarea.stimulus.js +174 -0
- package/dist/src/controllers/markdown-textarea.stimulus.js.map +7 -0
- package/dist/src/controllers/refresh-on-ts-changes.stimulus.js +90 -0
- package/dist/src/controllers/refresh-on-ts-changes.stimulus.js.map +7 -0
- package/dist/src/controllers/refresh-styles.stimulus.js +67 -0
- package/dist/src/controllers/refresh-styles.stimulus.js.map +7 -0
- package/dist/src/controllers/submit-on-input.stimulus.js +55 -0
- package/dist/src/controllers/submit-on-input.stimulus.js.map +7 -0
- package/dist/src/controllers/toast.stimulus.js +19 -0
- package/dist/src/controllers/toast.stimulus.js.map +7 -0
- package/dist/src/edit-jdd-field.js +94 -0
- package/dist/src/edit-jdd-field.js.map +7 -0
- package/dist/src/exportable-textarea.stimulus.js +71 -0
- package/dist/src/exportable-textarea.stimulus.js.map +7 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +7 -0
- package/dist/src/input-image-preview.stimulus.js +30 -0
- package/dist/src/input-image-preview.stimulus.js.map +7 -0
- package/dist/src/inputs/component-input-enum.js +30 -0
- package/dist/src/inputs/component-input-enum.js.map +7 -0
- package/dist/src/inputs/component-input-image.js +58 -0
- package/dist/src/inputs/component-input-image.js.map +7 -0
- package/dist/src/inputs/component-input-list.js +74 -0
- package/dist/src/inputs/component-input-list.js.map +7 -0
- package/dist/src/inputs/component-input-single-reference.js +31 -0
- package/dist/src/inputs/component-input-single-reference.js.map +7 -0
- package/dist/src/inputs/component-input-structured.js +36 -0
- package/dist/src/inputs/component-input-structured.js.map +7 -0
- package/dist/src/inputs/component-input-table.js +228 -0
- package/dist/src/inputs/component-input-table.js.map +7 -0
- package/dist/src/inputs/component-input.js +129 -0
- package/dist/src/inputs/component-input.js.map +7 -0
- package/dist/src/inputs/print-arg-path.js +7 -0
- package/dist/src/inputs/print-arg-path.js.map +7 -0
- package/dist/src/jdd-creator.js +131 -0
- package/dist/src/jdd-creator.js.map +7 -0
- package/dist/src/jdd-page.js +339 -0
- package/dist/src/jdd-page.js.map +7 -0
- package/dist/src/jdd-table-paste.stimulus.js +78 -0
- package/dist/src/jdd-table-paste.stimulus.js.map +7 -0
- package/dist/src/json-editor.stimulus.js +114 -0
- package/dist/src/json-editor.stimulus.js.map +7 -0
- package/dist/src/markdown-textarea.stimulus.js +174 -0
- package/dist/src/markdown-textarea.stimulus.js.map +7 -0
- package/dist/src/submit-on-input.stimulus.js +55 -0
- package/dist/src/submit-on-input.stimulus.js.map +7 -0
- package/dist/src/toast.stimulus.js +19 -0
- package/dist/src/toast.stimulus.js.map +7 -0
- package/esbuild.cjs +20 -0
- package/esbuild.js +23 -0
- package/jenkins.sanity.sh +3 -0
- package/lib/component-preview-actions.js +286 -0
- package/lib/component-preview-actions.js.map +1 -0
- package/lib/components.sreact.js +102 -0
- package/lib/components.sreact.js.map +1 -0
- package/lib/controllers/autogrow-textarea.stimulus.js +15 -0
- package/lib/controllers/autogrow-textarea.stimulus.js.map +1 -0
- package/lib/controllers/component-debugger.stimulus.js +188 -0
- package/lib/controllers/component-debugger.stimulus.js.map +1 -0
- package/lib/controllers/exportable-textarea.stimulus.js +79 -0
- package/lib/controllers/exportable-textarea.stimulus.js.map +1 -0
- package/lib/controllers/input-image-preview.stimulus.js +28 -0
- package/lib/controllers/input-image-preview.stimulus.js.map +1 -0
- package/lib/controllers/jdd-table-paste.stimulus.js +84 -0
- package/lib/controllers/jdd-table-paste.stimulus.js.map +1 -0
- package/lib/controllers/json-editor.stimulus.js +134 -0
- package/lib/controllers/json-editor.stimulus.js.map +1 -0
- package/lib/controllers/markdown-textarea.stimulus.js +186 -0
- package/lib/controllers/markdown-textarea.stimulus.js.map +1 -0
- package/lib/controllers/refresh-on-ts-changes.stimulus.js +123 -0
- package/lib/controllers/refresh-on-ts-changes.stimulus.js.map +1 -0
- package/lib/controllers/refresh-styles.stimulus.js +66 -0
- package/lib/controllers/refresh-styles.stimulus.js.map +1 -0
- package/lib/controllers/submit-on-input.stimulus.js +48 -0
- package/lib/controllers/submit-on-input.stimulus.js.map +1 -0
- package/lib/controllers/toast.stimulus.js +16 -0
- package/lib/controllers/toast.stimulus.js.map +1 -0
- package/lib/edit-jdd-field.js +102 -0
- package/lib/edit-jdd-field.js.map +1 -0
- package/lib/index.js +19 -0
- package/lib/index.js.map +1 -0
- package/lib/inputs/component-input-enum.js +25 -0
- package/lib/inputs/component-input-enum.js.map +1 -0
- package/lib/inputs/component-input-image.js +47 -0
- package/lib/inputs/component-input-image.js.map +1 -0
- package/lib/inputs/component-input-list.js +61 -0
- package/lib/inputs/component-input-list.js.map +1 -0
- package/lib/inputs/component-input-single-reference.js +36 -0
- package/lib/inputs/component-input-single-reference.js.map +1 -0
- package/lib/inputs/component-input-structured.js +42 -0
- package/lib/inputs/component-input-structured.js.map +1 -0
- package/lib/inputs/component-input-table.js +184 -0
- package/lib/inputs/component-input-table.js.map +1 -0
- package/lib/inputs/component-input.js +133 -0
- package/lib/inputs/component-input.js.map +1 -0
- package/lib/inputs/print-arg-path.js +7 -0
- package/lib/inputs/print-arg-path.js.map +1 -0
- package/lib/jdd-creator.js +113 -0
- package/lib/jdd-creator.js.map +1 -0
- package/lib/jdd-page.js +310 -0
- package/lib/jdd-page.js.map +1 -0
- package/package.json +61 -0
- package/src/component-preview-actions.ts +520 -0
- package/src/components.sreact.ts +100 -0
- package/src/controllers/autogrow-textarea.stimulus.ts +13 -0
- package/src/controllers/component-debugger.stimulus.ts +247 -0
- package/src/controllers/exportable-textarea.stimulus.ts +77 -0
- package/src/controllers/input-image-preview.stimulus.ts +29 -0
- package/src/controllers/jdd-table-paste.stimulus.ts +89 -0
- package/src/controllers/json-editor.stimulus.ts +127 -0
- package/src/controllers/markdown-textarea.stimulus.ts +198 -0
- package/src/controllers/refresh-on-ts-changes.stimulus.ts +112 -0
- package/src/controllers/refresh-styles.stimulus.ts +70 -0
- package/src/controllers/submit-on-input.stimulus.ts +66 -0
- package/src/controllers/toast.stimulus.ts +15 -0
- package/src/edit-jdd-field.ts +127 -0
- package/src/index.ts +2 -0
- package/src/inputs/component-input-enum.ts +36 -0
- package/src/inputs/component-input-image.ts +70 -0
- package/src/inputs/component-input-list.ts +91 -0
- package/src/inputs/component-input-single-reference.ts +45 -0
- package/src/inputs/component-input-structured.ts +51 -0
- package/src/inputs/component-input-table.ts +262 -0
- package/src/inputs/component-input.ts +158 -0
- package/src/inputs/print-arg-path.ts +3 -0
- package/src/jdd-creator.ts +151 -0
- package/src/jdd-page.ts +439 -0
- 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,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
|
+
}
|