@superleapai/flow-ui 2.5.1 → 2.5.2

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.
@@ -1,12 +1,12 @@
1
1
  /**
2
- * @superleapai/flow-ui v2.2.9
2
+ * @superleapai/flow-ui v2.5.1
3
3
  * A reusable design system for building multi-step forms
4
4
  *
5
5
  * Copyright (c) 2024-present SuperLeap
6
6
  * Licensed under MIT
7
7
  *
8
8
  * Build: development
9
- * Date: 2026-02-16T06:02:03.445Z
9
+ * Date: 2026-02-25T15:52:26.090Z
10
10
  *
11
11
  * For documentation and examples, visit:
12
12
  * https://github.com/superleap/superleap-flow
@@ -15,7 +15,7 @@
15
15
  'use strict';
16
16
 
17
17
  // ============================================
18
- // File 1/37: node_modules/superleap-sdk/superleap.js
18
+ // File 1/41: node_modules/superleap-sdk/superleap.js
19
19
  // ============================================
20
20
 
21
21
  /**
@@ -2642,7 +2642,7 @@
2642
2642
 
2643
2643
 
2644
2644
  // ============================================
2645
- // File 2/37: core/superleapClient.js
2645
+ // File 2/41: core/superleapClient.js
2646
2646
  // ============================================
2647
2647
 
2648
2648
  /**
@@ -2783,11 +2783,23 @@
2783
2783
  return mergeConfig({}, DEFAULT_CONFIG);
2784
2784
  }
2785
2785
 
2786
+ /**
2787
+ * Return the current base URL (from merged config after init).
2788
+ * Used by file-input and other components that build API URLs.
2789
+ *
2790
+ * @returns {string|null} baseUrl or null if not initialized
2791
+ */
2792
+ function getBaseUrl() {
2793
+ if (_config && _config.baseUrl) return _config.baseUrl;
2794
+ return null;
2795
+ }
2796
+
2786
2797
  var superleapClient = {
2787
2798
  init: init,
2788
2799
  getSdk: getSdk,
2789
2800
  isAvailable: isAvailable,
2790
2801
  getDefaultConfig: getDefaultConfig,
2802
+ getBaseUrl: getBaseUrl,
2791
2803
  };
2792
2804
 
2793
2805
  if (global) {
@@ -2798,7 +2810,7 @@
2798
2810
 
2799
2811
 
2800
2812
  // ============================================
2801
- // File 3/37: core/bridge.js
2813
+ // File 3/41: core/bridge.js
2802
2814
  // ============================================
2803
2815
 
2804
2816
  /**
@@ -3326,7 +3338,7 @@
3326
3338
 
3327
3339
 
3328
3340
  // ============================================
3329
- // File 4/37: core/crm.js
3341
+ // File 4/41: core/crm.js
3330
3342
  // ============================================
3331
3343
 
3332
3344
  /**
@@ -3567,12 +3579,13 @@
3567
3579
 
3568
3580
  /**
3569
3581
  * Tell the CRM to navigate to a path.
3570
- * @param {string} path
3582
+ * @param {string} url
3583
+ * @param {boolean} [newTab=false]
3571
3584
  */
3572
- function navigate(path) {
3585
+ function navigate(url, newTab) {
3573
3586
  var bridge = getBridge();
3574
3587
  if (bridge && bridge.isConnected()) {
3575
- bridge.send("crm:navigate", { path: path });
3588
+ bridge.send("crm:navigate", { url: url, new_tab: !!newTab });
3576
3589
  }
3577
3590
  }
3578
3591
 
@@ -3668,7 +3681,7 @@
3668
3681
 
3669
3682
 
3670
3683
  // ============================================
3671
- // File 5/37: components/label.js
3684
+ // File 5/41: components/label.js
3672
3685
  // ============================================
3673
3686
 
3674
3687
  /**
@@ -3785,7 +3798,7 @@
3785
3798
 
3786
3799
 
3787
3800
  // ============================================
3788
- // File 6/37: core/flow.js
3801
+ // File 6/41: core/flow.js
3789
3802
  // ============================================
3790
3803
 
3791
3804
  /**
@@ -4037,6 +4050,48 @@
4037
4050
  return field;
4038
4051
  }
4039
4052
 
4053
+ /**
4054
+ * Create a rich text editor field
4055
+ * @param {Object} config - Configuration object
4056
+ * @param {string} config.label - Field label
4057
+ * @param {string} config.fieldId - State key for this field
4058
+ * @param {string} [config.placeholder] - Placeholder when empty
4059
+ * @param {boolean} [config.required] - Whether field is required
4060
+ * @param {string} [config.helpText] - Optional help text for tooltip
4061
+ * @param {number} [config.minHeightPx] - Min height of editor area in pixels (default 400)
4062
+ * @param {boolean} [config.disabled] - Whether editor is disabled
4063
+ * @returns {HTMLElement} Field element
4064
+ */
4065
+ function createRichTextEditor(config) {
4066
+ const { label, fieldId, placeholder, required = false, helpText = null, minHeightPx = 400, disabled = false } = config;
4067
+
4068
+ const field = createFieldWrapper(label, required, helpText);
4069
+ field.setAttribute("data-field-id", fieldId);
4070
+
4071
+ if (getComponent("RichTextEditorComponent") && getComponent("RichTextEditorComponent").create) {
4072
+ const currentValue = get(fieldId) || "";
4073
+ const editorEl = getComponent("RichTextEditorComponent").create({
4074
+ value: currentValue,
4075
+ placeholder: placeholder || "",
4076
+ minHeightPx,
4077
+ disabled,
4078
+ onChange: (html) => set(fieldId, html),
4079
+ });
4080
+ editorEl._fieldId = fieldId;
4081
+ field.appendChild(editorEl);
4082
+ return field;
4083
+ }
4084
+
4085
+ const fallback = document.createElement("textarea");
4086
+ fallback.className = "textarea min-h-[400px]";
4087
+ fallback.placeholder = placeholder || `Enter ${label.toLowerCase()}`;
4088
+ fallback.value = get(fieldId) || "";
4089
+ fallback.disabled = disabled;
4090
+ fallback.addEventListener("change", (e) => set(fieldId, e.target.value));
4091
+ field.appendChild(fallback);
4092
+ return field;
4093
+ }
4094
+
4040
4095
  /**
4041
4096
  * Create a select dropdown field (using custom select component)
4042
4097
  * @param {Object} config - Configuration object
@@ -4447,6 +4502,7 @@
4447
4502
  size,
4448
4503
  canClear,
4449
4504
  initialLimit,
4505
+ initialFilter,
4450
4506
  helpText = null,
4451
4507
  } = config;
4452
4508
 
@@ -4466,6 +4522,7 @@
4466
4522
  size: size || "default",
4467
4523
  canClear: !!canClear,
4468
4524
  initialLimit,
4525
+ initialFilter,
4469
4526
  onChange: (value, record) => {
4470
4527
  set(fieldId, value);
4471
4528
  if (onChange) onChange(value, record);
@@ -4515,6 +4572,7 @@
4515
4572
  variant,
4516
4573
  size,
4517
4574
  initialLimit,
4575
+ initialFilter,
4518
4576
  displayFields,
4519
4577
  helpText = null,
4520
4578
  } = config;
@@ -4533,6 +4591,7 @@
4533
4591
  variant: variant || "default",
4534
4592
  size: size || "default",
4535
4593
  initialLimit,
4594
+ initialFilter,
4536
4595
  displayFields: displayFields || [],
4537
4596
  onValuesChange: (values, records) => {
4538
4597
  set(fieldId, values);
@@ -5090,6 +5149,40 @@
5090
5149
  return field;
5091
5150
  }
5092
5151
 
5152
+ /**
5153
+ * Create a checkbox group field (multiselect-like: options array, value array, onValuesChange)
5154
+ * @param {Object} config - { label, fieldId, options, required, helpText, variant, size, layout, disabled, onChange }
5155
+ * @returns {HTMLElement} Field wrapper containing checkbox group
5156
+ */
5157
+ function createCheckboxGroup(config) {
5158
+ const { label, fieldId, options = [], required = false, helpText = null, variant, size, layout = "vertical", disabled = false, onChange } = config;
5159
+
5160
+ const field = createFieldWrapper(label, required, helpText);
5161
+ field.setAttribute("data-field-id", fieldId);
5162
+
5163
+ if (getComponent("CheckboxGroup") && getComponent("CheckboxGroup").create) {
5164
+ const currentValues = get(fieldId) || [];
5165
+ const groupEl = getComponent("CheckboxGroup").create({
5166
+ fieldId,
5167
+ options,
5168
+ value: currentValues,
5169
+ variant: variant || "default",
5170
+ size: size || "default",
5171
+ layout,
5172
+ disabled,
5173
+ onValuesChange: (values) => {
5174
+ set(fieldId, values);
5175
+ if (onChange) onChange(values);
5176
+ },
5177
+ });
5178
+ groupEl._fieldId = fieldId;
5179
+ field.appendChild(groupEl);
5180
+ return field;
5181
+ }
5182
+
5183
+ return field;
5184
+ }
5185
+
5093
5186
  // ============================================================================
5094
5187
  // STEPPER COMPONENT
5095
5188
  // ============================================================================
@@ -5121,40 +5214,36 @@
5121
5214
  });
5122
5215
  }
5123
5216
 
5124
- // ============================================================================
5125
- // ALERT COMPONENT
5126
- // ============================================================================
5127
-
5128
5217
  /**
5129
- * Render alerts/errors
5130
- * Uses Alert component when available for design-system styling.
5131
- * @param {HTMLElement} container - Container element for alerts
5132
- * @param {Array} messages - Array of error/info messages (strings) or { title?, description } objects
5133
- * @param {string} type - Alert type: "error" | "info" | "success" | "warning" | "destructive" | "default"
5218
+ * Create a Tabs component (list + triggers + content panels)
5219
+ * @param {Object} config - { defaultValue?, value?, onChange?, tabs: [{ value, label, content }], size?, variant?, listClassName?, contentClassName? }
5220
+ * @returns {HTMLElement} Tabs root element
5134
5221
  */
5135
- function renderAlerts(container, messages = [], type = "error") {
5136
- if (!container) {return;}
5137
- container.innerHTML = "";
5138
-
5139
- const Alert = getComponent("Alert");
5140
- const useAlertComponent = Alert && typeof Alert.create === "function";
5141
-
5142
- messages.forEach((msg) => {
5143
- const description = typeof msg === "string" ? msg : (msg.description || msg.title || "");
5144
- const title = typeof msg === "object" && msg.title ? msg.title : "";
5222
+ function createTabs(config) {
5223
+ const Tabs = getComponent("Tabs");
5224
+ if (Tabs && typeof Tabs.create === "function") {
5225
+ return Tabs.create(config);
5226
+ }
5227
+ const fallback = document.createElement("div");
5228
+ fallback.className = "tabs-root";
5229
+ fallback.textContent = "Tabs component not loaded.";
5230
+ return fallback;
5231
+ }
5145
5232
 
5146
- if (useAlertComponent && (description || title)) {
5147
- const variantMap = { error: "error", info: "info", success: "success" };
5148
- const variant = variantMap[type] || type;
5149
- const alertEl = Alert.create({ title, description, variant });
5150
- if (alertEl) container.appendChild(alertEl);
5151
- } else {
5152
- const div = document.createElement("div");
5153
- div.className = `alert alert--${type}`;
5154
- div.textContent = description || title;
5155
- container.appendChild(div);
5156
- }
5157
- });
5233
+ /**
5234
+ * Create a Steps component (numbered step triggers + optional content panels)
5235
+ * @param {Object} config - { steps: [{ id, label, content? }], defaultValue?, value?, onChange?, size?, variant?, listClassName?, contentClassName?, showContent? }
5236
+ * @returns {HTMLElement} Steps root element
5237
+ */
5238
+ function createSteps(config) {
5239
+ const Steps = getComponent("Steps");
5240
+ if (Steps && typeof Steps.create === "function") {
5241
+ return Steps.create(config);
5242
+ }
5243
+ const fallback = document.createElement("div");
5244
+ fallback.className = "steps-root";
5245
+ fallback.textContent = "Steps component not loaded.";
5246
+ return fallback;
5158
5247
  }
5159
5248
 
5160
5249
  // ============================================================================
@@ -5319,6 +5408,34 @@
5319
5408
  return field;
5320
5409
  }
5321
5410
 
5411
+ // ============================================================================
5412
+ // ALERTS
5413
+ // ============================================================================
5414
+
5415
+ /**
5416
+ * Render multiple alert messages into a container
5417
+ * @param {HTMLElement} container - Container to append alerts to
5418
+ * @param {string[]} messages - Array of message strings
5419
+ * @param {string} [variant='default'] - 'default' | 'error' | 'warning' | 'success' | 'info' | 'destructive'
5420
+ */
5421
+ function renderAlerts(container, messages, variant = "default") {
5422
+ if (!container || !Array.isArray(messages)) return;
5423
+ const Alert = getComponent("Alert");
5424
+ if (Alert && typeof Alert.simple === "function") {
5425
+ messages.forEach((msg) => {
5426
+ const el = Alert.simple(msg, variant);
5427
+ if (el) container.appendChild(el);
5428
+ });
5429
+ } else {
5430
+ messages.forEach((msg) => {
5431
+ const div = document.createElement("div");
5432
+ div.className = "rounded border p-2 text-sm " + (variant === "error" ? "bg-red-50 border-red-200 text-red-800" : "bg-gray-50 border-gray-200");
5433
+ div.textContent = msg;
5434
+ container.appendChild(div);
5435
+ });
5436
+ }
5437
+ }
5438
+
5322
5439
  // ============================================================================
5323
5440
  // TOAST NOTIFICATIONS
5324
5441
  // ============================================================================
@@ -5455,6 +5572,7 @@
5455
5572
  // Form components
5456
5573
  createInput,
5457
5574
  createTextarea,
5575
+ createRichTextEditor,
5458
5576
  createSelect,
5459
5577
  createTimePicker,
5460
5578
  createDateTimePicker,
@@ -5470,6 +5588,7 @@
5470
5588
  createCurrency,
5471
5589
  createPhoneInput,
5472
5590
  createCheckbox,
5591
+ createCheckboxGroup,
5473
5592
 
5474
5593
  // Button (delegates to Button component when available; resolved at call time via getComponent)
5475
5594
  createButton: function (config) {
@@ -5490,10 +5609,12 @@
5490
5609
 
5491
5610
  // Stepper
5492
5611
  renderStepper,
5612
+ createTabs,
5613
+ createSteps,
5493
5614
 
5494
- // Alerts (now shows toast notifications)
5615
+ // Alerts
5616
+ renderAlerts,
5495
5617
  showToast,
5496
- renderAlerts, // Legacy support for static alerts
5497
5618
 
5498
5619
  // Table
5499
5620
  createDataTable,
@@ -5520,7 +5641,7 @@
5520
5641
 
5521
5642
 
5522
5643
  // ============================================
5523
- // File 7/37: components/toast.js
5644
+ // File 7/41: components/toast.js
5524
5645
  // ============================================
5525
5646
 
5526
5647
  /**
@@ -5869,7 +5990,7 @@
5869
5990
 
5870
5991
 
5871
5992
  // ============================================
5872
- // File 8/37: components/alert.js
5993
+ // File 8/41: components/alert.js
5873
5994
  // ============================================
5874
5995
 
5875
5996
  /**
@@ -6157,7 +6278,7 @@
6157
6278
 
6158
6279
 
6159
6280
  // ============================================
6160
- // File 9/37: components/button.js
6281
+ // File 9/41: components/button.js
6161
6282
  // ============================================
6162
6283
 
6163
6284
  /**
@@ -6364,7 +6485,7 @@
6364
6485
 
6365
6486
 
6366
6487
  // ============================================
6367
- // File 10/37: components/spinner.js
6488
+ // File 10/41: components/spinner.js
6368
6489
  // ============================================
6369
6490
 
6370
6491
  /**
@@ -6506,7 +6627,7 @@
6506
6627
 
6507
6628
 
6508
6629
  // ============================================
6509
- // File 11/37: components/badge.js
6630
+ // File 11/41: components/badge.js
6510
6631
  // ============================================
6511
6632
 
6512
6633
  /**
@@ -6647,7 +6768,7 @@
6647
6768
 
6648
6769
 
6649
6770
  // ============================================
6650
- // File 12/37: components/avatar.js
6771
+ // File 12/41: components/avatar.js
6651
6772
  // ============================================
6652
6773
 
6653
6774
  /**
@@ -6848,7 +6969,7 @@
6848
6969
 
6849
6970
 
6850
6971
  // ============================================
6851
- // File 13/37: components/icon.js
6972
+ // File 13/41: components/icon.js
6852
6973
  // ============================================
6853
6974
 
6854
6975
  /**
@@ -6896,12 +7017,12 @@
6896
7017
  ' stroke-width="1.5">' +
6897
7018
  TI_PATH_NONE +
6898
7019
  '<path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0"/><path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"/></svg>',
6899
- IconCurrencyDollar:
7020
+ IconStar:
6900
7021
  "<svg" +
6901
7022
  TI +
6902
7023
  ' stroke-width="1.5">' +
6903
7024
  TI_PATH_NONE +
6904
- '<path d="M16.7 8a3 3 0 0 0 -2.7 -2h-4a3 3 0 0 0 0 6h4a3 3 0 0 1 0 6h-4a3 3 0 0 1 -2.7 -2"/><path d="M12 3v3m0 12v3"/></svg>',
7025
+ '<path d="M12 17.75l-6.172 3.245l1.179 -6.873l-5 -4.867l6.9 -1l3.086 -6.253l3.086 6.253l6.9 1l-5 4.867l1.179 6.873z"/></svg>',
6905
7026
  IconPhone:
6906
7027
  "<svg" +
6907
7028
  TI +
@@ -6929,6 +7050,40 @@
6929
7050
  /** Filled circle for "just color" mode (IconOrColor when only icon_color is set) */
6930
7051
  IconCircleFilled:
6931
7052
  '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" fill="currentColor"/></svg>',
7053
+
7054
+ // Rich text editor / toolbar (Tabler Icons outline, stroke 2)
7055
+ IconBold:
7056
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-bold"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 5h6a3.5 3.5 0 0 1 0 7h-6l0 -7" /><path d="M13 12h1a3.5 3.5 0 0 1 0 7h-7v-7" /></svg>',
7057
+ IconItalic:
7058
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-italic"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M11 5l6 0" /><path d="M7 19l6 0" /><path d="M14 5l-4 14" /></svg>',
7059
+ IconUnderline:
7060
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-underline"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 5v5a5 5 0 0 0 10 0v-5" /><path d="M5 19h14" /></svg>',
7061
+ IconH1:
7062
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-h-1"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M19 18v-8l-2 2" /><path d="M4 6v12" /><path d="M12 6v12" /><path d="M11 18h2" /><path d="M3 18h2" /><path d="M4 12h8" /><path d="M3 6h2" /><path d="M11 6h2" /></svg>',
7063
+ IconH2:
7064
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-h-2"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M17 12a2 2 0 1 1 4 0c0 .591 -.417 1.318 -.816 1.858l-3.184 4.143l4 0" /><path d="M4 6v12" /><path d="M12 6v12" /><path d="M11 18h2" /><path d="M3 18h2" /><path d="M4 12h8" /><path d="M3 6h2" /><path d="M11 6h2" /></svg>',
7065
+ IconH3:
7066
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-h-3"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M19 14a2 2 0 1 0 -2 -2" /><path d="M17 16a2 2 0 1 0 2 -2" /><path d="M4 6v12" /><path d="M12 6v12" /><path d="M11 18h2" /><path d="M3 18h2" /><path d="M4 12h8" /><path d="M3 6h2" /><path d="M11 6h2" /></svg>',
7067
+ IconList:
7068
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-list"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 6l11 0" /><path d="M9 12l11 0" /><path d="M9 18l11 0" /><path d="M5 6l0 .01" /><path d="M5 12l0 .01" /><path d="M5 18l0 .01" /></svg>',
7069
+ IconListNumbers:
7070
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-list-numbers"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M11 6h9" /><path d="M11 12h9" /><path d="M12 18h8" /><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4" /><path d="M6 10v-6l-2 2" /></svg>',
7071
+ IconAlignLeft:
7072
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-align-left"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6l16 0" /><path d="M4 12l10 0" /><path d="M4 18l14 0" /></svg>',
7073
+ IconAlignCenter:
7074
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-align-center"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6l16 0" /><path d="M8 12l8 0" /><path d="M6 18l12 0" /></svg>',
7075
+ IconAlignRight:
7076
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-align-right"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6l16 0" /><path d="M10 12l10 0" /><path d="M6 18l14 0" /></svg>',
7077
+ IconCode:
7078
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-code"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 8l-4 4l4 4" /><path d="M17 8l4 4l-4 4" /><path d="M14 4l-4 16" /></svg>',
7079
+ IconLink:
7080
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-link"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 15l6 -6" /><path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" /><path d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" /></svg>',
7081
+ IconPhoto:
7082
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-photo"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 8h.01" /><path d="M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-12" /><path d="M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5" /><path d="M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3" /></svg>',
7083
+ IconArrowBackUp:
7084
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-arrow-back-up"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 14l-4 -4l4 -4" /><path d="M5 10h11a4 4 0 1 1 0 8h-1" /></svg>',
7085
+ IconArrowForwardUp:
7086
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-arrow-forward-up"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 14l4 -4l-4 -4" /><path d="M19 10h-11a4 4 0 1 0 0 8h1" /></svg>',
6932
7087
  };
6933
7088
 
6934
7089
  function join() {
@@ -7054,7 +7209,7 @@
7054
7209
 
7055
7210
 
7056
7211
  // ============================================
7057
- // File 14/37: components/popover.js
7212
+ // File 14/41: components/popover.js
7058
7213
  // ============================================
7059
7214
 
7060
7215
  /**
@@ -7077,6 +7232,7 @@
7077
7232
  * @param {Function} [config.onOpen] - Called when popover opens (before positioning)
7078
7233
  * @param {string} [config.bodyClassName] - Optional class for body wrapper (overrides default padding)
7079
7234
  * @param {string} [config.panelClassName] - Optional class to add to panel (e.g. for width)
7235
+ * @param {boolean} [config.modal=false] - If true, lock body scroll and show backdrop; only popover and trigger are interactive
7080
7236
  * @returns {Object} Popover API {show, hide, destroy, element}
7081
7237
  */
7082
7238
  function create(config = {}) {
@@ -7091,6 +7247,7 @@
7091
7247
  onOpen = null,
7092
7248
  bodyClassName = "",
7093
7249
  panelClassName = "",
7250
+ modal = true,
7094
7251
  } = config;
7095
7252
 
7096
7253
  const triggerEl =
@@ -7100,9 +7257,18 @@
7100
7257
  return { show: noop, hide: noop, destroy: noop, element: null };
7101
7258
  }
7102
7259
 
7260
+ // Wrap trigger in a relative container for layout (full width); popover is portaled to body
7261
+ const container = document.createElement("div");
7262
+ container.className = "relative w-full";
7263
+ const triggerParent = triggerEl.parentNode;
7264
+ if (triggerParent) {
7265
+ triggerParent.insertBefore(container, triggerEl);
7266
+ container.appendChild(triggerEl);
7267
+ }
7268
+
7103
7269
  const wrapper = document.createElement("div");
7104
7270
  wrapper.className =
7105
- "fixed z-50 pointer-events-none opacity-0 invisible transition-opacity duration-150 ease-out";
7271
+ "fixed z-50 pointer-events-none opacity-0 invisible transition-opacity duration-150 ease-out";
7106
7272
  wrapper.setAttribute("aria-hidden", "true");
7107
7273
 
7108
7274
  const panel = document.createElement("div");
@@ -7126,7 +7292,11 @@
7126
7292
  const body = document.createElement("div");
7127
7293
  body.className =
7128
7294
  bodyClassName ||
7129
- "text-reg-14 text-typography-secondary-text leading-5 [&_p]:mb-2 [&_p:last-child]:mb-0";
7295
+ "text-reg-14 text-typography-secondary-text leading-5 [&_p]:mb-2 [&_p:last-child]:mb-0 max-h-[90vh] overflow-y-auto overflow-x-hidden min-h-0";
7296
+ body.style.maxHeight = "90vh";
7297
+ body.style.overflowY = "auto";
7298
+ body.style.overflowX = "hidden";
7299
+ body.style.minHeight = "0";
7130
7300
  if (panelClassName) {
7131
7301
  panel.className = panel.className + " " + panelClassName;
7132
7302
  }
@@ -7138,57 +7308,126 @@
7138
7308
  panel.appendChild(body);
7139
7309
 
7140
7310
  wrapper.appendChild(panel);
7311
+ document.body.appendChild(wrapper);
7312
+
7313
+ var backdropEl = null;
7314
+ var resizeObserver = null;
7315
+
7316
+ function onBackdropWheel(e) {
7317
+ e.preventDefault();
7318
+ }
7319
+
7320
+ function applyModalOpen() {
7321
+ if (!modal) return;
7322
+ if (!backdropEl) {
7323
+ backdropEl = document.createElement("div");
7324
+ backdropEl.className =
7325
+ "fixed inset-0 z-40 bg-transparent pointer-events-auto";
7326
+ backdropEl.setAttribute("aria-hidden", "true");
7327
+ backdropEl.addEventListener("click", function () {
7328
+ hide();
7329
+ });
7330
+ backdropEl.addEventListener("wheel", onBackdropWheel, {
7331
+ passive: false,
7332
+ });
7333
+ }
7334
+ document.body.appendChild(backdropEl);
7335
+ wrapper.style.zIndex = "999";
7336
+ }
7337
+
7338
+ function applyModalClose() {
7339
+ if (!modal) return;
7340
+ if (backdropEl && backdropEl.parentNode) {
7341
+ backdropEl.parentNode.removeChild(backdropEl);
7342
+ }
7343
+ wrapper.style.zIndex = "";
7344
+ }
7141
7345
 
7142
7346
  function noop() {}
7143
7347
 
