@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.
@@ -10,16 +10,23 @@
10
10
  // Tabler-style inline SVGs (no external deps) – match React IconFile, IconCopy, IconEye, IconX
11
11
  var ICONS = {
12
12
  file: '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/></svg>',
13
- fileImage: '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>',
14
- filePdf: '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M9 12h6"/><path d="M9 16h6"/><path d="M9 8h1"/></svg>',
15
- fileVideo: '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 8-6 4 6 4V8Z"/><rect width="14" height="12" x="2" y="6" rx="2" ry="2"/></svg>',
16
- fileAudio: '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>',
17
- fileSpreadsheet: '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M8 12h8"/><path d="M8 16h8"/><path d="M8 8h8"/></svg>',
18
- fileText: '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/></svg>',
13
+ fileImage:
14
+ '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>',
15
+ filePdf:
16
+ '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M9 12h6"/><path d="M9 16h6"/><path d="M9 8h1"/></svg>',
17
+ fileVideo:
18
+ '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 8-6 4 6 4V8Z"/><rect width="14" height="12" x="2" y="6" rx="2" ry="2"/></svg>',
19
+ fileAudio:
20
+ '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>',
21
+ fileSpreadsheet:
22
+ '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M8 12h8"/><path d="M8 16h8"/><path d="M8 8h8"/></svg>',
23
+ fileText:
24
+ '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/></svg>',
19
25
  copy: '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>',
20
26
  eye: '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>',
21
27
  x: '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',
22
- loader: '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="animate-spin"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>',
28
+ loader:
29
+ '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="animate-spin"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>',
23
30
  };
24
31
 
25
32
  var IMAGE_EXT = /\.(jpe?g|png|gif|webp|bmp|svg|ico|avif)(\?|$)/i;
@@ -80,10 +87,15 @@
80
87
  }
81
88
 
82
89
  if (!baseUrl) {
83
- throw new Error("SuperLeap client not initialized. Call superleapClient.init({ baseUrl, apiKey }) first.");
90
+ throw new Error(
91
+ "Superleap client not initialized. Call superleapClient.init({ baseUrl, apiKey }) first.",
92
+ );
84
93
  }
85
94
 
86
- const fullUrl = uploadUrl.startsWith("http") ? uploadUrl : baseUrl.replace(/\/$/, "") + (uploadUrl.startsWith("/") ? uploadUrl : "/" + uploadUrl);
95
+ const fullUrl = uploadUrl.startsWith("http")
96
+ ? uploadUrl
97
+ : baseUrl.replace(/\/$/, "") +
98
+ (uploadUrl.startsWith("/") ? uploadUrl : "/" + uploadUrl);
87
99
 
88
100
  const headers = {};
89
101
  if (apiKey) {
@@ -103,14 +115,14 @@
103
115
 
104
116
  const result = await response.json();
105
117
  console.log("[S3FileUpload] Upload response:");
106
-
118
+
107
119
  const url = result?.data?.url;
108
-
120
+
109
121
  if (!url) {
110
122
  console.error("[S3FileUpload] No URL found in response:", result);
111
123
  throw new Error("Upload response did not contain a file URL");
112
124
  }
113
-
125
+
114
126
  return {
115
127
  url: url,
116
128
  };
@@ -135,8 +147,7 @@
135
147
  * @returns {HTMLElement} Field element
136
148
  */
137
149
  var UPLOAD_WRAPPER_CLASS = {
138
- base:
139
- "group relative flex w-full items-center justify-between border-1/2 rounded-4 text-typography-primary-text w-full transition-all ease-in-out focus-within:outline-none 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 group-has-[:disabled]:hover:border-border-primary",
150
+ base: "group relative flex w-full items-center justify-between border-1/2 rounded-4 text-typography-primary-text w-full transition-all ease-in-out focus-within:outline-none 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 group-has-[:disabled]:hover:border-border-primary",
140
151
  default:
141
152
  "border-border-primary bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
142
153
  error:
@@ -205,23 +216,29 @@
205
216
  UPLOAD_WRAPPER_CLASS.base,
206
217
  UPLOAD_WRAPPER_CLASS[currentVariant] || UPLOAD_WRAPPER_CLASS.default,
207
218
  sizeClass,
208
- className
219
+ className,
209
220
  );
210
221
  }
