@superleapai/flow-ui 1.0.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/CHANGELOG.md +65 -0
- package/LICENSE +21 -0
- package/README.md +451 -0
- package/components/alert.js +282 -0
- package/components/avatar.js +195 -0
- package/components/badge.js +135 -0
- package/components/button.js +201 -0
- package/components/checkbox.js +254 -0
- package/components/currency.js +227 -0
- package/components/date-time-picker/date-time-picker-utils.js +253 -0
- package/components/date-time-picker/date-time-picker.js +532 -0
- package/components/duration/duration-constants.js +46 -0
- package/components/duration/duration-utils.js +164 -0
- package/components/duration/duration.js +448 -0
- package/components/enum-multiselect.js +869 -0
- package/components/enum-select.js +831 -0
- package/components/enumeration.js +213 -0
- package/components/file-input.js +533 -0
- package/components/icon.js +200 -0
- package/components/input.js +259 -0
- package/components/label.js +111 -0
- package/components/multiselect.js +351 -0
- package/components/phone-input/phone-input.js +392 -0
- package/components/phone-input/phone-utils.js +157 -0
- package/components/popover.js +240 -0
- package/components/radio-group.js +435 -0
- package/components/record-multiselect.js +956 -0
- package/components/record-select.js +930 -0
- package/components/select.js +544 -0
- package/components/spinner.js +136 -0
- package/components/table.js +335 -0
- package/components/textarea.js +114 -0
- package/components/time-picker.js +357 -0
- package/components/toast.js +343 -0
- package/core/flow.js +1729 -0
- package/core/superleapClient.js +146 -0
- package/dist/output.css +2 -0
- package/index.d.ts +458 -0
- package/index.js +253 -0
- package/package.json +70 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MultiSelect Component (vanilla JS, Tailwind)
|
|
3
|
+
* Dropdown multiselect; same design system as Select, supports multiple values.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function (global) {
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
var CHEVRON_SVG =
|
|
10
|
+
'<svg width="16" height="16" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"/></svg>';
|
|
11
|
+
|
|
12
|
+
var CHECK_SVG =
|
|
13
|
+
'<svg width="14" height="14" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.4669 3.72684C11.7558 3.41574 12.2442 3.41574 12.5331 3.72684C12.822 4.03795 12.822 4.53753 12.5331 4.84863L6.81767 10.6736C6.52329 10.9901 6.05308 10.9901 5.7587 10.6736L2.46685 7.3463C2.17795 7.03519 2.17795 6.53561 2.46685 6.2245C2.75575 5.9134 3.24395 5.9134 3.53285 6.2245L6.28822 9.05351L11.4669 3.72684Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"/></svg>';
|
|
14
|
+
|
|
15
|
+
function triggerClasses(variant, size, disabled, isEmpty) {
|
|
16
|
+
var v = variant || "default";
|
|
17
|
+
var base =
|
|
18
|
+
"w-full items-center justify-between rounded-4 border-1/2 bg-fill-quarternary-fill-white !text-reg-13 focus:outline-none flex h-full truncate hover:cursor-pointer ";
|
|
19
|
+
var variantClasses = {
|
|
20
|
+
default:
|
|
21
|
+
"border-border-primary hover:border-primary-border focus:border-1/2 focus:border-primary-border",
|
|
22
|
+
error:
|
|
23
|
+
"border-error-border hover:border-error-border-hover focus:border-1/2 focus:border-error-border-hover",
|
|
24
|
+
warning:
|
|
25
|
+
"border-warning-border hover:border-warning-border-hover focus:border-1/2 focus:border-warning-border-hover",
|
|
26
|
+
borderless: "border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
|
|
27
|
+
inline:
|
|
28
|
+
"focus:border-transparent border border-transparent shadow-none rounded-0 bg-fill-quarternary-fill-white hover:bg-fill-tertiary-fill-light-gray hover:border-transparent",
|
|
29
|
+
};
|
|
30
|
+
var sizeClasses = {
|
|
31
|
+
default: "px-12 py-6",
|
|
32
|
+
large: "px-12 py-8",
|
|
33
|
+
small: "px-8 py-4",
|
|
34
|
+
};
|
|
35
|
+
var textColorClass = isEmpty ? " text-typography-quaternary-text" : " text-typography-primary-text";
|
|
36
|
+
var disabledClass = disabled
|
|
37
|
+
? " pointer-events-none cursor-not-allowed bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary"
|
|
38
|
+
: "";
|
|
39
|
+
return (
|
|
40
|
+
base +
|
|
41
|
+
(variantClasses[v] || variantClasses.default) +
|
|
42
|
+
" " +
|
|
43
|
+
(sizeClasses[size] || sizeClasses.default) +
|
|
44
|
+
textColorClass +
|
|
45
|
+
disabledClass
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function join() {
|
|
50
|
+
return Array.prototype.filter.call(arguments, Boolean).join(" ");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getOptionValue(opt) {
|
|
54
|
+
return opt.value !== undefined && opt.value !== null
|
|
55
|
+
? opt.value
|
|
56
|
+
: opt.slug || opt.id;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getOptionLabel(opt) {
|
|
60
|
+
return opt.label || opt.name || opt.display_name || opt.value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a multiselect dropdown component
|
|
65
|
+
* @param {Object} config
|
|
66
|
+
* @param {string} [config.fieldId] - Field ID for state management
|
|
67
|
+
* @param {Array} config.options - Array of { value, label } or { slug, display_name } (optional badge_config)
|
|
68
|
+
* @param {string} config.placeholder - Placeholder text
|
|
69
|
+
* @param {Array} [config.value] - Current selected values (array)
|
|
70
|
+
* @param {string} [config.label] - Label for summary when items selected (e.g. "selected" -> "3 selected")
|
|
71
|
+
* @param {Function} config.onValuesChange - Change handler (values: string[])
|
|
72
|
+
* @param {boolean} [config.disabled] - Whether multiselect is disabled
|
|
73
|
+
* @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'borderless' | 'inline'
|
|
74
|
+
* @param {string} [config.size] - 'default' | 'large' | 'small'
|
|
75
|
+
* @returns {HTMLElement} MultiSelect container element
|
|
76
|
+
*/
|
|
77
|
+
function createMultiSelect(config) {
|
|
78
|
+
var fieldId = config.fieldId;
|
|
79
|
+
var options = config.options || [];
|
|
80
|
+
var placeholder = config.placeholder || "Select options";
|
|
81
|
+
var summaryLabel = config.label || "selected";
|
|
82
|
+
var onValuesChange = config.onValuesChange;
|
|
83
|
+
var variant = config.variant || "default";
|
|
84
|
+
var size = config.size || "default";
|
|
85
|
+
var disabled = config.disabled === true;
|
|
86
|
+
|
|
87
|
+
var values = Array.isArray(config.value)
|
|
88
|
+
? config.value.slice()
|
|
89
|
+
: Array.isArray(config.values)
|
|
90
|
+
? config.values.slice()
|
|
91
|
+
: [];
|
|
92
|
+
|
|
93
|
+
var container = document.createElement("div");
|
|
94
|
+
container.className = "custom-multiselect relative w-full group";
|
|
95
|
+
if (fieldId) container.setAttribute("data-field-id", fieldId);
|
|
96
|
+
|
|
97
|
+
var triggerWrapper = document.createElement("span");
|
|
98
|
+
triggerWrapper.className =
|
|
99
|
+
"multiselect-trigger-wrapper relative flex w-full items-center justify-between gap-8";
|
|
100
|
+
|
|
101
|
+
var trigger = document.createElement("button");
|
|
102
|
+
trigger.type = "button";
|
|
103
|
+
trigger.className = triggerClasses(variant, size, disabled, values.length === 0);
|
|
104
|
+
trigger.disabled = disabled;
|
|
105
|
+
trigger.setAttribute("aria-haspopup", "listbox");
|
|
106
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
107
|
+
trigger.setAttribute("aria-multiselectable", "true");
|
|
108
|
+
trigger.setAttribute("aria-label", placeholder);
|
|
109
|
+
|
|
110
|
+
var triggerContent = document.createElement("div");
|
|
111
|
+
triggerContent.className = "flex flex-1 items-center gap-6 truncate text-left text-inherit";
|
|
112
|
+
|
|
113
|
+
function renderTriggerContent() {
|
|
114
|
+
triggerContent.innerHTML = "";
|
|
115
|
+
if (values.length === 0) {
|
|
116
|
+
var placeholderSpan = document.createElement("span");
|
|
117
|
+
placeholderSpan.className = "text-inherit";
|
|
118
|
+
placeholderSpan.textContent = placeholder;
|
|
119
|
+
triggerContent.appendChild(placeholderSpan);
|
|
120
|
+
} else {
|
|
121
|
+
var summary = document.createElement("span");
|
|
122
|
+
summary.className = "text-inherit";
|
|
123
|
+
summary.textContent = values.length + " " + summaryLabel;
|
|
124
|
+
triggerContent.appendChild(summary);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
renderTriggerContent();
|
|
128
|
+
|
|
129
|
+
trigger.appendChild(triggerContent);
|
|
130
|
+
|
|
131
|
+
var chevron = document.createElement("span");
|
|
132
|
+
chevron.className =
|
|
133
|
+
"ml-4 box-content flex size-16 items-center justify-center shrink-0 transition-transform duration-200 group-[.open]:rotate-180";
|
|
134
|
+
chevron.innerHTML = CHEVRON_SVG;
|
|
135
|
+
chevron.setAttribute("aria-hidden", "true");
|
|
136
|
+
trigger.appendChild(chevron);
|
|
137
|
+
|
|
138
|
+
triggerWrapper.appendChild(trigger);
|
|
139
|
+
container.appendChild(triggerWrapper);
|
|
140
|
+
|
|
141
|
+
var contentBase =
|
|
142
|
+
"custom-multiselect-content absolute left-0 right-0 z-50 max-h-[200px] min-w-[8rem] overflow-hidden rounded-4 bg-fill-quarternary-fill-white shadow-default-medium opacity-0 invisible transition-all duration-150 ease-out " +
|
|
143
|
+
"group-[.open]:opacity-100 group-[.open]:visible ";
|
|
144
|
+
|
|
145
|
+
var content = document.createElement("div");
|
|
146
|
+
content.setAttribute("role", "listbox");
|
|
147
|
+
content.setAttribute("aria-multiselectable", "true");
|
|
148
|
+
content.className = contentBase + "top-full mt-1 -translate-y-1 group-[.open]:translate-y-0";
|
|
149
|
+
|
|
150
|
+
var optionsList = document.createElement("div");
|
|
151
|
+
optionsList.className =
|
|
152
|
+
"overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
|
|
153
|
+
|
|
154
|
+
function isSelected(optionValue) {
|
|
155
|
+
return values.some(function (v) {
|
|
156
|
+
return v === optionValue || String(v) === String(optionValue);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function toggleValue(optionValue) {
|
|
161
|
+
var idx = values.findIndex(function (v) {
|
|
162
|
+
return v === optionValue || String(v) === String(optionValue);
|
|
163
|
+
});
|
|
164
|
+
if (idx >= 0) {
|
|
165
|
+
values.splice(idx, 1);
|
|
166
|
+
} else {
|
|
167
|
+
values.push(optionValue);
|
|
168
|
+
}
|
|
169
|
+
renderTriggerContent();
|
|
170
|
+
trigger.className = triggerClasses(variant, size, disabled, values.length === 0);
|
|
171
|
+
syncOptionStates();
|
|
172
|
+
if (onValuesChange) onValuesChange(values.slice());
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function syncOptionStates() {
|
|
176
|
+
optionsList.querySelectorAll("[role=option]").forEach(function (optEl) {
|
|
177
|
+
var dv = optEl.getAttribute("data-value");
|
|
178
|
+
var selected = values.some(function (v) {
|
|
179
|
+
return v === dv || String(v) === dv || (dv === "false" && v === false) || (dv === "true" && v === true);
|
|
180
|
+
});
|
|
181
|
+
optEl.setAttribute("aria-selected", selected);
|
|
182
|
+
var checkEl = optEl.querySelector(".multiselect-option-check");
|
|
183
|
+
if (checkEl) checkEl.style.visibility = selected ? "visible" : "hidden";
|
|
184
|
+
optEl.classList.toggle("bg-primary-surface", selected);
|
|
185
|
+
optEl.classList.toggle("hover:!bg-primary-surface-hover", selected);
|
|
186
|
+
optEl.classList.toggle("hover:bg-fill-tertiary-fill-light-gray", !selected);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function buildOptionsList() {
|
|
191
|
+
optionsList.innerHTML = "";
|
|
192
|
+
if (options.length === 0) {
|
|
193
|
+
var noOpt = document.createElement("div");
|
|
194
|
+
noOpt.className =
|
|
195
|
+
"flex h-full min-h-[100px] w-full items-center justify-center p-4 !text-reg-13 text-typography-quaternary-text";
|
|
196
|
+
noOpt.textContent = "No options available";
|
|
197
|
+
optionsList.appendChild(noOpt);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
options.forEach(function (opt) {
|
|
201
|
+
var optionValue = getOptionValue(opt);
|
|
202
|
+
var optionLabel = getOptionLabel(opt);
|
|
203
|
+
var selected = isSelected(optionValue);
|
|
204
|
+
|
|
205
|
+
var option = document.createElement("div");
|
|
206
|
+
option.setAttribute("role", "option");
|
|
207
|
+
option.setAttribute("data-value", optionValue);
|
|
208
|
+
option.setAttribute("aria-selected", selected);
|
|
209
|
+
option.className = join(
|
|
210
|
+
"relative flex w-full cursor-pointer select-none items-center gap-8 rounded-2 px-12 py-6 text-reg-13 outline-none first:rounded-t-4 last:rounded-b-4",
|
|
211
|
+
"hover:bg-fill-tertiary-fill-light-gray focus:bg-fill-tertiary-fill-light-gray",
|
|
212
|
+
selected ? "bg-primary-surface hover:!bg-primary-surface-hover" : ""
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
var optionContent = document.createElement("span");
|
|
216
|
+
optionContent.className = "flex items-center gap-8 flex-1 truncate";
|
|
217
|
+
optionContent.textContent = optionLabel;
|
|
218
|
+
option.appendChild(optionContent);
|
|
219
|
+
|
|
220
|
+
var checkSpan = document.createElement("span");
|
|
221
|
+
checkSpan.className = "multiselect-option-check ml-auto flex size-14 shrink-0 items-center justify-center";
|
|
222
|
+
checkSpan.innerHTML = CHECK_SVG;
|
|
223
|
+
checkSpan.style.visibility = selected ? "visible" : "hidden";
|
|
224
|
+
option.appendChild(checkSpan);
|
|
225
|
+
|
|
226
|
+
option.addEventListener("click", function (e) {
|
|
227
|
+
e.stopPropagation();
|
|
228
|
+
if (disabled) return;
|
|
229
|
+
toggleValue(optionValue);
|
|
230
|
+
});
|
|
231
|
+
option.addEventListener("mouseenter", function () {
|
|
232
|
+
if (disabled) return;
|
|
233
|
+
optionsList.querySelectorAll("[role=option]").forEach(function (o) {
|
|
234
|
+
if (o !== option && !isSelected(o.getAttribute("data-value"))) {
|
|
235
|
+
o.classList.remove("bg-fill-tertiary-fill-light-gray");
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
if (!option.classList.contains("bg-primary-surface")) {
|
|
239
|
+
option.classList.add("bg-fill-tertiary-fill-light-gray");
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
optionsList.appendChild(option);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
buildOptionsList();
|
|
247
|
+
|
|
248
|
+
content.appendChild(optionsList);
|
|
249
|
+
container.appendChild(content);
|
|
250
|
+
|
|
251
|
+
var isOpen = false;
|
|
252
|
+
|
|
253
|
+
function openDropdown() {
|
|
254
|
+
if (disabled) return;
|
|
255
|
+
document
|
|
256
|
+
.querySelectorAll(".custom-select.open, .record-select.open, .custom-multiselect.open")
|
|
257
|
+
.forEach(function (other) {
|
|
258
|
+
if (other !== container) {
|
|
259
|
+
other.classList.remove("open");
|
|
260
|
+
var t = other.querySelector(
|
|
261
|
+
"button, .custom-select-trigger, .record-select-trigger, .multiselect-trigger-wrapper button"
|
|
262
|
+
);
|
|
263
|
+
if (t) t.setAttribute("aria-expanded", "false");
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
isOpen = true;
|
|
267
|
+
container.classList.add("open");
|
|
268
|
+
trigger.setAttribute("aria-expanded", "true");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function closeDropdown() {
|
|
272
|
+
isOpen = false;
|
|
273
|
+
container.classList.remove("open");
|
|
274
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function toggleDropdown() {
|
|
278
|
+
if (isOpen) closeDropdown();
|
|
279
|
+
else openDropdown();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
trigger.addEventListener("click", function (e) {
|
|
283
|
+
e.preventDefault();
|
|
284
|
+
e.stopPropagation();
|
|
285
|
+
toggleDropdown();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
trigger.addEventListener("keydown", function (e) {
|
|
289
|
+
if (disabled) return;
|
|
290
|
+
switch (e.key) {
|
|
291
|
+
case "Enter":
|
|
292
|
+
case " ":
|
|
293
|
+
e.preventDefault();
|
|
294
|
+
toggleDropdown();
|
|
295
|
+
break;
|
|
296
|
+
case "ArrowDown":
|
|
297
|
+
e.preventDefault();
|
|
298
|
+
if (!isOpen) openDropdown();
|
|
299
|
+
break;
|
|
300
|
+
case "ArrowUp":
|
|
301
|
+
e.preventDefault();
|
|
302
|
+
if (!isOpen) openDropdown();
|
|
303
|
+
break;
|
|
304
|
+
case "Escape":
|
|
305
|
+
if (isOpen) {
|
|
306
|
+
e.preventDefault();
|
|
307
|
+
closeDropdown();
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
document.addEventListener("click", function (e) {
|
|
314
|
+
if (isOpen && !container.contains(e.target)) closeDropdown();
|
|
315
|
+
});
|
|
316
|
+
document.addEventListener("keydown", function (e) {
|
|
317
|
+
if (e.key === "Escape" && isOpen) closeDropdown();
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
container.updateValues = function (newValues) {
|
|
321
|
+
values = Array.isArray(newValues) ? newValues.slice() : [];
|
|
322
|
+
renderTriggerContent();
|
|
323
|
+
buildOptionsList();
|
|
324
|
+
syncOptionStates();
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
container.updateOptions = function (newOptions) {
|
|
328
|
+
options.length = 0;
|
|
329
|
+
options.push.apply(options, newOptions || []);
|
|
330
|
+
buildOptionsList();
|
|
331
|
+
syncOptionStates();
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
container.setDisabled = function (isDisabled) {
|
|
335
|
+
disabled = !!isDisabled;
|
|
336
|
+
trigger.disabled = disabled;
|
|
337
|
+
trigger.className = triggerClasses(variant, size, disabled, values.length === 0);
|
|
338
|
+
if (disabled && isOpen) closeDropdown();
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
container.getValues = function () {
|
|
342
|
+
return values.slice();
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
return container;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
global.MultiSelect = {
|
|
349
|
+
create: createMultiSelect,
|
|
350
|
+
};
|
|
351
|
+
})(typeof window !== "undefined" ? window : this);
|