@superleapai/flow-ui 2.5.18 → 2.5.20
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 +2 -2
- package/LICENSE +1 -1
- package/README.md +38 -38
- package/components/enum-multiselect.js +119 -56
- package/components/enum-select.js +90 -48
- package/components/file-input.js +111 -49
- package/components/multiselect.js +70 -3
- package/components/select.js +87 -55
- package/core/bridge.js +4 -6
- package/core/crm.js +17 -17
- package/core/flow.js +236 -61
- package/core/superleapClient.js +2 -2
- package/dist/superleap-flow.min.js +2 -2
- package/index.d.ts +33 -20
- package/index.js +41 -30
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -24,7 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
24
24
|
- Phone input (international)
|
|
25
25
|
- Currency input
|
|
26
26
|
- File upload
|
|
27
|
-
- Record select (
|
|
27
|
+
- Record select (Superleap integration)
|
|
28
28
|
- Record multi-select
|
|
29
29
|
- Enumeration select/multi-select
|
|
30
30
|
- Advanced components:
|
|
@@ -53,7 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
53
53
|
- Accessibility features
|
|
54
54
|
- Responsive design
|
|
55
55
|
- Dark mode compatible
|
|
56
|
-
-
|
|
56
|
+
- Superleap client integration
|
|
57
57
|
|
|
58
58
|
### Documentation
|
|
59
59
|
- README.md with complete API reference
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A comprehensive, reusable design system for building multi-step forms and UI components with vanilla JavaScript and Tailwind CSS.
|
|
4
4
|
|
|
5
|
-
**✨ Clean Architecture:** Only `FlowUI` and `
|
|
5
|
+
**✨ Clean Architecture:** Only `FlowUI` and `Superleap` are exposed to global scope. All components are internal.
|
|
6
6
|
|
|
7
7
|
## 📦 Installation
|
|
8
8
|
|
|
@@ -23,14 +23,14 @@ npm install @superleapai/flow-ui
|
|
|
23
23
|
<body>
|
|
24
24
|
<div id="app"></div>
|
|
25
25
|
|
|
26
|
-
<!-- Single script tag - includes
|
|
26
|
+
<!-- Single script tag - includes Superleap SDK and all components -->
|
|
27
27
|
<script src="https://cdn.jsdelivr.net/npm/@superleapai/flow-ui@latest/index.js"></script>
|
|
28
28
|
|
|
29
29
|
<script>
|
|
30
30
|
// Wait for library to be ready
|
|
31
31
|
document.addEventListener('superleap-flow:ready', function() {
|
|
32
|
-
// Initialize
|
|
33
|
-
|
|
32
|
+
// Initialize Superleap SDK (optional, for Record Select)
|
|
33
|
+
Superleap.init({
|
|
34
34
|
// apiKey: 'YOUR_API_KEY'
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -77,7 +77,7 @@ npm install @superleapai/flow-ui
|
|
|
77
77
|
|
|
78
78
|
## 🔌 CRM Embedded Usage (Inside iframes)
|
|
79
79
|
|
|
80
|
-
When your flow is embedded in the
|
|
80
|
+
When your flow is embedded in the Superleap CRM as an iframe, use `Superleap.connect()` to establish a secure connection. This automatically provides credentials and context without requiring hardcoded API keys.
|
|
81
81
|
|
|
82
82
|
```html
|
|
83
83
|
<!DOCTYPE html>
|
|
@@ -95,7 +95,7 @@ When your flow is embedded in the SuperLeap CRM as an iframe, use `SuperLeap.con
|
|
|
95
95
|
try {
|
|
96
96
|
// Connect to CRM - SDK is auto-initialized with credentials
|
|
97
97
|
// flowId must match the custom_id of your iframe component in the CRM
|
|
98
|
-
const { context, config } = await
|
|
98
|
+
const { context, config } = await Superleap.connect({
|
|
99
99
|
flowId: 'my-onboarding-flow' // Use the custom_id from your iframe component
|
|
100
100
|
});
|
|
101
101
|
|
|
@@ -137,22 +137,22 @@ When your flow is embedded in the SuperLeap CRM as an iframe, use `SuperLeap.con
|
|
|
137
137
|
const data = FlowUI.getState();
|
|
138
138
|
|
|
139
139
|
// Show loading in CRM
|
|
140
|
-
|
|
140
|
+
Superleap.setLoading(true);
|
|
141
141
|
|
|
142
142
|
try {
|
|
143
143
|
// Your business logic here
|
|
144
|
-
const sdk =
|
|
144
|
+
const sdk = Superleap.getSdk();
|
|
145
145
|
await sdk.records.create('contacts', data);
|
|
146
146
|
|
|
147
147
|
// Show success notification in CRM
|
|
148
|
-
|
|
148
|
+
Superleap.toast('Profile created successfully!', 'success');
|
|
149
149
|
|
|
150
150
|
// Close the form/modal
|
|
151
|
-
|
|
151
|
+
Superleap.closeForm();
|
|
152
152
|
} catch (error) {
|
|
153
|
-
|
|
153
|
+
Superleap.toast('Failed to create profile', 'error');
|
|
154
154
|
} finally {
|
|
155
|
-
|
|
155
|
+
Superleap.setLoading(false);
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
});
|
|
@@ -174,7 +174,7 @@ When your flow is embedded in the SuperLeap CRM as an iframe, use `SuperLeap.con
|
|
|
174
174
|
|
|
175
175
|
**Connect to CRM:**
|
|
176
176
|
```javascript
|
|
177
|
-
const { context, config } = await
|
|
177
|
+
const { context, config } = await Superleap.connect({
|
|
178
178
|
flowId: 'my-flow', // Required: the custom_id of your iframe component in CRM
|
|
179
179
|
crmOrigin: 'https://...', // Optional: validate CRM origin
|
|
180
180
|
autoInit: true, // Optional: auto-initialize SDK (default: true)
|
|
@@ -184,7 +184,7 @@ const { context, config } = await SuperLeap.connect({
|
|
|
184
184
|
|
|
185
185
|
**Access CRM Context:**
|
|
186
186
|
```javascript
|
|
187
|
-
const context =
|
|
187
|
+
const context = Superleap.getContext();
|
|
188
188
|
// {
|
|
189
189
|
// orgId: string,
|
|
190
190
|
// userId: string,
|
|
@@ -198,35 +198,35 @@ const context = SuperLeap.getContext();
|
|
|
198
198
|
**CRM Actions:**
|
|
199
199
|
```javascript
|
|
200
200
|
// Show/hide loading overlay in CRM
|
|
201
|
-
|
|
202
|
-
|
|
201
|
+
Superleap.setLoading(true);
|
|
202
|
+
Superleap.setLoading(false);
|
|
203
203
|
|
|
204
204
|
// Close the current form/modal in CRM
|
|
205
|
-
|
|
205
|
+
Superleap.closeForm();
|
|
206
206
|
|
|
207
207
|
// Show toast notification in CRM
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
Superleap.toast('Success!', 'success', 3000);
|
|
209
|
+
Superleap.toast('Error occurred', 'error');
|
|
210
|
+
Superleap.toast('Warning message', 'warning');
|
|
211
|
+
Superleap.toast('Info message', 'info');
|
|
212
212
|
|
|
213
213
|
// Navigate to a path in CRM
|
|
214
|
-
|
|
214
|
+
Superleap.navigate('/records/contacts/123');
|
|
215
215
|
```
|
|
216
216
|
|
|
217
217
|
**Custom Events (CRM ↔ Iframe Communication):**
|
|
218
218
|
```javascript
|
|
219
219
|
// Send custom event to CRM
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
Superleap.send('form-submitted', { values: {...} });
|
|
221
|
+
Superleap.send('validation-failed', { errors: [...] });
|
|
222
222
|
|
|
223
223
|
// Listen for custom events from CRM
|
|
224
|
-
const unsubscribe =
|
|
224
|
+
const unsubscribe = Superleap.on('prefill-data', (data) => {
|
|
225
225
|
FlowUI.setState(data);
|
|
226
226
|
});
|
|
227
227
|
|
|
228
228
|
// Remove listener
|
|
229
|
-
|
|
229
|
+
Superleap.off('prefill-data', callbackFunction);
|
|
230
230
|
|
|
231
231
|
// Or use unsubscribe function
|
|
232
232
|
unsubscribe();
|
|
@@ -234,14 +234,14 @@ unsubscribe();
|
|
|
234
234
|
|
|
235
235
|
**Check Connection Status:**
|
|
236
236
|
```javascript
|
|
237
|
-
if (
|
|
237
|
+
if (Superleap.isConnected()) {
|
|
238
238
|
// Connected to CRM
|
|
239
239
|
}
|
|
240
240
|
```
|
|
241
241
|
|
|
242
242
|
**Disconnect:**
|
|
243
243
|
```javascript
|
|
244
|
-
|
|
244
|
+
Superleap.disconnect();
|
|
245
245
|
```
|
|
246
246
|
|
|
247
247
|
#### React Native WebView Support
|
|
@@ -253,7 +253,7 @@ The bridge automatically detects and supports React Native WebView environments.
|
|
|
253
253
|
```javascript
|
|
254
254
|
// Only 2 objects in global scope
|
|
255
255
|
window.FlowUI // ✅ UI Framework with all components
|
|
256
|
-
window.
|
|
256
|
+
window.Superleap // ✅ SDK Client for API integration
|
|
257
257
|
|
|
258
258
|
// All components are internal (not global)
|
|
259
259
|
window.Toast // ❌ Not available
|
|
@@ -273,7 +273,7 @@ window.Alert // ❌ Not available
|
|
|
273
273
|
- 📊 **Data Tables** - Sortable, searchable tables
|
|
274
274
|
- 🎨 **Customizable** - Tailwind CSS for easy styling
|
|
275
275
|
- 📱 **Responsive** - Mobile-first design
|
|
276
|
-
- ✨ **Clean Architecture** - Only FlowUI and
|
|
276
|
+
- ✨ **Clean Architecture** - Only FlowUI and Superleap exposed globally
|
|
277
277
|
- 📦 **Single Script** - One file includes everything (SDK + components)
|
|
278
278
|
- 🚀 **Zero Dependencies** - Pure vanilla JavaScript
|
|
279
279
|
|
|
@@ -421,7 +421,7 @@ FlowUI.createFileUpload({
|
|
|
421
421
|
accept: '.pdf,.jpg,.png'
|
|
422
422
|
})
|
|
423
423
|
|
|
424
|
-
// Record select (requires
|
|
424
|
+
// Record select (requires Superleap SDK)
|
|
425
425
|
FlowUI.createRecordSelect({
|
|
426
426
|
label: 'Account',
|
|
427
427
|
fieldId: 'account',
|
|
@@ -519,25 +519,25 @@ Toast.success('Hello!')
|
|
|
519
519
|
Button.create({ text: 'Click me' })
|
|
520
520
|
```
|
|
521
521
|
|
|
522
|
-
###
|
|
522
|
+
### Superleap API
|
|
523
523
|
|
|
524
524
|
```javascript
|
|
525
525
|
// Initialize SDK
|
|
526
|
-
|
|
526
|
+
Superleap.init({
|
|
527
527
|
apiKey: 'YOUR_API_KEY',
|
|
528
528
|
baseUrl: 'https://app.superleap.com/api/v1'
|
|
529
529
|
})
|
|
530
530
|
|
|
531
531
|
// Get SDK instance
|
|
532
|
-
const sdk =
|
|
532
|
+
const sdk = Superleap.getSdk()
|
|
533
533
|
|
|
534
534
|
// Check availability
|
|
535
|
-
if (
|
|
535
|
+
if (Superleap.isAvailable()) {
|
|
536
536
|
// SDK is ready
|
|
537
537
|
}
|
|
538
538
|
|
|
539
539
|
// Get default config
|
|
540
|
-
const config =
|
|
540
|
+
const config = Superleap.getDefaultConfig()
|
|
541
541
|
```
|
|
542
542
|
|
|
543
543
|
## 🎨 Component Variants
|
|
@@ -573,13 +573,13 @@ See `example.html` for a comprehensive demonstration of all components.
|
|
|
573
573
|
Full TypeScript definitions included:
|
|
574
574
|
|
|
575
575
|
```typescript
|
|
576
|
-
import { FlowUI,
|
|
576
|
+
import { FlowUI, Superleap, SuperLeapConfig } from '@superleapai/flow-ui';
|
|
577
577
|
|
|
578
578
|
const config: SuperLeapConfig = {
|
|
579
579
|
apiKey: 'YOUR_KEY'
|
|
580
580
|
};
|
|
581
581
|
|
|
582
|
-
|
|
582
|
+
Superleap.init(config);
|
|
583
583
|
FlowUI.initState({ name: '' });
|
|
584
584
|
```
|
|
585
585
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* EnumMultiSelect Component (vanilla JS, Tailwind)
|
|
3
|
-
* A multiselect component that fetches options from
|
|
3
|
+
* A multiselect component that fetches options from Superleap SDK based on object column metadata.
|
|
4
4
|
* Supports dependent fields, URL-based enums, and search functionality.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Usage:
|
|
7
7
|
* var enumMultiSelect = EnumMultiSelect.create({
|
|
8
8
|
* fieldId: 'tags',
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"border-error-border hover:border-error-border-hover focus:border-1/2 focus:border-error-border-hover",
|
|
48
48
|
warning:
|
|
49
49
|
"border-warning-border hover:border-warning-border-hover focus:border-1/2 focus:border-warning-border-hover",
|
|
50
|
-
borderless:
|
|
50
|
+
borderless:
|
|
51
|
+
"border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
|
|
51
52
|
inline:
|
|
52
53
|
"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",
|
|
53
54
|
};
|
|
@@ -56,7 +57,9 @@
|
|
|
56
57
|
large: "px-12 py-8",
|
|
57
58
|
small: "px-8 py-4",
|
|
58
59
|
};
|
|
59
|
-
var textColorClass = isEmpty
|
|
60
|
+
var textColorClass = isEmpty
|
|
61
|
+
? " text-typography-quaternary-text"
|
|
62
|
+
: " text-typography-primary-text";
|
|
60
63
|
var disabledClass = disabled
|
|
61
64
|
? " pointer-events-none cursor-not-allowed bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary"
|
|
62
65
|
: "";
|
|
@@ -77,7 +80,10 @@
|
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
function getDep(name) {
|
|
80
|
-
if (
|
|
83
|
+
if (
|
|
84
|
+
typeof global.FlowUI !== "undefined" &&
|
|
85
|
+
typeof global.FlowUI._getComponent === "function"
|
|
86
|
+
) {
|
|
81
87
|
var c = global.FlowUI._getComponent(name);
|
|
82
88
|
if (c) return c;
|
|
83
89
|
}
|
|
@@ -168,7 +174,13 @@
|
|
|
168
174
|
|
|
169
175
|
var trigger = document.createElement("button");
|
|
170
176
|
trigger.type = "button";
|
|
171
|
-
trigger.className = triggerClasses(
|
|
177
|
+
trigger.className = triggerClasses(
|
|
178
|
+
variant,
|
|
179
|
+
size,
|
|
180
|
+
disabled,
|
|
181
|
+
values.length === 0,
|
|
182
|
+
canClear && values.length > 0 && !disabled,
|
|
183
|
+
);
|
|
172
184
|
trigger.disabled = disabled;
|
|
173
185
|
trigger.setAttribute("aria-haspopup", "listbox");
|
|
174
186
|
trigger.setAttribute("aria-expanded", "false");
|
|
@@ -176,7 +188,8 @@
|
|
|
176
188
|
trigger.setAttribute("aria-label", placeholder);
|
|
177
189
|
|
|
178
190
|
var triggerContent = document.createElement("div");
|
|
179
|
-
triggerContent.className =
|
|
191
|
+
triggerContent.className =
|
|
192
|
+
"flex flex-1 items-center gap-6 truncate text-left text-inherit";
|
|
180
193
|
|
|
181
194
|
function renderTriggerContent() {
|
|
182
195
|
triggerContent.innerHTML = "";
|
|
@@ -201,7 +214,7 @@
|
|
|
201
214
|
"ml-4 box-content flex size-16 items-center justify-center shrink-0 transition-transform duration-200 group-[.open]:rotate-180";
|
|
202
215
|
chevron.innerHTML = CHEVRON_SVG;
|
|
203
216
|
chevron.setAttribute("aria-hidden", "true");
|
|
204
|
-
|
|
217
|
+
|
|
205
218
|
var showChevron = variant !== "inline" || values.length === 0;
|
|
206
219
|
if (showChevron) {
|
|
207
220
|
trigger.appendChild(chevron);
|
|
@@ -216,19 +229,14 @@
|
|
|
216
229
|
"rounded-full absolute right-12 top-1/2 -translate-y-1/2 bg-transparent p-0 text-typography-tertiary-text hover:text-typography-secondary-text focus:outline-none";
|
|
217
230
|
clearBtn.innerHTML = X_SVG;
|
|
218
231
|
clearBtn.setAttribute("aria-label", "Clear selection");
|
|
219
|
-
clearBtn.style.display =
|
|
232
|
+
clearBtn.style.display =
|
|
233
|
+
canClear && values.length > 0 && !disabled ? "" : "none";
|
|
220
234
|
clearBtn.addEventListener("click", function (e) {
|
|
221
235
|
e.stopPropagation();
|
|
222
236
|
values = [];
|
|
223
237
|
renderTriggerContent();
|
|
224
|
-
trigger.className = triggerClasses(
|
|
225
|
-
|
|
226
|
-
size,
|
|
227
|
-
disabled,
|
|
228
|
-
true,
|
|
229
|
-
false
|
|
230
|
-
);
|
|
231
|
-
|
|
238
|
+
trigger.className = triggerClasses(variant, size, disabled, true, false);
|
|
239
|
+
|
|
232
240
|
// Update chevron visibility for inline variant
|
|
233
241
|
var shouldShowChevron = variant !== "inline" || values.length === 0;
|
|
234
242
|
if (shouldShowChevron && !chevron.parentNode) {
|
|
@@ -236,21 +244,22 @@
|
|
|
236
244
|
} else if (!shouldShowChevron && chevron.parentNode) {
|
|
237
245
|
chevron.parentNode.removeChild(chevron);
|
|
238
246
|
}
|
|
239
|
-
|
|
247
|
+
|
|
240
248
|
updateClearButton();
|
|
241
249
|
syncOptionStates();
|
|
242
|
-
|
|
250
|
+
|
|
243
251
|
if (onClear) onClear();
|
|
244
252
|
if (onChange) onChange([]);
|
|
245
253
|
});
|
|
246
254
|
triggerWrapper.appendChild(clearBtn);
|
|
247
255
|
|
|
248
256
|
container.appendChild(triggerWrapper);
|
|
249
|
-
|
|
257
|
+
|
|
250
258
|
// Helper to update clear button visibility
|
|
251
259
|
function updateClearButton() {
|
|
252
260
|
if (clearBtn) {
|
|
253
|
-
clearBtn.style.display =
|
|
261
|
+
clearBtn.style.display =
|
|
262
|
+
canClear && values.length > 0 && !disabled ? "" : "none";
|
|
254
263
|
}
|
|
255
264
|
}
|
|
256
265
|
|
|
@@ -258,15 +267,20 @@
|
|
|
258
267
|
var content = document.createElement("div");
|
|
259
268
|
content.setAttribute("role", "listbox");
|
|
260
269
|
content.setAttribute("aria-multiselectable", "true");
|
|
261
|
-
content.className =
|
|
270
|
+
content.className =
|
|
271
|
+
"w-full min-w-[200px] max-h-[30vh] overflow-hidden flex flex-col";
|
|
262
272
|
|
|
263
273
|
// Search input (using InputComponent like enum-select)
|
|
264
274
|
var searchContainer = document.createElement("div");
|
|
265
|
-
searchContainer.className =
|
|
266
|
-
|
|
275
|
+
searchContainer.className =
|
|
276
|
+
"py-2 border-b-1/2 border-border-primary hidden";
|
|
277
|
+
|
|
267
278
|
var searchInputWrapper = null;
|
|
268
279
|
var searchInput = null;
|
|
269
|
-
if (
|
|
280
|
+
if (
|
|
281
|
+
global.InputComponent &&
|
|
282
|
+
typeof global.InputComponent.create === "function"
|
|
283
|
+
) {
|
|
270
284
|
searchInputWrapper = global.InputComponent.create({
|
|
271
285
|
variant: "borderless",
|
|
272
286
|
inputSize: "small",
|
|
@@ -293,12 +307,12 @@
|
|
|
293
307
|
// Fallback to native input with search icon
|
|
294
308
|
var fallbackWrapper = document.createElement("div");
|
|
295
309
|
fallbackWrapper.className = "flex items-center gap-8";
|
|
296
|
-
|
|
310
|
+
|
|
297
311
|
var searchIconSpan = document.createElement("span");
|
|
298
312
|
searchIconSpan.className = "shrink-0 text-typography-tertiary-text";
|
|
299
313
|
searchIconSpan.innerHTML = SEARCH_ICON;
|
|
300
314
|
fallbackWrapper.appendChild(searchIconSpan);
|
|
301
|
-
|
|
315
|
+
|
|
302
316
|
var nativeSearchInput = document.createElement("input");
|
|
303
317
|
nativeSearchInput.type = "text";
|
|
304
318
|
nativeSearchInput.placeholder = "Search...";
|
|
@@ -375,8 +389,14 @@
|
|
|
375
389
|
values.push(optionValue);
|
|
376
390
|
}
|
|
377
391
|
renderTriggerContent();
|
|
378
|
-
trigger.className = triggerClasses(
|
|
379
|
-
|
|
392
|
+
trigger.className = triggerClasses(
|
|
393
|
+
variant,
|
|
394
|
+
size,
|
|
395
|
+
disabled,
|
|
396
|
+
values.length === 0,
|
|
397
|
+
canClear && values.length > 0 && !disabled,
|
|
398
|
+
);
|
|
399
|
+
|
|
380
400
|
// Update chevron visibility for inline variant
|
|
381
401
|
var shouldShowChevron = variant !== "inline" || values.length === 0;
|
|
382
402
|
if (shouldShowChevron && !chevron.parentNode) {
|
|
@@ -384,7 +404,7 @@
|
|
|
384
404
|
} else if (!shouldShowChevron && chevron.parentNode) {
|
|
385
405
|
chevron.parentNode.removeChild(chevron);
|
|
386
406
|
}
|
|
387
|
-
|
|
407
|
+
|
|
388
408
|
updateClearButton();
|
|
389
409
|
syncOptionStates();
|
|
390
410
|
if (onChange) onChange(values.slice());
|
|
@@ -394,14 +414,22 @@
|
|
|
394
414
|
optionsList.querySelectorAll("[role=option]").forEach(function (optEl) {
|
|
395
415
|
var dv = optEl.getAttribute("data-value");
|
|
396
416
|
var selected = values.some(function (v) {
|
|
397
|
-
return
|
|
417
|
+
return (
|
|
418
|
+
v === dv ||
|
|
419
|
+
String(v) === dv ||
|
|
420
|
+
(dv === "false" && v === false) ||
|
|
421
|
+
(dv === "true" && v === true)
|
|
422
|
+
);
|
|
398
423
|
});
|
|
399
424
|
optEl.setAttribute("aria-selected", selected);
|
|
400
425
|
var checkEl = optEl.querySelector(".multiselect-option-check");
|
|
401
426
|
if (checkEl) checkEl.style.visibility = selected ? "visible" : "hidden";
|
|
402
427
|
optEl.classList.toggle("bg-primary-surface", selected);
|
|
403
428
|
optEl.classList.toggle("hover:!bg-primary-surface-hover", selected);
|
|
404
|
-
optEl.classList.toggle(
|
|
429
|
+
optEl.classList.toggle(
|
|
430
|
+
"hover:bg-fill-tertiary-fill-light-gray",
|
|
431
|
+
!selected,
|
|
432
|
+
);
|
|
405
433
|
});
|
|
406
434
|
}
|
|
407
435
|
|
|
@@ -410,7 +438,8 @@
|
|
|
410
438
|
var filteredOptions = getFilteredOptions();
|
|
411
439
|
|
|
412
440
|
// Update search visibility
|
|
413
|
-
var shouldShowSearch =
|
|
441
|
+
var shouldShowSearch =
|
|
442
|
+
showSearch !== undefined ? showSearch : options.length > 10;
|
|
414
443
|
searchContainer.className = shouldShowSearch
|
|
415
444
|
? "p-8 border-b-1/2 border-border-primary "
|
|
416
445
|
: "p-8 hidden";
|
|
@@ -439,7 +468,7 @@
|
|
|
439
468
|
option.className = join(
|
|
440
469
|
"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",
|
|
441
470
|
"hover:bg-fill-tertiary-fill-light-gray focus:bg-fill-tertiary-fill-light-gray",
|
|
442
|
-
selected ? "bg-primary-surface hover:!bg-primary-surface-hover" : ""
|
|
471
|
+
selected ? "bg-primary-surface hover:!bg-primary-surface-hover" : "",
|
|
443
472
|
);
|
|
444
473
|
|
|
445
474
|
var optionContent = document.createElement("span");
|
|
@@ -448,7 +477,10 @@
|
|
|
448
477
|
var badgeConfig = opt.badge_config;
|
|
449
478
|
if (badgeConfig && (badgeConfig.icon_color || badgeConfig.start_icon)) {
|
|
450
479
|
var IconComponent = getIcon();
|
|
451
|
-
if (
|
|
480
|
+
if (
|
|
481
|
+
IconComponent &&
|
|
482
|
+
typeof IconComponent.createIconOrColor === "function"
|
|
483
|
+
) {
|
|
452
484
|
var iconEl = IconComponent.createIconOrColor({
|
|
453
485
|
start_icon: badgeConfig.start_icon,
|
|
454
486
|
icon_color: badgeConfig.icon_color,
|
|
@@ -475,7 +507,8 @@
|
|
|
475
507
|
|
|
476
508
|
// Add checkmark
|
|
477
509
|
var checkSpan = document.createElement("span");
|
|
478
|
-
checkSpan.className =
|
|
510
|
+
checkSpan.className =
|
|
511
|
+
"multiselect-option-check ml-auto flex size-14 shrink-0 items-center justify-center";
|
|
479
512
|
checkSpan.innerHTML = CHECK_SVG;
|
|
480
513
|
checkSpan.style.visibility = selected ? "visible" : "hidden";
|
|
481
514
|
option.appendChild(checkSpan);
|
|
@@ -497,7 +530,9 @@
|
|
|
497
530
|
function openDropdown() {
|
|
498
531
|
if (disabled) return;
|
|
499
532
|
document
|
|
500
|
-
.querySelectorAll(
|
|
533
|
+
.querySelectorAll(
|
|
534
|
+
".enum-select, .enum-multiselect, .custom-select, .record-select, .custom-multiselect",
|
|
535
|
+
)
|
|
501
536
|
.forEach(function (other) {
|
|
502
537
|
if (other !== container && other.popoverInstance) {
|
|
503
538
|
other.popoverInstance.hide();
|
|
@@ -521,7 +556,8 @@
|
|
|
521
556
|
}
|
|
522
557
|
|
|
523
558
|
function toggleDropdown() {
|
|
524
|
-
var isVisible =
|
|
559
|
+
var isVisible =
|
|
560
|
+
popover.element && popover.element.classList.contains("visible");
|
|
525
561
|
if (isVisible) {
|
|
526
562
|
closeDropdown();
|
|
527
563
|
} else {
|
|
@@ -560,7 +596,10 @@
|
|
|
560
596
|
|
|
561
597
|
trigger.addEventListener("keydown", function (e) {
|
|
562
598
|
if (disabled) return;
|
|
563
|
-
var isVisible =
|
|
599
|
+
var isVisible =
|
|
600
|
+
popover &&
|
|
601
|
+
popover.element &&
|
|
602
|
+
popover.element.classList.contains("visible");
|
|
564
603
|
switch (e.key) {
|
|
565
604
|
case "Enter":
|
|
566
605
|
case " ":
|
|
@@ -635,8 +674,11 @@
|
|
|
635
674
|
// Load options from SDK
|
|
636
675
|
function loadOptions() {
|
|
637
676
|
var client = getClient();
|
|
638
|
-
if (
|
|
639
|
-
|
|
677
|
+
if (
|
|
678
|
+
!client ||
|
|
679
|
+
(typeof client.isAvailable === "function" && !client.isAvailable())
|
|
680
|
+
) {
|
|
681
|
+
error = "Superleap SDK not initialized";
|
|
640
682
|
showError(error);
|
|
641
683
|
if (onError) onError(error);
|
|
642
684
|
return;
|
|
@@ -653,26 +695,38 @@
|
|
|
653
695
|
}
|
|
654
696
|
|
|
655
697
|
var model = sdk.model(objectSlug);
|
|
656
|
-
|
|
698
|
+
|
|
657
699
|
model
|
|
658
700
|
.getField(columnSlug)
|
|
659
701
|
.then(function (field) {
|
|
660
702
|
if (!field) {
|
|
661
|
-
throw new Error(
|
|
703
|
+
throw new Error(
|
|
704
|
+
"Field '" +
|
|
705
|
+
columnSlug +
|
|
706
|
+
"' not found in object '" +
|
|
707
|
+
objectSlug +
|
|
708
|
+
"'",
|
|
709
|
+
);
|
|
662
710
|
}
|
|
663
711
|
|
|
664
712
|
columnData = field;
|
|
665
713
|
|
|
666
714
|
// Check if it's a select/stage field with enum_group
|
|
667
715
|
if (!field.enumGroup || !field.enumGroup.values) {
|
|
668
|
-
throw new Error(
|
|
716
|
+
throw new Error(
|
|
717
|
+
"Field '" + columnSlug + "' does not have enum options",
|
|
718
|
+
);
|
|
669
719
|
}
|
|
670
720
|
|
|
671
721
|
var enumGroup = field.enumGroup;
|
|
672
722
|
var allOptions = enumGroup.values || [];
|
|
673
723
|
|
|
674
724
|
// Handle dependent fields
|
|
675
|
-
if (
|
|
725
|
+
if (
|
|
726
|
+
columnData.is_dependent_field &&
|
|
727
|
+
enumGroup.properties &&
|
|
728
|
+
enumGroup.properties.parent_column_slug
|
|
729
|
+
) {
|
|
676
730
|
var parentSlug = enumGroup.properties.parent_column_slug;
|
|
677
731
|
var parentValue = currentRecordData[parentSlug];
|
|
678
732
|
|
|
@@ -697,14 +751,20 @@
|
|
|
697
751
|
} else {
|
|
698
752
|
// Filter active options (or include archived if it's in the selected values)
|
|
699
753
|
options = allOptions.filter(function (option) {
|
|
700
|
-
return
|
|
754
|
+
return (
|
|
755
|
+
option.is_active === 1 ||
|
|
756
|
+
option.is_active === true ||
|
|
757
|
+
values.includes(option.slug)
|
|
758
|
+
);
|
|
701
759
|
});
|
|
702
760
|
}
|
|
703
761
|
|
|
704
762
|
// Sort by rank
|
|
705
763
|
options.sort(function (a, b) {
|
|
706
|
-
var rankA =
|
|
707
|
-
|
|
764
|
+
var rankA =
|
|
765
|
+
a.rank !== undefined && a.rank !== null ? a.rank : 999999;
|
|
766
|
+
var rankB =
|
|
767
|
+
b.rank !== undefined && b.rank !== null ? b.rank : 999999;
|
|
708
768
|
return rankA - rankB;
|
|
709
769
|
});
|
|
710
770
|
|
|
@@ -719,9 +779,9 @@
|
|
|
719
779
|
size,
|
|
720
780
|
disabled,
|
|
721
781
|
false,
|
|
722
|
-
canClear && values.length > 0 && !disabled
|
|
782
|
+
canClear && values.length > 0 && !disabled,
|
|
723
783
|
);
|
|
724
|
-
|
|
784
|
+
|
|
725
785
|
// Update chevron visibility for inline variant
|
|
726
786
|
var shouldShowChevron = variant !== "inline" || values.length === 0;
|
|
727
787
|
if (shouldShowChevron && !chevron.parentNode) {
|
|
@@ -729,7 +789,7 @@
|
|
|
729
789
|
} else if (!shouldShowChevron && chevron.parentNode) {
|
|
730
790
|
chevron.parentNode.removeChild(chevron);
|
|
731
791
|
}
|
|
732
|
-
|
|
792
|
+
|
|
733
793
|
updateClearButton();
|
|
734
794
|
}
|
|
735
795
|
|
|
@@ -757,9 +817,9 @@
|
|
|
757
817
|
size,
|
|
758
818
|
disabled,
|
|
759
819
|
values.length === 0,
|
|
760
|
-
canClear && values.length > 0 && !disabled
|
|
820
|
+
canClear && values.length > 0 && !disabled,
|
|
761
821
|
);
|
|
762
|
-
|
|
822
|
+
|
|
763
823
|
// Update chevron visibility for inline variant
|
|
764
824
|
var shouldShowChevron = variant !== "inline" || values.length === 0;
|
|
765
825
|
if (shouldShowChevron && !chevron.parentNode) {
|
|
@@ -767,7 +827,7 @@
|
|
|
767
827
|
} else if (!shouldShowChevron && chevron.parentNode) {
|
|
768
828
|
chevron.parentNode.removeChild(chevron);
|
|
769
829
|
}
|
|
770
|
-
|
|
830
|
+
|
|
771
831
|
updateClearButton();
|
|
772
832
|
syncOptionStates();
|
|
773
833
|
};
|
|
@@ -785,10 +845,13 @@
|
|
|
785
845
|
size,
|
|
786
846
|
disabled,
|
|
787
847
|
values.length === 0,
|
|
788
|
-
canClear && values.length > 0 && !disabled
|
|
848
|
+
canClear && values.length > 0 && !disabled,
|
|
789
849
|
);
|
|
790
850
|
updateClearButton();
|
|
791
|
-
var isVisible =
|
|
851
|
+
var isVisible =
|
|
852
|
+
popover &&
|
|
853
|
+
popover.element &&
|
|
854
|
+
popover.element.classList.contains("visible");
|
|
792
855
|
if (disabled && isVisible) closeDropdown();
|
|
793
856
|
};
|
|
794
857
|
|