@superleapai/flow-ui 2.6.21 → 2.6.23

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 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 (Superleap integration)
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
- - Superleap client integration
56
+ - SuperLeap client integration
57
57
 
58
58
  ### Documentation
59
59
  - README.md with complete API reference
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Superleap
3
+ Copyright (c) 2026 SuperLeap
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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 `Superleap` are exposed to global scope. All components are internal.
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 Superleap SDK and all components -->
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 Superleap SDK (optional, for Record Select)
33
- Superleap.init({
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 Superleap CRM as an iframe, use `Superleap.connect()` to establish a secure connection. This automatically provides credentials and context without requiring hardcoded API keys.
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 Superleap.connect({
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
- Superleap.setLoading(true);
140
+ SuperLeap.setLoading(true);
141
141
 
142
142
  try {
143
143
  // Your business logic here
144
- const sdk = Superleap.getSdk();
144
+ const sdk = SuperLeap.getSdk();
145
145
  await sdk.records.create('contacts', data);
146
146
 
147
147
  // Show success notification in CRM
148
- Superleap.toast('Profile created successfully!', 'success');
148
+ SuperLeap.toast('Profile created successfully!', 'success');
149
149
 
150
150
  // Close the form/modal
151
- Superleap.closeForm();
151
+ SuperLeap.closeForm();
152
152
  } catch (error) {
153
- Superleap.toast('Failed to create profile', 'error');
153
+ SuperLeap.toast('Failed to create profile', 'error');
154
154
  } finally {
155
- Superleap.setLoading(false);
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 Superleap.connect({
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 = Superleap.getContext();
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
- Superleap.setLoading(true);
202
- Superleap.setLoading(false);
201
+ SuperLeap.setLoading(true);
202
+ SuperLeap.setLoading(false);
203
203
 
204
204
  // Close the current form/modal in CRM
205
- Superleap.closeForm();
205
+ SuperLeap.closeForm();
206
206
 
207
207
  // Show toast notification in CRM
208
- Superleap.toast('Success!', 'success', 3000);
209
- Superleap.toast('Error occurred', 'error');
210
- Superleap.toast('Warning message', 'warning');
211
- Superleap.toast('Info message', 'info');
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
- Superleap.navigate('/records/contacts/123');
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
- Superleap.send('form-submitted', { values: {...} });
221
- Superleap.send('validation-failed', { errors: [...] });
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 = Superleap.on('prefill-data', (data) => {
224
+ const unsubscribe = SuperLeap.on('prefill-data', (data) => {
225
225
  FlowUI.setState(data);
226
226
  });
227
227
 
228
228
  // Remove listener
229
- Superleap.off('prefill-data', callbackFunction);
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 (Superleap.isConnected()) {
237
+ if (SuperLeap.isConnected()) {
238
238
  // Connected to CRM
239
239
  }
240
240
  ```
241
241
 
242
242
  **Disconnect:**
243
243
  ```javascript
244
- Superleap.disconnect();
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.Superleap // ✅ SDK Client for API integration
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 Superleap exposed globally
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 Superleap SDK)
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
- ### Superleap API
522
+ ### SuperLeap API
523
523
 
524
524
  ```javascript
525
525
  // Initialize SDK
526
- Superleap.init({
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 = Superleap.getSdk()
532
+ const sdk = SuperLeap.getSdk()
533
533
 
534
534
  // Check availability
535
- if (Superleap.isAvailable()) {
535
+ if (SuperLeap.isAvailable()) {
536
536
  // SDK is ready
537
537
  }
538
538
 
539
539
  // Get default config
540
- const config = Superleap.getDefaultConfig()
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, Superleap, SuperLeapConfig } from '@superleapai/flow-ui';
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
- Superleap.init(config);
582
+ SuperLeap.init(config);
583
583
  FlowUI.initState({ name: '' });
584
584
  ```
585
585
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * EnumMultiSelect Component (vanilla JS, Tailwind)
3
- * A multiselect component that fetches options from Superleap SDK based on object column metadata.
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:
@@ -678,7 +678,7 @@
678
678
  !client ||
679
679
  (typeof client.isAvailable === "function" && !client.isAvailable())
680
680
  ) {
681
- error = "Superleap SDK not initialized";
681
+ error = "SuperLeap SDK not initialized";
682
682
  showError(error);
683
683
  if (onError) onError(error);
684
684
  return;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * EnumSelect Component (vanilla JS, Tailwind)
3
- * A select component that fetches options from Superleap SDK based on object column metadata.
3
+ * A select 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:
@@ -612,7 +612,7 @@
612
612
  !client ||
613
613
  (typeof client.isAvailable === "function" && !client.isAvailable())
614
614
  ) {
615
- error = "Superleap SDK not initialized";
615
+ error = "SuperLeap SDK not initialized";
616
616
  showError(error);
617
617
  if (onError) onError(error);
618
618
  return;
@@ -88,7 +88,7 @@
88
88
 
89
89
  if (!baseUrl) {
90
90
  throw new Error(
91
- "Superleap client not initialized. Call superleapClient.init({ baseUrl, apiKey }) first.",
91
+ "SuperLeap client not initialized. Call superleapClient.init({ baseUrl, apiKey }) first.",
92
92
  );
93
93
  }
94
94
 
@@ -168,19 +168,21 @@
168
168
  * @param {Array} config.value - Current selected values (array of record ids)
169
169
  * @param {Function} config.onValuesChange - Change handler (values: string[], records?: Array) => void
170
170
  * @param {boolean} [config.disabled] - Whether select is disabled
171
+ * @param {boolean} [config.showDisplayFields] - If true, fetch schema and show secondary fields from objectSchema.properties.display_fields
171
172
  * @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'borderless' | 'inline'
172
173
  * @param {string} [config.size] - 'default' | 'large' | 'small'
173
174
  * @param {number} [config.initialLimit] - Initial fetch limit (default 50)
174
- * @param {Array<string>} [config.displayFields] - Fields to display as secondary info (e.g. ["email", "phone"])
175
175
  * @param {Object} [config.initialFilter] - Optional filter object to merge with search (e.g. { field: "status", operator: "exact", value: "active" } or { and: [...] })
176
- * @param {Object} [config.objectSchema] - Optional ObjectType; display_plural_name/display_singular_name/slug used for placeholder/search/summary label; properties.icon_data { icon?, color? } for static icon (not used for user; user shows Vivid Avatar)
177
176
  * @returns {HTMLElement} Record multiselect container element
178
177
  */
179
178
  function createRecordMultiSelect(config) {
180
179
  var fieldId = config.fieldId;
181
180
  var objectSlug = config.objectSlug;
182
- var objectSchema = config.objectSchema || null;
181
+ var objectSchema = null;
183
182
  var displayPlural = getObjectDisplayName(objectSchema, objectSlug, "plural");
183
+ var hasCustomPlaceholder = typeof config.placeholder === "string" && config.placeholder.length > 0;
184
+ var hasCustomSearchPlaceholder = typeof config.searchPlaceholder === "string" && config.searchPlaceholder.length > 0;
185
+ var hasCustomSummaryLabel = typeof config.label === "string" && config.label.length > 0;
184
186
  var placeholder =
185
187
  config.placeholder ||
186
188
  (displayPlural ? "Select " + displayPlural : "Select records");
@@ -189,11 +191,11 @@
189
191
  "Search " + (displayPlural || objectSlug || "") + "...";
190
192
  var summaryLabel = config.label || (displayPlural || "selected");
191
193
  var onValuesChange = config.onValuesChange;
194
+ var showDisplayFields = config.showDisplayFields === true;
192
195
  var variant = config.variant || "default";
193
196
  var size = config.size || "default";
194
197
  var initialLimit = config.initialLimit != null ? config.initialLimit : 50;
195
198
  var initialFilter = config.initialFilter || null; // Can be array, object, or function returning either
196
- var displayFields = config.displayFields || [];
197
199
 
198
200
  var disabled = config.disabled === true;
199
201
  var values = Array.isArray(config.value)
@@ -224,6 +226,21 @@
224
226
  var currentPage = 1;
225
227
  var isFetchingMore = false;
226
228
  var totalFetched = 0;
229
+ var schemaLoaded = false;
230
+ var schemaLoading = false;
231
+
232
+ function getSchemaDisplayFields() {
233
+ if (!showDisplayFields) return [];
234
+ var fields =
235
+ objectSchema &&
236
+ objectSchema.properties &&
237
+ Array.isArray(objectSchema.properties.display_fields)
238
+ ? objectSchema.properties.display_fields
239
+ : [];
240
+ return fields.filter(function (field) {
241
+ return typeof field === "string" && field.length > 0;
242
+ });
243
+ }
227
244
 
228
245
  // Trigger wrapper + button
229
246
  var triggerWrapper = document.createElement("span");
@@ -413,6 +430,7 @@
413
430
  popover.panel.style.minWidth = triggerWidthPx;
414
431
  popover.panel.style.width = triggerWidthPx;
415
432
  }
433
+ loadObjectSchema();
416
434
  loadInitialAndRender();
417
435
  setTimeout(function () {
418
436
  if (searchInputEl) searchInputEl.focus();
@@ -432,6 +450,57 @@
432
450
  });
433
451
  container.popoverInstance = popover;
434
452
 
453
+ function refreshSchemaDerivedText() {
454
+ displayPlural = getObjectDisplayName(objectSchema, objectSlug, "plural");
455
+ if (!hasCustomPlaceholder) {
456
+ placeholder = displayPlural
457
+ ? "Select " + displayPlural
458
+ : "Select records";
459
+ }
460
+ if (!hasCustomSearchPlaceholder) {
461
+ searchPlaceholder = "Search " + (displayPlural || objectSlug || "") + "...";
462
+ }
463
+ if (!hasCustomSummaryLabel) {
464
+ summaryLabel = displayPlural || "selected";
465
+ }
466
+
467
+ trigger.setAttribute("aria-label", placeholder);
468
+ renderTriggerContent();
469
+ if (searchInputWrapper && typeof searchInputWrapper.setPlaceholder === "function") {
470
+ searchInputWrapper.setPlaceholder(searchPlaceholder);
471
+ } else if (searchInputEl) {
472
+ searchInputEl.placeholder = searchPlaceholder;
473
+ }
474
+ if (searchInputEl) {
475
+ searchInputEl.setAttribute("aria-label", "Search " + (displayPlural || "records"));
476
+ }
477
+ }
478
+
479
+ function loadObjectSchema() {
480
+ if (schemaLoaded || schemaLoading) return;
481
+ if (!showDisplayFields) return;
482
+ var client = getClient();
483
+ if (!client || typeof client.getSdk !== "function") return;
484
+ var sdk = client.getSdk();
485
+ if (!sdk) return;
486
+ var model = sdk.model(objectSlug);
487
+ if (!model || typeof model.getSchema !== "function") return;
488
+
489
+ schemaLoading = true;
490
+ model
491
+ .getSchema()
492
+ .then(function (schema) {
493
+ objectSchema = schema && schema.data ? schema.data : schema;
494
+ schemaLoaded = true;
495
+ refreshSchemaDerivedText();
496
+ if (filteredRecords.length > 0) renderOptions();
497
+ })
498
+ .catch(function () {})
499
+ .finally(function () {
500
+ schemaLoading = false;
501
+ });
502
+ }
503
+
435
504
  function isSelected(recordId) {
436
505
  return values.some(function (v) {
437
506
  return v === recordId || String(v) === String(recordId);
@@ -483,7 +552,8 @@
483
552
  var sdk = client.getSdk();
484
553
  if (!sdk) return Promise.resolve({ records: [], hasMore: false });
485
554
  var model = sdk.model(objectSlug);
486
- var fields = ["id", "name"].concat(displayFields || []);
555
+ var schemaDisplayFields = getSchemaDisplayFields();
556
+ var fields = ["id", "name"].concat(schemaDisplayFields);
487
557
  var actualLimit = limit || initialLimit;
488
558
  var offset = page ? (page - 1) * actualLimit : 0;
489
559
 
@@ -517,9 +587,9 @@
517
587
  var mappedRecords = records.map(function (r) {
518
588
  var d = r.toJSON ? r.toJSON() : r;
519
589
  var iconUrl = d.icon || d.logo || d.icon_url || d.Icon__c || d.Logo__c || null;
520
- var displayLabels = displayFields ? displayFields.map(function (field) {
590
+ var displayLabels = schemaDisplayFields.map(function (field) {
521
591
  return d[field] || "";
522
- }) : [];
592
+ });
523
593
  return {
524
594
  id: d.id,
525
595
  value: d.id,
@@ -555,7 +625,8 @@
555
625
  return;
556
626
  }
557
627
  var model = sdk.model(objectSlug);
558
- var fields = ["id", "name"].concat(displayFields || []);
628
+ var schemaDisplayFields = getSchemaDisplayFields();
629
+ var fields = ["id", "name"].concat(schemaDisplayFields);
559
630
  model
560
631
  .select.apply(model, fields)
561
632
  .filterBy({
@@ -568,9 +639,9 @@
568
639
  selectedRecords = records.map(function (r) {
569
640
  var d = r.toJSON ? r.toJSON() : r;
570
641
  var iconUrl = d.icon || d.logo || d.icon_url || d.Icon__c || d.Logo__c || null;
571
- var displayLabels = displayFields ? displayFields.map(function (field) {
642
+ var displayLabels = schemaDisplayFields.map(function (field) {
572
643
  return d[field] || "";
573
- }) : [];
644
+ });
574
645
  return {
575
646
  id: d.id,
576
647
  value: d.id,
@@ -941,6 +1012,7 @@
941
1012
 
942
1013
  if (values && values.length > 0) loadSelectedRecords();
943
1014
  else renderTriggerContent();
1015
+ if (showDisplayFields) loadObjectSchema();
944
1016
 
945
1017
  return container;
946
1018
  }