@superleapai/flow-ui 2.4.7 → 2.5.1
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-group.js +209 -0
- package/components/file-input.js +26 -12
- package/core/flow.js +35 -0
- package/core/superleapClient.js +12 -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.d.ts +2 -0
- package/index.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CheckboxGroup Component (vanilla JS)
|
|
3
|
+
* Multi-select via checkboxes; same API as MultiSelect (options, value array, onValuesChange).
|
|
4
|
+
* Uses input.js-style variants and sizes for the group wrapper.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
(function (global) {
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
// Wrapper classes aligned with input.js variants
|
|
11
|
+
var WRAPPER_CLASS = {
|
|
12
|
+
base:
|
|
13
|
+
"group flex flex-col border-1/2 border-border-primary rounded-4 text-typography-primary-text w-full transition-all ease-in-out group-has-[:disabled]:cursor-not-allowed group-has-[:disabled]:border-border-primary group-has-[:disabled]:bg-fill-tertiary-fill-light-gray group-has-[:disabled]:text-typography-quaternary-text",
|
|
14
|
+
default:
|
|
15
|
+
"bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
|
|
16
|
+
error:
|
|
17
|
+
"border-error-base bg-fill-quarternary-fill-white hover:border-error-base focus-within:border-error-base",
|
|
18
|
+
warning:
|
|
19
|
+
"border-warning-base bg-fill-quarternary-fill-white hover:border-warning-base focus-within:border-warning-base",
|
|
20
|
+
success:
|
|
21
|
+
"border-success-base bg-fill-quarternary-fill-white hover:border-success-base focus-within:border-success-base",
|
|
22
|
+
borderless:
|
|
23
|
+
"border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
|
|
24
|
+
inline:
|
|
25
|
+
"border-transparent shadow-none rounded-0 bg-fill-quarternary-fill-white hover:bg-fill-tertiary-fill-light-gray focus-within:border-transparent focus:bg-fill-tertiary-fill-light-gray focus-within:bg-fill-tertiary-fill-light-gray",
|
|
26
|
+
sizeDefault: "px-12 py-6 gap-6",
|
|
27
|
+
sizeLarge: "px-12 py-8 gap-8",
|
|
28
|
+
sizeSmall: "px-12 py-4 gap-4",
|
|
29
|
+
disabled:
|
|
30
|
+
"cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function join() {
|
|
34
|
+
return Array.prototype.filter.call(arguments, Boolean).join(" ");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getOptionValue(opt) {
|
|
38
|
+
return opt.value !== undefined && opt.value !== null
|
|
39
|
+
? opt.value
|
|
40
|
+
: opt.slug || opt.id;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getOptionLabel(opt) {
|
|
44
|
+
return opt.label || opt.name || opt.display_name || opt.value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getDep(name) {
|
|
48
|
+
if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
|
|
49
|
+
var c = global.FlowUI._getComponent(name);
|
|
50
|
+
if (c) return c;
|
|
51
|
+
}
|
|
52
|
+
return global[name];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create a checkbox group component (multiselect-like: multiple values, options array)
|
|
57
|
+
* @param {Object} config
|
|
58
|
+
* @param {string} [config.fieldId] - Field ID for state management
|
|
59
|
+
* @param {Array} config.options - Array of { value, label } or { slug, display_name }
|
|
60
|
+
* @param {Array} [config.value] - Current selected values (array)
|
|
61
|
+
* @param {Function} config.onValuesChange - Change handler (values: string[])
|
|
62
|
+
* @param {boolean} [config.disabled] - Whether all checkboxes are disabled
|
|
63
|
+
* @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'success' | 'borderless' | 'inline'
|
|
64
|
+
* @param {string} [config.size] - 'default' | 'large' | 'small'
|
|
65
|
+
* @param {string} [config.layout] - 'vertical' | 'horizontal'
|
|
66
|
+
* @param {string} [config.className] - Extra class on wrapper
|
|
67
|
+
* @returns {HTMLElement} CheckboxGroup container element
|
|
68
|
+
*/
|
|
69
|
+
function createCheckboxGroup(config) {
|
|
70
|
+
var fieldId = config.fieldId;
|
|
71
|
+
var options = config.options || [];
|
|
72
|
+
var onValuesChange = config.onValuesChange;
|
|
73
|
+
var variant = config.variant || "default";
|
|
74
|
+
var size = config.size || "default";
|
|
75
|
+
var disabled = config.disabled === true;
|
|
76
|
+
var layout = config.layout || "vertical";
|
|
77
|
+
var className = config.className || "";
|
|
78
|
+
|
|
79
|
+
var values = Array.isArray(config.value)
|
|
80
|
+
? config.value.slice()
|
|
81
|
+
: Array.isArray(config.values)
|
|
82
|
+
? config.values.slice()
|
|
83
|
+
: [];
|
|
84
|
+
|
|
85
|
+
var Checkbox = getDep("Checkbox");
|
|
86
|
+
if (!Checkbox || typeof Checkbox.create !== "function") {
|
|
87
|
+
throw new Error("CheckboxGroup requires the Checkbox component. Load checkbox.js before checkbox-group.js.");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
var container = document.createElement("div");
|
|
91
|
+
container.setAttribute("role", "group");
|
|
92
|
+
container.setAttribute("aria-label", config.ariaLabel || "Checkbox group");
|
|
93
|
+
if (fieldId) container.setAttribute("data-field-id", fieldId);
|
|
94
|
+
|
|
95
|
+
var sizeClass = size === "large" ? WRAPPER_CLASS.sizeLarge : size === "small" ? WRAPPER_CLASS.sizeSmall : WRAPPER_CLASS.sizeDefault;
|
|
96
|
+
function applyWrapperClasses() {
|
|
97
|
+
container.className = join(
|
|
98
|
+
WRAPPER_CLASS.base,
|
|
99
|
+
disabled ? WRAPPER_CLASS.disabled : (WRAPPER_CLASS[variant] || WRAPPER_CLASS.default),
|
|
100
|
+
sizeClass,
|
|
101
|
+
layout === "horizontal" ? "flex-row flex-wrap" : "flex-col",
|
|
102
|
+
"custom-checkbox-group",
|
|
103
|
+
className
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
applyWrapperClasses();
|
|
107
|
+
container.setAttribute("data-checkbox-group-variant", variant);
|
|
108
|
+
|
|
109
|
+
function isSelected(optionValue) {
|
|
110
|
+
return values.some(function (v) {
|
|
111
|
+
return v === optionValue || String(v) === String(optionValue);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function toggleValue(optionValue) {
|
|
116
|
+
var idx = values.findIndex(function (v) {
|
|
117
|
+
return v === optionValue || String(v) === String(optionValue);
|
|
118
|
+
});
|
|
119
|
+
if (idx >= 0) {
|
|
120
|
+
values.splice(idx, 1);
|
|
121
|
+
} else {
|
|
122
|
+
values.push(optionValue);
|
|
123
|
+
}
|
|
124
|
+
if (onValuesChange) onValuesChange(values.slice());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
var optionsContainer = document.createElement("div");
|
|
128
|
+
optionsContainer.className = join(
|
|
129
|
+
"flex gap-8",
|
|
130
|
+
layout === "horizontal" ? "flex-row flex-wrap" : "flex-col"
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
function buildOptions() {
|
|
134
|
+
optionsContainer.innerHTML = "";
|
|
135
|
+
if (options.length === 0) {
|
|
136
|
+
var empty = document.createElement("div");
|
|
137
|
+
empty.className = "!text-reg-13 text-typography-quaternary-text py-4";
|
|
138
|
+
empty.textContent = "No options available";
|
|
139
|
+
optionsContainer.appendChild(empty);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
options.forEach(function (opt, index) {
|
|
144
|
+
var optionValue = getOptionValue(opt);
|
|
145
|
+
var optionLabel = getOptionLabel(opt);
|
|
146
|
+
var optionDisabled = disabled || !!opt.disabled;
|
|
147
|
+
var checked = isSelected(optionValue);
|
|
148
|
+
|
|
149
|
+
var cb = Checkbox.create({
|
|
150
|
+
id: (fieldId || "cbg") + "-" + index,
|
|
151
|
+
name: fieldId ? fieldId + "[]" : "checkbox-group-" + index,
|
|
152
|
+
checked: checked,
|
|
153
|
+
disabled: optionDisabled,
|
|
154
|
+
label: optionLabel,
|
|
155
|
+
align: "left",
|
|
156
|
+
size: size === "large" ? "large" : size === "small" ? "small" : "default",
|
|
157
|
+
onChange: function (isChecked) {
|
|
158
|
+
if (optionDisabled) return;
|
|
159
|
+
if (isChecked) {
|
|
160
|
+
if (!values.some(function (v) { return v === optionValue || String(v) === String(optionValue); })) {
|
|
161
|
+
values.push(optionValue);
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
var idx = values.findIndex(function (v) {
|
|
165
|
+
return v === optionValue || String(v) === String(optionValue);
|
|
166
|
+
});
|
|
167
|
+
if (idx >= 0) values.splice(idx, 1);
|
|
168
|
+
}
|
|
169
|
+
if (onValuesChange) onValuesChange(values.slice());
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
optionsContainer.appendChild(cb);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
buildOptions();
|
|
177
|
+
container.appendChild(optionsContainer);
|
|
178
|
+
|
|
179
|
+
container.updateValues = function (newValues) {
|
|
180
|
+
values = Array.isArray(newValues) ? newValues.slice() : [];
|
|
181
|
+
buildOptions();
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
container.setDisabled = function (isDisabled) {
|
|
185
|
+
disabled = !!isDisabled;
|
|
186
|
+
applyWrapperClasses();
|
|
187
|
+
var wrappers = optionsContainer.querySelectorAll(":scope > div");
|
|
188
|
+
for (var i = 0; i < wrappers.length; i++) {
|
|
189
|
+
if (typeof wrappers[i].setDisabled === "function") wrappers[i].setDisabled(disabled);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
container.setVariant = function (v) {
|
|
194
|
+
variant = v;
|
|
195
|
+
container.setAttribute("data-checkbox-group-variant", v);
|
|
196
|
+
applyWrapperClasses();
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
container.getValues = function () {
|
|
200
|
+
return values.slice();
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return container;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
global.CheckboxGroup = {
|
|
207
|
+
create: createCheckboxGroup,
|
|
208
|
+
};
|
|
209
|
+
})(typeof window !== "undefined" ? window : this);
|
package/components/file-input.js
CHANGED
|
@@ -40,6 +40,15 @@
|
|
|
40
40
|
return ICONS.file;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** Resolve client: use FlowUI._getComponent when bundle has captured globals, else global.superleapClient (same as enum-select) */
|
|
44
|
+
function getClient() {
|
|
45
|
+
if (global.FlowUI && typeof global.FlowUI._getComponent === "function") {
|
|
46
|
+
var c = global.FlowUI._getComponent("superleapClient");
|
|
47
|
+
if (c) return c;
|
|
48
|
+
}
|
|
49
|
+
return global.superleapClient;
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
/**
|
|
44
53
|
* Upload file to S3
|
|
45
54
|
* @param {File} file - File to upload
|
|
@@ -51,26 +60,31 @@
|
|
|
51
60
|
formData.append("file", file, file.name);
|
|
52
61
|
formData.append("is_private", String(!!isPrivate));
|
|
53
62
|
|
|
54
|
-
// Get upload
|
|
63
|
+
// Get upload path - can be configured via global.S3_UPLOAD_URL
|
|
55
64
|
const uploadUrl = global.S3_UPLOAD_URL || "/org/file/upload";
|
|
56
|
-
const baseUrl = global.SUPERLEAP_BASE_URL || "https://app.superleap.com/api/v1";
|
|
57
|
-
const fullUrl = uploadUrl.startsWith("http") ? uploadUrl : `${baseUrl}${uploadUrl}`;
|
|
58
65
|
|
|
59
|
-
//
|
|
60
|
-
|
|
66
|
+
// Base URL and API key from superleapClient only (same pattern as enum-select)
|
|
67
|
+
var client = getClient();
|
|
68
|
+
var baseUrl = null;
|
|
69
|
+
var apiKey = null;
|
|
61
70
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
: global.superleapClient;
|
|
71
|
+
if (client && typeof client.getBaseUrl === "function") {
|
|
72
|
+
baseUrl = client.getBaseUrl();
|
|
73
|
+
}
|
|
66
74
|
if (client && typeof client.getSdk === "function") {
|
|
67
|
-
|
|
68
|
-
apiKey = sdk
|
|
75
|
+
var sdk = client.getSdk();
|
|
76
|
+
apiKey = sdk ? sdk.apiKey : null;
|
|
69
77
|
}
|
|
70
78
|
} catch (e) {
|
|
71
|
-
console.warn("[S3FileUpload] Could not get
|
|
79
|
+
console.warn("[S3FileUpload] Could not get client:", e);
|
|
72
80
|
}
|
|
73
81
|
|
|
82
|
+
if (!baseUrl) {
|
|
83
|
+
throw new Error("SuperLeap client not initialized. Call superleapClient.init({ baseUrl, apiKey }) first.");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const fullUrl = uploadUrl.startsWith("http") ? uploadUrl : baseUrl.replace(/\/$/, "") + (uploadUrl.startsWith("/") ? uploadUrl : "/" + uploadUrl);
|
|
87
|
+
|
|
74
88
|
const headers = {};
|
|
75
89
|
if (apiKey) {
|
|
76
90
|
headers.Authorization = `Bearer ${apiKey}`;
|
package/core/flow.js
CHANGED
|
@@ -1347,6 +1347,40 @@
|
|
|
1347
1347
|
return field;
|
|
1348
1348
|
}
|
|
1349
1349
|
|
|
1350
|
+
/**
|
|
1351
|
+
* Create a checkbox group field (multiselect-like: options array, value array, onValuesChange)
|
|
1352
|
+
* @param {Object} config - { label, fieldId, options, required, helpText, variant, size, layout, disabled, onChange }
|
|
1353
|
+
* @returns {HTMLElement} Field wrapper containing checkbox group
|
|
1354
|
+
*/
|
|
1355
|
+
function createCheckboxGroup(config) {
|
|
1356
|
+
const { label, fieldId, options = [], required = false, helpText = null, variant, size, layout = "vertical", disabled = false, onChange } = config;
|
|
1357
|
+
|
|
1358
|
+
const field = createFieldWrapper(label, required, helpText);
|
|
1359
|
+
field.setAttribute("data-field-id", fieldId);
|
|
1360
|
+
|
|
1361
|
+
if (getComponent("CheckboxGroup") && getComponent("CheckboxGroup").create) {
|
|
1362
|
+
const currentValues = get(fieldId) || [];
|
|
1363
|
+
const groupEl = getComponent("CheckboxGroup").create({
|
|
1364
|
+
fieldId,
|
|
1365
|
+
options,
|
|
1366
|
+
value: currentValues,
|
|
1367
|
+
variant: variant || "default",
|
|
1368
|
+
size: size || "default",
|
|
1369
|
+
layout,
|
|
1370
|
+
disabled,
|
|
1371
|
+
onValuesChange: (values) => {
|
|
1372
|
+
set(fieldId, values);
|
|
1373
|
+
if (onChange) onChange(values);
|
|
1374
|
+
},
|
|
1375
|
+
});
|
|
1376
|
+
groupEl._fieldId = fieldId;
|
|
1377
|
+
field.appendChild(groupEl);
|
|
1378
|
+
return field;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
return field;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1350
1384
|
// ============================================================================
|
|
1351
1385
|
// STEPPER COMPONENT
|
|
1352
1386
|
// ============================================================================
|
|
@@ -1752,6 +1786,7 @@
|
|
|
1752
1786
|
createCurrency,
|
|
1753
1787
|
createPhoneInput,
|
|
1754
1788
|
createCheckbox,
|
|
1789
|
+
createCheckboxGroup,
|
|
1755
1790
|
|
|
1756
1791
|
// Button (delegates to Button component when available; resolved at call time via getComponent)
|
|
1757
1792
|
createButton: function (config) {
|
package/core/superleapClient.js
CHANGED
|
@@ -137,11 +137,23 @@
|
|
|
137
137
|
return mergeConfig({}, DEFAULT_CONFIG);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Return the current base URL (from merged config after init).
|
|
142
|
+
* Used by file-input and other components that build API URLs.
|
|
143
|
+
*
|
|
144
|
+
* @returns {string|null} baseUrl or null if not initialized
|
|
145
|
+
*/
|
|
146
|
+
function getBaseUrl() {
|
|
147
|
+
if (_config && _config.baseUrl) return _config.baseUrl;
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
140
151
|
var superleapClient = {
|
|
141
152
|
init: init,
|
|
142
153
|
getSdk: getSdk,
|
|
143
154
|
isAvailable: isAvailable,
|
|
144
155
|
getDefaultConfig: getDefaultConfig,
|
|
156
|
+
getBaseUrl: getBaseUrl,
|
|
145
157
|
};
|
|
146
158
|
|
|
147
159
|
if (global) {
|