211
222
  applyWrapperClasses();
212
223
  uploadWrapper.setAttribute("data-file-input-variant", currentVariant);
213
224
  uploadWrapper.setAttribute("data-file-input-size", currentInputSize);
214
- uploadWrapper.setAttribute("data-disabled", disabledState ? "true" : "false");
225
+ uploadWrapper.setAttribute(
226
+ "data-disabled",
227
+ disabledState ? "true" : "false",
228
+ );
215
229
 
216
230
  // Left content (button + status) – pointer-events-none so overlay input receives clicks
217
231
  const leftContent = document.createElement("div");
218
- leftContent.className = "pointer-events-none flex min-w-0 flex-1 items-center gap-8 truncate";
232
+ leftContent.className =
233
+ "pointer-events-none flex min-w-0 flex-1 items-center gap-8 truncate";
219
234
 
220
235
  var disabledChildClasses =
221
236
  "group-has-[:disabled]:text-typography-quaternary-text group-has-[:disabled]:bg-fill-tertiary-fill-light-gray group-has-[:disabled]:hover:bg-fill-tertiary-fill-light-gray";
222
237
  var statusTextBaseClass = "text-reg-13 min-w-0 flex-1 truncate";
223
- var statusTextDisabledClass = " group-has-[:disabled]:text-typography-quaternary-text";
224
- const useButtonComponent = global.Button && typeof global.Button.create === "function";
238
+ var statusTextDisabledClass =
239
+ " group-has-[:disabled]:text-typography-quaternary-text";
240
+ const useButtonComponent =
241
+ global.Button && typeof global.Button.create === "function";
225
242
  const initialButtonText = multiple ? "Choose files" : "Choose a file";
226
243
  const btn = useButtonComponent
227
244
  ? global.Button.create({
@@ -241,7 +258,10 @@
241
258
 
242
259
  // Status text: "No files chosen" (quaternary) or "X file(s) selected"
243
260
  const statusText = document.createElement("p");
244
- statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
261
+ statusText.className =
262
+ statusTextBaseClass +
263
+ " text-typography-quaternary-text" +
264
+ statusTextDisabledClass;
245
265
 
246
266
  // Hidden file input – overlays row, high z-index so it receives clicks
247
267
  const input = document.createElement("input");
@@ -250,7 +270,9 @@
250
270
  function applyInputClasses() {
251
271
  input.className =
252
272
  inputBaseClass +
253
- (disabledState ? " !pointer-events-none !cursor-not-allowed" : " cursor-pointer");
273
+ (disabledState
274
+ ? " !pointer-events-none !cursor-not-allowed"
275
+ : " cursor-pointer");
254
276
  }
255
277
  applyInputClasses();
256
278
  input.multiple = multiple;
@@ -294,7 +316,8 @@
294
316
  function makeCopyBtn(fileUrl) {
295
317
  const btn = document.createElement("button");
296
318
  btn.type = "button";
297
- btn.className = "shrink-0 rounded-2 p-4 text-typography-quaternary-text transition-colors hover:text-typography-primary-text focus:outline-none";
319
+ btn.className =
320
+ "shrink-0 rounded-2 p-4 text-typography-quaternary-text transition-colors hover:text-typography-primary-text focus:outline-none";
298
321
  btn.innerHTML = ICONS.copy;
299
322
  btn.setAttribute("aria-label", "Copy URL");
300
323
  btn.addEventListener("click", function (e) {
@@ -309,7 +332,7 @@
309
332
  if (global.FlowUI && global.FlowUI.showToast) {
310
333
  global.FlowUI.showToast("Copy failed", "error");
311
334
  }
312
- }
335
+ },
313
336
  );
314
337
  });