7144
7348
  function position() {
7145
- const rect = triggerEl.getBoundingClientRect();
7349
+ const triggerRect = triggerEl.getBoundingClientRect();
7146
7350
  const panelRect = panel.getBoundingClientRect();
7147
7351
  const gap = 8;
7148
- let top = 0;
7149
- let left = 0;
7352
+ const viewportHeight =
7353
+ window.innerHeight || document.documentElement.clientHeight;
7354
+ const viewportWidth =
7355
+ window.innerWidth || document.documentElement.clientWidth;
7356
+ const spaceBelow = viewportHeight - triggerRect.bottom;
7357
+ const spaceAbove = triggerRect.top;
7358
+ const spaceRight = viewportWidth - triggerRect.right;
7359
+ const spaceLeft = triggerRect.left;
7360
+
7361
+ // Flip placement when there is not enough space (prefer requested side, flip only when needed)
7362
+ let effectivePlacement = placement;
7363
+ if (
7364
+ placement === "bottom" &&
7365
+ spaceBelow < panelRect.height + gap &&
7366
+ spaceAbove >= panelRect.height + gap
7367
+ ) {
7368
+ effectivePlacement = "top";
7369
+ } else if (
7370
+ placement === "top" &&
7371
+ spaceAbove < panelRect.height + gap &&
7372
+ spaceBelow >= panelRect.height + gap
7373
+ ) {
7374
+ effectivePlacement = "bottom";
7375
+ } else if (
7376
+ placement === "right" &&
7377
+ spaceRight < panelRect.width + gap &&
7378
+ spaceLeft >= panelRect.width + gap
7379
+ ) {
7380
+ effectivePlacement = "left";
7381
+ } else if (
7382
+ placement === "left" &&
7383
+ spaceLeft < panelRect.width + gap &&
7384
+ spaceRight >= panelRect.width + gap
7385
+ ) {
7386
+ effectivePlacement = "right";
7387
+ }
7150
7388
 
7151
- // Alignment offset: start = 0, center = half diff, end = full diff
7152
- const alignLeft = (align === "center" ? (rect.width - panelRect.width) / 2 : align === "end" ? rect.width - panelRect.width : 0);
7153
- const alignTop = (align === "center" ? (rect.height - panelRect.height) / 2 : align === "end" ? rect.height - panelRect.height : 0);
7389
+ panel.setAttribute("data-side", effectivePlacement);
7154
7390
 
7155
- switch (placement) {
7391
+ let top = 0;
7392
+ let left = 0;
7393
+ const alignLeft =
7394
+ align === "center"
7395
+ ? (triggerRect.width - panelRect.width) / 2
7396
+ : align === "end"
7397
+ ? triggerRect.width - panelRect.width
7398
+ : 0;
7399
+ const alignTop =
7400
+ align === "center"
7401
+ ? (triggerRect.height - panelRect.height) / 2
7402
+ : align === "end"
7403
+ ? triggerRect.height - panelRect.height
7404
+ : 0;
7405
+
7406
+ switch (effectivePlacement) {
7156
7407
  case "bottom":
7157
- top = rect.bottom + gap;
7158
- left = rect.left + alignLeft;
7408
+ top = triggerRect.bottom + gap;
7409
+ left = triggerRect.left + alignLeft;
7159
7410
  break;
7160
7411
  case "top":
7161
- top = rect.top - panelRect.height - gap;
7162
- left = rect.left + alignLeft;
7412
+ top = triggerRect.top - panelRect.height - gap;
7413
+ left = triggerRect.left + alignLeft;
7163
7414
  break;
7164
7415
  case "right":
7165
- top = rect.top + alignTop;
7166
- left = rect.right + gap;
7416
+ top = triggerRect.top + alignTop;
7417
+ left = triggerRect.right + gap;
7167
7418
  break;
7168
7419
  case "left":
7169
- top = rect.top + alignTop;
7170
- left = rect.left - panelRect.width - gap;
7420
+ top = triggerRect.top + alignTop;
7421
+ left = triggerRect.left - panelRect.width - gap;
7171
7422
  break;
7172
7423
  default:
7173
- top = rect.bottom + gap;
7174
- left = rect.left + alignLeft;
7175
- }
7176
-
7177
- // Keep within viewport
7178
- const padding = 8;
7179
- if (left < padding) left = padding;
7180
- if (left + panelRect.width > window.innerWidth - padding) {
7181
- left = window.innerWidth - panelRect.width - padding;
7182
- }
7183
- if (top < padding) top = padding;
7184
- if (top + panelRect.height > window.innerHeight - padding) {
7185
- top = window.innerHeight - panelRect.height - padding;
7424
+ top = triggerRect.bottom + gap;
7425
+ left = triggerRect.left + alignLeft;
7186
7426
  }
7187
7427
 
7188
- wrapper.style.left = "0";
7189
- wrapper.style.top = "0";
7190
- wrapper.style.transform = "translate(" + left + "px, " + top + "px)";
7191
- // Force reflow so transform is applied before we show (avoids flash from left/top-left)
7428
+ wrapper.style.transform = "";
7429
+ wrapper.style.top = top + "px";
7430
+ wrapper.style.left = left + "px";
7192
7431
  wrapper.offsetHeight;
7193
7432
  }
7194
7433
 
@@ -7197,46 +7436,68 @@
7197
7436
  wrapper.classList.add("invisible", "opacity-0", "pointer-events-none");
7198
7437
  wrapper.classList.remove("visible", "opacity-100", "pointer-events-auto");
7199
7438
  wrapper.setAttribute("aria-hidden", "true");
7439
+ window.removeEventListener("scroll", onScrollOrResize, true);
7440
+ window.removeEventListener("resize", onScrollOrResize);
7441
+ if (resizeObserver && panel) {
7442
+ resizeObserver.disconnect();
7443
+ resizeObserver = null;
7444
+ }
7445
+ applyModalClose();
7200
7446
  if (onClose) onClose();
7201
7447
  }
7202
7448
 
7449
+ function onScrollOrResize() {
7450
+ if (wrapper.classList.contains("visible")) position();
7451
+ }
7452
+
7203
7453
  function show() {
7204
7454
  if (onOpen) onOpen();
7205
- var justAppended = !wrapper.parentNode;
7206
- if (justAppended) {
7207
- document.body.appendChild(wrapper);
7208
- }
7209
- // On first open, wait for layout so getBoundingClientRect() is correct (avoids wrong position / "from left" look)
7210
- if (justAppended) {
7211
- requestAnimationFrame(function () {
7212
- position();
7213
- wrapper.classList.remove("invisible", "opacity-0", "pointer-events-none");
7214
- wrapper.classList.add("visible", "opacity-100", "pointer-events-auto");
7215
- wrapper.setAttribute("aria-hidden", "false");
7216
- requestAnimationFrame(function () {
7217
- requestAnimationFrame(function () {
7218
- panel.setAttribute("data-state", "open");
7219
- });
7220
- });
7221
- });
7222
- } else {
7455
+ applyModalOpen();
7456
+ requestAnimationFrame(function () {
7223
7457
  position();
7224
- wrapper.classList.remove("invisible", "opacity-0", "pointer-events-none");
7458
+ wrapper.classList.remove(
7459
+ "invisible",
7460
+ "opacity-0",
7461
+ "pointer-events-none",
7462
+ );
7225
7463
  wrapper.classList.add("visible", "opacity-100", "pointer-events-auto");
7226
7464
  wrapper.setAttribute("aria-hidden", "false");
7465
+ window.addEventListener("scroll", onScrollOrResize, true);
7466
+ window.addEventListener("resize", onScrollOrResize);
7467
+ // Re-position when panel content size changes (e.g. async record list load in record-select)
7468
+ if (typeof ResizeObserver !== "undefined" && !resizeObserver) {
7469
+ resizeObserver = new ResizeObserver(function () {
7470
+ if (wrapper.classList.contains("visible")) position();
7471
+ });
7472
+ resizeObserver.observe(panel);
7473
+ }
7227
7474
  requestAnimationFrame(function () {
7228
7475
  requestAnimationFrame(function () {
7229
7476
  panel.setAttribute("data-state", "open");
7230
7477
  });
7231
7478
  });
7232
- }
7479
+ });
7233
7480
  }
7234
7481
 
