@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.
@@ -12,6 +12,9 @@
12
12
  var X_SVG =
13
13
  '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" xmlns="http://www.w3.org/2000/svg"><path d="M18 6L6 18M6 6l12 12"/></svg>';
14
14
 
15
+ var SEARCH_ICON =
16
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>';
17
+
15
18
  function triggerClasses(variant, size, disabled, placeholder, hasClear) {
16
19
  var v = variant || "default";
17
20
  var base =
@@ -73,6 +76,7 @@
73
76
  * @param {string} [config.size] - 'default' | 'large' | 'small'
74
77
  * @param {boolean} [config.canClear] - Show clear button when value is set
75
78
  * @param {Function} [config.onClear] - Called when clear is clicked
79
+ * @param {boolean} [config.showSearch] - Show search input (default: false)
76
80
  * @returns {HTMLElement} Select container element
77
81
  */
78
82
  function createCustomSelect(config) {
@@ -84,9 +88,11 @@
84
88
  var size = config.size || "default";
85
89
  var canClear = !!config.canClear;
86
90
  var onClear = config.onClear;
91
+ var showSearch = !!config.showSearch;
87
92
  var popoverWrapperClassName = config.popoverWrapperClassName || "";
88
93
  var onOpenCallback = config.onOpenCallback || null;
89
94
  var dropdownMaxHeightVh = config.dropdownMaxHeightVh != null ? config.dropdownMaxHeightVh : 30;
95
+ var searchQuery = "";
90
96
 
91
97
  var disabled = config.disabled === true;
92
98
  var value =
@@ -199,19 +205,76 @@
199
205
  content.className = "custom-select-content w-full overflow-hidden flex flex-col";
200
206
  content.style.maxHeight = dropdownMaxHeightVh + "vh";
201
207
 
208
+ var searchContainer = document.createElement("div");
209
+ searchContainer.className = showSearch ? "p-8 border-b-1/2 border-border-primary" : "p-8 hidden";
210
+
211
+ var searchInputWrapper = document.createElement("div");
212
+ searchInputWrapper.className = "flex items-center gap-8";
213
+ var searchIconSpan = document.createElement("span");
214
+ searchIconSpan.className = "shrink-0 text-typography-tertiary-text";
215
+ searchIconSpan.innerHTML = SEARCH_ICON;
216
+ searchInputWrapper.appendChild(searchIconSpan);
217
+
218
+ var searchInput = document.createElement("input");
219
+ searchInput.type = "text";
220
+ searchInput.placeholder = "Search...";
221
+ searchInput.className =
222
+ "w-full bg-transparent text-reg-13 text-typography-primary-text placeholder:text-typography-quaternary-text focus:outline-none border-none";
223
+ searchInput.setAttribute("aria-label", "Search options");
224
+ searchInput.addEventListener("input", function (e) {
225
+ searchQuery = e.target.value.trim();
226
+ renderOptions();
227
+ updateOptionsSelection();
228
+ });
229
+ searchInput.addEventListener("click", function (e) {
230
+ e.stopPropagation();
231
+ });
232
+ searchInput.addEventListener("keydown", function (e) {
233
+ e.stopPropagation();
234
+ });
235
+ searchInputWrapper.appendChild(searchInput);
236
+ searchContainer.appendChild(searchInputWrapper);
237
+ content.appendChild(searchContainer);
238
+
202
239
  var optionsList = document.createElement("div");
203
240
  optionsList.className =
204
241
  "overflow-y-auto p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
205
242
  optionsList.style.maxHeight = dropdownMaxHeightVh + "vh";
206
243
 
207
- if (options.length === 0) {
208
- var noOpt = document.createElement("div");
209
- noOpt.className =
210
- "flex h-full min-h-[100px] w-full items-center justify-center p-4 !text-reg-13 text-typography-quaternary-text";
211
- noOpt.textContent = "No options available";
212
- optionsList.appendChild(noOpt);
213
- } else {
214
- options.forEach(function (opt) {
244
+ function getFilteredOptions() {
245
+ if (!showSearch || !searchQuery) return options.slice();
246
+ var normalizedQuery = searchQuery.toLowerCase();
247
+ return options.filter(function (opt) {
248
+ var label =
249
+ opt.label || opt.name || opt.display_name || opt.value || "";
250
+ var optionValue =
251
+ opt.value !== undefined && opt.value !== null
252
+ ? opt.value
253
+ : opt.slug || opt.id || "";
254
+ return (
255
+ String(label).toLowerCase().includes(normalizedQuery) ||
256
+ String(optionValue).toLowerCase().includes(normalizedQuery)
257
+ );
258
+ });
259
+ }
260
+
261
+ function renderOptions() {
262
+ optionsList.innerHTML = "";
263
+ var filteredOptions = getFilteredOptions();
264
+ if (filteredOptions.length === 0) {
265
+ var noOpt = document.createElement("div");
266
+ noOpt.className =
267
+ "flex h-full min-h-[100px] w-full items-center justify-center p-4 !text-reg-13 text-typography-quaternary-text";
268
+ noOpt.textContent =
269
+ options.length === 0
270
+ ? "No options available"
271
+ : searchQuery
272
+ ? "No options found"
273
+ : "No options available";
274
+ optionsList.appendChild(noOpt);
275
+ return;
276
+ }
277
+ filteredOptions.forEach(function (opt) {
215
278
  var optionValue =
216
279
  opt.value !== undefined && opt.value !== null
217
280
  ? opt.value
@@ -250,6 +313,7 @@
250
313
  optionsList.appendChild(option);
251
314
  });
252
315
  }
316
+ renderOptions();
253
317
 
254
318
  content.appendChild(optionsList);
255
319
 
@@ -278,6 +342,8 @@
278
342
  });
279
343
  trigger.setAttribute("aria-expanded", "true");
280
344
  chevron.style.transform = "rotate(180deg)";
345
+ renderOptions();
346
+ updateOptionsSelection();
281
347
  highlightOptionByValue(value);
282
348
  if (popover.panel) {
283
349
  var triggerWidthPx = trigger.offsetWidth + "px";
@@ -285,11 +351,22 @@
285
351
  popover.panel.style.minWidth = triggerWidthPx;
286
352
  popover.panel.style.width = triggerWidthPx;
287
353
  }
354
+ if (showSearch) {
355
+ setTimeout(function () {
356
+ searchInput.focus();
357
+ }, 50);
358
+ }
288
359
  },
289
360
  onClose: function () {
290
361
  trigger.setAttribute("aria-expanded", "false");
291
362
  chevron.style.transform = "";
292
363
  highlightedIndex = -1;
364
+ if (showSearch) {
365
+ searchQuery = "";
366
+ searchInput.value = "";
367
+ renderOptions();
368
+ updateOptionsSelection();
369
+ }
293
370
  },
294
371
  });
