@superleapai/flow-ui 2.3.6 → 2.3.8

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.
@@ -77,6 +77,40 @@
77
77
  /** Filled circle for "just color" mode (IconOrColor when only icon_color is set) */
78
78
  IconCircleFilled:
79
79
  '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" fill="currentColor"/></svg>',
80
+
81
+ // Rich text editor / toolbar (Tabler Icons outline, stroke 2)
82
+ IconBold:
83
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-bold"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 5h6a3.5 3.5 0 0 1 0 7h-6l0 -7" /><path d="M13 12h1a3.5 3.5 0 0 1 0 7h-7v-7" /></svg>',
84
+ IconItalic:
85
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-italic"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M11 5l6 0" /><path d="M7 19l6 0" /><path d="M14 5l-4 14" /></svg>',
86
+ IconUnderline:
87
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-underline"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 5v5a5 5 0 0 0 10 0v-5" /><path d="M5 19h14" /></svg>',
88
+ IconH1:
89
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-h-1"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M19 18v-8l-2 2" /><path d="M4 6v12" /><path d="M12 6v12" /><path d="M11 18h2" /><path d="M3 18h2" /><path d="M4 12h8" /><path d="M3 6h2" /><path d="M11 6h2" /></svg>',
90
+ IconH2:
91
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-h-2"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M17 12a2 2 0 1 1 4 0c0 .591 -.417 1.318 -.816 1.858l-3.184 4.143l4 0" /><path d="M4 6v12" /><path d="M12 6v12" /><path d="M11 18h2" /><path d="M3 18h2" /><path d="M4 12h8" /><path d="M3 6h2" /><path d="M11 6h2" /></svg>',
92
+ IconH3:
93
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-h-3"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M19 14a2 2 0 1 0 -2 -2" /><path d="M17 16a2 2 0 1 0 2 -2" /><path d="M4 6v12" /><path d="M12 6v12" /><path d="M11 18h2" /><path d="M3 18h2" /><path d="M4 12h8" /><path d="M3 6h2" /><path d="M11 6h2" /></svg>',
94
+ IconList:
95
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-list"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 6l11 0" /><path d="M9 12l11 0" /><path d="M9 18l11 0" /><path d="M5 6l0 .01" /><path d="M5 12l0 .01" /><path d="M5 18l0 .01" /></svg>',
96
+ IconListNumbers:
97
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-list-numbers"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M11 6h9" /><path d="M11 12h9" /><path d="M12 18h8" /><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4" /><path d="M6 10v-6l-2 2" /></svg>',
98
+ IconAlignLeft:
99
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-align-left"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6l16 0" /><path d="M4 12l10 0" /><path d="M4 18l14 0" /></svg>',
100
+ IconAlignCenter:
101
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-align-center"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6l16 0" /><path d="M8 12l8 0" /><path d="M6 18l12 0" /></svg>',
102
+ IconAlignRight:
103
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-align-right"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6l16 0" /><path d="M10 12l10 0" /><path d="M6 18l14 0" /></svg>',
104
+ IconCode:
105
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-code"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 8l-4 4l4 4" /><path d="M17 8l4 4l-4 4" /><path d="M14 4l-4 16" /></svg>',
106
+ IconLink:
107
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-link"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 15l6 -6" /><path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" /><path d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" /></svg>',
108
+ IconPhoto:
109
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-photo"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 8h.01" /><path d="M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-12" /><path d="M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5" /><path d="M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3" /></svg>',
110
+ IconArrowBackUp:
111
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-arrow-back-up"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 14l-4 -4l4 -4" /><path d="M5 10h11a4 4 0 1 1 0 8h-1" /></svg>',
112
+ IconArrowForwardUp:
113
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-arrow-forward-up"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 14l4 -4l-4 -4" /><path d="M19 10h-11a4 4 0 1 0 0 8h1" /></svg>',
80
114
  };
81
115
 