7235
7482
  function destroy() {
7236
7483
  hide();
7484
+ applyModalClose();
7485
+ if (resizeObserver && panel) {
7486
+ resizeObserver.disconnect();
7487
+ resizeObserver = null;
7488
+ }
7489
+ if (backdropEl && backdropEl.parentNode) {
7490
+ backdropEl.parentNode.removeChild(backdropEl);
7491
+ }
7492
+ window.removeEventListener("scroll", onScrollOrResize, true);
7493
+ window.removeEventListener("resize", onScrollOrResize);
7237
7494
  if (wrapper.parentNode) {
7238
7495
  wrapper.parentNode.removeChild(wrapper);
7239
7496
  }
7497
+ if (container.parentNode && triggerEl.parentNode === container) {
7498
+ container.parentNode.insertBefore(triggerEl, container);
7499
+ container.parentNode.removeChild(container);
7500
+ }
7240
7501
  if (closeOnClickOutside) {
7241
7502
  document.removeEventListener("click", outsideClick);
7242
7503
  }
@@ -7279,6 +7540,8 @@
7279
7540
  show,
7280
7541
  hide,
7281
7542
  destroy,
7543
+ /** Re-run positioning (e.g. after async content load). Call when panel size changes. */
7544
+ updatePosition: position,
7282
7545
  setContent(newContent) {
7283
7546
  body.innerHTML = "";
7284
7547
  if (typeof newContent === "string") {
@@ -7300,7 +7563,7 @@
7300
7563
 
7301
7564
 
7302
7565
  // ============================================
7303
- // File 15/37: components/select.js
7566
+ // File 15/41: components/select.js
7304
7567
  // ============================================
7305
7568
 
7306
7569
  /**
@@ -7356,6 +7619,14 @@
7356
7619
  return Array.prototype.filter.call(arguments, Boolean).join(" ");
7357
7620
  }
7358
7621
 
7622
+ function getDep(name) {
7623
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
7624
+ var c = global.FlowUI._getComponent(name);
7625
+ if (c) return c;
7626
+ }
7627
+ return global[name];
7628
+ }
7629
+
7359
7630
  /**
7360
7631
  * Create a custom select component
7361
7632
  * @param {Object} config
@@ -7385,6 +7656,11 @@
7385
7656
  var value =
7386
7657
  config.value !== undefined && config.value !== null ? config.value : "";
7387
7658
 
7659
+ var Popover = getDep("Popover");
7660
+ if (!Popover || typeof Popover.create !== "function") {
7661
+ throw new Error("Select requires Popover");
7662
+ }
7663
+
7388
7664
  var selectedOption = options.find(function (opt) {
7389
7665
  var optValue =
7390
7666
  opt.value !== undefined && opt.value !== null
@@ -7484,15 +7760,11 @@
7484
7760
 
7485
7761
  var content = document.createElement("div");
7486
7762
  content.setAttribute("role", "listbox");
7487
- var contentBase =
7488
- "custom-select-content absolute left-0 right-0 z-50 max-h-[200px] min-w-[8rem] overflow-hidden rounded-4 bg-fill-quarternary-fill-white shadow-default-medium opacity-0 invisible transition-all duration-150 ease-out " +
7489
- "group-[.open]:opacity-100 group-[.open]:visible ";
7490
- content.className =
7491
- contentBase + "top-full mt-1 -translate-y-1 group-[.open]:translate-y-0";
7763
+ content.className = "custom-select-content w-full max-h-[45vh] overflow-hidden flex flex-col";
7492
7764
 
7493
7765
  var optionsList = document.createElement("div");
7494
7766
  optionsList.className =
7495
- "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
7767
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
7496
7768
 
7497
7769
  if (options.length === 0) {
7498
7770
  var noOpt = document.createElement("div");
@@ -7542,47 +7814,66 @@
7542
7814
  }
7543
7815
 
7544
7816
  content.appendChild(optionsList);
7545
- container.appendChild(content);
7546
7817
 
7547
- var isOpen = false;
7548
7818
  var highlightedIndex = -1;
7819
+ var popover = Popover.create({
7820
+ trigger: trigger,
7821
+ content: content,
7822
+ placement: "bottom",
7823
+ align: "start",
7824
+ closeOnClickOutside: true,
7825
+ bodyClassName: "p-0 overflow-hidden",
7826
+ panelClassName: "min-w-[var(--trigger-width)] max-h-[45vh] overflow-hidden",
7827
+ onOpen: function () {
7828
+ if (disabled) {
7829
+ popover.hide();
7830
+ return;
7831
+ }
7832
+ document
7833
+ .querySelectorAll(".custom-select, .record-select, .enum-select, .enum-multiselect, .custom-multiselect, .record-multiselect")
7834
+ .forEach(function (other) {
7835
+ if (other !== container && other.popoverInstance) {
7836
+ other.popoverInstance.hide();
7837
+ }
7838
+ });
7839
+ trigger.setAttribute("aria-expanded", "true");
7840
+ chevron.style.transform = "rotate(180deg)";
7841
+ highlightOptionByValue(value);
7842
+ if (popover.panel) {
7843
+ var triggerWidthPx = trigger.offsetWidth + "px";
7844
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
7845
+ popover.panel.style.minWidth = triggerWidthPx;
7846
+ popover.panel.style.width = triggerWidthPx;
7847
+ }
7848
+ },
7849
+ onClose: function () {
7850
+ trigger.setAttribute("aria-expanded", "false");
7851
+ chevron.style.transform = "";
7852
+ highlightedIndex = -1;
7853
+ },
7854
+ });
7855
+ container.popoverInstance = popover;
7549
7856
 
7550
7857
  function openDropdown() {
7551
7858
  if (disabled) return;
7552
- document
7553
- .querySelectorAll(".custom-select.open, .record-select.open")
7554
- .forEach(function (other) {
7555
- if (other !== container) {
7556
- other.classList.remove("open");
7557
- var t = other.querySelector(
7558
- "button, .custom-select-trigger, .record-select-trigger"
7559
- );
7560
- if (t) t.setAttribute("aria-expanded", "false");
7561
- }
7562
- });
7563
- isOpen = true;
7564
- container.classList.add("open");
7565
- trigger.setAttribute("aria-expanded", "true");
7566
- highlightOptionByValue(value);
7567
- updatePosition();
7859
+ popover.show();
7568
7860
  }
7569
7861
 
7570
7862
  function closeDropdown() {
7571
- isOpen = false;
7572
- container.classList.remove("open");
7573
- trigger.setAttribute("aria-expanded", "false");
7863
+ popover.hide();
7574
7864
  highlightedIndex = -1;
7575
7865
  }
7576
7866
 
7577
7867
  function toggleDropdown() {
7868
+ var isVisible = popover.element && popover.element.classList.contains("visible");
7578
7869
  var others = document.querySelectorAll(
7579
- ".custom-select.open, .record-select.open"
7870
+ ".custom-select, .record-select, .enum-select, .enum-multiselect, .custom-multiselect, .record-multiselect"
7580
7871
  );
7581
7872
  var hasOther = Array.from(others).some(function (s) {
7582
- return s !== container;
7873
+ return s !== container && s.popoverInstance && s.popoverInstance.element && s.popoverInstance.element.classList.contains("visible");
7583
7874
  });
7584
7875
  if (hasOther) openDropdown();
7585
- else if (isOpen) closeDropdown();
7876
+ else if (isVisible) closeDropdown();
7586
7877
  else openDropdown();
7587
7878
  }
7588
7879
 
@@ -7661,40 +7952,26 @@
7661
7952
 
7662
7953
  function scrollToOption(opt) {
7663
7954
  if (!opt) return;
7664
- var cr = content.getBoundingClientRect();
7955
+ var cr = optionsList.getBoundingClientRect();
7665
7956
  var top = opt.offsetTop;
7666
7957
  var bottom = top + opt.offsetHeight;
7667
- var st = content.scrollTop;
7958
+ var st = optionsList.scrollTop;
7668
7959
  var sb = st + cr.height;
7669
- if (top < st) content.scrollTop = top - 8;
7960
+ if (top < st) optionsList.scrollTop = top - 8;
7670
7961
  else if (bottom > sb)
7671
- content.scrollTop = bottom - cr.height + 8;
7672
- }
7673
-
7674
- function updatePosition() {
7675
- var rect = trigger.getBoundingClientRect();
7676
- var vh = window.innerHeight;
7677
- var below = vh - rect.bottom;
7678
- var above = rect.top;
7679
- if (below < 200 && above > below) {
7680
- content.className =
7681
- contentBase +
7682
- "bottom-full mb-1 translate-y-1 group-[.open]:translate-y-0";
7683
- } else {
7684
- content.className =
7685
- contentBase +
7686
- "top-full mt-1 -translate-y-1 group-[.open]:translate-y-0";
7687
- }
7962
+ optionsList.scrollTop = bottom - cr.height + 8;
7688
7963
  }
7689
7964
 
7690
7965
  trigger.addEventListener("click", function (e) {
7691
- e.preventDefault();
7692
- e.stopPropagation();
7693
- toggleDropdown();
7694
- });
7966
+ if (disabled) {
7967
+ e.preventDefault();
7968
+ e.stopImmediatePropagation();
7969
+ }
7970
+ }, true);
7695
7971
 
7696
7972
  trigger.addEventListener("keydown", function (e) {
7697
7973
  if (disabled) return;
7974
+ var isVisible = popover.element && popover.element.classList.contains("visible");
7698
7975
  switch (e.key) {
7699
7976
  case "Enter":
7700
7977
  case " ":
@@ -7703,16 +7980,16 @@
7703
7980
  break;
7704
7981
  case "ArrowDown":
7705
7982
  e.preventDefault();
7706
- if (!isOpen) openDropdown();
7983
+ if (!isVisible) openDropdown();
7707
7984
  else navigateOptions(1);
7708
7985
  break;
7709
7986
  case "ArrowUp":
7710
7987
  e.preventDefault();
7711
- if (!isOpen) openDropdown();
7988
+ if (!isVisible) openDropdown();
7712
7989
  else navigateOptions(-1);
7713
7990
  break;
7714
7991
  case "Escape":
7715
- if (isOpen) {
7992
+ if (isVisible) {
7716
7993
  e.preventDefault();
7717
7994
  closeDropdown();
7718
7995
  }
@@ -7733,13 +8010,6 @@
7733
8010
  scrollToOption(opt);
7734
8011
  }
7735
8012
 
7736
- document.addEventListener("click", function (e) {
7737
- if (isOpen && !container.contains(e.target)) closeDropdown();
7738
- });
7739
- document.addEventListener("keydown", function (e) {
7740
- if (e.key === "Escape" && isOpen) closeDropdown();
7741
- });
7742
-
7743
8013
  container.updateValue = function (newValue) {
7744
8014
  value =
7745
8015
  newValue !== undefined && newValue !== null ? newValue : "";
@@ -7836,7 +8106,8 @@
7836
8106
  canClear && !!value && !disabled
7837
8107
  );
7838
8108
  updateClearButton();
7839
- if (disabled && isOpen) closeDropdown();
8109
+ var isVisible = popover.element && popover.element.classList.contains("visible");
8110
+ if (disabled && isVisible) closeDropdown();
7840
8111
  };
7841
8112
 
7842
8113
  return container;
@@ -7850,7 +8121,7 @@
7850
8121
 
7851
8122
 
7852
8123
  // ============================================
7853
- // File 16/37: components/enum-select.js
8124
+ // File 16/41: components/enum-select.js
7854
8125
  // ============================================
7855
8126
 
7856
8127
  /**
@@ -7926,6 +8197,14 @@
7926
8197
  return Array.prototype.filter.call(arguments, Boolean).join(" ");
7927
8198
  }
7928
8199
 
8200
+ function getDep(name) {
8201
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
8202
+ var c = global.FlowUI._getComponent(name);
8203
+ if (c) return c;
8204
+ }
8205
+ return global[name];
8206
+ }
8207
+
7929
8208
  /** Resolve client: use FlowUI._getComponent when bundle has captured globals, else global.superleapClient */
7930
8209
  function getClient() {
7931
8210
  if (global.FlowUI && typeof global.FlowUI._getComponent === "function") {
@@ -7989,8 +8268,10 @@
7989
8268
  var error = null;
7990
8269
  var searchQuery = "";
7991
8270
  var popover = null;
7992
- var usePopover = !!(global.Popover && typeof global.Popover.create === "function");
7993
- var isOpen = false;
8271
+ var Popover = getDep("Popover");
8272
+ if (!Popover || typeof Popover.create !== "function") {
8273
+ throw new Error("EnumSelect requires Popover");
8274
+ }
7994
8275
 
7995
8276
  var container = document.createElement("div");
7996
8277
  container.className = "enum-select relative w-full group";
@@ -8074,13 +8355,7 @@
8074
8355
  // Create dropdown content
8075
8356
  var content = document.createElement("div");
8076
8357
  content.setAttribute("role", "listbox");
8077
- var contentBase = "w-full min-w-[200px]";
8078
- if (!usePopover) {
8079
- contentBase += " absolute left-0 right-0 z-50 max-h-[30vh] overflow-hidden rounded-4 bg-fill-quarternary-fill-white shadow-default-medium border-1/2 border-border-primary opacity-0 invisible transition-all duration-150 ease-out group-[.open]:opacity-100 group-[.open]:visible top-full mt-1 -translate-y-1 group-[.open]:translate-y-0 flex flex-col";
8080
- } else {
8081
- contentBase += " max-h-[30vh] overflow-hidden flex flex-col";
8082
- }
8083
- content.className = contentBase;
8358
+ content.className = "w-full min-w-[200px] max-h-[45vh] overflow-hidden flex flex-col";
8084
8359
 
8085
8360
  // Search input (using InputComponent like phone-input)
8086
8361
  var searchContainer = document.createElement("div");
@@ -8141,15 +8416,11 @@
8141
8416
  // Options list
8142
8417
  var optionsList = document.createElement("div");
8143
8418
  optionsList.className =
8144
- "overflow-y-auto max-h-[30vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white flex-1 min-h-0";
8419
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white flex-1 min-h-0";
8145
8420
 
8146
8421
  content.appendChild(searchContainer);
8147
8422
  content.appendChild(optionsList);
8148
8423
 
8149
- if (!usePopover) {
8150
- container.appendChild(content);
8151
- }
8152
-
8153
8424
  var highlightedIndex = -1;
8154
8425
 
8155
8426
  function showLoading() {
@@ -8272,37 +8543,14 @@
8272
8543
 
8273
8544
  function openDropdown() {
8274
8545
  if (disabled) return;
8275
- if (popover) {
8276
- // Close other open selects
8277
- document
8278
- .querySelectorAll(".enum-select, .custom-select, .record-select")
8279
- .forEach(function (other) {
8280
- if (other !== container && other.popoverInstance) {
8281
- other.popoverInstance.hide();
8282
- }
8283
- });
8284
- popover.show();
8285
- trigger.setAttribute("aria-expanded", "true");
8286
- highlightOptionByValue(value);
8287
- if (searchInput) {
8288
- setTimeout(function () {
8289
- searchInput.focus();
8290
- }, 50);
8291
- }
8292
- return;
8293
- }
8294
- // Fallback when Popover not available
8295
8546
  document
8296
- .querySelectorAll(".enum-select.open")
8547
+ .querySelectorAll(".enum-select, .custom-select, .record-select")
8297
8548
  .forEach(function (other) {
8298
- if (other !== container) {
8299
- other.classList.remove("open");
8300
- var t = other.querySelector("button");
8301
- if (t) t.setAttribute("aria-expanded", "false");
8549
+ if (other !== container && other.popoverInstance) {
8550
+ other.popoverInstance.hide();
8302
8551
  }
8303
8552
  });
8304
- isOpen = true;
8305
- container.classList.add("open");
8553
+ popover.show();
8306
8554
  trigger.setAttribute("aria-expanded", "true");
8307
8555
  highlightOptionByValue(value);
8308
8556
  if (searchInput) {
@@ -8313,12 +8561,7 @@
8313
8561
  }
8314
8562
 
8315
8563
  function closeDropdown() {
8316
- if (popover) {
8317
- popover.hide();
8318
- } else {
8319
- isOpen = false;
8320
- container.classList.remove("open");
8321
- }
8564
+ popover.hide();
8322
8565
  trigger.setAttribute("aria-expanded", "false");
8323
8566
  highlightedIndex = -1;
8324
8567
  clearSearch();
@@ -8326,9 +8569,7 @@
8326
8569
  }
8327
8570
 
8328
8571
  function toggleDropdown() {
8329
- var isVisible = popover
8330
- ? popover.element && popover.element.classList.contains("visible")
8331
- : isOpen;
8572
+ var isVisible = popover.element && popover.element.classList.contains("visible");
8332
8573
  if (isVisible) {
8333
8574
  closeDropdown();
8334
8575
  } else {
@@ -8439,10 +8680,7 @@
8439
8680
 
8440
8681
  // Initialize Popover
8441
8682
  function initializePopover() {
8442
- if (!usePopover) {
8443
- return;
8444
- }
8445
- popover = global.Popover.create({
8683
+ popover = Popover.create({
8446
8684
  trigger: trigger,
8447
8685
  content: content,
8448
8686
  placement: "bottom",
@@ -8458,6 +8696,12 @@
8458
8696
  onOpen: function () {
8459
8697
  trigger.setAttribute("aria-expanded", "true");
8460
8698
  chevron.style.transform = "rotate(180deg)";
8699
+ if (popover.panel) {
8700
+ var triggerWidthPx = trigger.offsetWidth + "px";
8701
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
8702
+ popover.panel.style.minWidth = triggerWidthPx;
8703
+ popover.panel.style.width = triggerWidthPx;
8704
+ }
8461
8705
  },
8462
8706
  bodyClassName: "p-0 overflow-hidden",
8463
8707
  panelClassName: "min-w-[var(--trigger-width)] overflow-hidden",
@@ -8465,13 +8709,6 @@
8465
8709
 
8466
8710
  // Store popover instance for cleanup
8467
8711
  container.popoverInstance = popover;
8468
-
8469
- // Set CSS variable for trigger width
8470
- var triggerWidth = trigger.offsetWidth;
8471
- if (popover.panel) {
8472
- popover.panel.style.setProperty("--trigger-width", triggerWidth + "px");
8473
- popover.panel.style.minWidth = triggerWidth + "px";
8474
- }
8475
8712
  }
8476
8713
 
8477
8714
  // Load options from SDK
@@ -8591,21 +8828,6 @@
8591
8828
  initializePopover();
8592
8829
  loadOptions();
8593
8830
 
8594
- if (!usePopover) {
8595
- trigger.addEventListener("click", function (e) {
8596
- e.preventDefault();
8597
- e.stopPropagation();
8598
- if (disabled) return;
8599
- toggleDropdown();
8600
- });
8601
- document.addEventListener("click", function (e) {
8602
- if (isOpen && !container.contains(e.target)) closeDropdown();
8603
- });
8604
- document.addEventListener("keydown", function (e) {
8605
- if (e.key === "Escape" && isOpen) closeDropdown();
8606
- });
8607
- }
8608
-
8609
8831
  // Public API
8610
8832
  container.updateValue = function (newValue) {
8611
8833
  value = newValue !== undefined && newValue !== null ? newValue : "";
@@ -8650,7 +8872,7 @@
8650
8872
  );
8651
8873
  updateClearButton();
8652
8874
  var isVisible = popover && popover.element && popover.element.classList.contains("visible");
8653
- if (disabled && (isVisible || isOpen)) closeDropdown();
8875
+ if (disabled && isVisible) closeDropdown();
8654
8876
  };
8655
8877
 
8656
8878
  container.reload = function () {
@@ -8682,13 +8904,13 @@
8682
8904
 
8683
8905
 
8684
8906
  // ============================================
8685
- // File 17/37: components/record-select.js
8907
+ // File 17/41: components/record-select.js
8686
8908
  // ============================================
8687
8909
 
8688
8910
  /**
8689
8911
  * Record Select Component (superleap-flow)
8690
8912
  * Single-record select with search; same trigger/content styling as Select.
8691
- * Uses: Popover (dropdown), InputComponent (search), Spinner (loading), Avatar (vivid for user).
8913
+ * Uses Popover for dropdown (required), InputComponent (search), Spinner (loading), Avatar (vivid for user).
8692
8914
  * Fetches records via superleapClient.getSdk().model(objectSlug).
8693
8915
  * For objectSlug === 'user' shows Vivid Avatar; otherwise static icon from schema or object map (database fallback).
8694
8916
  * Does not use icon from backend payload.
@@ -8696,20 +8918,31 @@
8696
8918
 
8697
8919
  (function (global) {
8698
8920
 
8699
- var Popover = global.Popover;
8700
- var InputComponent = global.InputComponent;
8701
- var Spinner = global.Spinner;
8702
-
8703
- /** When objectSlug === USERS, show Vivid Avatar (name-based color) instead of static icon */
8704
- var STANDARD_OBJECT_SLUGS_USERS = "user";
8705
-
8706
- /** Object slug -> Icon component iconStr + color (Tabler icon names). Used when objectSchema.properties.icon_data not provided. */
8707
- var OBJECT_SLUG_TO_ICON = {
8708
- team: { iconStr: "IconUsers", color: "primary" },
8921
+ function getDep(name) {
8922
+ if (
8923
+ typeof global.FlowUI !== "undefined" &&
8924
+ typeof global.FlowUI._getComponent === "function"
8925
+ ) {
8926
+ var c = global.FlowUI._getComponent(name);
8927
+ if (c) return c;
8928
+ }
8929
+ return global[name];
8930
+ }
8931
+
8932
+ var Popover = getDep("Popover");
8933
+ var InputComponent = getDep("InputComponent");
8934
+ var Spinner = getDep("Spinner");
8935
+
8936
+ /** When objectSlug === USERS, show Vivid Avatar (name-based color) instead of static icon */
8937
+ var STANDARD_OBJECT_SLUGS_USERS = "user";
8938
+
8939
+ /** Object slug -> Icon component iconStr + color (Tabler icon names). Used when objectSchema.properties.icon_data not provided. */
8940
+ var OBJECT_SLUG_TO_ICON = {
8941
+ team: { iconStr: "IconUsers", color: "primary" },
8709
8942
  role: { iconStr: "IconShield", color: "info" },
8710
8943
  iframe: { iconStr: "IconLayout", color: "neutral" },
8711
8944
  lead: { iconStr: "IconUser", color: "primary" },
8712
- opportunity: { iconStr: "IconCurrencyDollar", color: "success" },
8945
+ opportunity: { iconStr: "IconStar", color: "success" },
8713
8946
  call_log: { iconStr: "IconPhone", color: "info" },
8714
8947
  communication: { iconStr: "IconMessage", color: "primary" },
8715
8948
  history_field: { iconStr: "IconClock", color: "neutral" },
@@ -8720,7 +8953,7 @@
8720
8953
  '<svg width="16" height="16" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"/></svg>';
8721
8954
  var X_SVG =
8722
8955
  '<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>';
8723
-
8956
+
8724
8957
  var SEARCH_ICON =
8725
8958
  '<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>';
8726
8959
 
@@ -8735,7 +8968,8 @@
8735
8968
  "border-error-border hover:border-error-border-hover focus:border-1/2 focus:border-error-border-hover",
8736
8969
  warning:
8737
8970
  "border-warning-border hover:border-warning-border-hover focus:border-1/2 focus:border-warning-border-hover",
8738
- borderless: "border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
8971
+ borderless:
8972
+ "border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
8739
8973
  inline:
8740
8974
  "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",
8741
8975
  };
@@ -8744,7 +8978,9 @@
8744
8978
  large: "px-12 py-8",
8745
8979
  small: "px-8 py-4",
8746
8980
  };
8747
- var placeholderClass = placeholder ? " text-typography-quaternary-text" : "";
8981
+ var placeholderClass = placeholder
8982
+ ? " text-typography-quaternary-text"
8983
+ : "";
8748
8984
  var disabledClass = disabled
8749
8985
  ? " pointer-events-none cursor-not-allowed bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary"
8750
8986
  : "";
@@ -8800,15 +9036,27 @@
8800
9036
  function getObjectIconInfo(slug, objectSchema) {
8801
9037
  var color = "neutral";
8802
9038
  var iconStr = "IconDatabase";
8803
- if (objectSchema && objectSchema.properties && objectSchema.properties.icon_data) {
9039
+ if (
9040
+ objectSchema &&
9041
+ objectSchema.properties &&
9042
+ objectSchema.properties.icon_data
9043
+ ) {
8804
9044
  var iconData = objectSchema.properties.icon_data;
8805
- if (typeof iconData.color === "string" && iconData.color) color = iconData.color;
8806
- if (typeof iconData.icon === "string" && iconData.icon) iconStr = iconData.icon;
9045
+ if (typeof iconData.color === "string" && iconData.color)
9046
+ color = iconData.color;
9047
+ if (typeof iconData.icon === "string" && iconData.icon)
9048
+ iconStr = iconData.icon;
8807
9049
  }
8808
- if (!objectSchema || !objectSchema.properties || !objectSchema.properties.icon_data || !objectSchema.properties.icon_data.icon) {
9050
+ if (
9051
+ !objectSchema ||
9052
+ !objectSchema.properties ||
9053
+ !objectSchema.properties.icon_data ||
9054
+ !objectSchema.properties.icon_data.icon
9055
+ ) {
8809
9056
  if (OBJECT_SLUG_TO_ICON[slug]) {
8810
9057
  iconStr = OBJECT_SLUG_TO_ICON[slug].iconStr;
8811
- if (OBJECT_SLUG_TO_ICON[slug].color) color = OBJECT_SLUG_TO_ICON[slug].color;
9058
+ if (OBJECT_SLUG_TO_ICON[slug].color)
9059
+ color = OBJECT_SLUG_TO_ICON[slug].color;
8812
9060
  } else {
8813
9061
  iconStr = "IconDatabase";
8814
9062
  color = "neutral";
@@ -8831,6 +9079,7 @@
8831
9079
  * @param {string} [config.size] - 'default' | 'large' | 'small'
8832
9080
  * @param {boolean} [config.canClear] - Show clear button when value is set
8833
9081
  * @param {number} [config.initialLimit] - Initial fetch limit (default 50)
9082
+ * @param {Object} [config.initialFilter] - Optional filter object to merge with search (e.g. { field: "status", operator: "exact", value: "active" } or { and: [...] })
8834
9083
  * @param {Object} [config.objectSchema] - Optional object type/schema; properties.icon_data { svg?, color? } used for static icon (not used for user; user shows Vivid Avatar)
8835
9084
  * @returns {HTMLElement} Record select container element
8836
9085
  */
@@ -8839,12 +9088,14 @@
8839
9088
  var objectSlug = config.objectSlug;
8840
9089
  var objectSchema = config.objectSchema || null;
8841
9090
  var placeholder = config.placeholder || "Select a record";
8842
- var searchPlaceholder = config.searchPlaceholder || "Search " + (objectSlug || "") + "...";
9091
+ var searchPlaceholder =
9092
+ config.searchPlaceholder || "Search " + (objectSlug || "") + "...";
8843
9093
  var onChange = config.onChange;
8844
9094
  var variant = config.variant || "default";
8845
9095
  var size = config.size || "default";
8846
9096
  var canClear = !!config.canClear;
8847
9097
  var initialLimit = config.initialLimit != null ? config.initialLimit : 50;
9098
+ var initialFilter = config.initialFilter || null; // Can be array, object, or function returning either
8848
9099
 
8849
9100
  var disabled = config.disabled === true;
8850
9101
  var value =
@@ -8856,6 +9107,12 @@
8856
9107
  errEl.textContent = "Record select: objectSlug is required.";
8857
9108
  return errEl;
8858
9109
  }
9110
+ if (!Popover || typeof Popover.create !== "function") {
9111
+ var popoverErr = document.createElement("div");
9112
+ popoverErr.className = "text-reg-13 text-typography-quaternary-text";
9113
+ popoverErr.textContent = "Record select: Popover is required.";
9114
+ return popoverErr;
9115
+ }
8859
9116
 
8860
9117
  var container = document.createElement("div");
8861
9118
  container.className = "record-select relative w-full group";
@@ -8868,16 +9125,11 @@
8868
9125
  var isOpen = false;
8869
9126
  var searchTerm = "";
8870
9127
  var searchDebounceTimer = null;
8871
- var usePopover = Popover && typeof Popover.create === "function";
8872
9128
  var popover = null;
8873
9129
  var hasMoreRecords = true;
8874
9130
  var currentPage = 1;
8875
9131
  var isFetchingMore = false;
8876
9132
  var totalFetched = 0;
8877
- var contentBase = "record-select-content min-w-[8rem] ";
8878
- if (!usePopover) {
8879
- contentBase += "absolute left-0 right-0 z-50 max-h-[30vh] overflow-hidden rounded-4 bg-fill-quarternary-fill-white shadow-default-medium opacity-0 invisible transition-all duration-150 ease-out group-[.open]:opacity-100 group-[.open]:visible ";
8880
- }
8881
9133
 
8882
9134
  // Trigger wrapper + button (same structure as Select)
8883
9135
  var triggerWrapper = document.createElement("span");
@@ -8891,7 +9143,7 @@
8891
9143
  size,
8892
9144
  disabled,
8893
9145
  !value,
8894
- canClear && !!value && !disabled
9146
+ canClear && !!value && !disabled,
8895
9147
  );
8896
9148
  trigger.disabled = disabled;
8897
9149
  trigger.setAttribute("aria-haspopup", "listbox");
@@ -8900,7 +9152,8 @@
8900
9152
  trigger.classList.add("record-select-trigger");
8901
9153
 
8902
9154
  var triggerContent = document.createElement("div");
8903
- triggerContent.className = "record-select-trigger-content flex items-center gap-8 flex-1 min-w-0";
9155
+ triggerContent.className =
9156
+ "record-select-trigger-content flex items-center gap-8 flex-1 min-w-0";
8904
9157
 
8905
9158
  var triggerIcon = document.createElement("span");
8906
9159
  triggerIcon.className = "record-select-trigger-icon shrink-0 hidden";
@@ -8943,7 +9196,9 @@
8943
9196
  // Dropdown content: search + list (same content pattern as Select)
8944
9197
  var content = document.createElement("div");
8945
9198
  content.setAttribute("role", "listbox");
8946
- content.className = contentBase + (usePopover ? "max-h-[30vh] overflow-hidden flex flex-col" : "top-full mt-1 -translate-y-1 group-[.open]:translate-y-0");
9199
+ content.setAttribute("data-field-id", fieldId);
9200
+ content.className =
9201
+ "record-select-content max-h-[45vh] overflow-hidden flex flex-col";
8947
9202
 
8948
9203
  var searchWrap = document.createElement("div");
8949
9204
  searchWrap.className = "py-8 border-b-1/2 border-border-primary";
@@ -8974,12 +9229,12 @@
8974
9229
  } else {
8975
9230
  var fallbackWrapper = document.createElement("div");
8976
9231
  fallbackWrapper.className = "flex items-center gap-8 px-12";
8977
-
9232
+
8978
9233
  var searchIconSpan = document.createElement("span");
8979
9234
  searchIconSpan.className = "shrink-0 text-typography-tertiary-text";
8980
9235
  searchIconSpan.innerHTML = SEARCH_ICON;
8981
9236
  fallbackWrapper.appendChild(searchIconSpan);
8982
-
9237
+
8983
9238
  var searchInput = document.createElement("input");
8984
9239
  searchInput.type = "text";
8985
9240
  searchInput.className =
@@ -8993,76 +9248,83 @@
8993
9248
  content.appendChild(searchWrap);
8994
9249
 
8995
9250
  var optionsList = document.createElement("div");
8996
- optionsList.className = "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-select-options";
8997
-
9251
+ optionsList.className =
9252
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-select-options";
9253
+
8998
9254
  // Add scroll listener for infinite scroll
8999
9255
  optionsList.addEventListener("scroll", function () {
9000
9256
  if (isFetchingMore || !hasMoreRecords) return;
9001
9257
  var scrollHeight = optionsList.scrollHeight;
9002
9258
  var scrollTop = optionsList.scrollTop;
9003
9259
  var clientHeight = optionsList.clientHeight;
9004
-
9260
+
9005
9261
  // Trigger load more when scrolled to bottom (with 50px threshold)
9006
9262
  if (scrollTop + clientHeight >= scrollHeight - 50) {
9007
9263
  loadMoreRecords();
9008
9264
  }
9009
9265
  });
9010
-
9011
- content.appendChild(optionsList);
9012
9266
 
9013
- if (!usePopover) {
9014
- container.appendChild(content);
9015
- }
9267
+ content.appendChild(optionsList);
9016
9268
 
9017
- if (usePopover) {
9018
- popover = Popover.create({
9019
- trigger: trigger,
9020
- content: content,
9021
- placement: "bottom",
9022
- align: "start",
9023
- closeOnClickOutside: true,
9024
- bodyClassName: "p-0 overflow-hidden",
9025
- panelClassName: "max-h-[30vh] overflow-hidden",
9026
- onOpen: function () {
9027
- if (disabled) {
9028
- popover.hide();
9029
- return;
9030
- }
9031
- document.querySelectorAll(".custom-select.open, .record-select.open").forEach(function (other) {
9032
- if (other !== container) {
9033
- other.classList.remove("open");
9034
- var t = other.querySelector("button, .custom-select-trigger, .record-select-trigger");
9035
- if (t) t.setAttribute("aria-expanded", "false");
9036
- }
9037
- });
9038
- isOpen = true;
9039
- container.classList.add("open");
9040
- trigger.setAttribute("aria-expanded", "true");
9041
- searchTerm = "";
9042
- if (searchInputWrapper) searchInputWrapper.setValue("");
9043
- else if (searchInputEl) searchInputEl.value = "";
9044
- content.style.minWidth = trigger.offsetWidth + "px";
9045
- loadInitialAndRender();
9046
- setTimeout(function () {
9047
- if (searchInputEl) searchInputEl.focus();
9048
- }, 0);
9049
- },
9050
- onClose: function () {
9051
- isOpen = false;
9052
- container.classList.remove("open");
9053
- trigger.setAttribute("aria-expanded", "false");
9054
- searchTerm = "";
9055
- if (searchInputWrapper) searchInputWrapper.setValue("");
9056
- else if (searchInputEl) searchInputEl.value = "";
9057
- if (searchDebounceTimer) {
9058
- clearTimeout(searchDebounceTimer);
9059
- searchDebounceTimer = null;
9269
+ popover = Popover.create({
9270
+ trigger: trigger,
9271
+ content: content,
9272
+ placement: "bottom",
9273
+ align: "start",
9274
+ closeOnClickOutside: true,
9275
+ bodyClassName: "p-0 overflow-hidden",
9276
+ panelClassName: "min-w-[var(--trigger-width)] overflow-hidden",
9277
+ onOpen: function () {
9278
+ if (disabled) {
9279
+ popover.hide();
9280
+ return;
9281
+ }
9282
+ isOpen = true;
9283
+ container.classList.add("open");
9284
+ trigger.setAttribute("aria-expanded", "true");
9285
+ searchTerm = "";
9286
+ if (searchInputWrapper) searchInputWrapper.setValue("");
9287
+ else if (searchInputEl) searchInputEl.value = "";
9288
+ if (popover.panel) {
9289
+ var triggerWidthPx = trigger.offsetWidth + "px";
9290
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
9291
+ popover.panel.style.minWidth = triggerWidthPx;
9292
+ popover.panel.style.width = triggerWidthPx;
9293
+ }
9294
+ loadInitialAndRender();
9295
+ setTimeout(function () {
9296
+ if (searchInputEl) searchInputEl.focus();
9297
+ }, 0);
9298
+ // Let consumers (e.g. BANT Questions "Add Contact") inject content into the dropdown
9299
+ try {
9300
+ var doc =
9301
+ global.document ||
9302
+ (typeof document !== "undefined" ? document : null);
9303
+ if (doc && typeof global.CustomEvent !== "undefined") {
9304
+ doc.dispatchEvent(
9305
+ new global.CustomEvent("record-select:opened", {
9306
+ detail: { fieldId: fieldId, content: content },
9307
+ }),
9308
+ );
9060
9309
  }
9061
- },
9062
- });
9063
- }
9310
+ } catch (e) {}
9311
+ },
9312
+ onClose: function () {
9313
+ isOpen = false;
9314
+ container.classList.remove("open");
9315
+ trigger.setAttribute("aria-expanded", "false");
9316
+ searchTerm = "";
9317
+ if (searchInputWrapper) searchInputWrapper.setValue("");
9318
+ else if (searchInputEl) searchInputEl.value = "";
9319
+ if (searchDebounceTimer) {
9320
+ clearTimeout(searchDebounceTimer);
9321
+ searchDebounceTimer = null;
9322
+ }
9323
+ },
9324
+ });
9064
9325
 
9065
- if (clearBtn) clearBtn.style.display = canClear && value && !disabled ? "" : "none";
9326
+ if (clearBtn)
9327
+ clearBtn.style.display = canClear && value && !disabled ? "" : "none";
9066
9328
 
9067
9329
  function setValue(newVal) {
9068
9330
  value = newVal !== undefined && newVal !== null ? newVal : "";
@@ -9076,16 +9338,18 @@
9076
9338
 
9077
9339
  function updateTriggerDisplay() {
9078
9340
  if (selectedRecord) {
9079
- triggerText.textContent = selectedRecord.name || selectedRecord.label || value;
9341
+ triggerText.textContent =
9342
+ selectedRecord.name || selectedRecord.label || value;
9080
9343
  trigger.classList.remove("placeholder");
9081
9344
  trigger.className = triggerClasses(
9082
9345
  variant,
9083
9346
  size,
9084
9347
  disabled,
9085
9348
  false,
9086
- canClear && !!value && !disabled
9349
+ canClear && !!value && !disabled,
9087
9350
  );
9088
- triggerIcon.className = "record-select-trigger-icon shrink-0 flex items-center justify-center size-20 rounded-4 overflow-hidden";
9351
+ triggerIcon.className =
9352
+ "record-select-trigger-icon shrink-0 flex items-center justify-center size-20 rounded-4 overflow-hidden";
9089
9353
  triggerIcon.innerHTML = "";
9090
9354
  if (objectSlug === STANDARD_OBJECT_SLUGS_USERS) {
9091
9355
  var Avatar = getAvatar();
@@ -9097,7 +9361,9 @@
9097
9361
  });
9098
9362
  triggerIcon.appendChild(vividEl);
9099
9363
  } else {
9100
- renderStaticIconPlaceholder(selectedRecord.name || selectedRecord.label);
9364
+ renderStaticIconPlaceholder(
9365
+ selectedRecord.name || selectedRecord.label,
9366
+ );
9101
9367
  }
9102
9368
  } else {
9103
9369
  renderStaticObjectIcon();
@@ -9110,12 +9376,13 @@
9110
9376
  size,
9111
9377
  disabled,
9112
9378
  true,
9113
- canClear && !!value && !disabled
9379
+ canClear && !!value && !disabled,
9114
9380
  );
9115
9381
  triggerIcon.className = "record-select-trigger-icon shrink-0 hidden";
9116
9382
  triggerIcon.innerHTML = "";
9117
9383
  }
9118
- if (clearBtn) clearBtn.style.display = canClear && value && !disabled ? "" : "none";
9384
+ if (clearBtn)
9385
+ clearBtn.style.display = canClear && value && !disabled ? "" : "none";
9119
9386
  }
9120
9387
 
9121
9388
  function renderStaticObjectIcon() {
@@ -9152,18 +9419,39 @@
9152
9419
  var fields = ["id", "name"];
9153
9420
  var actualLimit = limit || initialLimit;
9154
9421
  var offset = page ? (page - 1) * actualLimit : 0;
9155
-
9422
+
9156
9423
  try {
9157
9424
  if (model && typeof model.select === "function") {
9158
9425
  var q = model.select.apply(model, fields);
9426
+ var filters = [];
9427
+ var resolvedFilter =
9428
+ typeof initialFilter === "function"
9429
+ ? initialFilter()
9430
+ : initialFilter;
9431
+ console.log(
9432
+ "[RecordSelect] initialFilter:",
9433
+ resolvedFilter,
9434
+ "| search:",
9435
+ search,
9436
+ );
9437
+ if (resolvedFilter) {
9438
+ filters = filters.concat(
9439
+ Array.isArray(resolvedFilter) ? resolvedFilter : [resolvedFilter],
9440
+ );
9441
+ }
9159
9442
  if (search && search.trim()) {
9160
- q = q.filterBy({
9443
+ filters.push({
9161
9444
  or: [
9162
9445
  { field: "name", operator: "contains", value: search.trim() },
9163
9446
  { field: "id", operator: "eq", value: search.trim() },
9164
9447
  ],
9165
9448
  });
9166
9449
  }
9450
+ if (filters.length > 0) {
9451
+ q = q.filterBy(
9452
+ filters.length === 1 ? filters[0] : { and: filters },
9453
+ );
9454
+ }
9167
9455
  var orderBy = ["name"];
9168
9456
  if (objectSlug === "account") orderBy.push("-ParentId");
9169
9457
  return q
@@ -9226,7 +9514,12 @@
9226
9514
  updateTriggerDisplay();
9227
9515
  })
9228
9516
  .catch(function () {
9229
- selectedRecord = { id: value, value: value, name: value, label: value };
9517
+ selectedRecord = {
9518
+ id: value,
9519
+ value: value,
9520
+ name: value,
9521
+ label: value,
9522
+ };
9230
9523
  updateTriggerDisplay();
9231
9524
  });
9232
9525
  }
@@ -9237,7 +9530,9 @@
9237
9530
  loadWrap.className =
9238
9531
  "flex flex-row items-center justify-center gap-8 py-12 px-12 w-full text-reg-12 text-typography-quaternary-text record-select-loading";
9239
9532
  if (Spinner && typeof Spinner.create === "function") {
9240
- loadWrap.appendChild(Spinner.create({ size: "small", text: "Loading..." }));
9533
+ loadWrap.appendChild(
9534
+ Spinner.create({ size: "small", text: "Loading..." }),
9535
+ );
9241
9536
  } else {
9242
9537
  var loadText = document.createElement("span");
9243
9538
  loadText.textContent = "Loading...";
@@ -9245,12 +9540,12 @@
9245
9540
  }
9246
9541
  optionsList.appendChild(loadWrap);
9247
9542
  }
9248
-
9543
+
9249
9544
  function showLoadingMore() {
9250
9545
  // Remove existing loading more indicator
9251
9546
  var existing = optionsList.querySelector(".record-select-loading-more");
9252
9547
  if (existing) existing.remove();
9253
-
9548
+
9254
9549
  var loadWrap = document.createElement("div");
9255
9550
  loadWrap.className =
9256
9551
  "flex flex-row items-center justify-center gap-8 py-8 px-12 w-full text-reg-12 text-typography-quaternary-text record-select-loading-more";
@@ -9263,9 +9558,11 @@
9263
9558
  }
9264
9559
  optionsList.appendChild(loadWrap);
9265
9560
  }
9266
-
9561
+
9267
9562
  function removeLoadingMore() {
9268
- var loadingMore = optionsList.querySelector(".record-select-loading-more");
9563
+ var loadingMore = optionsList.querySelector(
9564
+ ".record-select-loading-more",
9565
+ );
9269
9566
  if (loadingMore) loadingMore.remove();
9270
9567
  }
9271
9568
 
@@ -9284,13 +9581,15 @@
9284
9581
  existingOptions.forEach(function (opt) {
9285
9582
  opt.remove();
9286
9583
  });
9287
-
9584
+
9288
9585
  // Remove old loading/empty states
9289
- var oldStates = optionsList.querySelectorAll(".record-select-loading, .record-select-empty");
9586
+ var oldStates = optionsList.querySelectorAll(
9587
+ ".record-select-loading, .record-select-empty",
9588
+ );
9290
9589
  oldStates.forEach(function (el) {
9291
9590
  el.remove();
9292
9591
  });
9293
-
9592
+
9294
9593
  filteredRecords.forEach(function (rec) {
9295
9594
  var optionValue = rec.id || rec.value;
9296
9595
  var optionLabel = rec.name || rec.label || rec.value;
@@ -9305,7 +9604,7 @@
9305
9604
  "hover:bg-fill-tertiary-fill-light-gray focus:bg-fill-tertiary-fill-light-gray",
9306
9605
  isSelected
9307
9606
  ? "bg-primary-surface hover:!bg-primary-surface-hover"
9308
- : ""
9607
+ : "",
9309
9608
  );
9310
9609
 
9311
9610
  var optContent = document.createElement("span");
@@ -9363,70 +9662,93 @@
9363
9662
 
9364
9663
  optionsList.appendChild(option);
9365
9664
  });
9366
-
9665
+
9367
9666
  // Add loading more indicator at the bottom if fetching
9368
9667
  if (isFetchingMore) {
9369
9668
  showLoadingMore();
9370
9669
  }
9371
9670
  }
9372
9671
 
9672
+ function scheduleUpdatePosition() {
9673
+ if (popover && typeof popover.updatePosition === "function") {
9674
+ requestAnimationFrame(function () {
9675
+ requestAnimationFrame(function () {
9676
+ popover.updatePosition();
9677
+ });
9678
+ });
9679
+ }
9680
+ }
9681
+
9373
9682
  function loadInitialAndRender() {
9374
9683
  showLoading();
9375
9684
  currentPage = 1;
9376
9685
  hasMoreRecords = true;
9377
9686
  totalFetched = 0;
9378
-
9379
- fetchRecords(searchTerm, initialLimit, 1).then(function (result) {
9380
- allRecords = result.records;
9381
- filteredRecords = result.records;
9382
- hasMoreRecords = result.hasMore;
9383
- totalFetched = result.records.length;
9384
- currentPage = 1;
9385
-
9386
- if (value && !result.records.some(function (r) { return (r.id || r.value) === value; })) {
9387
- loadSelectedRecord();
9388
- } else if (value && result.records.length) {
9389
- var sel = result.records.find(function (r) { return (r.id || r.value) === value; });
9390
- if (sel) selectedRecord = sel;
9391
- updateTriggerDisplay();
9392
- }
9393
- if (filteredRecords.length === 0) {
9394
- showEmpty(searchTerm ? "No results found" : "No records available");
9395
- } else {
9396
- renderOptions();
9397
- }
9398
- }).catch(function () {
9399
- showEmpty("Failed to load records");
9400
- hasMoreRecords = false;
9401
- });
9687
+
9688
+ fetchRecords(searchTerm, initialLimit, 1)
9689
+ .then(function (result) {
9690
+ allRecords = result.records;
9691
+ filteredRecords = result.records;
9692
+ hasMoreRecords = result.hasMore;
9693
+ totalFetched = result.records.length;
9694
+ currentPage = 1;
9695
+
9696
+ if (
9697
+ value &&
9698
+ !result.records.some(function (r) {
9699
+ return (r.id || r.value) === value;
9700
+ })
9701
+ ) {
9702
+ loadSelectedRecord();
9703
+ } else if (value && result.records.length) {
9704
+ var sel = result.records.find(function (r) {
9705
+ return (r.id || r.value) === value;
9706
+ });
9707
+ if (sel) selectedRecord = sel;
9708
+ updateTriggerDisplay();
9709
+ }
9710
+ if (filteredRecords.length === 0) {
9711
+ showEmpty(searchTerm ? "No results found" : "No records available");
9712
+ } else {
9713
+ renderOptions();
9714
+ }
9715
+ scheduleUpdatePosition();
9716
+ })
9717
+ .catch(function () {
9718
+ showEmpty("Failed to load records");
9719
+ hasMoreRecords = false;
9720
+ scheduleUpdatePosition();
9721
+ });
9402
9722
  }
9403
-
9723
+
9404
9724
  function loadMoreRecords() {
9405
9725
  if (isFetchingMore || !hasMoreRecords) return;
9406
-
9726
+
9407
9727
  isFetchingMore = true;
9408
9728
  currentPage += 1;
9409
9729
  showLoadingMore();
9410
-
9411
- fetchRecords(searchTerm, initialLimit, currentPage).then(function (result) {
9412
- isFetchingMore = false;
9413
- removeLoadingMore();
9414
-
9415
- if (result.records.length > 0) {
9416
- allRecords = allRecords.concat(result.records);
9417
- filteredRecords = filteredRecords.concat(result.records);
9418
- totalFetched += result.records.length;
9419
- hasMoreRecords = result.hasMore;
9420
- renderOptions();
9421
- } else {
9730
+
9731
+ fetchRecords(searchTerm, initialLimit, currentPage)
9732
+ .then(function (result) {
9733
+ isFetchingMore = false;
9734
+ removeLoadingMore();
9735
+
9736
+ if (result.records.length > 0) {
9737
+ allRecords = allRecords.concat(result.records);
9738
+ filteredRecords = filteredRecords.concat(result.records);
9739
+ totalFetched += result.records.length;
9740
+ hasMoreRecords = result.hasMore;
9741
+ renderOptions();
9742
+ } else {
9743
+ hasMoreRecords = false;
9744
+ }
9745
+ })
9746
+ .catch(function (err) {
9747
+ console.error("[RecordSelect] loadMoreRecords error:", err);
9748
+ isFetchingMore = false;
9749
+ removeLoadingMore();
9422
9750
  hasMoreRecords = false;
9423
- }
9424
- }).catch(function (err) {
9425
- console.error("[RecordSelect] loadMoreRecords error:", err);
9426
- isFetchingMore = false;
9427
- removeLoadingMore();
9428
- hasMoreRecords = false;
9429
- });
9751
+ });
9430
9752
  }
9431
9753
 
9432
9754
  function debouncedSearch() {
@@ -9437,83 +9759,31 @@
9437
9759
  currentPage = 1;
9438
9760
  hasMoreRecords = true;
9439
9761
  totalFetched = 0;
9440
-
9441
- fetchRecords(searchTerm, initialLimit, 1).then(function (result) {
9442
- allRecords = result.records;
9443
- filteredRecords = result.records;
9444
- hasMoreRecords = result.hasMore;
9445
- totalFetched = result.records.length;
9446
-
9447
- if (result.records.length === 0) {
9448
- showEmpty("No results found");
9449
- } else {
9450
- renderOptions();
9451
- }
9452
- }).catch(function () {
9453
- showEmpty("Search failed");
9454
- hasMoreRecords = false;
9455
- });
9456
- }, 500);
9457
- }
9458
9762
 
9459
- function openDropdown() {
9460
- if (disabled) return;
9461
- if (usePopover && popover) {
9462
- popover.show();
9463
- return;
9464
- }
9465
- document
9466
- .querySelectorAll(".custom-select.open, .record-select.open")
9467
- .forEach(function (other) {
9468
- if (other !== container) {
9469
- other.classList.remove("open");
9470
- var t = other.querySelector(
9471
- "button, .custom-select-trigger, .record-select-trigger"
9472
- );
9473
- if (t) t.setAttribute("aria-expanded", "false");
9474
- }
9475
- });
9476
- isOpen = true;
9477
- container.classList.add("open");
9478
- trigger.setAttribute("aria-expanded", "true");
9479
- searchTerm = "";
9480
- if (searchInputWrapper) searchInputWrapper.setValue("");
9481
- else if (searchInputEl) searchInputEl.value = "";
9482
- loadInitialAndRender();
9483
- setTimeout(function () {
9484
- if (searchInputEl) searchInputEl.focus();
9485
- }, 0);
9486
- updatePosition();
9487
- }
9763
+ fetchRecords(searchTerm, initialLimit, 1)
9764
+ .then(function (result) {
9765
+ allRecords = result.records;
9766
+ filteredRecords = result.records;
9767
+ hasMoreRecords = result.hasMore;
9768
+ totalFetched = result.records.length;
9488
9769
 
9489
- function closeDropdown() {
9490
- if (usePopover && popover) {
9491
- popover.hide();
9492
- return;
9493
- }
9494
- isOpen = false;
9495
- container.classList.remove("open");
9496
- trigger.setAttribute("aria-expanded", "false");
9497
- searchTerm = "";
9498
- if (searchInputWrapper) searchInputWrapper.setValue("");
9499
- else if (searchInputEl) searchInputEl.value = "";
9500
- if (searchDebounceTimer) {
9501
- clearTimeout(searchDebounceTimer);
9502
- searchDebounceTimer = null;
9503
- }
9770
+ if (result.records.length === 0) {
9771
+ showEmpty("No results found");
9772
+ } else {
9773
+ renderOptions();
9774
+ }
9775
+ scheduleUpdatePosition();
9776
+ })
9777
+ .catch(function () {
9778
+ showEmpty("Search failed");
9779
+ hasMoreRecords = false;
9780
+ scheduleUpdatePosition();
9781
+ });
9782
+ }, 500);
9504
9783
  }
9505
9784
 
9506
- function updatePosition() {
9507
- if (usePopover) return;
9508
- var rect = trigger.getBoundingClientRect();
9509
- var vh = window.innerHeight;
9510
- var below = vh - rect.bottom;
9511
- var above = rect.top;
9512
- if (below < 200 && above > below) {
9513
- content.className = contentBase + "bottom-full mb-1 translate-y-1 group-[.open]:translate-y-0";
9514
- } else {
9515
- content.className = contentBase + "top-full mt-1 -translate-y-1 group-[.open]:translate-y-0";
9516
- }
9785
+ function closeDropdown() {
9786
+ popover.hide();
9517
9787
  }
9518
9788
 
9519
9789
  if (!searchInputWrapper && searchInputEl) {
@@ -9532,41 +9802,14 @@
9532
9802
  });
9533
9803
  }
9534
9804
 
9535
- if (usePopover && popover) {
9536
- trigger.addEventListener("keydown", function (e) {
9537
- if (disabled) return;
9538
- if (e.key === "Enter" || e.key === " ") {
9539
- e.preventDefault();
9540
- if (isOpen) popover.hide();
9541
- else popover.show();
9542
- }
9543
- });
9544
- } else {
9545
- trigger.addEventListener("click", function (e) {
9805
+ trigger.addEventListener("keydown", function (e) {
9806
+ if (disabled) return;
9807
+ if (e.key === "Enter" || e.key === " ") {
9546
9808
  e.preventDefault();
9547
- e.stopPropagation();
9548
- if (isOpen) closeDropdown();
9549
- else openDropdown();
9550
- });
9551
- trigger.addEventListener("keydown", function (e) {
9552
- if (disabled) return;
9553
- if (e.key === "Enter" || e.key === " ") {
9554
- e.preventDefault();
9555
- if (isOpen) closeDropdown();
9556
- else openDropdown();
9557
- }
9558
- if (e.key === "Escape" && isOpen) {
9559
- e.preventDefault();
9560
- closeDropdown();
9561
- }
9562
- });
9563
- document.addEventListener("click", function (e) {
9564
- if (isOpen && !container.contains(e.target)) closeDropdown();
9565
- });
9566
- document.addEventListener("keydown", function (e) {
9567
- if (e.key === "Escape" && isOpen) closeDropdown();
9568
- });
9569
- }
9809
+ if (isOpen) popover.hide();
9810
+ else popover.show();
9811
+ }
9812
+ });
9570
9813
 
9571
9814
  container.updateValue = function (newVal) {
9572
9815
  setValue(newVal);
@@ -9580,7 +9823,7 @@
9580
9823
  size,
9581
9824
  disabled,
9582
9825
  !value,
9583
- canClear && !!value && !disabled
9826
+ canClear && !!value && !disabled,
9584
9827
  );
9585
9828
  if (disabled && isOpen) closeDropdown();
9586
9829
  };
@@ -9599,7 +9842,7 @@
9599
9842
 
9600
9843
 
9601
9844
  // ============================================
9602
- // File 18/37: components/multiselect.js
9845
+ // File 18/41: components/multiselect.js
9603
9846
  // ============================================
9604
9847
 
9605
9848
  /**
@@ -9663,6 +9906,14 @@
9663
9906
  return opt.label || opt.name || opt.display_name || opt.value;
9664
9907
  }
9665
9908
 
9909
+ function getDep(name) {
9910
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
9911
+ var c = global.FlowUI._getComponent(name);
9912
+ if (c) return c;
9913
+ }
9914
+ return global[name];
9915
+ }
9916
+
9666
9917
  /**
9667
9918
  * Create a multiselect dropdown component
9668
9919
  * @param {Object} config
@@ -9693,6 +9944,11 @@
9693
9944
  ? config.values.slice()
9694
9945
  : [];
9695
9946
 
9947
+ var Popover = getDep("Popover");
9948
+ if (!Popover || typeof Popover.create !== "function") {
9949
+ throw new Error("MultiSelect requires Popover");
9950
+ }
9951
+
9696
9952
  var container = document.createElement("div");
9697
9953
  container.className = "custom-multiselect relative w-full group";
9698
9954
  if (fieldId) container.setAttribute("data-field-id", fieldId);
@@ -9741,18 +9997,14 @@
9741
9997
  triggerWrapper.appendChild(trigger);
9742
9998
  container.appendChild(triggerWrapper);
9743
9999
 
9744
- var contentBase =
9745
- "custom-multiselect-content absolute left-0 right-0 z-50 max-h-[200px] min-w-[8rem] overflow-hidden rounded-4 bg-fill-quarternary-fill-white shadow-default-medium opacity-0 invisible transition-all duration-150 ease-out " +
9746
- "group-[.open]:opacity-100 group-[.open]:visible ";
9747
-
9748
10000
  var content = document.createElement("div");
9749
10001
  content.setAttribute("role", "listbox");
9750
10002
  content.setAttribute("aria-multiselectable", "true");
9751
- content.className = contentBase + "top-full mt-1 -translate-y-1 group-[.open]:translate-y-0";
10003
+ content.className = "custom-multiselect-content w-full max-h-[45vh] overflow-hidden flex flex-col";
9752
10004
 
9753
10005
  var optionsList = document.createElement("div");
9754
10006
  optionsList.className =
9755
- "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
10007
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
9756
10008
 
9757
10009
  function isSelected(optionValue) {
9758
10010
  return values.some(function (v) {
@@ -9849,47 +10101,68 @@
9849
10101
  buildOptionsList();
9850
10102
 
9851
10103
  content.appendChild(optionsList);
9852
- container.appendChild(content);
9853
10104
 
9854
- var isOpen = false;
10105
+ var popover = Popover.create({
10106
+ trigger: trigger,
10107
+ content: content,
10108
+ placement: "bottom",
10109
+ align: "start",
10110
+ closeOnClickOutside: true,
10111
+ bodyClassName: "p-0 overflow-hidden",
10112
+ panelClassName: "min-w-[var(--trigger-width)] max-h-[45vh] overflow-hidden",
10113
+ onOpen: function () {
10114
+ if (disabled) {
10115
+ popover.hide();
10116
+ return;
10117
+ }
10118
+ document
10119
+ .querySelectorAll(".custom-select, .record-select, .custom-multiselect, .enum-select, .enum-multiselect, .record-multiselect")
10120
+ .forEach(function (other) {
10121
+ if (other !== container && other.popoverInstance) {
10122
+ other.popoverInstance.hide();
10123
+ }
10124
+ });
10125
+ trigger.setAttribute("aria-expanded", "true");
10126
+ chevron.style.transform = "rotate(180deg)";
10127
+ if (popover.panel) {
10128
+ var triggerWidthPx = trigger.offsetWidth + "px";
10129
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
10130
+ popover.panel.style.minWidth = triggerWidthPx;
10131
+ popover.panel.style.width = triggerWidthPx;
10132
+ }
10133
+ },
10134
+ onClose: function () {
10135
+ trigger.setAttribute("aria-expanded", "false");
10136
+ chevron.style.transform = "";
10137
+ },
10138
+ });
10139
+ container.popoverInstance = popover;
9855
10140
 
9856
10141
  function openDropdown() {
9857
10142
  if (disabled) return;
9858
- document
9859
- .querySelectorAll(".custom-select.open, .record-select.open, .custom-multiselect.open")
9860
- .forEach(function (other) {
9861
- if (other !== container) {
9862
- other.classList.remove("open");
9863
- var t = other.querySelector(
9864
- "button, .custom-select-trigger, .record-select-trigger, .multiselect-trigger-wrapper button"
9865
- );
9866
- if (t) t.setAttribute("aria-expanded", "false");
9867
- }
9868
- });
9869
- isOpen = true;
9870
- container.classList.add("open");
9871
- trigger.setAttribute("aria-expanded", "true");
10143
+ popover.show();
9872
10144
  }
9873
10145
 
9874
10146
  function closeDropdown() {
9875
- isOpen = false;
9876
- container.classList.remove("open");
9877
- trigger.setAttribute("aria-expanded", "false");
10147
+ popover.hide();
9878
10148
  }
9879
10149
 
9880
10150
  function toggleDropdown() {
9881
- if (isOpen) closeDropdown();
10151
+ var isVisible = popover.element && popover.element.classList.contains("visible");
10152
+ if (isVisible) closeDropdown();
9882
10153
  else openDropdown();
9883
10154
  }
9884
10155
 
9885
10156
  trigger.addEventListener("click", function (e) {
9886
- e.preventDefault();
9887
- e.stopPropagation();
9888
- toggleDropdown();
9889
- });
10157
+ if (disabled) {
10158
+ e.preventDefault();
10159
+ e.stopImmediatePropagation();
10160
+ }
10161
+ }, true);
9890
10162
 
9891
10163
  trigger.addEventListener("keydown", function (e) {
9892
10164
  if (disabled) return;
10165
+ var isVisible = popover.element && popover.element.classList.contains("visible");
9893
10166
  switch (e.key) {
9894
10167
  case "Enter":
9895
10168
  case " ":
@@ -9898,14 +10171,14 @@
9898
10171
  break;
9899
10172
  case "ArrowDown":
9900
10173
  e.preventDefault();
9901
- if (!isOpen) openDropdown();
10174
+ if (!isVisible) openDropdown();
9902
10175
  break;
9903
10176
  case "ArrowUp":
9904
10177
  e.preventDefault();
9905
- if (!isOpen) openDropdown();
10178
+ if (!isVisible) openDropdown();
9906
10179
  break;
9907
10180
  case "Escape":
9908
- if (isOpen) {
10181
+ if (isVisible) {
9909
10182
  e.preventDefault();
9910
10183
  closeDropdown();
9911
10184
  }
@@ -9913,13 +10186,6 @@
9913
10186
  }
9914
10187
  });
9915
10188
 
9916
- document.addEventListener("click", function (e) {
9917
- if (isOpen && !container.contains(e.target)) closeDropdown();
9918
- });
9919
- document.addEventListener("keydown", function (e) {
9920
- if (e.key === "Escape" && isOpen) closeDropdown();
9921
- });
9922
-
9923
10189
  container.updateValues = function (newValues) {
9924
10190
  values = Array.isArray(newValues) ? newValues.slice() : [];
9925
10191
  renderTriggerContent();
@@ -9938,7 +10204,8 @@
9938
10204
  disabled = !!isDisabled;
9939
10205
  trigger.disabled = disabled;
9940
10206
  trigger.className = triggerClasses(variant, size, disabled, values.length === 0);
9941
- if (disabled && isOpen) closeDropdown();
10207
+ var isVisible = popover.element && popover.element.classList.contains("visible");
10208
+ if (disabled && isVisible) closeDropdown();
9942
10209
  };
9943
10210
 
9944
10211
  container.getValues = function () {
@@ -9956,7 +10223,7 @@
9956
10223
 
9957
10224
 
9958
10225
  // ============================================
9959
- // File 19/37: components/enum-multiselect.js
10226
+ // File 19/41: components/enum-multiselect.js
9960
10227
  // ============================================
9961
10228
 
9962
10229
  /**
@@ -10036,6 +10303,14 @@
10036
10303
  return Array.prototype.filter.call(arguments, Boolean).join(" ");
10037
10304
  }
10038
10305
 
10306
+ function getDep(name) {
10307
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
10308
+ var c = global.FlowUI._getComponent(name);
10309
+ if (c) return c;
10310
+ }
10311
+ return global[name];
10312
+ }
10313
+
10039
10314
  /** Resolve client: use FlowUI._getComponent when bundle has captured globals, else global.superleapClient */
10040
10315
  function getClient() {
10041
10316
  if (global.FlowUI && typeof global.FlowUI._getComponent === "function") {
@@ -10104,8 +10379,10 @@
10104
10379
  var error = null;
10105
10380
  var searchQuery = "";
10106
10381
  var popover = null;
10107
- var usePopover = !!(global.Popover && typeof global.Popover.create === "function");
10108
- var isOpen = false;
10382
+ var Popover = getDep("Popover");
10383
+ if (!Popover || typeof Popover.create !== "function") {
10384
+ throw new Error("EnumMultiSelect requires Popover");
10385
+ }
10109
10386
 
10110
10387
  var container = document.createElement("div");
10111
10388
  container.className = "enum-multiselect relative w-full group";
@@ -10207,13 +10484,7 @@
10207
10484
  var content = document.createElement("div");
10208
10485
  content.setAttribute("role", "listbox");
10209
10486
  content.setAttribute("aria-multiselectable", "true");
10210
- var contentBase = "w-full min-w-[200px]";
10211
- if (!usePopover) {
10212
- contentBase += " absolute left-0 right-0 z-50 max-h-[30vh] overflow-hidden rounded-4 bg-fill-quarternary-fill-white shadow-default-medium border-1/2 border-border-primary opacity-0 invisible transition-all duration-150 ease-out group-[.open]:opacity-100 group-[.open]:visible top-full mt-1 -translate-y-1 group-[.open]:translate-y-0 flex flex-col";
10213
- } else {
10214
- contentBase += " max-h-[30vh] overflow-hidden flex flex-col";
10215
- }
10216
- content.className = contentBase;
10487
+ content.className = "w-full min-w-[200px] max-h-[45vh] overflow-hidden flex flex-col";
10217
10488
 
10218
10489
  // Search input (using InputComponent like enum-select)
10219
10490
  var searchContainer = document.createElement("div");
@@ -10274,15 +10545,11 @@
10274
10545
  // Options list
10275
10546
  var optionsList = document.createElement("div");
10276
10547
  optionsList.className =
10277
- "overflow-y-auto max-h-[30vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white flex-1 min-h-0";
10548
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white flex-1 min-h-0";
10278
10549
 
10279
10550
  content.appendChild(searchContainer);
10280
10551
  content.appendChild(optionsList);
10281
10552
 
10282
- if (!usePopover) {
10283
- container.appendChild(content);
10284
- }
10285
-
10286
10553
  var highlightedIndex = -1;
10287
10554
 
10288
10555
  function showLoading() {
@@ -10455,34 +10722,14 @@
10455
10722
 
10456
10723
  function openDropdown() {
10457
10724
  if (disabled) return;
10458
- if (popover) {
10459
- document
10460
- .querySelectorAll(".enum-select, .enum-multiselect, .custom-select, .record-select, .custom-multiselect")
10461
- .forEach(function (other) {
10462
- if (other !== container && other.popoverInstance) {
10463
- other.popoverInstance.hide();
10464
- }
10465
- });
10466
- popover.show();
10467
- trigger.setAttribute("aria-expanded", "true");
10468
- if (searchInput) {
10469
- setTimeout(function () {
10470
- searchInput.focus();
10471
- }, 50);
10472
- }
10473
- return;
10474
- }
10475
10725
  document
10476
- .querySelectorAll(".enum-multiselect.open, .enum-select.open")
10726
+ .querySelectorAll(".enum-select, .enum-multiselect, .custom-select, .record-select, .custom-multiselect")
10477
10727
  .forEach(function (other) {
10478
- if (other !== container) {
10479
- other.classList.remove("open");
10480
- var t = other.querySelector("button");
10481
- if (t) t.setAttribute("aria-expanded", "false");
10728
+ if (other !== container && other.popoverInstance) {
10729
+ other.popoverInstance.hide();
10482
10730
  }
10483
10731
  });
10484
- isOpen = true;
10485
- container.classList.add("open");
10732
+ popover.show();
10486
10733
  trigger.setAttribute("aria-expanded", "true");
10487
10734
  if (searchInput) {
10488
10735
  setTimeout(function () {
@@ -10492,12 +10739,7 @@
10492
10739
  }
10493
10740
 
10494
10741
  function closeDropdown() {
10495
- if (popover) {
10496
- popover.hide();
10497
- } else {
10498
- isOpen = false;
10499
- container.classList.remove("open");
10500
- }
10742
+ popover.hide();
10501
10743
  trigger.setAttribute("aria-expanded", "false");
10502
10744
  highlightedIndex = -1;
10503
10745
  clearSearch();
@@ -10505,9 +10747,7 @@
10505
10747
  }
10506
10748
 
10507
10749
  function toggleDropdown() {
10508
- var isVisible = popover
10509
- ? popover.element && popover.element.classList.contains("visible")
10510
- : isOpen;
10750
+ var isVisible = popover.element && popover.element.classList.contains("visible");
10511
10751
  if (isVisible) {
10512
10752
  closeDropdown();
10513
10753
  } else {
@@ -10587,10 +10827,7 @@
10587
10827
 
10588
10828
  // Initialize Popover
10589
10829
  function initializePopover() {
10590
- if (!usePopover) {
10591
- return;
10592
- }
10593
- popover = global.Popover.create({
10830
+ popover = Popover.create({
10594
10831
  trigger: trigger,
10595
10832
  content: content,
10596
10833
  placement: "bottom",
@@ -10606,6 +10843,12 @@
10606
10843
  onOpen: function () {
10607
10844
  trigger.setAttribute("aria-expanded", "true");
10608
10845
  chevron.style.transform = "rotate(180deg)";
10846
+ if (popover.panel) {
10847
+ var triggerWidthPx = trigger.offsetWidth + "px";
10848
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
10849
+ popover.panel.style.minWidth = triggerWidthPx;
10850
+ popover.panel.style.width = triggerWidthPx;
10851
+ }
10609
10852
  },
10610
10853
  bodyClassName: "p-0 overflow-hidden",
10611
10854
  panelClassName: "min-w-[var(--trigger-width)] overflow-hidden",
@@ -10613,13 +10856,6 @@
10613
10856
 
10614
10857
  // Store popover instance for cleanup
10615
10858
  container.popoverInstance = popover;
10616
-
10617
- // Set CSS variable for trigger width
10618
- var triggerWidth = trigger.offsetWidth;
10619
- if (popover.panel) {
10620
- popover.panel.style.setProperty("--trigger-width", triggerWidth + "px");
10621
- popover.panel.style.minWidth = triggerWidth + "px";
10622
- }
10623
10859
  }
10624
10860
 
10625
10861
  // Load options from SDK
@@ -10734,21 +10970,6 @@
10734
10970
  initializePopover();
10735
10971
  loadOptions();
10736
10972
 
10737
- if (!usePopover) {
10738
- trigger.addEventListener("click", function (e) {
10739
- e.preventDefault();
10740
- e.stopPropagation();
10741
- if (disabled) return;
10742
- toggleDropdown();
10743
- });
10744
- document.addEventListener("click", function (e) {
10745
- if (isOpen && !container.contains(e.target)) closeDropdown();
10746
- });
10747
- document.addEventListener("keydown", function (e) {
10748
- if (e.key === "Escape" && isOpen) closeDropdown();
10749
- });
10750
- }
10751
-
10752
10973
  // Public API
10753
10974
  container.updateValues = function (newValues) {
10754
10975
  values = Array.isArray(newValues) ? newValues.slice() : [];
@@ -10790,7 +11011,7 @@
10790
11011
  );
10791
11012
  updateClearButton();
10792
11013
  var isVisible = popover && popover.element && popover.element.classList.contains("visible");
10793
- if (disabled && (isVisible || isOpen)) closeDropdown();
11014
+ if (disabled && isVisible) closeDropdown();
10794
11015
  };
10795
11016
 
10796
11017
  container.reload = function () {
@@ -10826,7 +11047,7 @@
10826
11047
 
10827
11048
 
10828
11049
  // ============================================
10829
- // File 20/37: components/record-multiselect.js
11050
+ // File 20/41: components/record-multiselect.js
10830
11051
  // ============================================
10831
11052
 
10832
11053
  /**
@@ -10840,7 +11061,14 @@
10840
11061
 
10841
11062
  (function (global) {
10842
11063
 
10843
- var Popover = global.Popover;
11064
+ function getDep(name) {
11065
+ if (global.FlowUI && typeof global.FlowUI._getComponent === "function") {
11066
+ var c = global.FlowUI._getComponent(name);
11067
+ if (c) return c;
11068
+ }
11069
+ return global[name];
11070
+ }
11071
+
10844
11072
  var InputComponent = global.InputComponent;
10845
11073
  var Spinner = global.Spinner;
10846
11074
 
@@ -10853,7 +11081,7 @@
10853
11081
  role: { iconStr: "IconShield", color: "info" },
10854
11082
  iframe: { iconStr: "IconLayout", color: "neutral" },
10855
11083
  lead: { iconStr: "IconUser", color: "primary" },
10856
- opportunity: { iconStr: "IconCurrencyDollar", color: "success" },
11084
+ opportunity: { iconStr: "IconStar", color: "success" },
10857
11085
  call_log: { iconStr: "IconPhone", color: "info" },
10858
11086
  communication: { iconStr: "IconMessage", color: "primary" },
10859
11087
  history_field: { iconStr: "IconClock", color: "neutral" },
@@ -10975,6 +11203,7 @@
10975
11203
  * @param {string} [config.size] - 'default' | 'large' | 'small'
10976
11204
  * @param {number} [config.initialLimit] - Initial fetch limit (default 50)
10977
11205
  * @param {Array<string>} [config.displayFields] - Fields to display as secondary info (e.g. ["email", "phone"])
11206
+ * @param {Object} [config.initialFilter] - Optional filter object to merge with search (e.g. { field: "status", operator: "exact", value: "active" } or { and: [...] })
10978
11207
  * @param {Object} [config.objectSchema] - Optional object type/schema; properties.icon_data { icon?, color? } used for static icon (not used for user; user shows Vivid Avatar)
10979
11208
  * @returns {HTMLElement} Record multiselect container element
10980
11209
  */
@@ -10989,6 +11218,7 @@
10989
11218
  var variant = config.variant || "default";
10990
11219
  var size = config.size || "default";
10991
11220
  var initialLimit = config.initialLimit != null ? config.initialLimit : 50;
11221
+ var initialFilter = config.initialFilter || null; // Can be array, object, or function returning either
10992
11222
  var displayFields = config.displayFields || [];
10993
11223
 
10994
11224
  var disabled = config.disabled === true;
@@ -11013,19 +11243,13 @@
11013
11243
  var selectedRecords = [];
11014
11244
  var allRecords = [];
11015
11245
  var filteredRecords = [];
11016
- var isOpen = false;
11017
11246
  var searchTerm = "";
11018
11247
  var searchDebounceTimer = null;
11019
- var usePopover = Popover && typeof Popover.create === "function";
11020
11248
  var popover = null;
11021
11249
  var hasMoreRecords = true;
11022
11250
  var currentPage = 1;
11023
11251
  var isFetchingMore = false;
11024
11252
  var totalFetched = 0;
11025
- var contentBase = "record-multiselect-content min-w-[8rem] ";
11026
- if (!usePopover) {
11027
- contentBase += "absolute left-0 right-0 z-50 max-h-[30vh] overflow-hidden rounded-4 bg-fill-quarternary-fill-white shadow-default-medium opacity-0 invisible transition-all duration-150 ease-out group-[.open]:opacity-100 group-[.open]:visible ";
11028
- }
11029
11253
 
11030
11254
  // Trigger wrapper + button
11031
11255
  var triggerWrapper = document.createElement("span");
@@ -11120,7 +11344,7 @@
11120
11344
  var content = document.createElement("div");
11121
11345
  content.setAttribute("role", "listbox");
11122
11346
  content.setAttribute("aria-multiselectable", "true");
11123
- content.className = contentBase + (usePopover ? "max-h-[30vh] overflow-hidden flex flex-col" : "top-full mt-1 -translate-y-1 group-[.open]:translate-y-0");
11347
+ content.className = "record-multiselect-content max-h-[45vh] overflow-hidden flex flex-col";
11124
11348
 
11125
11349
  var searchWrap = document.createElement("div");
11126
11350
  searchWrap.className = "p-8 pb-4 border-b-1/2 border-border-primary ";
@@ -11170,7 +11394,7 @@
11170
11394
  content.appendChild(searchWrap);
11171
11395
 
11172
11396
  var optionsList = document.createElement("div");
11173
- optionsList.className = "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-multiselect-options";
11397
+ optionsList.className = "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-multiselect-options";
11174
11398
 
11175
11399
  // Add scroll listener for infinite scroll
11176
11400
  optionsList.addEventListener("scroll", function () {
@@ -11187,57 +11411,52 @@
11187
11411
 
11188
11412
  content.appendChild(optionsList);
11189
11413
 
11190
- if (!usePopover) {
11191
- container.appendChild(content);
11192
- }
11193
-
11194
- if (usePopover) {
11195
- popover = Popover.create({
11196
- trigger: trigger,
11197
- content: content,
11198
- placement: "bottom",
11199
- align: "start",
11200
- closeOnClickOutside: true,
11201
- bodyClassName: "p-0 overflow-hidden",
11202
- panelClassName: "max-h-[30vh] overflow-hidden",
11203
- onOpen: function () {
11204
- if (disabled) {
11205
- popover.hide();
11206
- return;
11207
- }
11208
- document.querySelectorAll(".custom-select.open, .record-select.open, .custom-multiselect.open, .record-multiselect.open").forEach(function (other) {
11209
- if (other !== container) {
11210
- other.classList.remove("open");
11211
- var t = other.querySelector("button, .custom-select-trigger, .record-select-trigger, .multiselect-trigger-wrapper button, .record-multiselect-trigger");
11212
- if (t) t.setAttribute("aria-expanded", "false");
11213
- }
11214
- });
11215
- isOpen = true;
11216
- container.classList.add("open");
11217
- trigger.setAttribute("aria-expanded", "true");
11218
- searchTerm = "";
11219
- if (searchInputWrapper) searchInputWrapper.setValue("");
11220
- else if (searchInputEl) searchInputEl.value = "";
11221
- content.style.minWidth = trigger.offsetWidth + "px";
11222
- loadInitialAndRender();
11223
- setTimeout(function () {
11224
- if (searchInputEl) searchInputEl.focus();
11225
- }, 0);
11226
- },
11227
- onClose: function () {
11228
- isOpen = false;
11229
- container.classList.remove("open");
11230
- trigger.setAttribute("aria-expanded", "false");
11231
- searchTerm = "";
11232
- if (searchInputWrapper) searchInputWrapper.setValue("");
11233
- else if (searchInputEl) searchInputEl.value = "";
11234
- if (searchDebounceTimer) {
11235
- clearTimeout(searchDebounceTimer);
11236
- searchDebounceTimer = null;
11237
- }
11238
- },
11239
- });
11414
+ var Popover = getDep("Popover");
11415
+ if (!Popover || typeof Popover.create !== "function") {
11416
+ throw new Error("RecordMultiSelect requires Popover");
11240
11417
  }
11418
+ popover = Popover.create({
11419
+ trigger: trigger,
11420
+ content: content,
11421
+ placement: "bottom",
11422
+ align: "start",
11423
+ closeOnClickOutside: true,
11424
+ bodyClassName: "p-0 overflow-hidden",
11425
+ panelClassName: "min-w-[var(--trigger-width)] overflow-hidden",
11426
+ onOpen: function () {
11427
+ if (disabled) {
11428
+ popover.hide();
11429
+ return;
11430
+ }
11431
+ container.classList.add("open");
11432
+ trigger.setAttribute("aria-expanded", "true");
11433
+ searchTerm = "";
11434
+ if (searchInputWrapper) searchInputWrapper.setValue("");
11435
+ else if (searchInputEl) searchInputEl.value = "";
11436
+ if (popover.panel) {
11437
+ var triggerWidthPx = trigger.offsetWidth + "px";
11438
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
11439
+ popover.panel.style.minWidth = triggerWidthPx;
11440
+ popover.panel.style.width = triggerWidthPx;
11441
+ }
11442
+ loadInitialAndRender();
11443
+ setTimeout(function () {
11444
+ if (searchInputEl) searchInputEl.focus();
11445
+ }, 0);
11446
+ },
11447
+ onClose: function () {
11448
+ container.classList.remove("open");
11449
+ trigger.setAttribute("aria-expanded", "false");
11450
+ searchTerm = "";
11451
+ if (searchInputWrapper) searchInputWrapper.setValue("");
11452
+ else if (searchInputEl) searchInputEl.value = "";
11453
+ if (searchDebounceTimer) {
11454
+ clearTimeout(searchDebounceTimer);
11455
+ searchDebounceTimer = null;
11456
+ }
11457
+ },
11458
+ });
11459
+ container.popoverInstance = popover;
11241
11460
 
11242
11461
  function isSelected(recordId) {
11243
11462
  return values.some(function (v) {
@@ -11297,14 +11516,23 @@
11297
11516
  try {
11298
11517
  if (model && typeof model.select === "function") {
11299
11518
  var q = model.select.apply(model, fields);
11519
+ var filters = [];
11520
+ var resolvedFilter = typeof initialFilter === 'function' ? initialFilter() : initialFilter;
11521
+ console.log('[RecordMultiselect] initialFilter:', resolvedFilter, '| search:', search);
11522
+ if (resolvedFilter) {
11523
+ filters = filters.concat(Array.isArray(resolvedFilter) ? resolvedFilter : [resolvedFilter]);
11524
+ }
11300
11525
  if (search && search.trim()) {
11301
- q = q.filterBy({
11526
+ filters.push({
11302
11527
  or: [
11303
11528
  { field: "name", operator: "contains", value: search.trim() },
11304
11529
  { field: "id", operator: "eq", value: search.trim() },
11305
11530
  ],
11306
11531
  });
11307
11532
  }
11533
+ if (filters.length > 0) {
11534
+ q = q.filterBy(filters.length === 1 ? filters[0] : { and: filters });
11535
+ }
11308
11536
  var orderBy = ["name"];
11309
11537
  if (objectSlug === "account") orderBy.push("-ParentId");
11310
11538
  return q
@@ -11563,6 +11791,16 @@
11563
11791
  }
11564
11792
  }
11565
11793
 
11794
+ function scheduleUpdatePosition() {
11795
+ if (popover && typeof popover.updatePosition === "function") {
11796
+ requestAnimationFrame(function () {
11797
+ requestAnimationFrame(function () {
11798
+ popover.updatePosition();
11799
+ });
11800
+ });
11801
+ }
11802
+ }
11803
+
11566
11804
  function loadInitialAndRender() {
11567
11805
  showLoading();
11568
11806
  currentPage = 1;
@@ -11581,9 +11819,11 @@
11581
11819
  } else {
11582
11820
  renderOptions();
11583
11821
  }
11822
+ scheduleUpdatePosition();
11584
11823
  }).catch(function () {
11585
11824
  showEmpty("Failed to load records");
11586
11825
  hasMoreRecords = false;
11826
+ scheduleUpdatePosition();
11587
11827
  });
11588
11828
  }
11589
11829
 
@@ -11635,73 +11875,15 @@
11635
11875
  } else {
11636
11876
  renderOptions();
11637
11877
  }
11878
+ scheduleUpdatePosition();
11638
11879
  }).catch(function () {
11639
11880
  showEmpty("Search failed");
11640
11881
  hasMoreRecords = false;
11882
+ scheduleUpdatePosition();
11641
11883
  });
11642
11884
  }, 500);
11643
11885
  }
11644
11886
 
11645
- function openDropdown() {
11646
- if (disabled) return;
11647
- if (usePopover && popover) {
11648
- popover.show();
11649
- return;
11650
- }
11651
- document
11652
- .querySelectorAll(".custom-select.open, .record-select.open, .custom-multiselect.open, .record-multiselect.open")
11653
- .forEach(function (other) {
11654
- if (other !== container) {
11655
- other.classList.remove("open");
11656
- var t = other.querySelector(
11657
- "button, .custom-select-trigger, .record-select-trigger, .multiselect-trigger-wrapper button, .record-multiselect-trigger"
11658
- );
11659
- if (t) t.setAttribute("aria-expanded", "false");
11660
- }
11661
- });
11662
- isOpen = true;
11663
- container.classList.add("open");
11664
- trigger.setAttribute("aria-expanded", "true");
11665
- searchTerm = "";
11666
- if (searchInputWrapper) searchInputWrapper.setValue("");
11667
- else if (searchInputEl) searchInputEl.value = "";
11668
- loadInitialAndRender();
11669
- setTimeout(function () {
11670
- if (searchInputEl) searchInputEl.focus();
11671
- }, 0);
11672
- updatePosition();
11673
- }
11674
-
11675
- function closeDropdown() {
11676
- if (usePopover && popover) {
11677
- popover.hide();
11678
- return;
11679
- }
11680
- isOpen = false;
11681
- container.classList.remove("open");
11682
- trigger.setAttribute("aria-expanded", "false");
11683
- searchTerm = "";
11684
- if (searchInputWrapper) searchInputWrapper.setValue("");
11685
- else if (searchInputEl) searchInputEl.value = "";
11686
- if (searchDebounceTimer) {
11687
- clearTimeout(searchDebounceTimer);
11688
- searchDebounceTimer = null;
11689
- }
11690
- }
11691
-
11692
- function updatePosition() {
11693
- if (usePopover) return;
11694
- var rect = trigger.getBoundingClientRect();
11695
- var vh = window.innerHeight;
11696
- var below = vh - rect.bottom;
11697
- var above = rect.top;
11698
- if (below < 200 && above > below) {
11699
- content.className = contentBase + "bottom-full mb-1 translate-y-1 group-[.open]:translate-y-0";
11700
- } else {
11701
- content.className = contentBase + "top-full mt-1 -translate-y-1 group-[.open]:translate-y-0";
11702
- }
11703
- }
11704
-
11705
11887
  if (!searchInputWrapper && searchInputEl) {
11706
11888
  searchInputEl.addEventListener("input", function () {
11707
11889
  searchTerm = this.value.trim();
@@ -11718,41 +11900,19 @@
11718
11900
  });
11719
11901
  }
11720
11902
 
11721
- if (usePopover && popover) {
11722
- trigger.addEventListener("keydown", function (e) {
11723
- if (disabled) return;
11724
- if (e.key === "Enter" || e.key === " ") {
11725
- e.preventDefault();
11726
- if (isOpen) popover.hide();
11727
- else popover.show();
11728
- }
11729
- });
11730
- } else {
11731
- trigger.addEventListener("click", function (e) {
11903
+ trigger.addEventListener("keydown", function (e) {
11904
+ if (disabled) return;
11905
+ var isVisible = popover.element && popover.element.classList.contains("visible");
11906
+ if (e.key === "Enter" || e.key === " ") {
11732
11907
  e.preventDefault();
11733
- e.stopPropagation();
11734
- if (isOpen) closeDropdown();
11735
- else openDropdown();
11736
- });
11737
- trigger.addEventListener("keydown", function (e) {
11738
- if (disabled) return;
11739
- if (e.key === "Enter" || e.key === " ") {
11740
- e.preventDefault();
11741
- if (isOpen) closeDropdown();
11742
- else openDropdown();
11743
- }
11744
- if (e.key === "Escape" && isOpen) {
11745
- e.preventDefault();
11746
- closeDropdown();
11747
- }
11748
- });
11749
- document.addEventListener("click", function (e) {
11750
- if (isOpen && !container.contains(e.target)) closeDropdown();
11751
- });
11752
- document.addEventListener("keydown", function (e) {
11753
- if (e.key === "Escape" && isOpen) closeDropdown();
11754
- });
11755
- }
11908
+ if (isVisible) popover.hide();
11909
+ else popover.show();
11910
+ }
11911
+ if (e.key === "Escape" && isVisible) {
11912
+ e.preventDefault();
11913
+ popover.hide();
11914
+ }
11915
+ });
11756
11916
 
11757
11917
  container.updateValues = function (newValues) {
11758
11918
  values = Array.isArray(newValues) ? newValues.slice() : [];
@@ -11763,7 +11923,8 @@
11763
11923
  disabled = !!isDisabled;
11764
11924
  trigger.disabled = disabled;
11765
11925
  trigger.className = triggerClasses(variant, size, disabled, values.length === 0);
11766
- if (disabled && isOpen) closeDropdown();
11926
+ var isVisible = popover.element && popover.element.classList.contains("visible");
11927
+ if (disabled && isVisible) popover.hide();
11767
11928
  };
11768
11929
 
11769
11930
  container.getValues = function () {
@@ -11788,7 +11949,7 @@
11788
11949
 
11789
11950
 
11790
11951
  // ============================================
11791
- // File 21/37: components/input.js
11952
+ // File 21/41: components/input.js
11792
11953
  // ============================================
11793
11954
 
11794
11955
  /**
@@ -11815,7 +11976,7 @@
11815
11976
 
11816
11977
  var WRAPPER_CLASS = {
11817
11978
  base:
11818
- "group flex items-center border-1/2 border-border-primary rounded-4 text-typography-primary-text gap-x-8 w-full transition-all ease-in-out",
11979
+ "group flex items-center border-1/2 border-border-primary rounded-4 text-typography-primary-text gap-x-8 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 group-has-[:disabled]:hover:border-border-primary group-has-[:disabled]:[&_input]:cursor-not-allowed group-has-[:disabled]:[&_input]:text-typography-quaternary-text",
11819
11980
  default:
11820
11981
  "bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
11821
11982
  error:
@@ -11890,13 +12051,16 @@
11890
12051
  var isPassword = type === "password";
11891
12052
 
11892
12053
  var wrapper = document.createElement("div");
11893
- wrapper.className = join(
11894
- WRAPPER_CLASS.base,
11895
- WRAPPER_CLASS[variant] || WRAPPER_CLASS.default,
11896
- inputSize === "large" ? WRAPPER_CLASS.sizeLarge : inputSize === "small" ? WRAPPER_CLASS.sizeSmall : WRAPPER_CLASS.sizeDefault,
11897
- disabled ? WRAPPER_CLASS.disabled : "",
11898
- config.className || ""
11899
- );
12054
+ var sizeClass = inputSize === "large" ? WRAPPER_CLASS.sizeLarge : inputSize === "small" ? WRAPPER_CLASS.sizeSmall : WRAPPER_CLASS.sizeDefault;
12055
+ function applyWrapperClasses() {
12056
+ wrapper.className = join(
12057
+ WRAPPER_CLASS.base,
12058
+ disabled ? WRAPPER_CLASS.disabled : (WRAPPER_CLASS[variant] || WRAPPER_CLASS.default),
12059
+ sizeClass,
12060
+ config.className || ""
12061
+ );
12062
+ }
12063
+ applyWrapperClasses();
11900
12064
  wrapper.setAttribute("data-input-variant", variant);
11901
12065
 
11902
12066
  if (config.prefixNode) {
@@ -12026,19 +12190,12 @@
12026
12190
  wrapper.setVariant = function (v) {
12027
12191
  variant = v;
12028
12192
  wrapper.setAttribute("data-input-variant", v);
12029
- wrapper.className = join(
12030
- WRAPPER_CLASS.base,
12031
- WRAPPER_CLASS[variant] || WRAPPER_CLASS.default,
12032
- inputSize === "large" ? WRAPPER_CLASS.sizeLarge : inputSize === "small" ? WRAPPER_CLASS.sizeSmall : WRAPPER_CLASS.sizeDefault,
12033
- disabled ? WRAPPER_CLASS.disabled : "",
12034
- config.className || ""
12035
- );
12193
+ applyWrapperClasses();
12036
12194
  };
12037
12195
  wrapper.setDisabled = function (d) {
12038
12196
  disabled = !!d;
12039
12197
  input.disabled = disabled;
12040
- wrapper.classList.toggle("cursor-not-allowed", disabled);
12041
- wrapper.classList.toggle("opacity-60", disabled);
12198
+ applyWrapperClasses();
12042
12199
  };
12043
12200
 
12044
12201
  return wrapper;
@@ -12053,7 +12210,7 @@
12053
12210
 
12054
12211
 
12055
12212
  // ============================================
12056
- // File 22/37: components/currency.js
12213
+ // File 22/41: components/currency.js
12057
12214
  // ============================================
12058
12215
 
12059
12216
  /**
@@ -12083,7 +12240,7 @@
12083
12240
  sizeLarge: "",
12084
12241
  sizeSmall: "",
12085
12242
  disabled:
12086
- "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
12243
+ "pointer-events-none cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
12087
12244
  };
12088
12245
 
12089
12246
  // Currency type label: fit-content, separator (border-r) on type only, full height
@@ -12270,7 +12427,12 @@
12270
12427
  wrapper.setDisabled = function (d) {
12271
12428
  disabled = !!d;
12272
12429
  input.disabled = disabled;
12273
- wrapper.classList.toggle("cursor-not-allowed", disabled);
12430
+ wrapper.className = join(
12431
+ WRAPPER_CLASS.base,
12432
+ WRAPPER_CLASS[variant] != null ? WRAPPER_CLASS[variant] : WRAPPER_CLASS.default,
12433
+ disabled ? WRAPPER_CLASS.disabled : "",
12434
+ config.className || ""
12435
+ );
12274
12436
  };
12275
12437
 
12276
12438
  return wrapper;
@@ -12286,7 +12448,7 @@
12286
12448
 
12287
12449
 
12288
12450
  // ============================================
12289
- // File 23/37: components/textarea.js
12451
+ // File 23/41: components/textarea.js
12290
12452
  // ============================================
12291
12453
 
12292
12454
  /**
@@ -12311,7 +12473,7 @@
12311
12473
  warning:
12312
12474
  "min-h-[80px] border-warning-base hover:border-warning-base focus:border-warning-base",
12313
12475
  disabled:
12314
- "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
12476
+ "pointer-events-none cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
12315
12477
  };
12316
12478
 
12317
12479
  function join() {
@@ -12338,67 +12500,407 @@
12338
12500
  * @returns {HTMLTextAreaElement} textarea element (with getValue/setValue/setVariant/setDisabled attached)
12339
12501
  */
12340
12502
  function create(config) {
12341
- var variant = config.variant || "default";
12503
+ var variant = config.variant || "default";
12504
+ var disabled = !!config.disabled;
12505
+
12506
+ var textarea = document.createElement("textarea");
12507
+ textarea.autocomplete = "off";
12508
+ if (config.placeholder != null) textarea.placeholder = config.placeholder;
12509
+ if (config.value != null) textarea.value = config.value;
12510
+ if (config.name) textarea.name = config.name;
12511
+ if (config.id) textarea.id = config.id;
12512
+ if (config.rows != null) textarea.rows = config.rows;
12513
+ if (config.maxLength != null) textarea.maxLength = config.maxLength;
12514
+ textarea.disabled = disabled;
12515
+ textarea.readOnly = !!config.readOnly;
12516
+
12517
+ textarea.className = join(
12518
+ TEXTAREA_CLASS.base,
12519
+ TEXTAREA_CLASS[variant] || TEXTAREA_CLASS.default,
12520
+ disabled ? TEXTAREA_CLASS.disabled : "",
12521
+ config.className || ""
12522
+ );
12523
+ textarea.setAttribute("data-textarea-variant", variant);
12524
+
12525
+ if (config.onChange) textarea.addEventListener("change", config.onChange);
12526
+ if (config.onInput) textarea.addEventListener("input", config.onInput);
12527
+ if (config.onFocus) textarea.addEventListener("focus", config.onFocus);
12528
+ if (config.onBlur) textarea.addEventListener("blur", config.onBlur);
12529
+
12530
+ textarea.getInput = function () {
12531
+ return textarea;
12532
+ };
12533
+ textarea.setValue = function (v) {
12534
+ textarea.value = v;
12535
+ };
12536
+ textarea.getValue = function () {
12537
+ return textarea.value;
12538
+ };
12539
+ textarea.setVariant = function (v) {
12540
+ variant = v;
12541
+ textarea.setAttribute("data-textarea-variant", v);
12542
+ textarea.className = join(
12543
+ TEXTAREA_CLASS.base,
12544
+ TEXTAREA_CLASS[variant] || TEXTAREA_CLASS.default,
12545
+ disabled ? TEXTAREA_CLASS.disabled : "",
12546
+ config.className || ""
12547
+ );
12548
+ };
12549
+ textarea.setDisabled = function (d) {
12550
+ disabled = !!d;
12551
+ textarea.disabled = disabled;
12552
+ textarea.className = join(
12553
+ TEXTAREA_CLASS.base,
12554
+ TEXTAREA_CLASS[variant] || TEXTAREA_CLASS.default,
12555
+ disabled ? TEXTAREA_CLASS.disabled : "",
12556
+ config.className || ""
12557
+ );
12558
+ };
12559
+
12560
+ return textarea;
12561
+ }
12562
+
12563
+ global.TextareaComponent = {
12564
+ create: create,
12565
+ };
12566
+ })(typeof window !== "undefined" ? window : undefined);
12567
+
12568
+
12569
+
12570
+ // ============================================
12571
+ // File 24/41: components/richtext-editor.js
12572
+ // ============================================
12573
+
12574
+ /**
12575
+ * Rich Text Editor Component (vanilla JS)
12576
+ * Toolbar + contenteditable area with formatting (bold, italic, underline, headings, lists, alignment, link, image, code block, undo/redo).
12577
+ * Styling matches design: rounded-12, toolbar bg-fill-tertiary-fill-light-gray, content area with min-height.
12578
+ */
12579
+
12580
+ (function (global) {
12581
+
12582
+ var RICH_TEXT_CONTENT_STYLES =
12583
+ ".rich-text-editor-content ul{list-style-type:disc;padding-left:1.5em;margin:0.5em 0}" +
12584
+ ".rich-text-editor-content ol{list-style-type:decimal;padding-left:1.5em;margin:0.5em 0}" +
12585
+ ".rich-text-editor-content li{margin:0.25em 0}" +
12586
+ ".rich-text-editor-content li p{margin:0}" +
12587
+ ".rich-text-editor-content h1{font-size:1.5rem;font-weight:700;line-height:1.3;margin:0.75em 0 0.5em}" +
12588
+ ".rich-text-editor-content h2{font-size:1.25rem;font-weight:600;line-height:1.3;margin:0.75em 0 0.5em}" +
12589
+ ".rich-text-editor-content h3{font-size:1.125rem;font-weight:600;line-height:1.3;margin:0.75em 0 0.5em}" +
12590
+ ".rich-text-editor-content p{margin:0.5em 0}" +
12591
+ ".rich-text-editor-content a{color:var(--color-primary-500);text-decoration:underline;cursor:pointer}" +
12592
+ ".rich-text-editor-content pre{background:var(--color-neutral-100);border-radius:var(--sizes-size-8);padding:0.75em 1em;margin:0.5em 0;overflow-x:auto}" +
12593
+ ".rich-text-editor-content code{font-family:ui-monospace,monospace;font-size:0.875em}" +
12594
+ ".rich-text-editor-content img{max-width:100%;height:auto;margin:0.5em 0}" +
12595
+ ".rich-text-editor-content blockquote{border-left:3px solid var(--color-neutral-200);padding-left:1em;margin:0.5em 0;color:var(--color-neutral-600)}" +
12596
+ ".rich-text-editor-content hr{border:none;border-top:1px solid var(--color-neutral-150);margin:1em 0}" +
12597
+ ".rich-text-editor-content .ProseMirror,.rich-text-editor-content [contenteditable]{outline:none}";
12598
+
12599
+ function join() {
12600
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
12601
+ }
12602
+
12603
+ function getDep(name) {
12604
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
12605
+ var c = global.FlowUI._getComponent(name);
12606
+ if (c) return c;
12607
+ }
12608
+ return global[name];
12609
+ }
12610
+
12611
+ /** Get Tabler icon element (16px) from Icon component for toolbar. */
12612
+ function getTablerIcon(iconName) {
12613
+ var Icon = getDep("Icon");
12614
+ if (!Icon || !Icon.iconMap || !Icon.iconMap[iconName]) return null;
12615
+ var svgStr = Icon.iconMap[iconName];
12616
+ var s16 = svgStr.replace(/width="24"/, 'width="16"').replace(/height="24"/, 'height="16"').replace(/width="20"/, 'width="16"').replace(/height="20"/, 'height="16"');
12617
+ var span = document.createElement("span");
12618
+ span.className = "flex items-center justify-center size-16";
12619
+ span.innerHTML = s16;
12620
+ return span;
12621
+ }
12622
+
12623
+ function createToolbarButton(opts) {
12624
+ var Button = getDep("Button");
12625
+ if (!Button || typeof Button.create !== "function") {
12626
+ throw new Error("RichTextEditor requires Button");
12627
+ }
12628
+ var icon = opts.iconStr ? getTablerIcon(opts.iconStr) : null;
12629
+ return Button.create({
12630
+ variant: "outline",
12631
+ size: "default",
12632
+ title: opts.title,
12633
+ icon: icon,
12634
+ onClick: opts.onClick,
12635
+ disabled: opts.disabled,
12636
+ });
12637
+ }
12638
+
12639
+ function createSeparator() {
12640
+ var sep = document.createElement("div");
12641
+ sep.className = "w-px h-16 bg-border-primary mx-4";
12642
+ sep.setAttribute("aria-hidden", "true");
12643
+ return sep;
12644
+ }
12645
+
12646
+ /**
12647
+ * Create a rich text editor
12648
+ * @param {Object} config
12649
+ * @param {string} [config.value] - Initial HTML content
12650
+ * @param {string} [config.placeholder] - Placeholder when empty
12651
+ * @param {number} [config.minHeightPx] - Min height of editor area (default 400)
12652
+ * @param {boolean} [config.disabled]
12653
+ * @param {Function} [config.onChange] - (html: string) => void
12654
+ * @returns {HTMLElement} Wrapper element with getValue/setValue/setDisabled/getInput
12655
+ */
12656
+ function create(config) {
12657
+ var value = config.value != null ? String(config.value) : "";
12658
+ var placeholder = config.placeholder != null ? config.placeholder : "";
12659
+ var minHeightPx = config.minHeightPx != null ? config.minHeightPx : 400;
12342
12660
  var disabled = !!config.disabled;
12661
+ var onChange = config.onChange;
12343
12662
 
12344
- var textarea = document.createElement("textarea");
12345
- textarea.autocomplete = "off";
12346
- if (config.placeholder != null) textarea.placeholder = config.placeholder;
12347
- if (config.value != null) textarea.value = config.value;
12348
- if (config.name) textarea.name = config.name;
12349
- if (config.id) textarea.id = config.id;
12350
- if (config.rows != null) textarea.rows = config.rows;
12351
- if (config.maxLength != null) textarea.maxLength = config.maxLength;
12352
- textarea.disabled = disabled;
12353
- textarea.readOnly = !!config.readOnly;
12663
+ var wrapper = document.createElement("div");
12664
+ wrapper.className = "w-full rounded-12 border border-borderColor-border-primary shadow-soft-2x-small";
12354
12665
 
12355
- textarea.className = join(
12356
- TEXTAREA_CLASS.base,
12357
- TEXTAREA_CLASS[variant] || TEXTAREA_CLASS.default,
12358
- disabled ? TEXTAREA_CLASS.disabled : "",
12359
- config.className || ""
12666
+ var styleEl = document.createElement("style");
12667
+ styleEl.textContent = RICH_TEXT_CONTENT_STYLES;
12668
+ wrapper.appendChild(styleEl);
12669
+
12670
+ var toolbar = document.createElement("div");
12671
+ toolbar.className =
12672
+ "flex flex-wrap gap-4 rounded-t-12 border-borderColor-border-primary bg-fill-tertiary-fill-light-gray p-6";
12673
+ toolbar.setAttribute("role", "toolbar");
12674
+
12675
+ var editorEl = document.createElement("div");
12676
+ editorEl.contentEditable = !disabled;
12677
+ editorEl.className = join(
12678
+ "rich-text-editor-content max-w-none rounded-b-12 border-t border-borderColor-border-primary p-8 text-reg-14 text-typography-primary-text"
12360
12679
  );
12361
- textarea.setAttribute("data-textarea-variant", variant);
12680
+ editorEl.style.minHeight = minHeightPx + "px";
12681
+ if (value) editorEl.innerHTML = value;
12682
+ editorEl.setAttribute("data-placeholder", placeholder);
12362
12683
 
12363
- if (config.onChange) textarea.addEventListener("change", config.onChange);
12364
- if (config.onInput) textarea.addEventListener("input", config.onInput);
12365
- if (config.onFocus) textarea.addEventListener("focus", config.onFocus);
12366
- if (config.onBlur) textarea.addEventListener("blur", config.onBlur);
12684
+ function isEmpty() {
12685
+ var text = (editorEl.textContent || "").trim();
12686
+ if (text) return false;
12687
+ var html = (editorEl.innerHTML || "").replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]+>/g, "");
12688
+ return !html.trim();
12689
+ }
12690
+ function updatePlaceholder() {
12691
+ if (placeholder && isEmpty()) {
12692
+ editorEl.classList.add("empty");
12693
+ editorEl.setAttribute("data-placeholder", placeholder);
12694
+ } else {
12695
+ editorEl.classList.remove("empty");
12696
+ editorEl.removeAttribute("data-placeholder");
12697
+ }
12698
+ }
12699
+ updatePlaceholder();
12367
12700
 
12368
- textarea.getInput = function () {
12369
- return textarea;
12370
- };
12371
- textarea.setValue = function (v) {
12372
- textarea.value = v;
12701
+ function getHtml() {
12702
+ return editorEl.innerHTML;
12703
+ }
12704
+ function setHtml(html) {
12705
+ editorEl.innerHTML = html || "";
12706
+ updatePlaceholder();
12707
+ }
12708
+ function notifyChange() {
12709
+ if (typeof onChange === "function") onChange(getHtml());
12710
+ }
12711
+
12712
+ function isActive(cmd, val) {
12713
+ try {
12714
+ return document.queryCommandState(cmd);
12715
+ } catch (e) {
12716
+ return false;
12717
+ }
12718
+ }
12719
+ function blockTag() {
12720
+ var sel = window.getSelection();
12721
+ if (!sel || sel.rangeCount === 0) return null;
12722
+ var node = sel.anchorNode;
12723
+ while (node && node !== editorEl) {
12724
+ if (node.nodeType === 1) {
12725
+ var n = node.nodeName.toLowerCase();
12726
+ if (["h1", "h2", "h3", "p", "div", "pre", "blockquote"].indexOf(n) !== -1) return n;
12727
+ }
12728
+ node = node.parentNode;
12729
+ }
12730
+ return null;
12731
+ }
12732
+ function isAlignment(align) {
12733
+ try {
12734
+ if (align === "left") return document.queryCommandState("justifyLeft");
12735
+ if (align === "center") return document.queryCommandState("justifyCenter");
12736
+ if (align === "right") return document.queryCommandState("justifyRight");
12737
+ } catch (e) {}
12738
+ return false;
12739
+ }
12740
+
12741
+ function refreshToolbar() {
12742
+ toolbar.querySelectorAll("button").forEach(function (btn) {
12743
+ var cmd = btn.getAttribute("data-command");
12744
+ var val = btn.getAttribute("data-value");
12745
+ if (!cmd) return;
12746
+ var active = false;
12747
+ if (cmd === "formatBlock") active = blockTag() === val;
12748
+ else if (cmd === "justifyLeft" && val === "left") active = isAlignment("left");
12749
+ else if (cmd === "justifyCenter" && val === "center") active = isAlignment("center");
12750
+ else if (cmd === "justifyRight" && val === "right") active = isAlignment("right");
12751
+ else active = isActive(cmd);
12752
+ btn.classList.toggle("bg-primary-base", active);
12753
+ btn.classList.toggle("border-primary-base", active);
12754
+ btn.classList.toggle("text-typography-invert-text", active);
12755
+ btn.classList.toggle("bg-fill-quarternary-fill-white", !active);
12756
+ btn.classList.toggle("border-border-primary", !active);
12757
+ btn.classList.toggle("text-typography-primary-text", !active);
12758
+ });
12759
+ }
12760
+
12761
+ function exec(cmd, value) {
12762
+ editorEl.focus();
12763
+ document.execCommand(cmd, false, value != null ? value : null);
12764
+ refreshToolbar();
12765
+ notifyChange();
12766
+ }
12767
+
12768
+ function insertCodeBlock() {
12769
+ editorEl.focus();
12770
+ var sel = window.getSelection();
12771
+ if (sel && sel.rangeCount) {
12772
+ var range = sel.getRangeAt(0);
12773
+ var pre = document.createElement("pre");
12774
+ var code = document.createElement("code");
12775
+ code.textContent = "code here";
12776
+ pre.appendChild(code);
12777
+ range.insertNode(pre);
12778
+ range.setStart(code, 0);
12779
+ range.setEnd(code, 0);
12780
+ sel.removeAllRanges();
12781
+ sel.addRange(range);
12782
+ }
12783
+ refreshToolbar();
12784
+ notifyChange();
12785
+ }
12786
+
12787
+ function addLink() {
12788
+ var url = window.prompt("Enter the URL:", "https://");
12789
+ if (url) {
12790
+ exec("createLink", url);
12791
+ }
12792
+ }
12793
+
12794
+ function addImage() {
12795
+ var input = document.createElement("input");
12796
+ input.type = "file";
12797
+ input.accept = "image/*";
12798
+ input.onchange = function (e) {
12799
+ var file = e.target && e.target.files && e.target.files[0];
12800
+ if (file) {
12801
+ var reader = new FileReader();
12802
+ reader.onload = function (ev) {
12803
+ var src = ev.target && ev.target.result;
12804
+ if (src) {
12805
+ editorEl.focus();
12806
+ document.execCommand("insertImage", false, src);
12807
+ notifyChange();
12808
+ }
12809
+ };
12810
+ reader.readAsDataURL(file);
12811
+ }
12812
+ };
12813
+ input.click();
12814
+ }
12815
+
12816
+ function undo() {
12817
+ editorEl.focus();
12818
+ document.execCommand("undo", false, null);
12819
+ refreshToolbar();
12820
+ notifyChange();
12821
+ }
12822
+ function redo() {
12823
+ editorEl.focus();
12824
+ document.execCommand("redo", false, null);
12825
+ refreshToolbar();
12826
+ notifyChange();
12827
+ }
12828
+
12829
+ function addBtn(iconStr, title, onClick, dataCommand, dataValue) {
12830
+ var btn = createToolbarButton({
12831
+ iconStr: iconStr,
12832
+ title: title,
12833
+ onClick: function () {
12834
+ if (disabled) return;
12835
+ onClick();
12836
+ }});
12837
+ if (dataCommand) btn.setAttribute("data-command", dataCommand);
12838
+ if (dataValue != null) btn.setAttribute("data-value", dataValue);
12839
+ toolbar.appendChild(btn);
12840
+ }
12841
+
12842
+ addBtn("IconBold", "Bold (Ctrl+B)", function () { exec("bold"); }, "bold");
12843
+ addBtn("IconItalic", "Italic (Ctrl+I)", function () { exec("italic"); }, "italic");
12844
+ addBtn("IconUnderline", "Underline (Ctrl+U)", function () { exec("underline"); }, "underline");
12845
+ toolbar.appendChild(createSeparator());
12846
+ addBtn("IconH1", "Heading 1", function () { exec("formatBlock", "h1"); }, "formatBlock", "h1");
12847
+ addBtn("IconH2", "Heading 2", function () { exec("formatBlock", "h2"); }, "formatBlock", "h2");
12848
+ addBtn("IconH3", "Heading 3", function () { exec("formatBlock", "h3"); }, "formatBlock", "h3");
12849
+ toolbar.appendChild(createSeparator());
12850
+ addBtn("IconList", "Bullet List", function () { exec("insertUnorderedList"); }, "insertUnorderedList");
12851
+ addBtn("IconListNumbers", "Ordered List", function () { exec("insertOrderedList"); }, "insertOrderedList");
12852
+ toolbar.appendChild(createSeparator());
12853
+ addBtn("IconAlignLeft", "Align Left", function () { exec("justifyLeft"); }, "justifyLeft", "left");
12854
+ addBtn("IconAlignCenter", "Align Center", function () { exec("justifyCenter"); }, "justifyCenter", "center");
12855
+ addBtn("IconAlignRight", "Align Right", function () { exec("justifyRight"); }, "justifyRight", "right");
12856
+ toolbar.appendChild(createSeparator());
12857
+ addBtn("IconCode", "Code Block", insertCodeBlock, "formatBlock", "pre");
12858
+ addBtn("IconLink", "Add Link", addLink, "link");
12859
+ addBtn("IconPhoto", "Insert Image", addImage);
12860
+ toolbar.appendChild(createSeparator());
12861
+ addBtn("IconArrowBackUp", "Undo", undo);
12862
+ addBtn("IconArrowForwardUp", "Redo", redo);
12863
+
12864
+ editorEl.addEventListener("input", function () {
12865
+ updatePlaceholder();
12866
+ refreshToolbar();
12867
+ notifyChange();
12868
+ });
12869
+ editorEl.addEventListener("keyup", refreshToolbar);
12870
+ editorEl.addEventListener("mouseup", refreshToolbar);
12871
+ editorEl.addEventListener("focus", refreshToolbar);
12872
+
12873
+ if (placeholder) {
12874
+ var placeholderStyles = document.createElement("style");
12875
+ placeholderStyles.textContent =
12876
+ ".rich-text-editor-content.empty:before{content:attr(data-placeholder);color:var(--color-neutral-400);pointer-events:none}";
12877
+ wrapper.appendChild(placeholderStyles);
12878
+ }
12879
+
12880
+ wrapper.appendChild(toolbar);
12881
+ wrapper.appendChild(editorEl);
12882
+
12883
+ wrapper.getInput = function () {
12884
+ return editorEl;
12373
12885
  };
12374
- textarea.getValue = function () {
12375
- return textarea.value;
12886
+ wrapper.getValue = function () {
12887
+ return getHtml();
12376
12888
  };
12377
- textarea.setVariant = function (v) {
12378
- variant = v;
12379
- textarea.setAttribute("data-textarea-variant", v);
12380
- textarea.className = join(
12381
- TEXTAREA_CLASS.base,
12382
- TEXTAREA_CLASS[variant] || TEXTAREA_CLASS.default,
12383
- disabled ? TEXTAREA_CLASS.disabled : "",
12384
- config.className || ""
12385
- );
12889
+ wrapper.setValue = function (v) {
12890
+ setHtml(v);
12386
12891
  };
12387
- textarea.setDisabled = function (d) {
12892
+ wrapper.setDisabled = function (d) {
12388
12893
  disabled = !!d;
12389
- textarea.disabled = disabled;
12390
- textarea.className = join(
12391
- TEXTAREA_CLASS.base,
12392
- TEXTAREA_CLASS[variant] || TEXTAREA_CLASS.default,
12393
- disabled ? TEXTAREA_CLASS.disabled : "",
12394
- config.className || ""
12395
- );
12894
+ editorEl.contentEditable = !disabled;
12895
+ toolbar.querySelectorAll("button").forEach(function (b) {
12896
+ b.disabled = disabled;
12897
+ });
12396
12898
  };
12397
12899
 
12398
- return textarea;
12900
+ return wrapper;
12399
12901
  }
12400
12902
 
12401
- global.TextareaComponent = {
12903
+ global.RichTextEditorComponent = {
12402
12904
  create: create,
12403
12905
  };
12404
12906
  })(typeof window !== "undefined" ? window : undefined);
@@ -12406,7 +12908,7 @@
12406
12908
 
12407
12909
 
12408
12910
  // ============================================
12409
- // File 24/37: components/checkbox.js
12911
+ // File 25/41: components/checkbox.js
12410
12912
  // ============================================
12411
12913
 
12412
12914
  /**
@@ -12445,10 +12947,18 @@
12445
12947
  };
12446
12948
 
12447
12949
  var CHECKBOX_BASE_CLASS =
12448
- "flex items-center justify-center rounded-2 border-1/2 border-borderColor-border-primary bg-fill-quarternary-fill-white p-4 transition-all hover:border-primary-base hover:shadow-primary-focused disabled:cursor-not-allowed disabled:border-borderColor-border-primary disabled:opacity-50 disabled:hover:shadow-none";
12950
+ "flex items-center justify-center rounded-2 border-1/2 border-borderColor-border-primary bg-fill-quarternary-fill-white p-4 transition-all";
12951
+
12952
+ var CHECKBOX_ACTIVE_CLASS =
12953
+ "hover:border-primary-base hover:shadow-primary-focused cursor-pointer";
12954
+
12955
+ var CHECKBOX_DISABLED_CLASS =
12956
+ "cursor-not-allowed opacity-50";
12449
12957
 
12450
12958
  var CHECKBOX_CHECKED_CLASS =
12451
- "data-checked:border-transparent data-checked:bg-primary-base data-checked:hover:border-primary-base data-checked:hover:shadow-primary-focused data-checked:disabled:border-borderColor-border-primary";
12959
+ "data-checked:border-transparent data-checked:bg-primary-base";
12960
+ var CHECKBOX_CHECKED_ACTIVE_CLASS =
12961
+ "data-checked:hover:border-primary-base data-checked:hover:shadow-primary-focused";
12452
12962
 
12453
12963
  var LABEL_BASE_CLASS =
12454
12964
  "cursor-pointer pb-0 text-reg-12 leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70";
@@ -12512,12 +13022,16 @@
12512
13022
 
12513
13023
  // Custom checkbox visual
12514
13024
  var checkboxBox = document.createElement("div");
12515
- checkboxBox.className = join(
12516
- CHECKBOX_BASE_CLASS,
12517
- CHECKBOX_CHECKED_CLASS,
12518
- CHECKBOX_SIZES[size] || CHECKBOX_SIZES.default,
12519
- "cursor-pointer"
12520
- );
13025
+ function applyCheckboxBoxClasses() {
13026
+ var sizeClass = CHECKBOX_SIZES[size] || CHECKBOX_SIZES.default;
13027
+ checkboxBox.className = join(
13028
+ CHECKBOX_BASE_CLASS,
13029
+ disabled ? CHECKBOX_DISABLED_CLASS : join(CHECKBOX_ACTIVE_CLASS, CHECKBOX_CHECKED_ACTIVE_CLASS),
13030
+ CHECKBOX_CHECKED_CLASS,
13031
+ sizeClass
13032
+ );
13033
+ }
13034
+ applyCheckboxBoxClasses();
12521
13035
  checkboxBox.setAttribute("role", "checkbox");
12522
13036
  checkboxBox.setAttribute("tabindex", disabled ? "-1" : "0");
12523
13037
  checkboxBox.setAttribute("aria-checked", indeterminate ? "mixed" : checked ? "true" : "false");
@@ -12634,6 +13148,7 @@
12634
13148
  } else {
12635
13149
  checkboxBox.removeAttribute("aria-disabled");
12636
13150
  }
13151
+ applyCheckboxBoxClasses();
12637
13152
  updateCheckedState();
12638
13153
  };
12639
13154
 
@@ -12666,7 +13181,210 @@
12666
13181
 
12667
13182
 
12668
13183
  // ============================================
12669
- // File 25/37: components/radio-group.js
13184
+ // File 26/41: components/checkbox-group.js
13185
+ // ============================================
13186
+
13187
+ /**
13188
+ * CheckboxGroup Component (vanilla JS)
13189
+ * Multi-select via checkboxes; same API as MultiSelect (options, value array, onValuesChange).
13190
+ * Uses input.js-style variants and sizes for the group wrapper.
13191
+ */
13192
+
13193
+ (function (global) {
13194
+
13195
+ // Wrapper classes aligned with input.js variants
13196
+ var WRAPPER_CLASS = {
13197
+ base:
13198
+ "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",
13199
+ default:
13200
+ "bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
13201
+ error:
13202
+ "border-error-base bg-fill-quarternary-fill-white hover:border-error-base focus-within:border-error-base",
13203
+ warning:
13204
+ "border-warning-base bg-fill-quarternary-fill-white hover:border-warning-base focus-within:border-warning-base",
13205
+ success:
13206
+ "border-success-base bg-fill-quarternary-fill-white hover:border-success-base focus-within:border-success-base",
13207
+ borderless:
13208
+ "border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
13209
+ inline:
13210
+ "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",
13211
+ sizeDefault: "px-12 py-6 gap-6",
13212
+ sizeLarge: "px-12 py-8 gap-8",
13213
+ sizeSmall: "px-12 py-4 gap-4",
13214
+ disabled:
13215
+ "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
13216
+ };
13217
+
13218
+ function join() {
13219
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
13220
+ }
13221
+
13222
+ function getOptionValue(opt) {
13223
+ return opt.value !== undefined && opt.value !== null
13224
+ ? opt.value
13225
+ : opt.slug || opt.id;
13226
+ }
13227
+
13228
+ function getOptionLabel(opt) {
13229
+ return opt.label || opt.name || opt.display_name || opt.value;
13230
+ }
13231
+
13232
+ function getDep(name) {
13233
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
13234
+ var c = global.FlowUI._getComponent(name);
13235
+ if (c) return c;
13236
+ }
13237
+ return global[name];
13238
+ }
13239
+
13240
+ /**
13241
+ * Create a checkbox group component (multiselect-like: multiple values, options array)
13242
+ * @param {Object} config
13243
+ * @param {string} [config.fieldId] - Field ID for state management
13244
+ * @param {Array} config.options - Array of { value, label } or { slug, display_name }
13245
+ * @param {Array} [config.value] - Current selected values (array)
13246
+ * @param {Function} config.onValuesChange - Change handler (values: string[])
13247
+ * @param {boolean} [config.disabled] - Whether all checkboxes are disabled
13248
+ * @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'success' | 'borderless' | 'inline'
13249
+ * @param {string} [config.size] - 'default' | 'large' | 'small'
13250
+ * @param {string} [config.layout] - 'vertical' | 'horizontal'
13251
+ * @param {string} [config.className] - Extra class on wrapper
13252
+ * @returns {HTMLElement} CheckboxGroup container element
13253
+ */
13254
+ function createCheckboxGroup(config) {
13255
+ var fieldId = config.fieldId;
13256
+ var options = config.options || [];
13257
+ var onValuesChange = config.onValuesChange;
13258
+ var variant = config.variant || "default";
13259
+ var size = config.size || "default";
13260
+ var disabled = config.disabled === true;
13261
+ var layout = config.layout || "vertical";
13262
+ var className = config.className || "";
13263
+
13264
+ var values = Array.isArray(config.value)
13265
+ ? config.value.slice()
13266
+ : Array.isArray(config.values)
13267
+ ? config.values.slice()
13268
+ : [];
13269
+
13270
+ var Checkbox = getDep("Checkbox");
13271
+ if (!Checkbox || typeof Checkbox.create !== "function") {
13272
+ throw new Error("CheckboxGroup requires the Checkbox component. Load checkbox.js before checkbox-group.js.");
13273
+ }
13274
+
13275
+ var container = document.createElement("div");
13276
+ container.setAttribute("role", "group");
13277
+ container.setAttribute("aria-label", config.ariaLabel || "Checkbox group");
13278
+ if (fieldId) container.setAttribute("data-field-id", fieldId);
13279
+
13280
+ var sizeClass = size === "large" ? WRAPPER_CLASS.sizeLarge : size === "small" ? WRAPPER_CLASS.sizeSmall : WRAPPER_CLASS.sizeDefault;
13281
+ function applyWrapperClasses() {
13282
+ container.className = join(
13283
+ WRAPPER_CLASS.base,
13284
+ disabled ? WRAPPER_CLASS.disabled : (WRAPPER_CLASS[variant] || WRAPPER_CLASS.default),
13285
+ sizeClass,
13286
+ layout === "horizontal" ? "flex-row flex-wrap" : "flex-col",
13287
+ "custom-checkbox-group",
13288
+ className
13289
+ );
13290
+ }
13291
+ applyWrapperClasses();
13292
+ container.setAttribute("data-checkbox-group-variant", variant);
13293
+
13294
+ function isSelected(optionValue) {
13295
+ return values.some(function (v) {
13296
+ return v === optionValue || String(v) === String(optionValue);
13297
+ });
13298
+ }
13299
+
13300
+ var optionsContainer = document.createElement("div");
13301
+ optionsContainer.className = join(
13302
+ "flex gap-8",
13303
+ layout === "horizontal" ? "flex-row flex-wrap" : "flex-col"
13304
+ );
13305
+
13306
+ function buildOptions() {
13307
+ optionsContainer.innerHTML = "";
13308
+ if (options.length === 0) {
13309
+ var empty = document.createElement("div");
13310
+ empty.className = "!text-reg-13 text-typography-quaternary-text py-4";
13311
+ empty.textContent = "No options available";
13312
+ optionsContainer.appendChild(empty);
13313
+ return;
13314
+ }
13315
+
13316
+ options.forEach(function (opt, index) {
13317
+ var optionValue = getOptionValue(opt);
13318
+ var optionLabel = getOptionLabel(opt);
13319
+ var optionDisabled = disabled || !!opt.disabled;
13320
+ var checked = isSelected(optionValue);
13321
+
13322
+ var cb = Checkbox.create({
13323
+ id: (fieldId || "cbg") + "-" + index,
13324
+ name: fieldId ? fieldId + "[]" : "checkbox-group-" + index,
13325
+ checked: checked,
13326
+ disabled: optionDisabled,
13327
+ label: optionLabel,
13328
+ align: "left",
13329
+ size: size === "large" ? "large" : size === "small" ? "small" : "default",
13330
+ onChange: function (isChecked) {
13331
+ if (optionDisabled) return;
13332
+ if (isChecked) {
13333
+ if (!values.some(function (v) { return v === optionValue || String(v) === String(optionValue); })) {
13334
+ values.push(optionValue);
13335
+ }
13336
+ } else {
13337
+ var idx = values.findIndex(function (v) {
13338
+ return v === optionValue || String(v) === String(optionValue);
13339
+ });
13340
+ if (idx >= 0) values.splice(idx, 1);
13341
+ }
13342
+ if (onValuesChange) onValuesChange(values.slice());
13343
+ },
13344
+ });
13345
+ optionsContainer.appendChild(cb);
13346
+ });
13347
+ }
13348
+
13349
+ buildOptions();
13350
+ container.appendChild(optionsContainer);
13351
+
13352
+ container.updateValues = function (newValues) {
13353
+ values = Array.isArray(newValues) ? newValues.slice() : [];
13354
+ buildOptions();
13355
+ };
13356
+
13357
+ container.setDisabled = function (isDisabled) {
13358
+ disabled = !!isDisabled;
13359
+ applyWrapperClasses();
13360
+ var wrappers = optionsContainer.querySelectorAll(":scope > div");
13361
+ for (var i = 0; i < wrappers.length; i++) {
13362
+ if (typeof wrappers[i].setDisabled === "function") wrappers[i].setDisabled(disabled);
13363
+ }
13364
+ };
13365
+
13366
+ container.setVariant = function (v) {
13367
+ variant = v;
13368
+ container.setAttribute("data-checkbox-group-variant", v);
13369
+ applyWrapperClasses();
13370
+ };
13371
+
13372
+ container.getValues = function () {
13373
+ return values.slice();
13374
+ };
13375
+
13376
+ return container;
13377
+ }
13378
+
13379
+ global.CheckboxGroup = {
13380
+ create: createCheckboxGroup,
13381
+ };
13382
+ })(typeof window !== "undefined" ? window : undefined);
13383
+
13384
+
13385
+
13386
+ // ============================================
13387
+ // File 27/41: components/radio-group.js
12670
13388
  // ============================================
12671
13389
 
12672
13390
  /**
@@ -13077,7 +13795,7 @@
13077
13795
 
13078
13796
 
13079
13797
  // ============================================
13080
- // File 26/37: components/enumeration.js
13798
+ // File 28/41: components/enumeration.js
13081
13799
  // ============================================
13082
13800
 
13083
13801
  /**
@@ -13089,7 +13807,7 @@
13089
13807
  (function (global) {
13090
13808
 
13091
13809
  var BASE_CLASS =
13092
- "flex items-center border rounded-4 text-typography-primary-text gap-4 !text-reg-13";
13810
+ "flex items-center border-1/2 rounded-4 text-typography-primary-text gap-4 !text-reg-13";
13093
13811
 
13094
13812
  var VARIANTS = {
13095
13813
  default:
@@ -13113,7 +13831,7 @@
13113
13831
  };
13114
13832
 
13115
13833
  var DISABLED_CLASS =
13116
- "pointer-events-none !cursor-not-allowed opacity-50";
13834
+ "pointer-events-none !cursor-not-allowed opacity-50 bg-fill-tertiary-fill-light-gray";
13117
13835
  var READONLY_CLASS = "pointer-events-none";
13118
13836
 
13119
13837
  var ITEM_BASE_CLASS =
@@ -13178,14 +13896,17 @@
13178
13896
  var children = opts.children;
13179
13897
 
13180
13898
  var wrapper = document.createElement("div");
13181
- wrapper.className = join(
13182
- BASE_CLASS,
13183
- VARIANTS[variant] != null ? VARIANTS[variant] : VARIANTS.default,
13184
- SIZES[size] != null ? SIZES[size] : SIZES.default,
13185
- disabled ? DISABLED_CLASS : "",
13186
- readOnly ? READONLY_CLASS : "",
13187
- className
13188
- );
13899
+ function applyWrapperClasses() {
13900
+ wrapper.className = join(
13901
+ BASE_CLASS,
13902
+ VARIANTS[variant] != null ? VARIANTS[variant] : VARIANTS.default,
13903
+ SIZES[size] != null ? SIZES[size] : SIZES.default,
13904
+ disabled ? DISABLED_CLASS : "",
13905
+ readOnly ? READONLY_CLASS : "",
13906
+ className
13907
+ );
13908
+ }
13909
+ applyWrapperClasses();
13189
13910
  wrapper.setAttribute("data-enumeration-variant", variant);
13190
13911
 
13191
13912
  var count =
@@ -13255,6 +13976,22 @@
13255
13976
  }
13256
13977
  }
13257
13978
 
13979
+ wrapper.setDisabled = function (d) {
13980
+ disabled = d === true;
13981
+ for (var j = 0; j < itemElements.length; j++) {
13982
+ itemElements[j].setAttribute("tabindex", disabled || readOnly ? "-1" : "0");
13983
+ }
13984
+ applyWrapperClasses();
13985
+ };
13986
+
13987
+ wrapper.setReadOnly = function (r) {
13988
+ readOnly = r === true;
13989
+ for (var j = 0; j < itemElements.length; j++) {
13990
+ itemElements[j].setAttribute("tabindex", disabled || readOnly ? "-1" : "0");
13991
+ }
13992
+ applyWrapperClasses();
13993
+ };
13994
+
13258
13995
  return wrapper;
13259
13996
  }
13260
13997
 
@@ -13296,7 +14033,7 @@
13296
14033
 
13297
14034
 
13298
14035
  // ============================================
13299
- // File 27/37: components/time-picker.js
14036
+ // File 29/41: components/time-picker.js
13300
14037
  // ============================================
13301
14038
 
13302
14039
  /**
@@ -13596,12 +14333,12 @@
13596
14333
  if (periodColumn) periodColumn.scrollToSelected();
13597
14334
  }
13598
14335
 
14336
+ // Trigger must be in DOM before Popover.create() so Popover can wrap it and insert its panel into the document.
14337
+ container.appendChild(trigger);
14338
+
13599
14339
  var Popover = getDep("Popover");
13600
14340
  if (!Popover || typeof Popover.create !== "function") {
13601
- container.appendChild(trigger);
13602
- container.updateValue = function (newVal) { value = typeof newVal === "string" ? newVal : ""; };
13603
- container.setDisabled = function (isDisabled) { trigger.disabled = !!isDisabled; };
13604
- return container;
14341
+ throw new Error("TimePicker requires Popover");
13605
14342
  }
13606
14343
  var popover = Popover.create({
13607
14344
  trigger: trigger,
@@ -13622,8 +14359,6 @@
13622
14359
  });
13623
14360
  hidePopover = popover.hide;
13624
14361
 
13625
- container.appendChild(trigger);
13626
-
13627
14362
  container.updateValue = function (newVal) {
13628
14363
  value = typeof newVal === "string" ? newVal : "";
13629
14364
  var p = parseValue(value);
@@ -13659,7 +14394,7 @@
13659
14394
 
13660
14395
 
13661
14396
  // ============================================
13662
- // File 28/37: components/duration/duration-utils.js
14397
+ // File 30/41: components/duration/duration-utils.js
13663
14398
  // ============================================
13664
14399
 
13665
14400
  /**
@@ -13829,7 +14564,7 @@
13829
14564
 
13830
14565
 
13831
14566
  // ============================================
13832
- // File 29/37: components/duration/duration-constants.js
14567
+ // File 31/41: components/duration/duration-constants.js
13833
14568
  // ============================================
13834
14569
 
13835
14570
  /**
@@ -13881,7 +14616,7 @@
13881
14616
 
13882
14617
 
13883
14618
  // ============================================
13884
- // File 30/37: components/duration/duration.js
14619
+ // File 32/41: components/duration/duration.js
13885
14620
  // ============================================
13886
14621
 
13887
14622
  /**
@@ -13925,7 +14660,7 @@
13925
14660
  sizeLarge: "px-12 py-8",
13926
14661
  sizeSmall: "px-12 py-4",
13927
14662
  disabled:
13928
- "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary opacity-60",
14663
+ "pointer-events-none cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
13929
14664
  };
13930
14665
 
13931
14666
  function join() {
@@ -14318,8 +15053,13 @@
14318
15053
  };
14319
15054
  container.setDisabled = function (d) {
14320
15055
  disabled = !!d;
14321
- triggerWrapper.classList.toggle("cursor-not-allowed", disabled);
14322
- triggerWrapper.classList.toggle("opacity-60", disabled);
15056
+ triggerWrapper.className = join(
15057
+ TRIGGER_CLASS.base,
15058
+ TRIGGER_CLASS[variant] != null ? TRIGGER_CLASS[variant] : TRIGGER_CLASS.default,
15059
+ size === "large" ? TRIGGER_CLASS.sizeLarge : size === "small" ? TRIGGER_CLASS.sizeSmall : TRIGGER_CLASS.sizeDefault,
15060
+ disabled ? TRIGGER_CLASS.disabled : "",
15061
+ className
15062
+ );
14323
15063
  triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
14324
15064
  if (disabled) popoverApi.hide();
14325
15065
  };
@@ -14335,7 +15075,7 @@
14335
15075
 
14336
15076
 
14337
15077
  // ============================================
14338
- // File 31/37: components/date-time-picker/date-time-picker-utils.js
15078
+ // File 33/41: components/date-time-picker/date-time-picker-utils.js
14339
15079
  // ============================================
14340
15080
 
14341
15081
  /**
@@ -14594,7 +15334,7 @@
14594
15334
 
14595
15335
 
14596
15336
  // ============================================
14597
- // File 32/37: components/date-time-picker/date-time-picker.js
15337
+ // File 34/41: components/date-time-picker/date-time-picker.js
14598
15338
  // ============================================
14599
15339
 
14600
15340
  /**
@@ -14771,7 +15511,7 @@
14771
15511
  variant = "outline";
14772
15512
  }
14773
15513
  var btnClassName = join(
14774
- "!text-reg-12 my-2 flex-1 !px-0 !py-0",
15514
+ "!text-reg-12 my-2 flex-1",
14775
15515
  cell.currentMonth ? "" : "text-typography-quaternary-text"
14776
15516
  );
14777
15517
  var btn = Button.create({
@@ -14839,13 +15579,15 @@
14839
15579
  var displayMonth = validDate ? new Date(validDate.getTime()) : new Date();
14840
15580
 
14841
15581
  var triggerWrapper = document.createElement("div");
14842
- var triggerClasses = join(
14843
- "group flex items-center border-1/2 border-border-primary rounded-4 text-typography-primary-text gap-x-8 w-full transition-all ease-in-out",
14844
- "bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
14845
- size === "large" ? "px-12 py-8" : size === "small" ? "px-12 py-4" : "px-12 py-6",
14846
- disabled ? "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary" : "cursor-pointer"
14847
- );
14848
- triggerWrapper.className = triggerClasses;
15582
+ function getTriggerClassName(disabledState) {
15583
+ return join(
15584
+ "group flex items-center border-1/2 border-border-primary rounded-4 text-typography-primary-text gap-x-8 w-full transition-all ease-in-out",
15585
+ "bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
15586
+ size === "large" ? "px-12 py-8" : size === "small" ? "px-12 py-4" : "px-12 py-6",
15587
+ disabledState ? "pointer-events-none cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary" : "cursor-pointer"
15588
+ );
15589
+ }
15590
+ triggerWrapper.className = getTriggerClassName(disabled);
14849
15591
  triggerWrapper.setAttribute("role", "button");
14850
15592
  triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
14851
15593
  triggerWrapper.setAttribute("aria-haspopup", "dialog");
@@ -15113,8 +15855,7 @@
15113
15855
  };
15114
15856
  container.setDisabled = function (d) {
15115
15857
  disabled = !!d;
15116
- triggerWrapper.classList.toggle("cursor-not-allowed", disabled);
15117
- triggerWrapper.classList.toggle("opacity-60", disabled);
15858
+ triggerWrapper.className = getTriggerClassName(disabled);
15118
15859
  triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
15119
15860
  if (disabled) popoverApi.hide();
15120
15861
  };
@@ -15130,7 +15871,7 @@
15130
15871
 
15131
15872
 
15132
15873
  // ============================================
15133
- // File 33/37: components/phone-input/phone-utils.js
15874
+ // File 35/41: components/phone-input/phone-utils.js
15134
15875
  // ============================================
15135
15876
 
15136
15877
  /**
@@ -15293,7 +16034,7 @@
15293
16034
 
15294
16035
 
15295
16036
  // ============================================
15296
- // File 34/37: components/phone-input/phone-input.js
16037
+ // File 36/41: components/phone-input/phone-input.js
15297
16038
  // ============================================
15298
16039
 
15299
16040
  /**
@@ -15691,7 +16432,7 @@
15691
16432
 
15692
16433
 
15693
16434
  // ============================================
15694
- // File 35/37: components/file-input.js
16435
+ // File 37/41: components/file-input.js
15695
16436
  // ============================================
15696
16437
 
15697
16438
  /**
@@ -15735,6 +16476,15 @@
15735
16476
  return ICONS.file;
15736
16477
  }
15737
16478
 
16479
+ /** Resolve client: use FlowUI._getComponent when bundle has captured globals, else global.superleapClient (same as enum-select) */
16480
+ function getClient() {
16481
+ if (global.FlowUI && typeof global.FlowUI._getComponent === "function") {
16482
+ var c = global.FlowUI._getComponent("superleapClient");
16483
+ if (c) return c;
16484
+ }
16485
+ return global.superleapClient;
16486
+ }
16487
+
15738
16488
  /**
15739
16489
  * Upload file to S3
15740
16490
  * @param {File} file - File to upload
@@ -15746,26 +16496,31 @@
15746
16496
  formData.append("file", file, file.name);
15747
16497
  formData.append("is_private", String(!!isPrivate));
15748
16498
 
15749
- // Get upload URL - can be configured via global.S3_UPLOAD_URL
16499
+ // Get upload path - can be configured via global.S3_UPLOAD_URL
15750
16500
  const uploadUrl = global.S3_UPLOAD_URL || "/org/file/upload";
15751
- const baseUrl = global.SUPERLEAP_BASE_URL || "https://app.superleap.com/api/v1";
15752
- const fullUrl = uploadUrl.startsWith("http") ? uploadUrl : `${baseUrl}${uploadUrl}`;
15753
16501
 
15754
- // Get API key: use FlowUI._getComponent when globals were captured (index.js), else global.superleapClient
15755
- let apiKey = null;
16502
+ // Base URL and API key from superleapClient only (same pattern as enum-select)
16503
+ var client = getClient();
16504
+ var baseUrl = null;
16505
+ var apiKey = null;
15756
16506
  try {
15757
- const client =
15758
- global.FlowUI && typeof global.FlowUI._getComponent === "function"
15759
- ? global.FlowUI._getComponent("superleapClient")
15760
- : global.superleapClient;
16507
+ if (client && typeof client.getBaseUrl === "function") {
16508
+ baseUrl = client.getBaseUrl();
16509
+ }
15761
16510
  if (client && typeof client.getSdk === "function") {
15762
- const sdk = client.getSdk();
15763
- apiKey = sdk?.apiKey;
16511
+ var sdk = client.getSdk();
16512
+ apiKey = sdk ? sdk.apiKey : null;
15764
16513
  }
15765
16514
  } catch (e) {
15766
- console.warn("[S3FileUpload] Could not get API key:", e);
16515
+ console.warn("[S3FileUpload] Could not get client:", e);
15767
16516
  }
15768
16517
 
16518
+ if (!baseUrl) {
16519
+ throw new Error("SuperLeap client not initialized. Call superleapClient.init({ baseUrl, apiKey }) first.");
16520
+ }
16521
+
16522
+ const fullUrl = uploadUrl.startsWith("http") ? uploadUrl : baseUrl.replace(/\/$/, "") + (uploadUrl.startsWith("/") ? uploadUrl : "/" + uploadUrl);
16523
+
15769
16524
  const headers = {};
15770
16525
  if (apiKey) {
15771
16526
  headers.Authorization = `Bearer ${apiKey}`;
@@ -15809,8 +16564,36 @@
15809
16564
  * @param {boolean} config.isPrivate - Whether files should be private
15810
16565
  * @param {number} config.maxFiles - Maximum number of files (for multiple mode)
15811
16566
  * @param {number} config.maxFileSize - Maximum file size in bytes
16567
+ * @param {boolean} [config.disabled] - Whether the file upload is disabled
16568
+ * @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'success' | 'borderless' | 'inline'
16569
+ * @param {string} [config.inputSize] - 'default' | 'large' | 'small'
16570
+ * @param {string} [config.className] - Extra class on upload wrapper
15812
16571
  * @returns {HTMLElement} Field element
15813
16572
  */
16573
+ var UPLOAD_WRAPPER_CLASS = {
16574
+ base:
16575
+ "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",
16576
+ default:
16577
+ "border-border-primary bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
16578
+ error:
16579
+ "border-error-base bg-fill-quarternary-fill-white hover:border-error-base focus-within:border-error-base",
16580
+ warning:
16581
+ "border-warning-base bg-fill-quarternary-fill-white hover:border-warning-base focus-within:border-warning-base",
16582
+ success:
16583
+ "border-success-base bg-fill-quarternary-fill-white hover:border-success-base focus-within:border-success-base",
16584
+ borderless:
16585
+ "border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
16586
+ inline:
16587
+ "border-transparent shadow-none rounded-0 bg-fill-quarternary-fill-white hover:bg-fill-tertiary-fill-light-gray focus-within:border-transparent focus-within:bg-fill-tertiary-fill-light-gray",
16588
+ sizeDefault: "px-12 py-4",
16589
+ sizeLarge: "px-12 py-6",
16590
+ sizeSmall: "px-12 py-2",
16591
+ };
16592
+
16593
+ function joinClasses() {
16594
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
16595
+ }
16596
+
15814
16597
  function create(config) {
15815
16598
  const {
15816
16599
  label,
@@ -15822,7 +16605,14 @@
15822
16605
  isPrivate = false,
15823
16606
  maxFiles = null,
15824
16607
  maxFileSize = 10 * 1024 * 1024, // 10MB default
16608
+ disabled = false,
16609
+ variant = "default",
16610
+ inputSize = "default",
16611
+ className = "",
15825
16612
  } = config;
16613
+ let disabledState = !!disabled;
16614
+ let currentVariant = variant;
16615
+ let currentInputSize = inputSize;
15826
16616
 
15827
16617
  if (!global.FlowUI) {
15828
16618
  throw new Error("FlowUI not available");
@@ -15840,12 +16630,33 @@
15840
16630
 
15841
16631
  // Upload row: button + status + optional end spinner (match Select/MultiSelect trigger)
15842
16632
  const uploadWrapper = document.createElement("div");
15843
- uploadWrapper.className = "relative flex w-full items-center justify-between rounded-4 border-1/2 border-border-primary bg-fill-quarternary-fill-white px-8 py-4 hover:border-primary-border focus-within:outline-none focus-within:border-1/2 focus-within:border-primary-border";
16633
+ var sizeClass =
16634
+ currentInputSize === "large"
16635
+ ? UPLOAD_WRAPPER_CLASS.sizeLarge
16636
+ : currentInputSize === "small"
16637
+ ? UPLOAD_WRAPPER_CLASS.sizeSmall
16638
+ : UPLOAD_WRAPPER_CLASS.sizeDefault;
16639
+ function applyWrapperClasses() {
16640
+ uploadWrapper.className = joinClasses(
16641
+ UPLOAD_WRAPPER_CLASS.base,
16642
+ UPLOAD_WRAPPER_CLASS[currentVariant] || UPLOAD_WRAPPER_CLASS.default,
16643
+ sizeClass,
16644
+ className
16645
+ );
16646
+ }
16647
+ applyWrapperClasses();
16648
+ uploadWrapper.setAttribute("data-file-input-variant", currentVariant);
16649
+ uploadWrapper.setAttribute("data-file-input-size", currentInputSize);
16650
+ uploadWrapper.setAttribute("data-disabled", disabledState ? "true" : "false");
15844
16651
 
15845
16652
  // Left content (button + status) – pointer-events-none so overlay input receives clicks
15846
16653
  const leftContent = document.createElement("div");
15847
16654
  leftContent.className = "pointer-events-none flex min-w-0 flex-1 items-center gap-8 truncate";
15848
16655
 
16656
+ var disabledChildClasses =
16657
+ "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";
16658
+ var statusTextBaseClass = "text-reg-13 min-w-0 flex-1 truncate";
16659
+ var statusTextDisabledClass = " group-has-[:disabled]:text-typography-quaternary-text";
15849
16660
  const useButtonComponent = global.Button && typeof global.Button.create === "function";
15850
16661
  const initialButtonText = multiple ? "Choose files" : "Choose a file";
15851
16662
  const btn = useButtonComponent
@@ -15853,31 +16664,41 @@
15853
16664
  variant: "outline",
15854
16665
  size: "small",
15855
16666
  text: initialButtonText,
15856
- className: "shrink-0 truncate",
16667
+ className: "shrink-0 truncate " + disabledChildClasses,
15857
16668
  })
15858
16669
  : (function () {
15859
16670
  const el = document.createElement("div");
15860
- el.className = "shrink-0 truncate rounded-2 border-1/2 border-border-primary bg-fill-tertiary-fill-light-gray px-8 py-1 text-reg-13 text-typography-primary-text transition-colors duration-150 hover:bg-fill-secondary-fill-gray";
16671
+ el.className =
16672
+ "shrink-0 truncate rounded-2 border-1/2 border-border-primary bg-fill-tertiary-fill-light-gray px-8 py-1 text-reg-13 text-typography-primary-text transition-colors duration-150 hover:bg-fill-secondary-fill-gray " +
16673
+ disabledChildClasses;
15861
16674
  el.textContent = initialButtonText;
15862
16675
  return el;
15863
16676
  })();
15864
16677
 
15865
16678
  // Status text: "No files chosen" (quaternary) or "X file(s) selected"
15866
16679
  const statusText = document.createElement("p");
15867
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-quaternary-text";
16680
+ statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
15868
16681
 
15869
16682
  // Hidden file input – overlays row, high z-index so it receives clicks
15870
16683
  const input = document.createElement("input");
15871
16684
  input.type = "file";
15872
- input.className = "absolute inset-0 z-10 w-full cursor-pointer opacity-0";
16685
+ const inputBaseClass = "absolute inset-0 z-10 w-full opacity-0";
16686
+ function applyInputClasses() {
16687
+ input.className =
16688
+ inputBaseClass +
16689
+ (disabledState ? " !pointer-events-none !cursor-not-allowed" : " cursor-pointer");
16690
+ }
16691
+ applyInputClasses();
15873
16692
  input.multiple = multiple;
16693
+ input.disabled = disabledState;
15874
16694
  if (accept !== "*") {
15875
16695
  input.accept = accept;
15876
16696
  }
15877
16697
 
15878
16698
  // End icon slot: spinner when uploading (match Select chevron position)
15879
16699
  const endIconSlot = document.createElement("div");
15880
- endIconSlot.className = "ml-4 flex size-16 items-center justify-center shrink-0 text-typography-quaternary-text";
16700
+ endIconSlot.className =
16701
+ "ml-4 flex size-16 items-center justify-center shrink-0 text-typography-quaternary-text group-has-[:disabled]:text-typography-quaternary-text";
15881
16702
  endIconSlot.style.display = "none";
15882
16703
 
15883
16704
  // Uploaded files: badge list (match UploadedFilePreviewer – flex-wrap gap-2)
@@ -15916,15 +16737,13 @@
15916
16737
  e.stopPropagation();
15917
16738
  navigator.clipboard.writeText(fileUrl).then(
15918
16739
  function () {
15919
- if (global.FlowUI && global.FlowUI.renderAlerts) {
15920
- const ac = document.getElementById("alerts");
15921
- if (ac) global.FlowUI.renderAlerts(ac, ["Link copied"], "success");
16740
+ if (global.FlowUI && global.FlowUI.showToast) {
16741
+ global.FlowUI.showToast("Link copied", "success");
15922
16742
  }
15923
16743
  },
15924
16744
  function () {
15925
- if (global.FlowUI && global.FlowUI.renderAlerts) {
15926
- const ac = document.getElementById("alerts");
15927
- if (ac) global.FlowUI.renderAlerts(ac, ["Copy failed"], "error");
16745
+ if (global.FlowUI && global.FlowUI.showToast) {
16746
+ global.FlowUI.showToast("Copy failed", "error");
15928
16747
  }
15929
16748
  }
15930
16749
  );
@@ -16060,13 +16879,13 @@
16060
16879
 
16061
16880
  if (!filesChosen) {
16062
16881
  statusText.textContent = "No files chosen";
16063
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-quaternary-text";
16882
+ statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
16064
16883
  } else if (uploadingCount > 0) {
16065
16884
  statusText.textContent = "Uploading…";
16066
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-quaternary-text";
16885
+ statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
16067
16886
  } else {
16068
16887
  statusText.textContent = `${uploadedCount} file${uploadedCount !== 1 ? "s" : ""} selected`;
16069
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-primary-text";
16888
+ statusText.className = statusTextBaseClass + " text-typography-primary-text" + statusTextDisabledClass;
16070
16889
  }
16071
16890
 
16072
16891
  endIconSlot.style.display = uploadingCount > 0 ? "flex" : "none";
@@ -16120,11 +16939,8 @@
16120
16939
  async function uploadFile(file) {
16121
16940
  const error = validateFile(file);
16122
16941
  if (error) {
16123
- if (global.FlowUI && global.FlowUI.renderAlerts) {
16124
- const alertsContainer = document.getElementById("alerts");
16125
- if (alertsContainer) {
16126
- global.FlowUI.renderAlerts(alertsContainer, [error], "error");
16127
- }
16942
+ if (global.FlowUI && global.FlowUI.showToast) {
16943
+ global.FlowUI.showToast(error, "error");
16128
16944
  }
16129
16945
  return;
16130
16946
  }
@@ -16152,15 +16968,8 @@
16152
16968
  uploadingFiles = uploadingFiles.filter((f) => f.file !== file);
16153
16969
  renderUploadingList();
16154
16970
  updateStatus();
16155
- if (global.FlowUI && global.FlowUI.renderAlerts) {
16156
- const alertsContainer = document.getElementById("alerts");
16157
- if (alertsContainer) {
16158
- global.FlowUI.renderAlerts(
16159
- alertsContainer,
16160
- [`Failed to upload "${file.name}": ${error.message}`],
16161
- "error"
16162
- );
16163
- }
16971
+ if (global.FlowUI && global.FlowUI.showToast) {
16972
+ global.FlowUI.showToast(`Failed to upload "${file.name}": ${error.message}`, "error");
16164
16973
  }
16165
16974
  }
16166
16975
  }
@@ -16173,11 +16982,8 @@
16173
16982
  // Check max files limit
16174
16983
  if (multiple && maxFiles && uploadedFiles.length + files.length > maxFiles) {
16175
16984
  const message = `You can only upload ${maxFiles} file${maxFiles !== 1 ? "s" : ""}`;
16176
- if (global.FlowUI && global.FlowUI.renderAlerts) {
16177
- const alertsContainer = document.getElementById("alerts");
16178
- if (alertsContainer) {
16179
- global.FlowUI.renderAlerts(alertsContainer, [message], "error");
16180
- }
16985
+ if (global.FlowUI && global.FlowUI.showToast) {
16986
+ global.FlowUI.showToast(message, "error");
16181
16987
  }
16182
16988
  input.value = "";
16183
16989
  return;
@@ -16216,6 +17022,32 @@
16216
17022
  loadExistingFiles();
16217
17023
  updateStatus();
16218
17024
 
17025
+ field.setDisabled = function (d) {
17026
+ disabledState = !!d;
17027
+ input.disabled = disabledState;
17028
+ applyInputClasses();
17029
+ applyWrapperClasses();
17030
+ uploadWrapper.setAttribute("data-disabled", disabledState ? "true" : "false");
17031
+ };
17032
+
17033
+ field.setVariant = function (v) {
17034
+ currentVariant = v || "default";
17035
+ uploadWrapper.setAttribute("data-file-input-variant", currentVariant);
17036
+ applyWrapperClasses();
17037
+ };
17038
+
17039
+ field.setInputSize = function (s) {
17040
+ currentInputSize = s || "default";
17041
+ sizeClass =
17042
+ currentInputSize === "large"
17043
+ ? UPLOAD_WRAPPER_CLASS.sizeLarge
17044
+ : currentInputSize === "small"
17045
+ ? UPLOAD_WRAPPER_CLASS.sizeSmall
17046
+ : UPLOAD_WRAPPER_CLASS.sizeDefault;
17047
+ uploadWrapper.setAttribute("data-file-input-size", currentInputSize);
17048
+ applyWrapperClasses();
17049
+ };
17050
+
16219
17051
  return field;
16220
17052
  }
16221
17053
 
@@ -16230,7 +17062,7 @@
16230
17062
 
16231
17063
 
16232
17064
  // ============================================
16233
- // File 36/37: components/table.js
17065
+ // File 38/41: components/table.js
16234
17066
  // ============================================
16235
17067
 
16236
17068
  /**
@@ -16571,7 +17403,455 @@
16571
17403
 
16572
17404
 
16573
17405
  // ============================================
16574
- // File 37/37: index.js
17406
+ // File 39/41: components/tabs.js
17407
+ // ============================================
17408
+
17409
+ /**
17410
+ * Tabs Component (vanilla JS)
17411
+ * Tabbed interface with list, triggers, and content panels.
17412
+ * Ref: Radix-style Tabs; design tokens match design system.
17413
+ */
17414
+
17415
+ (function (global) {
17416
+
17417
+ function getComponent(name) {
17418
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
17419
+ var c = global.FlowUI._getComponent(name);
17420
+ if (c) return c;
17421
+ }
17422
+ return global[name];
17423
+ }
17424
+
17425
+ var LIST_BASE_CLASS =
17426
+ "inline-flex items-center justify-center gap-2 rounded-4 bg-fill-tertiary-fill-light-gray p-4";
17427
+
17428
+ /** Button variant classes for active (outline) vs inactive (ghost) */
17429
+ var BUTTON_OUTLINE =
17430
+ "shadow-soft-extra-small group bg-fill-quarternary-fill-white border-1/2 border-border-primary text-typography-primary-text hover:bg-fill-tertiary-fill-light-gray active:bg-fill-secondary-fill-gray disabled:opacity-50 disabled:border-fill-secondary-fill-gray";
17431
+ var BUTTON_GHOST =
17432
+ "group text-typography-primary-text hover:bg-fill-tertiary-fill-light-gray active:bg-fill-secondary-fill-gray disabled:text-typography-quaternary-text";
17433
+ var BUTTON_BASE =
17434
+ "inline-flex items-center justify-center whitespace-nowrap !text-med-12 transition-colors disabled:pointer-events-none hover:cursor-pointer h-fit";
17435
+ var BUTTON_SIZES = {
17436
+ default: "px-8 py-4 gap-4 rounded-4",
17437
+ small: "px-8 py-4 gap-4 rounded-4",
17438
+ large: "px-16 py-8 gap-4 rounded-4",
17439
+ };
17440
+
17441
+ var CONTENT_BASE_CLASS =
17442
+ "ring-offset-background focus-visible:ring-ring h-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2";
17443
+
17444
+ function join() {
17445
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
17446
+ }
17447
+
17448
+ /**
17449
+ * Create a full Tabs component (list + triggers + content panels)
17450
+ * @param {Object} config
17451
+ * @param {string} [config.defaultValue] - initial active tab value
17452
+ * @param {string} [config.value] - controlled active value
17453
+ * @param {Function} [config.onChange] - (value) => void when tab changes
17454
+ * @param {Array<{value: string, label: string, content: HTMLElement|string}>} config.tabs - tab definitions
17455
+ * @param {string} [config.size] - 'default' | 'small' | 'large'
17456
+ * @param {string} [config.listClassName] - extra class on list
17457
+ * @param {string} [config.contentClassName] - extra class on content wrapper
17458
+ * @returns {HTMLElement} root element (wrapper containing list + content area)
17459
+ */
17460
+ function create(config) {
17461
+ var opts = config || {};
17462
+ var defaultValue = opts.defaultValue;
17463
+ var controlledValue = opts.value;
17464
+ var onChange = opts.onChange;
17465
+ var tabs = opts.tabs || [];
17466
+ var size = opts.size || "default";
17467
+ var listClassName = opts.listClassName || "";
17468
+ var contentClassName = opts.contentClassName || "";
17469
+
17470
+ var sizeClass = BUTTON_SIZES[size] || BUTTON_SIZES.default;
17471
+
17472
+ var root = document.createElement("div");
17473
+ root.className = "tabs-root w-full";
17474
+
17475
+ var activeValue = controlledValue !== undefined ? controlledValue : defaultValue !== undefined ? defaultValue : (tabs[0] && tabs[0].value) || "";
17476
+
17477
+ // List container
17478
+ var list = document.createElement("div");
17479
+ list.setAttribute("role", "tablist");
17480
+ list.className = join(LIST_BASE_CLASS, listClassName);
17481
+
17482
+ // Content container (holds all panels; we show/hide by value)
17483
+ var contentWrapper = document.createElement("div");
17484
+ contentWrapper.className = join("tabs-content-wrapper mt-4", contentClassName);
17485
+
17486
+ var triggerEls = [];
17487
+ var contentPanels = [];
17488
+
17489
+ var Button = getComponent("Button");
17490
+
17491
+ tabs.forEach(function (tab, index) {
17492
+ var value = tab.value;
17493
+ var label = tab.label != null ? tab.label : value;
17494
+ var content = tab.content;
17495
+
17496
+ var isActive = value === activeValue;
17497
+ var trigger = Button.create({
17498
+ variant: isActive ? "outline" : "ghost",
17499
+ size: size === "small" ? "small" : size === "large" ? "large" : "default",
17500
+ text: label,
17501
+ type: "button",
17502
+ className: "mx-2",
17503
+ onClick: function () {
17504
+ if (activeValue === value) return;
17505
+ if (controlledValue === undefined) {
17506
+ activeValue = value;
17507
+ updateActiveState();
17508
+ }
17509
+ if (typeof onChange === "function") {
17510
+ onChange(value);
17511
+ }
17512
+ },
17513
+ });
17514
+
17515
+ trigger.setAttribute("role", "tab");
17516
+ trigger.setAttribute("aria-selected", value === activeValue ? "true" : "false");
17517
+ trigger.setAttribute("data-state", value === activeValue ? "active" : "inactive");
17518
+ trigger.setAttribute("data-value", value);
17519
+ trigger.tabIndex = value === activeValue ? 0 : -1;
17520
+
17521
+ trigger.addEventListener("keydown", function (e) {
17522
+ if (e.key === "Enter" || e.key === " ") {
17523
+ e.preventDefault();
17524
+ trigger.click();
17525
+ }
17526
+ if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
17527
+ e.preventDefault();
17528
+ var nextIndex = e.key === "ArrowRight" ? index + 1 : index - 1;
17529
+ if (nextIndex >= 0 && nextIndex < tabs.length) {
17530
+ var nextTrigger = triggerEls[nextIndex];
17531
+ if (nextTrigger) {
17532
+ nextTrigger.focus();
17533
+ nextTrigger.click();
17534
+ }
17535
+ }
17536
+ }
17537
+ });
17538
+
17539
+ list.appendChild(trigger);
17540
+ triggerEls.push(trigger);
17541
+
17542
+ var panel = document.createElement("div");
17543
+ panel.setAttribute("role", "tabpanel");
17544
+ panel.setAttribute("aria-hidden", value !== activeValue ? "true" : "false");
17545
+ panel.setAttribute("data-value", value);
17546
+ panel.className = join(CONTENT_BASE_CLASS, value !== activeValue ? "hidden" : "");
17547
+ if (typeof content === "string") {
17548
+ panel.innerHTML = content;
17549
+ } else if (content && content.nodeType === 1) {
17550
+ panel.appendChild(content);
17551
+ }
17552
+ contentWrapper.appendChild(panel);
17553
+ contentPanels.push(panel);
17554
+ });
17555
+
17556
+ function updateActiveState() {
17557
+ triggerEls.forEach(function (t, i) {
17558
+ var val = tabs[i] && tabs[i].value;
17559
+ var isActive = val === activeValue;
17560
+ t.setAttribute("aria-selected", isActive ? "true" : "false");
17561
+ t.setAttribute("data-state", isActive ? "active" : "inactive");
17562
+ t.tabIndex = isActive ? 0 : -1;
17563
+ t.className = join(BUTTON_BASE, isActive ? BUTTON_OUTLINE : BUTTON_GHOST, sizeClass, "mx-2");
17564
+ });
17565
+ contentPanels.forEach(function (p, i) {
17566
+ var val = tabs[i] && tabs[i].value;
17567
+ var isActive = val === activeValue;
17568
+ p.setAttribute("aria-hidden", isActive ? "false" : "true");
17569
+ p.classList.toggle("hidden", !isActive);
17570
+ });
17571
+ }
17572
+
17573
+ root.appendChild(list);
17574
+ root.appendChild(contentWrapper);
17575
+
17576
+ root.getValue = function () {
17577
+ return activeValue;
17578
+ };
17579
+
17580
+ root.setValue = function (newValue) {
17581
+ if (newValue === activeValue) return;
17582
+ activeValue = newValue;
17583
+ updateActiveState();
17584
+ };
17585
+
17586
+ return root;
17587
+ }
17588
+
17589
+ var Tabs = {
17590
+ create: create,
17591
+ };
17592
+
17593
+ if (typeof module !== "undefined" && module.exports) {
17594
+ module.exports = Tabs;
17595
+ } else {
17596
+ global.Tabs = Tabs;
17597
+ }
17598
+ })(typeof window !== "undefined" ? window : undefined);
17599
+
17600
+
17601
+
17602
+ // ============================================
17603
+ // File 40/41: components/steps.js
17604
+ // ============================================
17605
+
17606
+ /**
17607
+ * Steps Component (vanilla JS)
17608
+ * Multi-step flow with numbered step triggers and optional content panels.
17609
+ * Use for wizards / step-by-step forms (e.g. "1 Step One", "2 Step Two").
17610
+ */
17611
+
17612
+ (function (global) {
17613
+
17614
+ function getComponent(name) {
17615
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
17616
+ var c = global.FlowUI._getComponent(name);
17617
+ if (c) return c;
17618
+ }
17619
+ return global[name];
17620
+ }
17621
+
17622
+ var LIST_BASE_CLASS =
17623
+ "flex items-center flex-wrap gap-0 rounded-4 border border-border-primary bg-fill-quarternary-fill-white p-4";
17624
+
17625
+ /** Inactive step: muted label color only (button stays ghost large) */
17626
+ var INACTIVE_LABEL_CLASS = "!text-typography-quaternary-text";
17627
+
17628
+ /** Box around step number: active = dark fill + invert text, inactive = dark gray fill + tertiary text */
17629
+ var NUMBER_BOX_ACTIVE =
17630
+ "flex h-16 w-16 items-center justify-center rounded-4 bg-typography-primary-text text-typography-invert-text shrink-0";
17631
+ var NUMBER_BOX_INACTIVE =
17632
+ "flex h-16 w-16 items-center justify-center rounded-4 bg-fill-primary-fill-dark-gray text-typography-tertiary-text shrink-0";
17633
+
17634
+ /** Horizontal line between steps (step1 ——— step2) */
17635
+ var CONNECTOR_CLASS = "flex-1 min-w-12 max-w-32 h-0 border-t border-border-primary shrink-0 mx-2 self-center";
17636
+
17637
+ var CONTENT_BASE_CLASS =
17638
+ "ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2 h-full";
17639
+
17640
+ function join() {
17641
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
17642
+ }
17643
+
17644
+ /**
17645
+ * Create a Steps component (numbered step triggers + optional content)
17646
+ * Design: ghost large Button for all steps; inactive steps use muted label color. Connectors between steps. Optional title/description above.
17647
+ * @param {Object} config
17648
+ * @param {Array<{id: string, label: string, content?: HTMLElement|string}>} config.steps - step definitions
17649
+ * @param {string} [config.defaultValue] - initial active step id
17650
+ * @param {string} [config.value] - controlled active step id
17651
+ * @param {Function} [config.onChange] - (stepId) => void when step changes
17652
+ * @param {string} [config.title] - optional title above steps
17653
+ * @param {string} [config.description] - optional description above steps
17654
+ * @param {string} [config.listClassName] - extra class on step list
17655
+ * @param {string} [config.contentClassName] - extra class on content wrapper
17656
+ * @param {boolean} [config.showContent=true] - whether to render content panels (if steps have content)
17657
+ * @returns {HTMLElement} root element
17658
+ */
17659
+ function create(config) {
17660
+ var opts = config || {};
17661
+ var steps = opts.steps || [];
17662
+ var defaultValue = opts.defaultValue;
17663
+ var controlledValue = opts.value;
17664
+ var onChange = opts.onChange;
17665
+ var title = opts.title;
17666
+ var description = opts.description;
17667
+ var listClassName = opts.listClassName || "";
17668
+ var contentClassName = opts.contentClassName || "";
17669
+ var showContent = opts.showContent !== false;
17670
+
17671
+ var root = document.createElement("div");
17672
+ root.className = "steps-root w-full";
17673
+
17674
+ var activeId =
17675
+ controlledValue !== undefined
17676
+ ? controlledValue
17677
+ : defaultValue !== undefined
17678
+ ? defaultValue
17679
+ : (steps[0] && steps[0].id) || "";
17680
+
17681
+ if (title != null && title !== "") {
17682
+ var titleEl = document.createElement("h3");
17683
+ titleEl.className = "text-typography-primary-text";
17684
+ titleEl.textContent = title;
17685
+ root.appendChild(titleEl);
17686
+ }
17687
+ if (description != null && description !== "") {
17688
+ var descEl = document.createElement("p");
17689
+ descEl.className = "text-typography-secondary-text";
17690
+ descEl.textContent = description;
17691
+ root.appendChild(descEl);
17692
+ }
17693
+
17694
+ // Step list (horizontal: step, connector, step, connector, ...)
17695
+ var list = document.createElement("div");
17696
+ list.setAttribute("role", "tablist");
17697
+ list.setAttribute("aria-label", "Steps");
17698
+ list.className = join(LIST_BASE_CLASS, listClassName);
17699
+
17700
+ var triggerEls = [];
17701
+ var numberBoxEls = [];
17702
+ var contentPanels = [];
17703
+ var contentWrapper = null;
17704
+
17705
+ if (showContent && steps.some(function (s) { return s.content != null; })) {
17706
+ contentWrapper = document.createElement("div");
17707
+ contentWrapper.className = join("steps-content-wrapper mt-4", contentClassName);
17708
+ }
17709
+
17710
+ var Button = getComponent("Button");
17711
+
17712
+ steps.forEach(function (step, index) {
17713
+ var stepId = step.id;
17714
+ var label = step.label != null ? step.label : stepId;
17715
+ var content = step.content;
17716
+ var stepNumber = index + 1;
17717
+ var isActive = stepId === activeId;
17718
+
17719
+ var trigger = Button.create({
17720
+ variant: "ghost",
17721
+ size: "large",
17722
+ text: "\u00A0",
17723
+ type: "button",
17724
+ className: isActive ? "" : INACTIVE_LABEL_CLASS,
17725
+ onClick: function () {
17726
+ if (activeId === stepId) return;
17727
+ if (controlledValue === undefined) {
17728
+ activeId = stepId;
17729
+ updateActiveState();
17730
+ }
17731
+ if (typeof onChange === "function") {
17732
+ onChange(stepId);
17733
+ }
17734
+ },
17735
+ });
17736
+
17737
+ var numberBox = document.createElement("span");
17738
+ numberBox.setAttribute("aria-hidden", "true");
17739
+ numberBox.className = isActive ? NUMBER_BOX_ACTIVE : NUMBER_BOX_INACTIVE;
17740
+ numberBox.textContent = String(stepNumber);
17741
+
17742
+ trigger.innerHTML = "";
17743
+ trigger.appendChild(numberBox);
17744
+ trigger.appendChild(document.createTextNode(" " + label));
17745
+
17746
+ trigger.setAttribute("role", "tab");
17747
+ trigger.setAttribute("aria-selected", isActive ? "true" : "false");
17748
+ trigger.setAttribute("data-state", isActive ? "active" : "inactive");
17749
+ trigger.setAttribute("data-step-id", stepId);
17750
+ trigger.tabIndex = isActive ? 0 : -1;
17751
+
17752
+ numberBoxEls.push(numberBox);
17753
+
17754
+ trigger.addEventListener("keydown", function (e) {
17755
+ if (e.key === "Enter" || e.key === " ") {
17756
+ e.preventDefault();
17757
+ trigger.click();
17758
+ }
17759
+ if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
17760
+ e.preventDefault();
17761
+ var nextIndex = e.key === "ArrowRight" ? index + 1 : index - 1;
17762
+ if (nextIndex >= 0 && nextIndex < steps.length) {
17763
+ var nextTrigger = triggerEls[nextIndex];
17764
+ if (nextTrigger) {
17765
+ nextTrigger.focus();
17766
+ nextTrigger.click();
17767
+ }
17768
+ }
17769
+ }
17770
+ });
17771
+
17772
+ list.appendChild(trigger);
17773
+ triggerEls.push(trigger);
17774
+
17775
+ if (index < steps.length - 1) {
17776
+ var connector = document.createElement("span");
17777
+ connector.className = CONNECTOR_CLASS;
17778
+ connector.setAttribute("aria-hidden", "true");
17779
+ list.appendChild(connector);
17780
+ }
17781
+
17782
+ if (contentWrapper && content != null) {
17783
+ var panel = document.createElement("div");
17784
+ panel.setAttribute("role", "tabpanel");
17785
+ panel.setAttribute("aria-hidden", stepId !== activeId ? "true" : "false");
17786
+ panel.setAttribute("data-step-id", stepId);
17787
+ panel.className = join(CONTENT_BASE_CLASS, stepId !== activeId ? "hidden" : "");
17788
+ if (typeof content === "string") {
17789
+ panel.innerHTML = content;
17790
+ } else if (content && content.nodeType === 1) {
17791
+ panel.appendChild(content);
17792
+ }
17793
+ contentWrapper.appendChild(panel);
17794
+ contentPanels.push(panel);
17795
+ }
17796
+ });
17797
+
17798
+ function updateActiveState() {
17799
+ triggerEls.forEach(function (t, i) {
17800
+ var step = steps[i];
17801
+ var id = step && step.id;
17802
+ var isActive = id === activeId;
17803
+ t.setAttribute("aria-selected", isActive ? "true" : "false");
17804
+ t.setAttribute("data-state", isActive ? "active" : "inactive");
17805
+ t.tabIndex = isActive ? 0 : -1;
17806
+ if (isActive) {
17807
+ t.classList.remove(INACTIVE_LABEL_CLASS);
17808
+ } else {
17809
+ t.classList.add(INACTIVE_LABEL_CLASS);
17810
+ }
17811
+ numberBoxEls[i].className = isActive ? NUMBER_BOX_ACTIVE : NUMBER_BOX_INACTIVE;
17812
+ });
17813
+ contentPanels.forEach(function (p, i) {
17814
+ var step = steps[i];
17815
+ var id = step && step.id;
17816
+ var isActive = id === activeId;
17817
+ p.setAttribute("aria-hidden", isActive ? "false" : "true");
17818
+ p.classList.toggle("hidden", !isActive);
17819
+ });
17820
+ }
17821
+
17822
+ root.appendChild(list);
17823
+ if (contentWrapper) {
17824
+ root.appendChild(contentWrapper);
17825
+ }
17826
+
17827
+ root.getValue = function () {
17828
+ return activeId;
17829
+ };
17830
+
17831
+ root.setValue = function (newId) {
17832
+ if (newId === activeId) return;
17833
+ activeId = newId;
17834
+ updateActiveState();
17835
+ };
17836
+
17837
+ return root;
17838
+ }
17839
+
17840
+ var Steps = {
17841
+ create: create,
17842
+ };
17843
+
17844
+ if (typeof module !== "undefined" && module.exports) {
17845
+ module.exports = Steps;
17846
+ } else {
17847
+ global.Steps = Steps;
17848
+ }
17849
+ })(typeof window !== "undefined" ? window : undefined);
17850
+
17851
+
17852
+
17853
+ // ============================================
17854
+ // File 41/41: index.js
16575
17855
  // ============================================
16576
17856
 
16577
17857
  /**
@@ -16647,6 +17927,7 @@
16647
17927
  require("./components/input.js");
16648
17928
  require("./components/currency.js");
16649
17929
  require("./components/textarea.js");
17930
+ require("./components/richtext-editor.js");
16650
17931
  require("./components/duration/duration-utils.js");
16651
17932
  require("./components/duration/duration-constants.js");
16652
17933
  require("./components/duration/duration.js");
@@ -16662,8 +17943,11 @@
16662
17943
  require("./components/phone-input/phone-utils.js");
16663
17944
  require("./components/phone-input/phone-input.js");
16664
17945
  require("./components/checkbox.js");
17946
+ require("./components/checkbox-group.js");
16665
17947
  require("./components/radio-group.js");
16666
17948
  require("./components/table.js");
17949
+ require("./components/tabs.js");
17950
+ require("./components/steps.js");
16667
17951
 
16668
17952
  // Export FlowUI and SuperLeap from global scope
16669
17953
  if (typeof global !== "undefined" && global.FlowUI) {
@@ -16674,6 +17958,7 @@
16674
17958
  getSdk: global.superleapClient.getSdk,
16675
17959
  isAvailable: global.superleapClient.isAvailable,
16676
17960
  getDefaultConfig: global.superleapClient.getDefaultConfig,
17961
+ getBaseUrl: global.superleapClient.getBaseUrl,
16677
17962
  }
16678
17963
  : null;
16679
17964
 
@@ -16689,6 +17974,7 @@
16689
17974
  getSdk: window.superleapClient.getSdk,
16690
17975
  isAvailable: window.superleapClient.isAvailable,
16691
17976
  getDefaultConfig: window.superleapClient.getDefaultConfig,
17977
+ getBaseUrl: window.superleapClient.getBaseUrl,
16692
17978
  }
16693
17979
  : null;
16694
17980
 
@@ -16724,6 +18010,7 @@
16724
18010
  "InputComponent",
16725
18011
  "CurrencyComponent",
16726
18012
  "TextareaComponent",
18013
+ "RichTextEditorComponent",
16727
18014
  "Duration",
16728
18015
  "DateTimePicker",
16729
18016
  "Enumeration",
@@ -16735,8 +18022,11 @@
16735
18022
  "FileInput",
16736
18023
  "PhoneInput",
16737
18024
  "Checkbox",
18025
+ "CheckboxGroup",
16738
18026
  "RadioGroup",
16739
18027
  "SuperleapTable",
18028
+ "Tabs",
18029
+ "Steps",
16740
18030
  ];
16741
18031
 
16742
18032
  componentNames.forEach((name) => {
@@ -16766,6 +18056,7 @@
16766
18056
  getSdk: client.getSdk.bind(client),
16767
18057
  isAvailable: client.isAvailable.bind(client),
16768
18058
  getDefaultConfig: client.getDefaultConfig.bind(client),
18059
+ getBaseUrl: client.getBaseUrl.bind(client),
16769
18060
  // Bridge methods (from crm.js)
16770
18061
  connect: client.connect ? client.connect.bind(client) : undefined,
16771
18062
  isConnected: client.isConnected
@@ -16824,7 +18115,7 @@
16824
18115
  },
16825
18116
  });
16826
18117
  document.dispatchEvent(event);
16827
- console.log("[Superleap-Flow] Library ready - v2.2.9");
18118
+ console.log("[Superleap-Flow] Library ready - v2.5.1");
16828
18119
  }
16829
18120
 
16830
18121
  // Wait for DOM to be ready before dispatching event