315
338
  return btn;
@@ -318,7 +341,8 @@
318
341
  function makeViewBtn(fileUrl) {
319
342
  const btn = document.createElement("button");
320
343
  btn.type = "button";
321
- btn.className = "shrink-0 rounded-2 p-4 text-typography-quaternary-text transition-colors hover:text-typography-primary-text focus:outline-none";
344
+ btn.className =
345
+ "shrink-0 rounded-2 p-4 text-typography-quaternary-text transition-colors hover:text-typography-primary-text focus:outline-none";
322
346
  btn.innerHTML = ICONS.eye;
323
347
  btn.setAttribute("aria-label", "View file");
324
348
  btn.addEventListener("click", function (e) {
@@ -331,7 +355,8 @@
331
355
  function makeRemoveBtn(onRemove) {
332
356
  const btn = document.createElement("button");
333
357
  btn.type = "button";
334
- btn.className = "shrink-0 rounded-2 p-4 text-error-text-base transition-colors hover:bg-error-surface focus:outline-none";
358
+ btn.className =
359
+ "shrink-0 rounded-2 p-4 text-error-text-base transition-colors hover:bg-error-surface focus:outline-none";
335
360
  btn.innerHTML = ICONS.x;
336
361
  btn.setAttribute("aria-label", "Remove file");
337
362
  btn.addEventListener("click", function (e) {
@@ -348,7 +373,8 @@
348
373
 
349
374
  uploadedFiles.forEach(function (file, index) {
350
375
  var nameEl = document.createElement("p");
351
- nameEl.className = "max-w-[100px] truncate text-reg-10 text-typography-secondary-text";
376
+ nameEl.className =
377
+ "max-w-[100px] truncate text-reg-10 text-typography-secondary-text";
352
378
  nameEl.textContent = file.name;
353
379
  nameEl.title = file.url;
354
380
 
@@ -356,12 +382,14 @@
356
382
  actionsWrap.className = "flex items-center gap-2 shrink-0";
357
383
  actionsWrap.appendChild(makeCopyBtn(file.url));
358
384
  actionsWrap.appendChild(makeViewBtn(file.url));
359
- actionsWrap.appendChild(makeRemoveBtn(function () {
360
- uploadedFiles.splice(index, 1);
361
- saveToState();
362
- renderFilesList();
363
- updateStatus();
364
- }));
385
+ actionsWrap.appendChild(
386
+ makeRemoveBtn(function () {
387
+ uploadedFiles.splice(index, 1);
388
+ saveToState();
389
+ renderFilesList();
390
+ updateStatus();
391
+ }),
392
+ );
365
393
 
366
394
  var badge;
367
395
  if (useBadge) {
@@ -373,12 +401,15 @@
373
401
  className: "text-typography-primary-text",
374
402
  });
375
403
  var iconWraps = badge.querySelectorAll(".flex.size-16");
376
- if (iconWraps.length >= 1) iconWraps[0].classList.add("text-typography-secondary-text");
404
+ if (iconWraps.length >= 1)
405
+ iconWraps[0].classList.add("text-typography-secondary-text");
377
406
  } else {
378
407
  badge = document.createElement("div");
379
- badge.className = "inline-flex items-center gap-4 rounded-4 border-1/2 border-border-primary bg-fill-tertiary-fill-light-gray px-8 py-2 text-med-12 text-typography-primary-text";
408
+ badge.className =
409
+ "inline-flex items-center gap-4 rounded-4 border-1/2 border-border-primary bg-fill-tertiary-fill-light-gray px-8 py-2 text-med-12 text-typography-primary-text";
380
410
  var iconWrap = document.createElement("span");
381
- iconWrap.className = "flex size-16 shrink-0 items-center justify-center text-typography-secondary-text";
411
+ iconWrap.className =
412
+ "flex size-16 shrink-0 items-center justify-center text-typography-secondary-text";
382
413
  iconWrap.innerHTML = getFileTypeIcon(file.name || file.url);
383
414
  badge.appendChild(iconWrap);
384
415
  badge.appendChild(nameEl);
@@ -396,7 +427,8 @@
396
427
 
397
428
  uploadingFiles.forEach(function (item) {
398
429
  var nameEl = document.createElement("p");
399
- nameEl.className = "max-w-[100px] truncate text-reg-10 text-typography-secondary-text";
430
+ nameEl.className =
431
+ "max-w-[100px] truncate text-reg-10 text-typography-secondary-text";
400
432
  nameEl.textContent = item.name;
401
433
 
402
434
  var badge;
@@ -408,12 +440,15 @@
408
440
  className: "text-typography-primary-text",
409
441
  });
410
442
  var iconWraps = badge.querySelectorAll(".flex.size-16");
411
- if (iconWraps.length >= 1) iconWraps[0].classList.add("text-typography-secondary-text");
443
+ if (iconWraps.length >= 1)
444
+ iconWraps[0].classList.add("text-typography-secondary-text");
412
445
  } else {
413
446
  badge = document.createElement("div");
414
- badge.className = "inline-flex items-center gap-4 rounded-4 border-1/2 border-border-primary bg-fill-tertiary-fill-light-gray px-8 py-2 text-med-12";
447
+ badge.className =
448
+ "inline-flex items-center gap-4 rounded-4 border-1/2 border-border-primary bg-fill-tertiary-fill-light-gray px-8 py-2 text-med-12";
415
449
  var iconWrap = document.createElement("span");
416
- iconWrap.className = "flex size-16 shrink-0 items-center justify-center text-typography-secondary-text";
450
+ iconWrap.className =
451
+ "flex size-16 shrink-0 items-center justify-center text-typography-secondary-text";
417
452
  iconWrap.innerHTML = ICONS.loader;
418
453
  badge.appendChild(iconWrap);
419
454
  badge.appendChild(nameEl);
@@ -443,13 +478,22 @@
443
478
 
444
479
  if (!filesChosen) {
445
480
  statusText.textContent = "No files chosen";
446
- statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
481
+ statusText.className =
482
+ statusTextBaseClass +
483
+ " text-typography-quaternary-text" +
484
+ statusTextDisabledClass;
447
485
  } else if (uploadingCount > 0) {
448
486
  statusText.textContent = "Uploading…";
449
- statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
487
+ statusText.className =
488
+ statusTextBaseClass +
489
+ " text-typography-quaternary-text" +
490
+ statusTextDisabledClass;
450
491
  } else {
451
492
  statusText.textContent = `${uploadedCount} file${uploadedCount !== 1 ? "s" : ""} selected`;
452
- statusText.className = statusTextBaseClass + " text-typography-primary-text" + statusTextDisabledClass;
493
+ statusText.className =
494
+ statusTextBaseClass +
495
+ " text-typography-primary-text" +
496
+ statusTextDisabledClass;
453
497
  }
454
498
 
455
499
  endIconSlot.style.display = uploadingCount > 0 ? "flex" : "none";
@@ -486,7 +530,9 @@
486
530
  return fileExtension === type.toLowerCase();
487
531
  }
488
532
  if (type.includes("/")) {
489
- return fileType === type || fileType.startsWith(type.split("/")[0] + "/");
533
+ return (
534
+ fileType === type || fileType.startsWith(type.split("/")[0] + "/")
535
+ );
490
536
  }
491
537
  return false;
492
538
  });
@@ -517,9 +563,13 @@
517
563
  const result = await uploadFileToS3(file, isPrivate);
518
564
  const fileUrl = result?.url;
519
565
  if (!fileUrl) {
520
- throw new Error(`No URL returned from upload for file "${file.name}"`);
566
+ throw new Error(
567
+ `No URL returned from upload for file "${file.name}"`,
568
+ );
521
569
  }
522
- console.log(`[S3FileUpload] File uploaded successfully: ${file.name} -> ${fileUrl}`);
570
+ console.log(
571
+ `[S3FileUpload] File uploaded successfully: ${file.name} -> ${fileUrl}`,
572
+ );
523
573
 
524
574
  uploadingFiles = uploadingFiles.filter((f) => f.file !== file);
525
575
  uploadedFiles.push({ url: fileUrl, name: file.name });
@@ -533,7 +583,10 @@
533
583
  renderUploadingList();
534
584
  updateStatus();
535
585
  if (global.FlowUI && global.FlowUI.showToast) {
536
- global.FlowUI.showToast(`Failed to upload "${file.name}": ${error.message}`, "error");
586
+ global.FlowUI.showToast(
587
+ `Failed to upload "${file.name}": ${error.message}`,
588
+ "error",
589
+ );
537
590
  }
538
591
  }
539
592
  }
@@ -541,10 +594,16 @@
541
594
  // Handle file selection
542
595
  input.addEventListener("change", async function (e) {
543
596
  const files = Array.from(e.target.files || []);
544
- if (files.length === 0) {return;}
597
+ if (files.length === 0) {
598
+ return;
599
+ }
545
600
 
546
601
  // Check max files limit
547
- if (multiple && maxFiles && uploadedFiles.length + files.length > maxFiles) {
602
+ if (
603
+ multiple &&
604
+ maxFiles &&
605
+ uploadedFiles.length + files.length > maxFiles
606
+ ) {
548
607
  const message = `You can only upload ${maxFiles} file${maxFiles !== 1 ? "s" : ""}`;
549
608
  if (global.FlowUI && global.FlowUI.showToast) {
550
609
  global.FlowUI.showToast(message, "error");
@@ -591,7 +650,10 @@
591
650
  input.disabled = disabledState;
592
651
  applyInputClasses();
593
652
  applyWrapperClasses();
594
- uploadWrapper.setAttribute("data-disabled", disabledState ? "true" : "false");
653
+ uploadWrapper.setAttribute(
654
+ "data-disabled",
655
+ disabledState ? "true" : "false",
656
+ );
595
657
  };
596
658
 
597
659
  field.setVariant = function (v) {
@@ -12,6 +12,9 @@
12
12
  var CHECK_SVG =
13
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
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, isEmpty) {
16
19
  var v = variant || "default";
17
20
  var base =
@@ -80,6 +83,7 @@
80
83
  * @param {boolean} [config.disabled] - Whether multiselect is disabled
81
84
  * @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'borderless' | 'inline'
82
85
  * @param {string} [config.size] - 'default' | 'large' | 'small'
86
+ * @param {boolean} [config.showSearch] - Show search input (default: false)
83
87
  * @returns {HTMLElement} MultiSelect container element
84
88
  */
85
89
  function createMultiSelect(config) {
@@ -90,7 +94,9 @@
90
94
  var onValuesChange = config.onValuesChange;
91
95
  var variant = config.variant || "default";
92
96
  var size = config.size || "default";
97
+ var showSearch = !!config.showSearch;
93
98
  var disabled = config.disabled === true;
99
+ var searchQuery = "";
94
100
 
95
101
  var values = Array.isArray(config.value)
96
102
  ? config.value.slice()
@@ -156,6 +162,35 @@
156
162
  content.setAttribute("aria-multiselectable", "true");
157
163
  content.className = "custom-multiselect-content w-full max-h-[30vh] overflow-hidden flex flex-col";
158
164
 
165
+ var searchContainer = document.createElement("div");
166
+ searchContainer.className = showSearch ? "p-8 border-b-1/2 border-border-primary" : "p-8 hidden";
167
+ var searchInputWrapper = document.createElement("div");
168
+ searchInputWrapper.className = "flex items-center gap-8";
169
+ var searchIconSpan = document.createElement("span");
170
+ searchIconSpan.className = "shrink-0 text-typography-tertiary-text";
171
+ searchIconSpan.innerHTML = SEARCH_ICON;
172
+ searchInputWrapper.appendChild(searchIconSpan);
173
+ var searchInput = document.createElement("input");
174
+ searchInput.type = "text";
175
+ searchInput.placeholder = "Search...";
176
+ searchInput.className =
177
+ "w-full bg-transparent text-reg-13 text-typography-primary-text placeholder:text-typography-quaternary-text focus:outline-none border-none";
178
+ searchInput.setAttribute("aria-label", "Search options");
179
+ searchInput.addEventListener("input", function (e) {
180
+ searchQuery = e.target.value.trim();
181
+ buildOptionsList();
182
+ syncOptionStates();
183
+ });
184
+ searchInput.addEventListener("click", function (e) {
185
+ e.stopPropagation();
186
+ });
187
+ searchInput.addEventListener("keydown", function (e) {
188
+ e.stopPropagation();
189
+ });
190
+ searchInputWrapper.appendChild(searchInput);
191
+ searchContainer.appendChild(searchInputWrapper);
192
+ content.appendChild(searchContainer);
193
+
159
194
  var optionsList = document.createElement("div");
160
195
  optionsList.className =
161
196
  "overflow-y-auto max-h-[30vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
@@ -196,17 +231,36 @@
196
231
  });
197
232
  }
198
233
 
234
+ function getFilteredOptions() {
235
+ if (!showSearch || !searchQuery) return options.slice();
236
+ var normalizedQuery = searchQuery.toLowerCase();
237
+ return options.filter(function (opt) {
238
+ var optionValue = getOptionValue(opt);
239
+ var optionLabel = getOptionLabel(opt);
240
+ return (
241
+ String(optionLabel || "").toLowerCase().includes(normalizedQuery) ||
242
+ String(optionValue || "").toLowerCase().includes(normalizedQuery)
243
+ );
244
+ });
245
+ }
246
+
199
247
  function buildOptionsList() {
200
248
  optionsList.innerHTML = "";
201
- if (options.length === 0) {
249
+ var filteredOptions = getFilteredOptions();
250
+ if (filteredOptions.length === 0) {
202
251
  var noOpt = document.createElement("div");
203
252
  noOpt.className =
204
253
  "flex h-full min-h-[100px] w-full items-center justify-center p-4 !text-reg-13 text-typography-quaternary-text";
205
- noOpt.textContent = "No options available";
254
+ noOpt.textContent =
255
+ options.length === 0
256
+ ? "No options available"
257
+ : searchQuery
258
+ ? "No options found"
259
+ : "No options available";
206
260
  optionsList.appendChild(noOpt);
207
261
  return;
208
262
  }
209
- options.forEach(function (opt) {
263
+ filteredOptions.forEach(function (opt) {
210
264
  var optionValue = getOptionValue(opt);
211
265
  var optionLabel = getOptionLabel(opt);
212
266
  var selected = isSelected(optionValue);
@@ -278,16 +332,29 @@
278
332
  });
279
333
  trigger.setAttribute("aria-expanded", "true");
280
334
  chevron.style.transform = "rotate(180deg)";
335
+ buildOptionsList();
336
+ syncOptionStates();
281
337
  if (popover.panel) {
282
338
  var triggerWidthPx = trigger.offsetWidth + "px";
283
339
  popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
284
340
  popover.panel.style.minWidth = triggerWidthPx;
285
341
  popover.panel.style.width = triggerWidthPx;
286
342
  }
343
+ if (showSearch) {
344
+ setTimeout(function () {
345
+ searchInput.focus();
346
+ }, 50);
347
+ }
287
348
  },
288
349
  onClose: function () {
289
350
  trigger.setAttribute("aria-expanded", "false");
290
351
  chevron.style.transform = "";
352
+ if (showSearch) {
353
+ searchQuery = "";
354
+ searchInput.value = "";
355
+ buildOptionsList();
356
+ syncOptionStates();
357
+ }
291
358
  },
292
359
  });
293
360
  container.popoverInstance = popover;