295
372
  container.popoverInstance = popover;
@@ -486,53 +563,8 @@
486
563
  container.updateOptions = function (newOptions) {
487
564
  options.length = 0;
488
565
  options.push.apply(options, newOptions);
489
- optionsList.innerHTML = "";
490
-
491
- if (newOptions.length === 0) {
492
- var noOpt = document.createElement("div");
493
- noOpt.className =
494
- "flex h-full min-h-[100px] w-full items-center justify-center p-4 !text-reg-13 text-typography-quaternary-text";
495
- noOpt.textContent = "No options available";
496
- optionsList.appendChild(noOpt);
497
- return;
498
- }
499
-
500
- newOptions.forEach(function (opt) {
501
- var optionValue =
502
- opt.value !== undefined && opt.value !== null
503
- ? opt.value
504
- : opt.slug || opt.id;
505
- var optionLabel =
506
- opt.label || opt.name || opt.display_name || opt.value;
507
- var isSelected = optionValue === value;
508
-
509
- var option = document.createElement("div");
510
- option.setAttribute("role", "option");
511
- option.setAttribute("data-value", optionValue);
512
- option.setAttribute("aria-selected", isSelected);
513
- option.className = join(
514
- "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",
515
- "hover:bg-fill-tertiary-fill-light-gray focus:bg-fill-tertiary-fill-light-gray",
516
- isSelected
517
- ? "bg-primary-surface hover:!bg-primary-surface-hover"
518
- : ""
519
- );
520
-
521
- var optionContent = document.createElement("span");
522
- optionContent.className = "flex items-center gap-8 flex-1 truncate";
523
- optionContent.textContent = optionLabel;
524
- option.appendChild(optionContent);
525
-
526
- option.addEventListener("click", function () {
527
- if (disabled) return;
528
- selectOption(optionValue);
529
- });
530
- option.addEventListener("mouseenter", function () {
531
- if (disabled) return;
532
- highlightOption(option);
533
- });
534
- optionsList.appendChild(option);
535
- });
566
+ renderOptions();
567
+ updateOptionsSelection();
536
568
  };
