@superleapai/flow-ui 2.3.5 → 2.3.7
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/components/checkbox.js +21 -8
- package/components/currency.js +7 -2
- package/components/date-time-picker/date-time-picker.js +10 -9
- package/components/duration/duration.js +8 -3
- package/components/enumeration.js +29 -10
- package/components/file-input.js +101 -9
- package/components/icon.js +34 -0
- package/components/input.js +13 -17
- package/components/popover.js +59 -3
- package/components/richtext-editor.js +336 -0
- package/components/textarea.js +1 -1
- package/core/flow.js +72 -0
- package/dist/output.css +1 -1
- package/dist/superleap-flow.js +681 -383
- package/dist/superleap-flow.js.map +1 -1
- package/dist/superleap-flow.min.js +2 -2
- package/index.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rich Text Editor Component (vanilla JS)
|
|
3
|
+
* Toolbar + contenteditable area with formatting (bold, italic, underline, headings, lists, alignment, link, image, code block, undo/redo).
|
|
4
|
+
* Styling matches design: rounded-12, toolbar bg-fill-tertiary-fill-light-gray, content area with min-height.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
(function (global) {
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
var RICH_TEXT_CONTENT_STYLES =
|
|
11
|
+
".rich-text-editor-content ul{list-style-type:disc;padding-left:1.5em;margin:0.5em 0}" +
|
|
12
|
+
".rich-text-editor-content ol{list-style-type:decimal;padding-left:1.5em;margin:0.5em 0}" +
|
|
13
|
+
".rich-text-editor-content li{margin:0.25em 0}" +
|
|
14
|
+
".rich-text-editor-content li p{margin:0}" +
|
|
15
|
+
".rich-text-editor-content h1{font-size:1.5rem;font-weight:700;line-height:1.3;margin:0.75em 0 0.5em}" +
|
|
16
|
+
".rich-text-editor-content h2{font-size:1.25rem;font-weight:600;line-height:1.3;margin:0.75em 0 0.5em}" +
|
|
17
|
+
".rich-text-editor-content h3{font-size:1.125rem;font-weight:600;line-height:1.3;margin:0.75em 0 0.5em}" +
|
|
18
|
+
".rich-text-editor-content p{margin:0.5em 0}" +
|
|
19
|
+
".rich-text-editor-content a{color:var(--color-primary-500);text-decoration:underline;cursor:pointer}" +
|
|
20
|
+
".rich-text-editor-content pre{background:var(--color-neutral-100);border-radius:var(--sizes-size-8);padding:0.75em 1em;margin:0.5em 0;overflow-x:auto}" +
|
|
21
|
+
".rich-text-editor-content code{font-family:ui-monospace,monospace;font-size:0.875em}" +
|
|
22
|
+
".rich-text-editor-content img{max-width:100%;height:auto;margin:0.5em 0}" +
|
|
23
|
+
".rich-text-editor-content blockquote{border-left:3px solid var(--color-neutral-200);padding-left:1em;margin:0.5em 0;color:var(--color-neutral-600)}" +
|
|
24
|
+
".rich-text-editor-content hr{border:none;border-top:1px solid var(--color-neutral-150);margin:1em 0}" +
|
|
25
|
+
".rich-text-editor-content .ProseMirror,.rich-text-editor-content [contenteditable]{outline:none}";
|
|
26
|
+
|
|
27
|
+
function join() {
|
|
28
|
+
return Array.prototype.filter.call(arguments, Boolean).join(" ");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getDep(name) {
|
|
32
|
+
if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
|
|
33
|
+
var c = global.FlowUI._getComponent(name);
|
|
34
|
+
if (c) return c;
|
|
35
|
+
}
|
|
36
|
+
return global[name];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Get Tabler icon element (16px) from Icon component for toolbar. */
|
|
40
|
+
function getTablerIcon(iconName) {
|
|
41
|
+
var Icon = getDep("Icon");
|
|
42
|
+
if (!Icon || !Icon.iconMap || !Icon.iconMap[iconName]) return null;
|
|
43
|
+
var svgStr = Icon.iconMap[iconName];
|
|
44
|
+
var s16 = svgStr.replace(/width="24"/, 'width="16"').replace(/height="24"/, 'height="16"').replace(/width="20"/, 'width="16"').replace(/height="20"/, 'height="16"');
|
|
45
|
+
var span = document.createElement("span");
|
|
46
|
+
span.className = "flex items-center justify-center size-16";
|
|
47
|
+
span.innerHTML = s16;
|
|
48
|
+
return span;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function createToolbarButton(opts) {
|
|
52
|
+
var Button = getDep("Button");
|
|
53
|
+
if (!Button || typeof Button.create !== "function") {
|
|
54
|
+
throw new Error("RichTextEditor requires Button");
|
|
55
|
+
}
|
|
56
|
+
var icon = opts.iconStr ? getTablerIcon(opts.iconStr) : null;
|
|
57
|
+
return Button.create({
|
|
58
|
+
variant: opts.active ? "primary" : "outline",
|
|
59
|
+
size: "default",
|
|
60
|
+
title: opts.title,
|
|
61
|
+
icon: icon,
|
|
62
|
+
onClick: opts.onClick,
|
|
63
|
+
disabled: opts.disabled,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createSeparator() {
|
|
68
|
+
var sep = document.createElement("div");
|
|
69
|
+
sep.className = "w-px h-16 bg-border-primary mx-4";
|
|
70
|
+
sep.setAttribute("aria-hidden", "true");
|
|
71
|
+
return sep;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create a rich text editor
|
|
76
|
+
* @param {Object} config
|
|
77
|
+
* @param {string} [config.value] - Initial HTML content
|
|
78
|
+
* @param {string} [config.placeholder] - Placeholder when empty
|
|
79
|
+
* @param {number} [config.minHeightPx] - Min height of editor area (default 400)
|
|
80
|
+
* @param {boolean} [config.disabled]
|
|
81
|
+
* @param {Function} [config.onChange] - (html: string) => void
|
|
82
|
+
* @returns {HTMLElement} Wrapper element with getValue/setValue/setDisabled/getInput
|
|
83
|
+
*/
|
|
84
|
+
function create(config) {
|
|
85
|
+
var value = config.value != null ? String(config.value) : "";
|
|
86
|
+
var placeholder = config.placeholder != null ? config.placeholder : "";
|
|
87
|
+
var minHeightPx = config.minHeightPx != null ? config.minHeightPx : 400;
|
|
88
|
+
var disabled = !!config.disabled;
|
|
89
|
+
var onChange = config.onChange;
|
|
90
|
+
|
|
91
|
+
var wrapper = document.createElement("div");
|
|
92
|
+
wrapper.className = "w-full rounded-12 border border-borderColor-border-primary shadow-soft-2x-small";
|
|
93
|
+
|
|
94
|
+
var styleEl = document.createElement("style");
|
|
95
|
+
styleEl.textContent = RICH_TEXT_CONTENT_STYLES;
|
|
96
|
+
wrapper.appendChild(styleEl);
|
|
97
|
+
|
|
98
|
+
var toolbar = document.createElement("div");
|
|
99
|
+
toolbar.className =
|
|
100
|
+
"flex flex-wrap gap-4 rounded-t-12 border-borderColor-border-primary bg-fill-tertiary-fill-light-gray p-6";
|
|
101
|
+
toolbar.setAttribute("role", "toolbar");
|
|
102
|
+
|
|
103
|
+
var editorEl = document.createElement("div");
|
|
104
|
+
editorEl.contentEditable = !disabled;
|
|
105
|
+
editorEl.className = join(
|
|
106
|
+
"rich-text-editor-content max-w-none rounded-b-12 border-t border-borderColor-border-primary p-8 text-reg-14 text-typography-primary-text"
|
|
107
|
+
);
|
|
108
|
+
editorEl.style.minHeight = minHeightPx + "px";
|
|
109
|
+
if (value) editorEl.innerHTML = value;
|
|
110
|
+
editorEl.setAttribute("data-placeholder", placeholder);
|
|
111
|
+
|
|
112
|
+
function isEmpty() {
|
|
113
|
+
var text = (editorEl.textContent || "").trim();
|
|
114
|
+
if (text) return false;
|
|
115
|
+
var html = (editorEl.innerHTML || "").replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]+>/g, "");
|
|
116
|
+
return !html.trim();
|
|
117
|
+
}
|
|
118
|
+
function updatePlaceholder() {
|
|
119
|
+
if (placeholder && isEmpty()) {
|
|
120
|
+
editorEl.classList.add("empty");
|
|
121
|
+
editorEl.setAttribute("data-placeholder", placeholder);
|
|
122
|
+
} else {
|
|
123
|
+
editorEl.classList.remove("empty");
|
|
124
|
+
editorEl.removeAttribute("data-placeholder");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
updatePlaceholder();
|
|
128
|
+
|
|
129
|
+
function getHtml() {
|
|
130
|
+
return editorEl.innerHTML;
|
|
131
|
+
}
|
|
132
|
+
function setHtml(html) {
|
|
133
|
+
editorEl.innerHTML = html || "";
|
|
134
|
+
updatePlaceholder();
|
|
135
|
+
}
|
|
136
|
+
function notifyChange() {
|
|
137
|
+
if (typeof onChange === "function") onChange(getHtml());
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function isActive(cmd, val) {
|
|
141
|
+
try {
|
|
142
|
+
return document.queryCommandState(cmd);
|
|
143
|
+
} catch (e) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function blockTag() {
|
|
148
|
+
var sel = window.getSelection();
|
|
149
|
+
if (!sel || sel.rangeCount === 0) return null;
|
|
150
|
+
var node = sel.anchorNode;
|
|
151
|
+
while (node && node !== editorEl) {
|
|
152
|
+
if (node.nodeType === 1) {
|
|
153
|
+
var n = node.nodeName.toLowerCase();
|
|
154
|
+
if (["h1", "h2", "h3", "p", "div", "pre", "blockquote"].indexOf(n) !== -1) return n;
|
|
155
|
+
}
|
|
156
|
+
node = node.parentNode;
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
function isAlignment(align) {
|
|
161
|
+
try {
|
|
162
|
+
if (align === "left") return document.queryCommandState("justifyLeft");
|
|
163
|
+
if (align === "center") return document.queryCommandState("justifyCenter");
|
|
164
|
+
if (align === "right") return document.queryCommandState("justifyRight");
|
|
165
|
+
} catch (e) {}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function refreshToolbar() {
|
|
170
|
+
toolbar.querySelectorAll("button").forEach(function (btn) {
|
|
171
|
+
var cmd = btn.getAttribute("data-command");
|
|
172
|
+
var val = btn.getAttribute("data-value");
|
|
173
|
+
if (!cmd) return;
|
|
174
|
+
var active = false;
|
|
175
|
+
if (cmd === "formatBlock") active = blockTag() === val;
|
|
176
|
+
else if (cmd === "justifyLeft" && val === "left") active = isAlignment("left");
|
|
177
|
+
else if (cmd === "justifyCenter" && val === "center") active = isAlignment("center");
|
|
178
|
+
else if (cmd === "justifyRight" && val === "right") active = isAlignment("right");
|
|
179
|
+
else active = isActive(cmd);
|
|
180
|
+
btn.classList.toggle("bg-primary-base", active);
|
|
181
|
+
btn.classList.toggle("border-primary-base", active);
|
|
182
|
+
btn.classList.toggle("text-typography-invert-text", active);
|
|
183
|
+
btn.classList.toggle("bg-fill-quarternary-fill-white", !active);
|
|
184
|
+
btn.classList.toggle("border-border-primary", !active);
|
|
185
|
+
btn.classList.toggle("text-typography-primary-text", !active);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function exec(cmd, value) {
|
|
190
|
+
editorEl.focus();
|
|
191
|
+
document.execCommand(cmd, false, value != null ? value : null);
|
|
192
|
+
refreshToolbar();
|
|
193
|
+
notifyChange();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function insertCodeBlock() {
|
|
197
|
+
editorEl.focus();
|
|
198
|
+
var sel = window.getSelection();
|
|
199
|
+
if (sel && sel.rangeCount) {
|
|
200
|
+
var range = sel.getRangeAt(0);
|
|
201
|
+
var pre = document.createElement("pre");
|
|
202
|
+
var code = document.createElement("code");
|
|
203
|
+
code.textContent = "code here";
|
|
204
|
+
pre.appendChild(code);
|
|
205
|
+
range.insertNode(pre);
|
|
206
|
+
range.setStart(code, 0);
|
|
207
|
+
range.setEnd(code, 0);
|
|
208
|
+
sel.removeAllRanges();
|
|
209
|
+
sel.addRange(range);
|
|
210
|
+
}
|
|
211
|
+
refreshToolbar();
|
|
212
|
+
notifyChange();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function addLink() {
|
|
216
|
+
var url = window.prompt("Enter the URL:", "https://");
|
|
217
|
+
if (url) {
|
|
218
|
+
exec("createLink", url);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function addImage() {
|
|
223
|
+
var input = document.createElement("input");
|
|
224
|
+
input.type = "file";
|
|
225
|
+
input.accept = "image/*";
|
|
226
|
+
input.onchange = function (e) {
|
|
227
|
+
var file = e.target && e.target.files && e.target.files[0];
|
|
228
|
+
if (file) {
|
|
229
|
+
var reader = new FileReader();
|
|
230
|
+
reader.onload = function (ev) {
|
|
231
|
+
var src = ev.target && ev.target.result;
|
|
232
|
+
if (src) {
|
|
233
|
+
editorEl.focus();
|
|
234
|
+
document.execCommand("insertImage", false, src);
|
|
235
|
+
notifyChange();
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
reader.readAsDataURL(file);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
input.click();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function undo() {
|
|
245
|
+
editorEl.focus();
|
|
246
|
+
document.execCommand("undo", false, null);
|
|
247
|
+
refreshToolbar();
|
|
248
|
+
notifyChange();
|
|
249
|
+
}
|
|
250
|
+
function redo() {
|
|
251
|
+
editorEl.focus();
|
|
252
|
+
document.execCommand("redo", false, null);
|
|
253
|
+
refreshToolbar();
|
|
254
|
+
notifyChange();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function addBtn(iconStr, title, onClick, dataCommand, dataValue) {
|
|
258
|
+
var btn = createToolbarButton({
|
|
259
|
+
iconStr: iconStr,
|
|
260
|
+
title: title,
|
|
261
|
+
onClick: function () {
|
|
262
|
+
if (disabled) return;
|
|
263
|
+
onClick();
|
|
264
|
+
},
|
|
265
|
+
active: false,
|
|
266
|
+
});
|
|
267
|
+
if (dataCommand) btn.setAttribute("data-command", dataCommand);
|
|
268
|
+
if (dataValue != null) btn.setAttribute("data-value", dataValue);
|
|
269
|
+
toolbar.appendChild(btn);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
addBtn("IconBold", "Bold (Ctrl+B)", function () { exec("bold"); }, "bold");
|
|
273
|
+
addBtn("IconItalic", "Italic (Ctrl+I)", function () { exec("italic"); }, "italic");
|
|
274
|
+
addBtn("IconUnderline", "Underline (Ctrl+U)", function () { exec("underline"); }, "underline");
|
|
275
|
+
toolbar.appendChild(createSeparator());
|
|
276
|
+
addBtn("IconH1", "Heading 1", function () { exec("formatBlock", "h1"); }, "formatBlock", "h1");
|
|
277
|
+
addBtn("IconH2", "Heading 2", function () { exec("formatBlock", "h2"); }, "formatBlock", "h2");
|
|
278
|
+
addBtn("IconH3", "Heading 3", function () { exec("formatBlock", "h3"); }, "formatBlock", "h3");
|
|
279
|
+
toolbar.appendChild(createSeparator());
|
|
280
|
+
addBtn("IconList", "Bullet List", function () { exec("insertUnorderedList"); }, "insertUnorderedList");
|
|
281
|
+
addBtn("IconListNumbers", "Ordered List", function () { exec("insertOrderedList"); }, "insertOrderedList");
|
|
282
|
+
toolbar.appendChild(createSeparator());
|
|
283
|
+
addBtn("IconAlignLeft", "Align Left", function () { exec("justifyLeft"); }, "justifyLeft", "left");
|
|
284
|
+
addBtn("IconAlignCenter", "Align Center", function () { exec("justifyCenter"); }, "justifyCenter", "center");
|
|
285
|
+
addBtn("IconAlignRight", "Align Right", function () { exec("justifyRight"); }, "justifyRight", "right");
|
|
286
|
+
toolbar.appendChild(createSeparator());
|
|
287
|
+
addBtn("IconCode", "Code Block", insertCodeBlock, "formatBlock", "pre");
|
|
288
|
+
addBtn("IconLink", "Add Link", addLink, "link");
|
|
289
|
+
addBtn("IconPhoto", "Insert Image", addImage);
|
|
290
|
+
toolbar.appendChild(createSeparator());
|
|
291
|
+
addBtn("IconArrowBackUp", "Undo", undo);
|
|
292
|
+
addBtn("IconArrowForwardUp", "Redo", redo);
|
|
293
|
+
|
|
294
|
+
editorEl.addEventListener("input", function () {
|
|
295
|
+
updatePlaceholder();
|
|
296
|
+
refreshToolbar();
|
|
297
|
+
notifyChange();
|
|
298
|
+
});
|
|
299
|
+
editorEl.addEventListener("keyup", refreshToolbar);
|
|
300
|
+
editorEl.addEventListener("mouseup", refreshToolbar);
|
|
301
|
+
editorEl.addEventListener("focus", refreshToolbar);
|
|
302
|
+
|
|
303
|
+
if (placeholder) {
|
|
304
|
+
var placeholderStyles = document.createElement("style");
|
|
305
|
+
placeholderStyles.textContent =
|
|
306
|
+
".rich-text-editor-content.empty:before{content:attr(data-placeholder);color:var(--color-neutral-400);pointer-events:none}";
|
|
307
|
+
wrapper.appendChild(placeholderStyles);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
wrapper.appendChild(toolbar);
|
|
311
|
+
wrapper.appendChild(editorEl);
|
|
312
|
+
|
|
313
|
+
wrapper.getInput = function () {
|
|
314
|
+
return editorEl;
|
|
315
|
+
};
|
|
316
|
+
wrapper.getValue = function () {
|
|
317
|
+
return getHtml();
|
|
318
|
+
};
|
|
319
|
+
wrapper.setValue = function (v) {
|
|
320
|
+
setHtml(v);
|
|
321
|
+
};
|
|
322
|
+
wrapper.setDisabled = function (d) {
|
|
323
|
+
disabled = !!d;
|
|
324
|
+
editorEl.contentEditable = !disabled;
|
|
325
|
+
toolbar.querySelectorAll("button").forEach(function (b) {
|
|
326
|
+
b.disabled = disabled;
|
|
327
|
+
});
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
return wrapper;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
global.RichTextEditorComponent = {
|
|
334
|
+
create: create,
|
|
335
|
+
};
|
|
336
|
+
})(typeof window !== "undefined" ? window : this);
|
package/components/textarea.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
warning:
|
|
22
22
|
"min-h-[80px] border-warning-base hover:border-warning-base focus:border-warning-base",
|
|
23
23
|
disabled:
|
|
24
|
-
"cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
|
|
24
|
+
"pointer-events-none cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
function join() {
|
package/core/flow.js
CHANGED
|
@@ -248,6 +248,48 @@
|
|
|
248
248
|
return field;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Create a rich text editor field
|
|
253
|
+
* @param {Object} config - Configuration object
|
|
254
|
+
* @param {string} config.label - Field label
|
|
255
|
+
* @param {string} config.fieldId - State key for this field
|
|
256
|
+
* @param {string} [config.placeholder] - Placeholder when empty
|
|
257
|
+
* @param {boolean} [config.required] - Whether field is required
|
|
258
|
+
* @param {string} [config.helpText] - Optional help text for tooltip
|
|
259
|
+
* @param {number} [config.minHeightPx] - Min height of editor area in pixels (default 400)
|
|
260
|
+
* @param {boolean} [config.disabled] - Whether editor is disabled
|
|
261
|
+
* @returns {HTMLElement} Field element
|
|
262
|
+
*/
|
|
263
|
+
function createRichTextEditor(config) {
|
|
264
|
+
const { label, fieldId, placeholder, required = false, helpText = null, minHeightPx = 400, disabled = false } = config;
|
|
265
|
+
|
|
266
|
+
const field = createFieldWrapper(label, required, helpText);
|
|
267
|
+
field.setAttribute("data-field-id", fieldId);
|
|
268
|
+
|
|
269
|
+
if (getComponent("RichTextEditorComponent") && getComponent("RichTextEditorComponent").create) {
|
|
270
|
+
const currentValue = get(fieldId) || "";
|
|
271
|
+
const editorEl = getComponent("RichTextEditorComponent").create({
|
|
272
|
+
value: currentValue,
|
|
273
|
+
placeholder: placeholder || "",
|
|
274
|
+
minHeightPx,
|
|
275
|
+
disabled,
|
|
276
|
+
onChange: (html) => set(fieldId, html),
|
|
277
|
+
});
|
|
278
|
+
editorEl._fieldId = fieldId;
|
|
279
|
+
field.appendChild(editorEl);
|
|
280
|
+
return field;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const fallback = document.createElement("textarea");
|
|
284
|
+
fallback.className = "textarea min-h-[400px]";
|
|
285
|
+
fallback.placeholder = placeholder || `Enter ${label.toLowerCase()}`;
|
|
286
|
+
fallback.value = get(fieldId) || "";
|
|
287
|
+
fallback.disabled = disabled;
|
|
288
|
+
fallback.addEventListener("change", (e) => set(fieldId, e.target.value));
|
|
289
|
+
field.appendChild(fallback);
|
|
290
|
+
return field;
|
|
291
|
+
}
|
|
292
|
+
|
|
251
293
|
/**
|
|
252
294
|
* Create a select dropdown field (using custom select component)
|
|
253
295
|
* @param {Object} config - Configuration object
|
|
@@ -1494,6 +1536,34 @@
|
|
|
1494
1536
|
return field;
|
|
1495
1537
|
}
|
|
1496
1538
|
|
|
1539
|
+
// ============================================================================
|
|
1540
|
+
// ALERTS
|
|
1541
|
+
// ============================================================================
|
|
1542
|
+
|
|
1543
|
+
/**
|
|
1544
|
+
* Render multiple alert messages into a container
|
|
1545
|
+
* @param {HTMLElement} container - Container to append alerts to
|
|
1546
|
+
* @param {string[]} messages - Array of message strings
|
|
1547
|
+
* @param {string} [variant='default'] - 'default' | 'error' | 'warning' | 'success' | 'info' | 'destructive'
|
|
1548
|
+
*/
|
|
1549
|
+
function renderAlerts(container, messages, variant = "default") {
|
|
1550
|
+
if (!container || !Array.isArray(messages)) return;
|
|
1551
|
+
const Alert = getComponent("Alert");
|
|
1552
|
+
if (Alert && typeof Alert.simple === "function") {
|
|
1553
|
+
messages.forEach((msg) => {
|
|
1554
|
+
const el = Alert.simple(msg, variant);
|
|
1555
|
+
if (el) container.appendChild(el);
|
|
1556
|
+
});
|
|
1557
|
+
} else {
|
|
1558
|
+
messages.forEach((msg) => {
|
|
1559
|
+
const div = document.createElement("div");
|
|
1560
|
+
div.className = "rounded border p-2 text-sm " + (variant === "error" ? "bg-red-50 border-red-200 text-red-800" : "bg-gray-50 border-gray-200");
|
|
1561
|
+
div.textContent = msg;
|
|
1562
|
+
container.appendChild(div);
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1497
1567
|
// ============================================================================
|
|
1498
1568
|
// TOAST NOTIFICATIONS
|
|
1499
1569
|
// ============================================================================
|
|
@@ -1630,6 +1700,7 @@
|
|
|
1630
1700
|
// Form components
|
|
1631
1701
|
createInput,
|
|
1632
1702
|
createTextarea,
|
|
1703
|
+
createRichTextEditor,
|
|
1633
1704
|
createSelect,
|
|
1634
1705
|
createTimePicker,
|
|
1635
1706
|
createDateTimePicker,
|
|
@@ -1667,6 +1738,7 @@
|
|
|
1667
1738
|
renderStepper,
|
|
1668
1739
|
|
|
1669
1740
|
// Alerts
|
|
1741
|
+
renderAlerts,
|
|
1670
1742
|
showToast,
|
|
1671
1743
|
|
|
1672
1744
|
// Table
|