82
116
  function join() {
@@ -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);
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Steps Component (vanilla JS)
3
+ * Multi-step flow with numbered step triggers and optional content panels.
4
+ * Use for wizards / step-by-step forms (e.g. "1 Step One", "2 Step Two").
5
+ */
6
+
7
+ (function (global) {
8
+ "use strict";
9
+
10
+ function getComponent(name) {
11
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
12
+ var c = global.FlowUI._getComponent(name);
13
+ if (c) return c;
14
+ }
15
+ return global[name];
16
+ }
17
+
18
+ var LIST_BASE_CLASS =
19
+ "flex items-center flex-wrap gap-0 rounded-4 border border-border-primary bg-fill-quarternary-fill-white p-4";
20
+
21
+ /** Inactive step: muted label color only (button stays ghost large) */
22
+ var INACTIVE_LABEL_CLASS = "!text-typography-quaternary-text";
23
+
24
+ /** Box around step number: active = dark fill + invert text, inactive = dark gray fill + tertiary text */
25
+ var NUMBER_BOX_ACTIVE =
26
+ "flex h-16 w-16 items-center justify-center rounded-4 bg-typography-primary-text text-typography-invert-text shrink-0";
27
+ var NUMBER_BOX_INACTIVE =
28
+ "flex h-16 w-16 items-center justify-center rounded-4 bg-fill-primary-fill-dark-gray text-typography-tertiary-text shrink-0";
29
+
30
+ /** Horizontal line between steps (step1 ——— step2) */
31
+ var CONNECTOR_CLASS = "flex-1 min-w-12 max-w-32 h-0 border-t border-border-primary shrink-0 mx-2 self-center";
32
+
33
+ var CONTENT_BASE_CLASS =
34
+ "ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2 h-full";
35
+
36
+ function join() {
37
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
38
+ }
39
+
40
+ /**
41
+ * Create a Steps component (numbered step triggers + optional content)
42
+ * Design: ghost large Button for all steps; inactive steps use muted label color. Connectors between steps. Optional title/description above.
43
+ * @param {Object} config
44
+ * @param {Array<{id: string, label: string, content?: HTMLElement|string}>} config.steps - step definitions
45
+ * @param {string} [config.defaultValue] - initial active step id
46
+ * @param {string} [config.value] - controlled active step id
47
+ * @param {Function} [config.onChange] - (stepId) => void when step changes
48
+ * @param {string} [config.title] - optional title above steps
49
+ * @param {string} [config.description] - optional description above steps
50
+ * @param {string} [config.listClassName] - extra class on step list
51
+ * @param {string} [config.contentClassName] - extra class on content wrapper
52
+ * @param {boolean} [config.showContent=true] - whether to render content panels (if steps have content)
53
+ * @returns {HTMLElement} root element
54
+ */
55
+ function create(config) {
56
+ var opts = config || {};
57
+ var steps = opts.steps || [];
58
+ var defaultValue = opts.defaultValue;
59
+ var controlledValue = opts.value;
60
+ var onChange = opts.onChange;
61
+ var title = opts.title;
62
+ var description = opts.description;
63
+ var listClassName = opts.listClassName || "";
64
+ var contentClassName = opts.contentClassName || "";
65
+ var showContent = opts.showContent !== false;
66
+
67
+ var root = document.createElement("div");
68
+ root.className = "steps-root w-full";
69
+
70
+ var activeId =
71
+ controlledValue !== undefined
72
+ ? controlledValue
73
+ : defaultValue !== undefined
74
+ ? defaultValue
75
+ : (steps[0] && steps[0].id) || "";
76
+
77
+ if (title != null && title !== "") {
78
+ var titleEl = document.createElement("h3");
79
+ titleEl.className = "text-typography-primary-text";
80
+ titleEl.textContent = title;
81
+ root.appendChild(titleEl);
82
+ }
83
+ if (description != null && description !== "") {
84
+ var descEl = document.createElement("p");
85
+ descEl.className = "text-typography-secondary-text";
86
+ descEl.textContent = description;
87
+ root.appendChild(descEl);
88
+ }
89
+
90
+ // Step list (horizontal: step, connector, step, connector, ...)
91
+ var list = document.createElement("div");
92
+ list.setAttribute("role", "tablist");
93
+ list.setAttribute("aria-label", "Steps");
94
+ list.className = join(LIST_BASE_CLASS, listClassName);
95
+
96
+ var triggerEls = [];
97
+ var numberBoxEls = [];
98
+ var contentPanels = [];
99
+ var contentWrapper = null;
100
+
101
+ if (showContent && steps.some(function (s) { return s.content != null; })) {
102
+ contentWrapper = document.createElement("div");
103
+ contentWrapper.className = join("steps-content-wrapper mt-4", contentClassName);
104
+ }
105
+
106
+ var Button = getComponent("Button");
107
+
108
+ steps.forEach(function (step, index) {
109
+ var stepId = step.id;
110
+ var label = step.label != null ? step.label : stepId;
111
+ var content = step.content;
112
+ var stepNumber = index + 1;
113
+ var isActive = stepId === activeId;
114
+
115
+ var trigger = Button.create({
116
+ variant: "ghost",
117
+ size: "large",
118
+ text: "\u00A0",
119
+ type: "button",
120
+ className: isActive ? "" : INACTIVE_LABEL_CLASS,
121
+ onClick: function () {
122
+ if (activeId === stepId) return;
123
+ if (controlledValue === undefined) {
124
+ activeId = stepId;
125
+ updateActiveState();
126
+ }
127
+ if (typeof onChange === "function") {
128
+ onChange(stepId);
129
+ }
130
+ },
131
+ });
132
+
133
+ var numberBox = document.createElement("span");
134
+ numberBox.setAttribute("aria-hidden", "true");
135
+ numberBox.className = isActive ? NUMBER_BOX_ACTIVE : NUMBER_BOX_INACTIVE;
136
+ numberBox.textContent = String(stepNumber);
137
+
138
+ trigger.innerHTML = "";
139
+ trigger.appendChild(numberBox);
140
+ trigger.appendChild(document.createTextNode(" " + label));
141
+
142
+ trigger.setAttribute("role", "tab");
143
+ trigger.setAttribute("aria-selected", isActive ? "true" : "false");
144
+ trigger.setAttribute("data-state", isActive ? "active" : "inactive");
145
+ trigger.setAttribute("data-step-id", stepId);
146
+ trigger.tabIndex = isActive ? 0 : -1;
147
+
148
+ numberBoxEls.push(numberBox);
149
+
150
+ trigger.addEventListener("keydown", function (e) {
151
+ if (e.key === "Enter" || e.key === " ") {
152
+ e.preventDefault();
153
+ trigger.click();
154
+ }
155
+ if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
156
+ e.preventDefault();
157
+ var nextIndex = e.key === "ArrowRight" ? index + 1 : index - 1;
158
+ if (nextIndex >= 0 && nextIndex < steps.length) {
159
+ var nextTrigger = triggerEls[nextIndex];
160
+ if (nextTrigger) {
161
+ nextTrigger.focus();
162
+ nextTrigger.click();
163
+ }
164
+ }
165
+ }
166
+ });
167
+
168
+ list.appendChild(trigger);
169
+ triggerEls.push(trigger);
170
+
171
+ if (index < steps.length - 1) {
172
+ var connector = document.createElement("span");
173
+ connector.className = CONNECTOR_CLASS;
174
+ connector.setAttribute("aria-hidden", "true");
175
+ list.appendChild(connector);
176
+ }
177
+
178
+ if (contentWrapper && content != null) {
179
+ var panel = document.createElement("div");
180
+ panel.setAttribute("role", "tabpanel");
181
+ panel.setAttribute("aria-hidden", stepId !== activeId ? "true" : "false");
182
+ panel.setAttribute("data-step-id", stepId);
183
+ panel.className = join(CONTENT_BASE_CLASS, stepId !== activeId ? "hidden" : "");
184
+ if (typeof content === "string") {
185
+ panel.innerHTML = content;
186
+ } else if (content && content.nodeType === 1) {
187
+ panel.appendChild(content);
188
+ }
189
+ contentWrapper.appendChild(panel);
190
+ contentPanels.push(panel);
191
+ }
192
+ });
193
+
194
+ function updateActiveState() {
195
+ triggerEls.forEach(function (t, i) {
196
+ var step = steps[i];
197
+ var id = step && step.id;
198
+ var isActive = id === activeId;
199
+ t.setAttribute("aria-selected", isActive ? "true" : "false");
200
+ t.setAttribute("data-state", isActive ? "active" : "inactive");
201
+ t.tabIndex = isActive ? 0 : -1;
202
+ if (isActive) {
203
+ t.classList.remove(INACTIVE_LABEL_CLASS);
204
+ } else {
205
+ t.classList.add(INACTIVE_LABEL_CLASS);
206
+ }
207
+ numberBoxEls[i].className = isActive ? NUMBER_BOX_ACTIVE : NUMBER_BOX_INACTIVE;
208
+ });
209
+ contentPanels.forEach(function (p, i) {
210
+ var step = steps[i];
211
+ var id = step && step.id;
212
+ var isActive = id === activeId;
213
+ p.setAttribute("aria-hidden", isActive ? "false" : "true");
214
+ p.classList.toggle("hidden", !isActive);
215
+ });
216
+ }
217
+
218
+ root.appendChild(list);
219
+ if (contentWrapper) {
220
+ root.appendChild(contentWrapper);
221
+ }
222
+
223
+ root.getValue = function () {
224
+ return activeId;
225
+ };
226
+
227
+ root.setValue = function (newId) {
228
+ if (newId === activeId) return;
229
+ activeId = newId;
230
+ updateActiveState();
231
+ };
232
+
233
+ return root;
234
+ }
235
+
236
+ var Steps = {
237
+ create: create,
238
+ };
239
+
240
+ if (typeof module !== "undefined" && module.exports) {
241
+ module.exports = Steps;
242
+ } else {
243
+ global.Steps = Steps;
244
+ }
245
+ })(typeof window !== "undefined" ? window : this);