537
569
 
538
570
  container.setDisabled = function (isDisabled) {
package/core/bridge.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * Native WebView via window.ReactNativeWebView.
7
7
  *
8
8
  * This module is transport-only — it knows nothing about CRM concepts,
9
- * the SuperLeap SDK, or FlowUI state. The higher-level core/crm.js
9
+ * the Superleap SDK, or FlowUI state. The higher-level core/crm.js
10
10
  * builds on top of it.
11
11
  *
12
12
  * Exposed temporarily as window.SuperleapBridge, then captured into
@@ -327,7 +327,7 @@
327
327
  if (!_bridgeId) {
328
328
  reject(
329
329
  new Error(
330
- "SuperleapBridge: No _bridgeId found in URL. Is this page embedded in the SuperLeap CRM?",
330
+ "SuperleapBridge: No _bridgeId found in URL. Is this page embedded in the Superleap CRM?",
331
331
  ),
332
332
  );
333
333
  return;
@@ -391,7 +391,7 @@
391
391
  var err = new Error(
392
392
  "SuperleapBridge: Handshake timed out after " +
393
393
  timeoutMs +
394
- "ms. Is this page embedded in the SuperLeap CRM?",
394
+ "ms. Is this page embedded in the Superleap CRM?",
395
395
  );
396
396
  if (_pendingReject) _pendingReject(err);
397
397
  cleanup();
@@ -422,9 +422,7 @@
422
422
  */
423
423
  function send(action, payload) {
424
424
  if (!_connected) {
425
- throw new Error(
426
- "SuperleapBridge: Not connected. Call connect() first.",
427
- );
425
+ throw new Error("SuperleapBridge: Not connected. Call connect() first.");
428
426
  }
429
427
  sendRaw(createEnvelope(action, payload));
430
428
  }
package/core/crm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Superleap-Flow CRM Bridge Extensions
3
3
  *
4
- * This module extends the existing SuperLeap object (from superleapClient.js)
4
+ * This module extends the existing Superleap object (from superleapClient.js)
5
5
  * with CRM bridge functionality. It wraps the low-level bridge transport
6
6
  * (core/bridge.js) and adds:
7
7
  *
@@ -14,7 +14,7 @@
14
14
  * read from the URL). User code should listen for the 'superleap-flow:ready'
15
15
  * event — by the time it fires, the SDK is initialized and context is available.
16
16
  *
17
- * This extends the existing SuperLeap API from superleapClient.js which
17
+ * This extends the existing Superleap API from superleapClient.js which
18
18
  * already has: init(), getSdk(), isAvailable(), getDefaultConfig().
19
19
  */
20
20
 
@@ -73,7 +73,7 @@
73
73
  if (c) return c;
74
74
  }
75
75
  if (global.superleapClient) return global.superleapClient;
76
- if (global.SuperLeap) return global.SuperLeap;
76
+ if (global.Superleap) return global.Superleap;
77
77
  return null;
78
78
  }
79
79
 
