PageSourceSearch

https://auxdata.ai/js/ui/dialogs/userinput.js

js auxdata.ai collected 2026-10-05 11:50:28 UTC 22,196 bytes, 688 lines download raw bytes

1// @ts-check
2import { AtomButton } from "../atoms/atombutton.js";
3import { AtomContainer } from "../atoms/atomcontainer.js";
4import { AtomContainerHorizontal } from "../atoms/atomcontainerhorizontal.js";
5import { AtomContainerVertical } from "../atoms/atomcontainervertical.js";
6import { AtomDisplayHeader3 } from "../atoms/atomdisplayheader3.js";
7import { AtomDisplayParagraph } from "../atoms/atomdisplayparagraph.js";
8import { AtomInputSelection } from "../atoms/atominputselection.js";
9import { AtomInputText } from "../atoms/atominputtext.js";
10import { AtomInputTextArea } from "../atoms/atominputtextarea.js";
11import { MoleculeFileDropZoneSelection } from "../molecules/moleculefiledropzoneselection.js";
12import { MoleculeLabelCheckBox } from "../molecules/moleculelabelcheckbox.js";
13import { LoadingScreen } from "../helper/loadingscreen.js";
14import { UserMessage } from "./usermessage.js";
15
16
17export class UserInput extends UserMessage {
18
19    /**
20     * @param {string} title
21     * @param {string} label
22     * @param {string | null} defaultValue
23     * @param {Function} callbackOk
24     * @param {Function | null} callbacCancel
25     */
26    constructor(title, label, defaultValue, callbackOk, callbacCancel) {
27        super(title, label, "../images/msg_question.png")
28        this.callbackOk = callbackOk
29        this.callbackCancel = callbacCancel;
30        this.defaultValue = defaultValue;
31
32        /**
33         * @type {AtomInputText | AtomInputTextArea | AtomInputSelection | null}
34         */
35        this.inputField = null;
36
37        /**
38         * @type {string | number | null | Object}
39         */
40        this.callbackId = -1;
41
42        /** @type {string | null} */
43        this._inputTestid = null;
44
45        /** @type {string | null} */
46        this._saveTestid = null;
47    }
48
49    // can be overridden in derived class for custom buttons
50    addButtons() {
51        this.addSaveButton("OK", this.callbackOk, this._saveTestid ?? undefined)
52        this.addCancelButton("Abbrechen", this.callbackCancel);
53    }
54
55    ok() {
56        if (!this.inputField) {
57            return super.ok();
58        }
59
60        if (this.saveDataCallback) {
61
62            let ret = null;
63
64            if (this.callbackId == -1) {
65                ret = this.saveDataCallback(this.inputField.getValue());
66            } else {
67                ret = this.saveDataCallback(this.callbackId, this.inputField.getValue());
68            }
69
70            if (ret != null || ret != undefined) {
71                if (ret == false) {
72                    return;
73                }
74            }
75        }
76
77        this.close();
78    }
79
80    cancel() {
81        LoadingScreen.hide();
82        super.cancel();
83    }
84}
85
86export class SecurityQuestion extends UserInput {
87
88    /**
89     * @param {string} title
90     * @param {string} label
91     * @param {Function} callbackYes
92     * @param {Function | null} callbackNo
93     */
94    constructor(title, label, callbackYes, callbackNo) {
95        super(title, label, "", callbackYes, callbackNo);
96        this.imagePath =  "../images/msg_warn.png";
97        this.callbackYes = callbackYes
98        this.callbackNo = callbackNo;
99        
100    }
101
102    // can be overridden in derived class for custom buttons
103    addButtons() {
104        if (this.buttonContainer != null) {
105            this.saveButton = new AtomButton(this.buttonContainer, "", "Ja", "./images/ok.svg", "confirm-yes");
106            this.setSaveDataFunction(this.callbackYes);
107        }
108        this.addCancelButton("Nein", this.callbackNo);
109    }
110
111    ok() {
112        
113        if (this.saveDataCallback) {
114
115            let ret = null;
116
117            if (this.callbackId == -1) {
118                ret = this.saveDataCallback();
119            } else {
120                ret = this.saveDataCallback(this.callbackId);
121            }
122
123            if (ret != null || ret != undefined) {
124                if (ret == false) {
125                    return;
126                }
127            }
128        }
129
130        this.close();
131    }
132
133
134    /**
135     * @param {string} title
136     * @param {string} label
137     * @param {Function} callbackYes
138     * @param {Function | null} [callbackNo]
139     */
140    static show(title, label, callbackYes, callbackNo = null) {
141        const dlg = new SecurityQuestion(title, label, callbackYes, callbackNo);
142        dlg.exec();
143    }
144
145    /**
146     * @param {string} title
147     * @param {string} label
148     * @param {any} id
149     * @param {Function} callbackOk
150     * @param {Function | null} [callbackCancel]
151     */
152    static showWithId(title, label, id, callbackOk, callbackCancel = null) {
153        const dlg = new SecurityQuestion(title, label, callbackOk, callbackCancel);
154        dlg.callbackId = id;
155        dlg.exec();
156    }
157}
158
159export class LineInput extends UserInput {
160
161    /**
162     * @param {string} title
163     * @param {string} label
164     * @param {string | null} defaultValue
165     * @param {Function} callbackOk
166     * @param {Function | null} callbacCancel
167     */
168    constructor(title, label, defaultValue, callbackOk, callbacCancel) {
169        super(title, label, defaultValue, callbackOk, callbacCancel);
170    }
171
172    /**
173     * @param {AtomContainerHorizontal} parent
174     */
175    addInputField(parent) {
176
177        if (this.defaultValue != null) {
178            this.inputField = new AtomInputText(parent, "inputText", this.defaultValue, this._inputTestid ?? undefined)
179        } else {
180            this.inputField = new AtomInputText(parent, "inputText", "", this._inputTestid ?? undefined);
181        }
182    }
183
184    /**
185     * @param {string} title
186     * @param {string} label
187     * @param {string | null} defaultValue
188     * @param {Function} callbackOk
189     * @param {Function | null} [callbackCancel]
190     */
191    static show(title, label, defaultValue, callbackOk, callbackCancel = null) {
192        const dlg = new LineInput(title, label, defaultValue, callbackOk, callbackCancel);
193        dlg.exec();
194    }
195
196    /**
197     * @param {string} title
198     * @param {string} label
199     * @param {string | null} defaultValue
200     * @param {any} id
201     * @param {Function} callbackOk
202     * @param {Function | null} [callbackCancel]
203     */
204    static showWithId(title, label, id, defaultValue, callbackOk, callbackCancel = null) {
205        const dlg = new LineInput(title, label, defaultValue, callbackOk, callbackCancel);
206        dlg.callbackId = id;
207        dlg.exec();
208    }
209}
210
211export class TextInput extends UserInput {
212
213    /**
214     * @param {string} title
215     * @param {string} label
216     * @param {string | null} defaultValue
217     * @param {Function} callbackOk
218     * @param {Function | null} callbacCancel
219     */
220    constructor(title, label, defaultValue, callbackOk, callbacCancel) {
221        super(title, label, defaultValue, callbackOk, callbacCancel)
222    }
223
224    renderUi() {
225        this.createOverlay();
226        const dialog = document.createElement('div');
227        dialog.id = this.id;
228        dialog.className = "dialog";
229        // i don't know why but if I set it here it works in css it will be ignored
230        dialog.style.zIndex = "60000";
231        dialog.style.position = 'absolute';
232        dialog.style.top = '50%';
233        dialog.style.left = '50%';
234        dialog.style.transform = 'translate(-50%, -50%)';
235        dialog.style.backgroundColor = 'white';
236        dialog.style.height = "400px";
237        dialog.style.minHeight = "400px";
238        dialog.style.maxHeight = "min(600px, calc(100vh - 24px))";
239        dialog.style.marginTop = "5%";
240        // Stay within the viewport on narrow screens — otherwise the
241        // fixed 400px min width is cut off on the right on mobile.
242        dialog.style.minWidth = "min(400px, calc(100vw - 24px))";
243        dialog.style.maxWidth = "min(600px, calc(100vw - 24px))";
244        dialog.style.width = "min(600px, calc(100vw - 24px))";
245        dialog.style.boxSizing = "border-box";
246
247        this.setRenderedElement(dialog);
248        document.body.appendChild(dialog);
249        this.translator.refreshTranslationsInBackend();
250    }
251
252    /**
253     * @param {AtomContainerHorizontal} parent
254     */
255    addInputField(parent) {
256
257        if (this.defaultValue != null) {
258            this.inputField = new AtomInputTextArea(parent, "inputText", this.defaultValue)
259        } else {
260            this.inputField = new AtomInputTextArea(parent, "inputText", "");
261        }
262    }
263
264    /**
265     * @param {string} title
266     * @param {string} label
267     * @param {string | null} defaultValue
268     * @param {Function} callbackOk
269     * @param {Function | null} [callbackCancel]
270     */
271    static show(title, label, defaultValue, callbackOk, callbackCancel = null) {
272        const dlg = new TextInput(title, label, defaultValue, callbackOk, callbackCancel);
273        dlg.exec();
274    }
275
276    /**
277     * @param {string} title
278     * @param {string} label
279     * @param {string | null} defaultValue
280     * @param {string | number} id
281     * @param {Function} callbackOk
282     * @param {Function | null} [callbackCancel]
283     */
284    static showWithId(title, label, id, defaultValue, callbackOk, callbackCancel = null) {
285        const dlg = new TextInput(title, label, defaultValue, callbackOk, callbackCancel);
286        dlg.callbackId = id;
287        dlg.exec();
288    }
289}
290
291export class SelectValue extends UserInput {
292
293    /**
294     * @param {string} title
295     * @param {string} label
296     * @param {any} selections
297     * @param {string | null} defaultValue
298     * @param {Function} callbackOk
299     * @param {Function | null} callbacCancel
300     */
301    constructor(title, label, selections, defaultValue, callbackOk, callbacCancel) {
302        super(title, label, defaultValue, callbackOk, callbacCancel)
303        this.selections = selections;
304    }
305
306    /**
307     * @param {AtomContainerHorizontal} parent
308     */
309    addInputField(parent) {
310
311        if (this.defaultValue != null) {
312            this.inputField = new AtomInputSelection(parent, "selection", this.defaultValue)
313        } else {
314            this.inputField = new AtomInputSelection(parent, "selection", "")
315        }
316    }
317
318    fillData() {
319        this.selections.forEach((/** @type {{ id: string; name: string; }} */ entry) => {
320            if (this.inputField instanceof AtomInputSelection) {
321                this.inputField?.addSelection(entry.id, entry.name);
322            }
323                    
324        });
325    }
326
327       /**
328     * @param {string} title
329     * @param {string} label
330     * @param {any} selections
331     * @param {string | null} defaultValue
332     * @param {string | number} id
333     * @param {Function} callbackOk
334     * @param {Function | null} [callbackCancel]
335     */
336    static showWithId(title, label, selections, defaultValue, id, callbackOk, callbackCancel = null) {
337        const dlg = new SelectValue(title, label, selections, defaultValue, callbackOk, callbackCancel);
338        dlg.callbackId = id;
339        dlg.exec();
340    }
341}
342
343export class FilleInput extends UserMessage {
344
345    /**
346     * Dieser Dialog wird ausschliesslich fuer Importe benutzt. Der Hinweis unter
347     * der Ueberschrift nennt deshalb standardmaessig JSON; Dialoge mit weiteren
348     * erlaubten Formaten (Agent-Backup, Swagger) geben ihren eigenen Text mit.
349     */
350    static HINT_JSON = "JSON (.json)";
351
352    /**
353     * @param {string} title
354     * @param {string} label
355     * @param {Function} callbackOk
356     * @param {Function | null} callbacCancel
357     * @param {string} [hintText]
358     */
359    constructor(title, label, callbackOk, callbacCancel, hintText = FilleInput.HINT_JSON) {
360        // No side image — the drop zone takes the full dialog width.
361        super(title, label, "");
362        this.callbackOk = callbackOk
363        this.callbackCancel = callbacCancel;
364        this._hintText = hintText;
365
366        /** @type {MoleculeFileDropZoneSelection | null} */
367        this.dropzone = null;
368
369        /**
370         * @type {string | number | null | Object}
371         */
372        this.callbackId = -1;
373
374        this._multi = false;
375    }
376
377    renderUi() {
378        this.createOverlay();
379        const dialog = document.createElement('div');
380        dialog.id = this.id;
381        dialog.className = "dialog";
382        dialog.style.zIndex = "60000";
383        dialog.style.position = 'absolute';
384        dialog.style.top = '50%';
385        dialog.style.left = '50%';
386        dialog.style.transform = 'translate(-50%, -50%)';
387        dialog.style.backgroundColor = 'white';
388        dialog.style.display = 'flex';
389        dialog.style.flexDirection = 'column';
390        dialog.style.height = "auto";
391        dialog.style.minHeight = "0";
392        dialog.style.maxHeight = "min(560px, calc(100vh - 24px))";
393        dialog.style.marginTop = "0";
394        dialog.style.minWidth = "min(480px, calc(100vw - 24px))";
395        dialog.style.maxWidth = "min(640px, calc(100vw - 24px))";
396        dialog.style.width = "min(640px, calc(100vw - 24px))";
397        dialog.style.boxSizing = "border-box";
398
399        this.setRenderedElement(dialog);
400        document.body.appendChild(dialog);
401        this.translator.refreshTranslationsInBackend();
402    }
403
404    // Override UserMessage.setupDialogUi: no image wrapper — full-width drop zone.
405    /**
406     * @param {AtomContainer} parent
407     */
408    setupDialogUi(parent) {
409        if (this.label && this.label !== "") {
410            new AtomDisplayParagraph(parent, "message", this.label);
411        }
412        this.addInputField(parent);
413        this.addButtons();
414    }
415
416    /**
417     * @param {AtomContainer} parent
418     */
419    addInputField(parent) {
420        this.dropzone = new MoleculeFileDropZoneSelection(
421            parent,
422            "filleinput",
423            "Datei hier ablegen oder klicken",
424            "files",
425            this._hintText,
426        );
427    }
428
429    addButtons() {
430        this.addSaveButton("OK", this.callbackOk)
431        this.addCancelButton("Abbrechen", this.callbackCancel);
432    }
433
434    async ok() {
435        if (this.saveDataCallback) {
436            let ret = null;
437
438            LoadingScreen.show();
439            const files = this.dropzone?.getFileList();
440
441            if (this._multi) {
442                const arr = files ? Array.from(files) : [];
443                ret = (this.callbackId == -1)
444                    ? this.saveDataCallback(arr)
445                    : this.saveDataCallback(this.callbackId, arr);
446            } else {
447                const fileObj = (files && files.length > 0) ? files[0] : null;
448                ret = (this.callbackId == -1)
449                    ? this.saveDataCallback(fileObj)
450                    : this.saveDataCallback(this.callbackId, fileObj);
451            }
452
453            LoadingScreen.hide();
454
455            if (ret != null || ret != undefined) {
456                if (ret == false) {
457                    return;
458                }
459            }
460        }
461
462        this.close();
463    }
464
465    /**
466     * @param {string} title
467     * @param {string} label
468     * @param {Function} callbackOk
469     * @param {Function | null} [callbackCancel]
470     * @param {string} [hintText]  – erlaubte Dateiformate; ohne Angabe JSON
471     */
472    static show(title, label, callbackOk, callbackCancel = null, hintText = FilleInput.HINT_JSON) {
473        const dlg = new FilleInput(title, label, callbackOk, callbackCancel, hintText);
474        dlg.exec();
475    }
476
477    /**
478     * @param {string} title
479     * @param {string} label
480     * @param {any} id
481     * @param {Function} callbackOk
482     * @param {Function | null} [callbackCancel]
483     * @param {string} [hintText]  – erlaubte Dateiformate; ohne Angabe JSON
484     */
485    static showWithId(title, label, id, callbackOk, callbackCancel = null, hintText = FilleInput.HINT_JSON) {
486        const dlg = new FilleInput(title, label, callbackOk, callbackCancel, hintText);
487        dlg.callbackId = id;
488        dlg.exec();
489    }
490
491    /**
492     * Multi-file variant: the OK callback receives a File[] of every selected
493     * file (the drop zone already allows multiple in "files" mode).
494     * @param {string} title
495     * @param {string} label
496     * @param {Function} callbackOk  receives File[]
497     * @param {Function | null} [callbackCancel]
498     * @param {string} [hintText]  – erlaubte Dateiformate; ohne Angabe JSON
499     */
500    static showMulti(title, label, callbackOk, callbackCancel = null, hintText = FilleInput.HINT_JSON) {
501        const dlg = new FilleInput(title, label, callbackOk, callbackCancel, hintText);
502        dlg._multi = true;
503        dlg.exec();
504    }
505}
506
507/**
508 * Modal dialog showing the bilanz of a bulk operation:
509 * "X von Y erfolgreich, Z fehlgeschlagen" plus a collapsible per-failure
510 * list. Same visual frame as SecurityQuestion (built on UserMessage).
511 *
512 * Usage:
513 *   BulkResultDialog.show("Löschen abgeschlossen", successCount, failures, () => { ... });
514 *
515 * `failures` is an array of { documentId, name, error } objects matching
516 * domain.BulkDocumentFailure. The callback fires when the user clicks OK.
517 */
518export class BulkResultDialog extends UserInput {
519
520    /**
521     * @param {string} title
522     * @param {number} successCount
523     * @param {Array<{documentId: string, name: string, error: string}>} failures
524     * @param {Function | null} callbackOk
525     */
526    constructor(title, successCount, failures, callbackOk) {
527        const total = successCount + (failures?.length ?? 0);
528        const failedCount = failures?.length ?? 0;
529        const label = `${successCount} von ${total} erfolgreich, ${failedCount} fehlgeschlagen.`;
530
531        super(title, label, "", callbackOk ?? (() => {}), null);
532
533        this.imagePath = failedCount === 0
534            ? "../images/msg_info.png"
535            : "../images/msg_warn.png";
536        this.failures = failures ?? [];
537    }
538
539    addButtons() {
540        this.addSaveButton("OK", this.callbackOk ?? (() => {}));
541    }
542
543    addInputField(parent) {
544        if (this.failures.length === 0) return;
545
546        // Build failure list through the atom layer so it lives inside the
547        // dialog body and gets cleaned up with the dialog. (Earlier co
547de
548        // used document.createElement and dangled <details> on document.body.)
549        const list = new AtomContainerVertical(parent, "bulk-failures-list");
550        list.setCssClass("bulk-failures-list");
551
552        new AtomDisplayHeader3(list, "bulk-failures-header", `Fehlerdetails (${this.failures.length})`);
553
554        for (const f of this.failures) {
555            const labelText = (f.name && f.name !== "") ? f.name : f.documentId;
556            const errText = f.error || "Unbekannter Fehler";
557            new AtomDisplayParagraph(list, "", `${labelText}: ${errText}`);
558        }
559    }
560
561    ok() {
562        if (this.callbackOk) {
563            try { this.callbackOk(); } catch (err) { console.error(err); }
564        }
565        this.close();
566    }
567
568    /**
569     * @param {string} title
570     * @param {number} successCount
571     * @param {Array<{documentId: string, name: string, error: string}>} failures
572     * @param {Function | null} [callbackOk]
573     */
574    static show(title, successCount, failures, callbackOk = null) {
575        const dlg = new BulkResultDialog(title, successCount, failures, callbackOk);
576        dlg.exec();
577    }
578}
579
580/**
581 * Confirms an export and lets the user choose whether transitively called
582 * functions/services are included as a self-contained dependency bundle.
583 * Same visual frame as LineInput, but the input field is a single checkbox
584 * (checked by default) instead of a text field.
585 *
586 * Usage:
587 *   ExportOptionsDialog.show("Exportieren", "...", (includeDeps) => { ... });
588 */
589export class ExportOptionsDialog extends UserInput {
590
591    /**
592     * @param {string} title
593     * @param {string} label
594     * @param {(includeDeps: boolean) => void} onConfirm
595     */
596    constructor(title, label, onConfirm) {
597        super(title, label, null, onConfirm, null);
598
599        /** @type {MoleculeLabelCheckBox | null} */
600        this.includeDepsCheckbox = null;
601    }
602
603    /**
604     * @param {AtomContainerHorizontal} parent
605     */
606    addInputField(parent) {
607        this.includeDepsCheckbox = new MoleculeLabelCheckBox(
608            parent,
609            "export-options-include-deps",
610            "Aufgerufene Funktionen/Services rekursiv mitexportieren",
611            true,
612        );
613    }
614
615    ok() {
616        const includeDeps = this.includeDepsCheckbox ? this.includeDepsCheckbox.getValue() : true;
617
618        if (this.saveDataCallback) {
619            this.saveDataCallback(includeDeps);
620        }
621
622        this.close();
623    }
624
625    /**
626     * @param {string} title
627     * @param {string} label
628     * @param {(includeDeps: boolean) => void} onConfirm
629     */
630    static show(title, label, onConfirm) {
631        const dlg = new ExportOptionsDialog(title, label, onConfirm);
632        dlg.exec();
633    }
634}
635
636/**
637 * Confirms a container-wide reorganize and lets the user choose between the
638 * selective run (only documents whose processing is outdated) and a full
639 * rebuild. Same frame as SecurityQuestion, plus one checkbox.
640 */
641export class ReorganizeContainerQuestion extends SecurityQuestion {
642
643    /**
644     * @param {string} title
645     * @param {string} label
646     * @param {(onlyOutdated: boolean) => void} callbackYes
647     */
648    constructor(title, label, callbackYes) {
649        super(title, label, callbackYes, null);
650
651        /** @type {MoleculeLabelCheckBox | null} */
652        this.onlyOutdatedCheckbox = null;
653    }
654
655    /**
656     * @param {AtomContainerVertical} parent
657     */
658    addInputField(parent) {
659        // Default checked: the selective run is the sensible default; a full
660        // rebuild stays one click away.
661        this.onlyOutdatedCheckbox = new MoleculeLabelCheckBox(
662            parent,
663            "reorganize-only-outdated",
664            "Nur veraltete Dokumente neu aufbereiten",
665            true,
666        );
667    }
668
669    ok() {
670        const onlyOutdated = this.onlyOutdatedCheckbox ? this.onlyOutdatedCheckbox.getValue() : true;
671
672        if (this.saveDataCallback) {
673            this.saveDataCallback(onlyOutdated);
674        }
675
676        this.close();
677    }
678
679    /**
680     * @param {string} title
681     * @param {string} label
682     * @param {(onlyOutdated: boolean) => void} callbackYes
683     */
684    static show(title, label, callbackYes) {
685        const dlg = new ReorganizeContainerQuestion(title, label, callbackYes);
686        dlg.exec();
687    }
688}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.