@@ -84,7 +84,7 @@
84
84
  /**
85
85
  * Connect to the CRM. Performs the postMessage handshake, receives
86
86
  * credentials and context, and (by default) auto-initializes the
87
- * SuperLeap SDK.
87
+ * Superleap SDK.
88
88
  *
89
89
  * If already connected, silently resolves with the existing context/config.
90
90
  * If a connection is in flight (e.g. auto-connect), returns the same promise.
@@ -92,7 +92,7 @@
92
92
  * @param {Object} [options]
93
93
  * @param {string} [options.bridgeId] – explicit bridgeId override (auto-read from URL)
94
94
  * @param {string} [options.crmOrigin] – expected CRM origin for validation
95
- * @param {boolean} [options.autoInit] – auto-call SuperLeap.init() (default true)
95
+ * @param {boolean} [options.autoInit] – auto-call Superleap.init() (default true)
96
96
  * @param {number} [options.timeout] – handshake timeout in ms (default 5000)
97
97
  * @returns {Promise<{ context: Object, config: Object }>}
98
98
  */
@@ -125,7 +125,7 @@
125
125
  _context = (payload && payload.context) || {};
126
126
  _config = (payload && payload.config) || {};
127
127
 
128
- // Auto-initialize the SuperLeap SDK
128
+ // Auto-initialize the Superleap SDK
129
129
  var sdkConfig = payload && payload.sdkConfig;
130
130
  if (opts.autoInit !== false && sdkConfig) {
131
131
  var client = getSuperLeapClient();
@@ -248,18 +248,17 @@
248
248
  }
249
249
 
250
250
  /**
251
- * Register a callback for when the CRM form is submitted successfully.
252
- * The CRM sends a 'crm:formSubmitSuccess' message after the form saves.
251
+ * Notify the CRM that the form was submitted successfully.
252
+ * The CRM listens for 'crm:formSubmitSuccess' and can react
253
+ * (e.g. close the form, refresh data, show a toast).
253
254
  *
254
- * @param {Function} callbackreceives (payload)
255
- * @returns {Function} unsubscribe function
255
+ * @param {Object} [payload]optional data about the submission
256
256
  */
257
- function onFormSubmitSuccess(callback) {
257
+ function formSubmittedSuccessfully(payload) {
258
258
  var bridge = getBridge();
259
- if (!bridge) {
260
- return function () {};
259
+ if (bridge && bridge.isConnected()) {
260
+ bridge.send("crm:formSubmitSuccess", payload || {});
261
261
  }
262
- return bridge.onMessage("crm:onFormSubmitSuccess", callback);
263
262
  }
264
263
 
265
264
  // ---------------------------------------------------------------------------
@@ -325,7 +324,7 @@
325
324
  }
326
325
 
327
326
  // ---------------------------------------------------------------------------
328
- // Extend the existing superleapClient (which becomes SuperLeap)
327
+ // Extend the existing superleapClient (which becomes Superleap)
329
328
  // ---------------------------------------------------------------------------
330
329
 
331
330
  // Wait for superleapClient to be defined, then extend it
@@ -336,7 +335,8 @@
336
335
  global.superleapClient.disconnect = disconnect;
337
336
  global.superleapClient.setLoading = setLoading;
338
337
  global.superleapClient.closeForm = closeForm;
339
- global.superleapClient.onFormSubmitSuccess = onFormSubmitSuccess;
338
+ global.superleapClient.formSubmittedSuccessfully =
339
+ formSubmittedSuccessfully;
340
340
  global.superleapClient.toast = toast;
341
341
  global.superleapClient.navigate = navigate;
342
342
  global.superleapClient.getContext = getContext;
@@ -347,7 +347,7 @@
347
347
  // superleapClient not loaded yet — this shouldn't happen if scripts
348
348
  // load in order, but handle it gracefully
349
349
  console.warn(
350
- "SuperLeap CRM Bridge: superleapClient not found. Make sure core/superleapClient.js loads before core/crm.js",
350
+ "Superleap CRM Bridge: superleapClient not found. Make sure core/superleapClient.js loads before core/crm.js",
351
351
  );
352
352
  }
353
353
  })(typeof window !== "undefined" ? window : this);