@superleapai/flow-ui 2.5.0 → 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.3.4
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-19T14:43:01.927Z
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
  /**
@@ -3669,7 +3681,7 @@
3669
3681
 
3670
3682
 
3671
3683
  // ============================================
3672
- // File 5/37: components/label.js
3684
+ // File 5/41: components/label.js
3673
3685
  // ============================================
3674
3686
 
3675
3687
  /**
@@ -3786,7 +3798,7 @@
3786
3798
 
3787
3799
 
3788
3800
  // ============================================
3789
- // File 6/37: core/flow.js
3801
+ // File 6/41: core/flow.js
3790
3802
  // ============================================
3791
3803
 
3792
3804
  /**
@@ -4038,6 +4050,48 @@
4038
4050
  return field;
4039
4051
  }
4040
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
+
4041
4095
  /**
4042
4096
  * Create a select dropdown field (using custom select component)
4043
4097
  * @param {Object} config - Configuration object
@@ -4448,6 +4502,7 @@
4448
4502
  size,
4449
4503
  canClear,
4450
4504
  initialLimit,
4505
+ initialFilter,
4451
4506
  helpText = null,
4452
4507
  } = config;
4453
4508
 
@@ -4467,6 +4522,7 @@
4467
4522
  size: size || "default",
4468
4523
  canClear: !!canClear,
4469
4524
  initialLimit,
4525
+ initialFilter,
4470
4526
  onChange: (value, record) => {
4471
4527
  set(fieldId, value);
4472
4528
  if (onChange) onChange(value, record);
@@ -4516,6 +4572,7 @@
4516
4572
  variant,
4517
4573
  size,
4518
4574
  initialLimit,
4575
+ initialFilter,
4519
4576
  displayFields,
4520
4577
  helpText = null,
4521
4578
  } = config;
@@ -4534,6 +4591,7 @@
4534
4591
  variant: variant || "default",
4535
4592
  size: size || "default",
4536
4593
  initialLimit,
4594
+ initialFilter,
4537
4595
  displayFields: displayFields || [],
4538
4596
  onValuesChange: (values, records) => {
4539
4597
  set(fieldId, values);
@@ -5091,6 +5149,40 @@
5091
5149
  return field;
5092
5150
  }
5093
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
+
5094
5186
  // ============================================================================
5095
5187
  // STEPPER COMPONENT
5096
5188
  // ============================================================================
@@ -5122,6 +5214,38 @@
5122
5214
  });
5123
5215
  }
5124
5216
 
5217
+ /**
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
5221
+ */
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
+ }
5232
+
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;
5247
+ }
5248
+
5125
5249
  // ============================================================================
5126
5250
  // TABLE COMPONENT
5127
5251
  // ============================================================================
@@ -5284,6 +5408,34 @@
5284
5408
  return field;
5285
5409
  }
5286
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
+
5287
5439
  // ============================================================================
5288
5440
  // TOAST NOTIFICATIONS
5289
5441
  // ============================================================================
@@ -5420,6 +5572,7 @@
5420
5572
  // Form components
5421
5573
  createInput,
5422
5574
  createTextarea,
5575
+ createRichTextEditor,
5423
5576
  createSelect,
5424
5577
  createTimePicker,
5425
5578
  createDateTimePicker,
@@ -5435,6 +5588,7 @@
5435
5588
  createCurrency,
5436
5589
  createPhoneInput,
5437
5590
  createCheckbox,
5591
+ createCheckboxGroup,
5438
5592
 
5439
5593
  // Button (delegates to Button component when available; resolved at call time via getComponent)
5440
5594
  createButton: function (config) {
@@ -5455,8 +5609,11 @@
5455
5609
 
5456
5610
  // Stepper
5457
5611
  renderStepper,
5612
+ createTabs,
5613
+ createSteps,
5458
5614
 
5459
5615
  // Alerts
5616
+ renderAlerts,
5460
5617
  showToast,
5461
5618
 
5462
5619
  // Table
@@ -5484,7 +5641,7 @@
5484
5641
 
5485
5642
 
5486
5643
  // ============================================
5487
- // File 7/37: components/toast.js
5644
+ // File 7/41: components/toast.js
5488
5645
  // ============================================
5489
5646
 
5490
5647
  /**
@@ -5833,7 +5990,7 @@
5833
5990
 
5834
5991
 
5835
5992
  // ============================================
5836
- // File 8/37: components/alert.js
5993
+ // File 8/41: components/alert.js
5837
5994
  // ============================================
5838
5995
 
5839
5996
  /**
@@ -6121,7 +6278,7 @@
6121
6278
 
6122
6279
 
6123
6280
  // ============================================
6124
- // File 9/37: components/button.js
6281
+ // File 9/41: components/button.js
6125
6282
  // ============================================
6126
6283
 
6127
6284
  /**
@@ -6328,7 +6485,7 @@
6328
6485
 
6329
6486
 
6330
6487
  // ============================================
6331
- // File 10/37: components/spinner.js
6488
+ // File 10/41: components/spinner.js
6332
6489
  // ============================================
6333
6490
 
6334
6491
  /**
@@ -6470,7 +6627,7 @@
6470
6627
 
6471
6628
 
6472
6629
  // ============================================
6473
- // File 11/37: components/badge.js
6630
+ // File 11/41: components/badge.js
6474
6631
  // ============================================
6475
6632
 
6476
6633
  /**
@@ -6611,7 +6768,7 @@
6611
6768
 
6612
6769
 
6613
6770
  // ============================================
6614
- // File 12/37: components/avatar.js
6771
+ // File 12/41: components/avatar.js
6615
6772
  // ============================================
6616
6773
 
6617
6774
  /**
@@ -6812,7 +6969,7 @@
6812
6969
 
6813
6970
 
6814
6971
  // ============================================
6815
- // File 13/37: components/icon.js
6972
+ // File 13/41: components/icon.js
6816
6973
  // ============================================
6817
6974
 
6818
6975
  /**
@@ -6893,6 +7050,40 @@
6893
7050
  /** Filled circle for "just color" mode (IconOrColor when only icon_color is set) */
6894
7051
  IconCircleFilled:
6895
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>',
6896
7087
  };
6897
7088
 
6898
7089
  function join() {
@@ -7018,7 +7209,7 @@
7018
7209
 
7019
7210
 
7020
7211
  // ============================================
7021
- // File 14/37: components/popover.js
7212
+ // File 14/41: components/popover.js
7022
7213
  // ============================================
7023
7214
 
7024
7215
  /**
@@ -7041,6 +7232,7 @@
7041
7232
  * @param {Function} [config.onOpen] - Called when popover opens (before positioning)
7042
7233
  * @param {string} [config.bodyClassName] - Optional class for body wrapper (overrides default padding)
7043
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
7044
7236
  * @returns {Object} Popover API {show, hide, destroy, element}
7045
7237
  */
7046
7238
  function create(config = {}) {
@@ -7055,6 +7247,7 @@
7055
7247
  onOpen = null,
7056
7248
  bodyClassName = "",
7057
7249
  panelClassName = "",
7250
+ modal = true,
7058
7251
  } = config;
7059
7252
 
7060
7253
  const triggerEl =
@@ -7064,7 +7257,7 @@
7064
7257
  return { show: noop, hide: noop, destroy: noop, element: null };
7065
7258
  }
7066
7259
 
7067
- // Wrap trigger in a relative container so popover can be absolute relative to it (full width so parent controls it)
7260
+ // Wrap trigger in a relative container for layout (full width); popover is portaled to body
7068
7261
  const container = document.createElement("div");
7069
7262
  container.className = "relative w-full";
7070
7263
  const triggerParent = triggerEl.parentNode;
@@ -7075,7 +7268,7 @@
7075
7268
 
7076
7269
  const wrapper = document.createElement("div");
7077
7270
  wrapper.className =
7078
- "absolute 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";
7079
7272
  wrapper.setAttribute("aria-hidden", "true");
7080
7273
 
7081
7274
  const panel = document.createElement("div");
@@ -7099,7 +7292,11 @@
7099
7292
  const body = document.createElement("div");
7100
7293
  body.className =
7101
7294
  bodyClassName ||
7102
- "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";
7103
7300
  if (panelClassName) {
7104
7301
  panel.className = panel.className + " " + panelClassName;
7105
7302
  }
@@ -7111,7 +7308,40 @@
7111
7308
  panel.appendChild(body);
7112
7309
 
7113
7310
  wrapper.appendChild(panel);
7114
- container.appendChild(wrapper);
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
+ }
7115
7345
 
7116
7346
  function noop() {}
7117
7347
 
@@ -7119,33 +7349,80 @@
7119
7349
  const triggerRect = triggerEl.getBoundingClientRect();
7120
7350
  const panelRect = panel.getBoundingClientRect();
7121
7351
  const gap = 8;
7122
- let top = 0;
7123
- 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
+ }
7124
7388
 
7125
- // Alignment offset: start = 0, center = half diff, end = full diff
7126
- const alignLeft = (align === "center" ? (triggerRect.width - panelRect.width) / 2 : align === "end" ? triggerRect.width - panelRect.width : 0);
7127
- const alignTop = (align === "center" ? (triggerRect.height - panelRect.height) / 2 : align === "end" ? triggerRect.height - panelRect.height : 0);
7389
+ panel.setAttribute("data-side", effectivePlacement);
7128
7390
 
7129
- 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) {
7130
7407
  case "bottom":
7131
- top = triggerRect.height + gap;
7132
- left = alignLeft;
7408
+ top = triggerRect.bottom + gap;
7409
+ left = triggerRect.left + alignLeft;
7133
7410
  break;
7134
7411
  case "top":
7135
- top = -panelRect.height - gap;
7136
- left = alignLeft;
7412
+ top = triggerRect.top - panelRect.height - gap;
7413
+ left = triggerRect.left + alignLeft;
7137
7414
  break;
7138
7415
  case "right":
7139
- top = alignTop;
7140
- left = triggerRect.width + gap;
7416
+ top = triggerRect.top + alignTop;
7417
+ left = triggerRect.right + gap;
7141
7418
  break;
7142
7419
  case "left":
7143
- top = alignTop;
7144
- left = -panelRect.width - gap;
7420
+ top = triggerRect.top + alignTop;
7421
+ left = triggerRect.left - panelRect.width - gap;
7145
7422
  break;
7146
7423
  default:
7147
- top = triggerRect.height + gap;
7148
- left = alignLeft;
7424
+ top = triggerRect.bottom + gap;
7425
+ left = triggerRect.left + alignLeft;
7149
7426
  }
7150
7427
 
7151
7428
  wrapper.style.transform = "";
@@ -7159,16 +7436,41 @@
7159
7436
  wrapper.classList.add("invisible", "opacity-0", "pointer-events-none");
7160
7437
  wrapper.classList.remove("visible", "opacity-100", "pointer-events-auto");
7161
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();
7162
7446
  if (onClose) onClose();
7163
7447
  }
7164
7448
 
7449
+ function onScrollOrResize() {
7450
+ if (wrapper.classList.contains("visible")) position();
7451
+ }
7452
+
7165
7453
  function show() {
7166
7454
  if (onOpen) onOpen();
7455
+ applyModalOpen();
7167
7456
  requestAnimationFrame(function () {
7168
7457
  position();
7169
- wrapper.classList.remove("invisible", "opacity-0", "pointer-events-none");
7458
+ wrapper.classList.remove(
7459
+ "invisible",
7460
+ "opacity-0",
7461
+ "pointer-events-none",
7462
+ );
7170
7463
  wrapper.classList.add("visible", "opacity-100", "pointer-events-auto");
7171
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
+ }
7172
7474
  requestAnimationFrame(function () {
7173
7475
  requestAnimationFrame(function () {
7174
7476
  panel.setAttribute("data-state", "open");
@@ -7179,6 +7481,16 @@
7179
7481
 
7180
7482
  function destroy() {
7181
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);
7182
7494
  if (wrapper.parentNode) {
7183
7495
  wrapper.parentNode.removeChild(wrapper);
7184
7496
  }
@@ -7228,6 +7540,8 @@
7228
7540
  show,
7229
7541
  hide,
7230
7542
  destroy,
7543
+ /** Re-run positioning (e.g. after async content load). Call when panel size changes. */
7544
+ updatePosition: position,
7231
7545
  setContent(newContent) {
7232
7546
  body.innerHTML = "";
7233
7547
  if (typeof newContent === "string") {
@@ -7249,7 +7563,7 @@
7249
7563
 
7250
7564
 
7251
7565
  // ============================================
7252
- // File 15/37: components/select.js
7566
+ // File 15/41: components/select.js
7253
7567
  // ============================================
7254
7568
 
7255
7569
  /**
@@ -7446,11 +7760,11 @@
7446
7760
 
7447
7761
  var content = document.createElement("div");
7448
7762
  content.setAttribute("role", "listbox");
7449
- content.className = "custom-select-content w-full max-h-[200px] overflow-hidden flex flex-col";
7763
+ content.className = "custom-select-content w-full max-h-[45vh] overflow-hidden flex flex-col";
7450
7764
 
7451
7765
  var optionsList = document.createElement("div");
7452
7766
  optionsList.className =
7453
- "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";
7454
7768
 
7455
7769
  if (options.length === 0) {
7456
7770
  var noOpt = document.createElement("div");
@@ -7509,7 +7823,7 @@
7509
7823
  align: "start",
7510
7824
  closeOnClickOutside: true,
7511
7825
  bodyClassName: "p-0 overflow-hidden",
7512
- panelClassName: "min-w-[var(--trigger-width)] max-h-[200px] overflow-hidden",
7826
+ panelClassName: "min-w-[var(--trigger-width)] max-h-[45vh] overflow-hidden",
7513
7827
  onOpen: function () {
7514
7828
  if (disabled) {
7515
7829
  popover.hide();
@@ -7807,7 +8121,7 @@
7807
8121
 
7808
8122
 
7809
8123
  // ============================================
7810
- // File 16/37: components/enum-select.js
8124
+ // File 16/41: components/enum-select.js
7811
8125
  // ============================================
7812
8126
 
7813
8127
  /**
@@ -8041,7 +8355,7 @@
8041
8355
  // Create dropdown content
8042
8356
  var content = document.createElement("div");
8043
8357
  content.setAttribute("role", "listbox");
8044
- content.className = "w-full min-w-[200px] max-h-[30vh] overflow-hidden flex flex-col";
8358
+ content.className = "w-full min-w-[200px] max-h-[45vh] overflow-hidden flex flex-col";
8045
8359
 
8046
8360
  // Search input (using InputComponent like phone-input)
8047
8361
  var searchContainer = document.createElement("div");
@@ -8102,7 +8416,7 @@
8102
8416
  // Options list
8103
8417
  var optionsList = document.createElement("div");
8104
8418
  optionsList.className =
8105
- "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";
8106
8420
 
8107
8421
  content.appendChild(searchContainer);
8108
8422
  content.appendChild(optionsList);
@@ -8590,7 +8904,7 @@
8590
8904
 
8591
8905
 
8592
8906
  // ============================================
8593
- // File 17/37: components/record-select.js
8907
+ // File 17/41: components/record-select.js
8594
8908
  // ============================================
8595
8909
 
8596
8910
  /**
@@ -8604,9 +8918,20 @@
8604
8918
 
8605
8919
  (function (global) {
8606
8920
 
8607
- var Popover = global.Popover;
8608
- var InputComponent = global.InputComponent;
8609
- var Spinner = global.Spinner;
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");
8610
8935
 
8611
8936
  /** When objectSlug === USERS, show Vivid Avatar (name-based color) instead of static icon */
8612
8937
  var STANDARD_OBJECT_SLUGS_USERS = "user";
@@ -8628,7 +8953,7 @@
8628
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>';
8629
8954
  var X_SVG =
8630
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>';
8631
-
8956
+
8632
8957
  var SEARCH_ICON =
8633
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>';
8634
8959
 
@@ -8643,7 +8968,8 @@
8643
8968
  "border-error-border hover:border-error-border-hover focus:border-1/2 focus:border-error-border-hover",
8644
8969
  warning:
8645
8970
  "border-warning-border hover:border-warning-border-hover focus:border-1/2 focus:border-warning-border-hover",
8646
- 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",
8647
8973
  inline:
8648
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",
8649
8975
  };
@@ -8652,7 +8978,9 @@
8652
8978
  large: "px-12 py-8",
8653
8979
  small: "px-8 py-4",
8654
8980
  };
8655
- var placeholderClass = placeholder ? " text-typography-quaternary-text" : "";
8981
+ var placeholderClass = placeholder
8982
+ ? " text-typography-quaternary-text"
8983
+ : "";
8656
8984
  var disabledClass = disabled
8657
8985
  ? " pointer-events-none cursor-not-allowed bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary"
8658
8986
  : "";
@@ -8708,15 +9036,27 @@
8708
9036
  function getObjectIconInfo(slug, objectSchema) {
8709
9037
  var color = "neutral";
8710
9038
  var iconStr = "IconDatabase";
8711
- if (objectSchema && objectSchema.properties && objectSchema.properties.icon_data) {
9039
+ if (
9040
+ objectSchema &&
9041
+ objectSchema.properties &&
9042
+ objectSchema.properties.icon_data
9043
+ ) {
8712
9044
  var iconData = objectSchema.properties.icon_data;
8713
- if (typeof iconData.color === "string" && iconData.color) color = iconData.color;
8714
- 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;
8715
9049
  }
8716
- 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
+ ) {
8717
9056
  if (OBJECT_SLUG_TO_ICON[slug]) {
8718
9057
  iconStr = OBJECT_SLUG_TO_ICON[slug].iconStr;
8719
- 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;
8720
9060
  } else {
8721
9061
  iconStr = "IconDatabase";
8722
9062
  color = "neutral";
@@ -8739,6 +9079,7 @@
8739
9079
  * @param {string} [config.size] - 'default' | 'large' | 'small'
8740
9080
  * @param {boolean} [config.canClear] - Show clear button when value is set
8741
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: [...] })
8742
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)
8743
9084
  * @returns {HTMLElement} Record select container element
8744
9085
  */
@@ -8747,12 +9088,14 @@
8747
9088
  var objectSlug = config.objectSlug;
8748
9089
  var objectSchema = config.objectSchema || null;
8749
9090
  var placeholder = config.placeholder || "Select a record";
8750
- var searchPlaceholder = config.searchPlaceholder || "Search " + (objectSlug || "") + "...";
9091
+ var searchPlaceholder =
9092
+ config.searchPlaceholder || "Search " + (objectSlug || "") + "...";
8751
9093
  var onChange = config.onChange;
8752
9094
  var variant = config.variant || "default";
8753
9095
  var size = config.size || "default";
8754
9096
  var canClear = !!config.canClear;
8755
9097
  var initialLimit = config.initialLimit != null ? config.initialLimit : 50;
9098
+ var initialFilter = config.initialFilter || null; // Can be array, object, or function returning either
8756
9099
 
8757
9100
  var disabled = config.disabled === true;
8758
9101
  var value =
@@ -8800,7 +9143,7 @@
8800
9143
  size,
8801
9144
  disabled,
8802
9145
  !value,
8803
- canClear && !!value && !disabled
9146
+ canClear && !!value && !disabled,
8804
9147
  );
8805
9148
  trigger.disabled = disabled;
8806
9149
  trigger.setAttribute("aria-haspopup", "listbox");
@@ -8809,7 +9152,8 @@
8809
9152
  trigger.classList.add("record-select-trigger");
8810
9153
 
8811
9154
  var triggerContent = document.createElement("div");
8812
- 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";
8813
9157
 
8814
9158
  var triggerIcon = document.createElement("span");
8815
9159
  triggerIcon.className = "record-select-trigger-icon shrink-0 hidden";
@@ -8852,7 +9196,9 @@
8852
9196
  // Dropdown content: search + list (same content pattern as Select)
8853
9197
  var content = document.createElement("div");
8854
9198
  content.setAttribute("role", "listbox");
8855
- content.className = "record-select-content max-h-[30vh] overflow-hidden flex flex-col";
9199
+ content.setAttribute("data-field-id", fieldId);
9200
+ content.className =
9201
+ "record-select-content max-h-[45vh] overflow-hidden flex flex-col";
8856
9202
 
8857
9203
  var searchWrap = document.createElement("div");
8858
9204
  searchWrap.className = "py-8 border-b-1/2 border-border-primary";
@@ -8883,12 +9229,12 @@
8883
9229
  } else {
8884
9230
  var fallbackWrapper = document.createElement("div");
8885
9231
  fallbackWrapper.className = "flex items-center gap-8 px-12";
8886
-
9232
+
8887
9233
  var searchIconSpan = document.createElement("span");
8888
9234
  searchIconSpan.className = "shrink-0 text-typography-tertiary-text";
8889
9235
  searchIconSpan.innerHTML = SEARCH_ICON;
8890
9236
  fallbackWrapper.appendChild(searchIconSpan);
8891
-
9237
+
8892
9238
  var searchInput = document.createElement("input");
8893
9239
  searchInput.type = "text";
8894
9240
  searchInput.className =
@@ -8902,21 +9248,22 @@
8902
9248
  content.appendChild(searchWrap);
8903
9249
 
8904
9250
  var optionsList = document.createElement("div");
8905
- optionsList.className = "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-select-options";
8906
-
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
+
8907
9254
  // Add scroll listener for infinite scroll
8908
9255
  optionsList.addEventListener("scroll", function () {
8909
9256
  if (isFetchingMore || !hasMoreRecords) return;
8910
9257
  var scrollHeight = optionsList.scrollHeight;
8911
9258
  var scrollTop = optionsList.scrollTop;
8912
9259
  var clientHeight = optionsList.clientHeight;
8913
-
9260
+
8914
9261
  // Trigger load more when scrolled to bottom (with 50px threshold)
8915
9262
  if (scrollTop + clientHeight >= scrollHeight - 50) {
8916
9263
  loadMoreRecords();
8917
9264
  }
8918
9265
  });
8919
-
9266
+
8920
9267
  content.appendChild(optionsList);
8921
9268
 
8922
9269
  popover = Popover.create({
@@ -8926,30 +9273,41 @@
8926
9273
  align: "start",
8927
9274
  closeOnClickOutside: true,
8928
9275
  bodyClassName: "p-0 overflow-hidden",
8929
- panelClassName: "max-h-[30vh] overflow-hidden",
9276
+ panelClassName: "min-w-[var(--trigger-width)] overflow-hidden",
8930
9277
  onOpen: function () {
8931
9278
  if (disabled) {
8932
9279
  popover.hide();
8933
9280
  return;
8934
9281
  }
8935
- document.querySelectorAll(".custom-select.open, .record-select.open").forEach(function (other) {
8936
- if (other !== container) {
8937
- other.classList.remove("open");
8938
- var t = other.querySelector("button, .custom-select-trigger, .record-select-trigger");
8939
- if (t) t.setAttribute("aria-expanded", "false");
8940
- }
8941
- });
8942
9282
  isOpen = true;
8943
9283
  container.classList.add("open");
8944
9284
  trigger.setAttribute("aria-expanded", "true");
8945
9285
  searchTerm = "";
8946
9286
  if (searchInputWrapper) searchInputWrapper.setValue("");
8947
9287
  else if (searchInputEl) searchInputEl.value = "";
8948
- content.style.minWidth = trigger.offsetWidth + "px";
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
+ }
8949
9294
  loadInitialAndRender();
8950
9295
  setTimeout(function () {
8951
9296
  if (searchInputEl) searchInputEl.focus();
8952
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
+ );
9309
+ }
9310
+ } catch (e) {}
8953
9311
  },
8954
9312
  onClose: function () {
8955
9313
  isOpen = false;
@@ -8965,7 +9323,8 @@
8965
9323
  },
8966
9324
  });
8967
9325
 
8968
- if (clearBtn) clearBtn.style.display = canClear && value && !disabled ? "" : "none";
9326
+ if (clearBtn)
9327
+ clearBtn.style.display = canClear && value && !disabled ? "" : "none";
8969
9328
 
8970
9329
  function setValue(newVal) {
8971
9330
  value = newVal !== undefined && newVal !== null ? newVal : "";
@@ -8979,16 +9338,18 @@
8979
9338
 
8980
9339
  function updateTriggerDisplay() {
8981
9340
  if (selectedRecord) {
8982
- triggerText.textContent = selectedRecord.name || selectedRecord.label || value;
9341
+ triggerText.textContent =
9342
+ selectedRecord.name || selectedRecord.label || value;
8983
9343
  trigger.classList.remove("placeholder");
8984
9344
  trigger.className = triggerClasses(
8985
9345
  variant,
8986
9346
  size,
8987
9347
  disabled,
8988
9348
  false,
8989
- canClear && !!value && !disabled
9349
+ canClear && !!value && !disabled,
8990
9350
  );
8991
- 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";
8992
9353
  triggerIcon.innerHTML = "";
8993
9354
  if (objectSlug === STANDARD_OBJECT_SLUGS_USERS) {
8994
9355
  var Avatar = getAvatar();
@@ -9000,7 +9361,9 @@
9000
9361
  });
9001
9362
  triggerIcon.appendChild(vividEl);
9002
9363
  } else {
9003
- renderStaticIconPlaceholder(selectedRecord.name || selectedRecord.label);
9364
+ renderStaticIconPlaceholder(
9365
+ selectedRecord.name || selectedRecord.label,
9366
+ );
9004
9367
  }
9005
9368
  } else {
9006
9369
  renderStaticObjectIcon();
@@ -9013,12 +9376,13 @@
9013
9376
  size,
9014
9377
  disabled,
9015
9378
  true,
9016
- canClear && !!value && !disabled
9379
+ canClear && !!value && !disabled,
9017
9380
  );
9018
9381
  triggerIcon.className = "record-select-trigger-icon shrink-0 hidden";
9019
9382
  triggerIcon.innerHTML = "";
9020
9383
  }
9021
- if (clearBtn) clearBtn.style.display = canClear && value && !disabled ? "" : "none";
9384
+ if (clearBtn)
9385
+ clearBtn.style.display = canClear && value && !disabled ? "" : "none";
9022
9386
  }
9023
9387
 
9024
9388
  function renderStaticObjectIcon() {
@@ -9055,18 +9419,39 @@
9055
9419
  var fields = ["id", "name"];
9056
9420
  var actualLimit = limit || initialLimit;
9057
9421
  var offset = page ? (page - 1) * actualLimit : 0;
9058
-
9422
+
9059
9423
  try {
9060
9424
  if (model && typeof model.select === "function") {
9061
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
+ }
9062
9442
  if (search && search.trim()) {
9063
- q = q.filterBy({
9443
+ filters.push({
9064
9444
  or: [
9065
9445
  { field: "name", operator: "contains", value: search.trim() },
9066
9446
  { field: "id", operator: "eq", value: search.trim() },
9067
9447
  ],
9068
9448
  });
9069
9449
  }
9450
+ if (filters.length > 0) {
9451
+ q = q.filterBy(
9452
+ filters.length === 1 ? filters[0] : { and: filters },
9453
+ );
9454
+ }
9070
9455
  var orderBy = ["name"];
9071
9456
  if (objectSlug === "account") orderBy.push("-ParentId");
9072
9457
  return q
@@ -9129,7 +9514,12 @@
9129
9514
  updateTriggerDisplay();
9130
9515
  })
9131
9516
  .catch(function () {
9132
- selectedRecord = { id: value, value: value, name: value, label: value };
9517
+ selectedRecord = {
9518
+ id: value,
9519
+ value: value,
9520
+ name: value,
9521
+ label: value,
9522
+ };
9133
9523
  updateTriggerDisplay();
9134
9524
  });
9135
9525
  }
@@ -9140,7 +9530,9 @@
9140
9530
  loadWrap.className =
9141
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";
9142
9532
  if (Spinner && typeof Spinner.create === "function") {
9143
- loadWrap.appendChild(Spinner.create({ size: "small", text: "Loading..." }));
9533
+ loadWrap.appendChild(
9534
+ Spinner.create({ size: "small", text: "Loading..." }),
9535
+ );
9144
9536
  } else {
9145
9537
  var loadText = document.createElement("span");
9146
9538
  loadText.textContent = "Loading...";
@@ -9148,12 +9540,12 @@
9148
9540
  }
9149
9541
  optionsList.appendChild(loadWrap);
9150
9542
  }
9151
-
9543
+
9152
9544
  function showLoadingMore() {
9153
9545
  // Remove existing loading more indicator
9154
9546
  var existing = optionsList.querySelector(".record-select-loading-more");
9155
9547
  if (existing) existing.remove();
9156
-
9548
+
9157
9549
  var loadWrap = document.createElement("div");
9158
9550
  loadWrap.className =
9159
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";
@@ -9166,9 +9558,11 @@
9166
9558
  }
9167
9559
  optionsList.appendChild(loadWrap);
9168
9560
  }
9169
-
9561
+
9170
9562
  function removeLoadingMore() {
9171
- var loadingMore = optionsList.querySelector(".record-select-loading-more");
9563
+ var loadingMore = optionsList.querySelector(
9564
+ ".record-select-loading-more",
9565
+ );
9172
9566
  if (loadingMore) loadingMore.remove();
9173
9567
  }
9174
9568
 
@@ -9187,13 +9581,15 @@
9187
9581
  existingOptions.forEach(function (opt) {
9188
9582
  opt.remove();
9189
9583
  });
9190
-
9584
+
9191
9585
  // Remove old loading/empty states
9192
- var oldStates = optionsList.querySelectorAll(".record-select-loading, .record-select-empty");
9586
+ var oldStates = optionsList.querySelectorAll(
9587
+ ".record-select-loading, .record-select-empty",
9588
+ );
9193
9589
  oldStates.forEach(function (el) {
9194
9590
  el.remove();
9195
9591
  });
9196
-
9592
+
9197
9593
  filteredRecords.forEach(function (rec) {
9198
9594
  var optionValue = rec.id || rec.value;
9199
9595
  var optionLabel = rec.name || rec.label || rec.value;
@@ -9208,7 +9604,7 @@
9208
9604
  "hover:bg-fill-tertiary-fill-light-gray focus:bg-fill-tertiary-fill-light-gray",
9209
9605
  isSelected
9210
9606
  ? "bg-primary-surface hover:!bg-primary-surface-hover"
9211
- : ""
9607
+ : "",
9212
9608
  );
9213
9609
 
9214
9610
  var optContent = document.createElement("span");
@@ -9266,70 +9662,93 @@
9266
9662
 
9267
9663
  optionsList.appendChild(option);
9268
9664
  });
9269
-
9665
+
9270
9666
  // Add loading more indicator at the bottom if fetching
9271
9667
  if (isFetchingMore) {
9272
9668
  showLoadingMore();
9273
9669
  }
9274
9670
  }
9275
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
+
9276
9682
  function loadInitialAndRender() {
9277
9683
  showLoading();
9278
9684
  currentPage = 1;
9279
9685
  hasMoreRecords = true;
9280
9686
  totalFetched = 0;
9281
-
9282
- fetchRecords(searchTerm, initialLimit, 1).then(function (result) {
9283
- allRecords = result.records;
9284
- filteredRecords = result.records;
9285
- hasMoreRecords = result.hasMore;
9286
- totalFetched = result.records.length;
9287
- currentPage = 1;
9288
-
9289
- if (value && !result.records.some(function (r) { return (r.id || r.value) === value; })) {
9290
- loadSelectedRecord();
9291
- } else if (value && result.records.length) {
9292
- var sel = result.records.find(function (r) { return (r.id || r.value) === value; });
9293
- if (sel) selectedRecord = sel;
9294
- updateTriggerDisplay();
9295
- }
9296
- if (filteredRecords.length === 0) {
9297
- showEmpty(searchTerm ? "No results found" : "No records available");
9298
- } else {
9299
- renderOptions();
9300
- }
9301
- }).catch(function () {
9302
- showEmpty("Failed to load records");
9303
- hasMoreRecords = false;
9304
- });
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
+ });
9305
9722
  }
9306
-
9723
+
9307
9724
  function loadMoreRecords() {
9308
9725
  if (isFetchingMore || !hasMoreRecords) return;
9309
-
9726
+
9310
9727
  isFetchingMore = true;
9311
9728
  currentPage += 1;
9312
9729
  showLoadingMore();
9313
-
9314
- fetchRecords(searchTerm, initialLimit, currentPage).then(function (result) {
9315
- isFetchingMore = false;
9316
- removeLoadingMore();
9317
-
9318
- if (result.records.length > 0) {
9319
- allRecords = allRecords.concat(result.records);
9320
- filteredRecords = filteredRecords.concat(result.records);
9321
- totalFetched += result.records.length;
9322
- hasMoreRecords = result.hasMore;
9323
- renderOptions();
9324
- } 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();
9325
9750
  hasMoreRecords = false;
9326
- }
9327
- }).catch(function (err) {
9328
- console.error("[RecordSelect] loadMoreRecords error:", err);
9329
- isFetchingMore = false;
9330
- removeLoadingMore();
9331
- hasMoreRecords = false;
9332
- });
9751
+ });
9333
9752
  }
9334
9753
 
9335
9754
  function debouncedSearch() {
@@ -9340,22 +9759,26 @@
9340
9759
  currentPage = 1;
9341
9760
  hasMoreRecords = true;
9342
9761
  totalFetched = 0;
9343
-
9344
- fetchRecords(searchTerm, initialLimit, 1).then(function (result) {
9345
- allRecords = result.records;
9346
- filteredRecords = result.records;
9347
- hasMoreRecords = result.hasMore;
9348
- totalFetched = result.records.length;
9349
-
9350
- if (result.records.length === 0) {
9351
- showEmpty("No results found");
9352
- } else {
9353
- renderOptions();
9354
- }
9355
- }).catch(function () {
9356
- showEmpty("Search failed");
9357
- hasMoreRecords = false;
9358
- });
9762
+
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;
9769
+
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
+ });
9359
9782
  }, 500);
9360
9783
  }
9361
9784
 
@@ -9400,7 +9823,7 @@
9400
9823
  size,
9401
9824
  disabled,
9402
9825
  !value,
9403
- canClear && !!value && !disabled
9826
+ canClear && !!value && !disabled,
9404
9827
  );
9405
9828
  if (disabled && isOpen) closeDropdown();
9406
9829
  };
@@ -9419,7 +9842,7 @@
9419
9842
 
9420
9843
 
9421
9844
  // ============================================
9422
- // File 18/37: components/multiselect.js
9845
+ // File 18/41: components/multiselect.js
9423
9846
  // ============================================
9424
9847
 
9425
9848
  /**
@@ -9577,11 +10000,11 @@
9577
10000
  var content = document.createElement("div");
9578
10001
  content.setAttribute("role", "listbox");
9579
10002
  content.setAttribute("aria-multiselectable", "true");
9580
- content.className = "custom-multiselect-content w-full max-h-[200px] overflow-hidden flex flex-col";
10003
+ content.className = "custom-multiselect-content w-full max-h-[45vh] overflow-hidden flex flex-col";
9581
10004
 
9582
10005
  var optionsList = document.createElement("div");
9583
10006
  optionsList.className =
9584
- "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";
9585
10008
 
9586
10009
  function isSelected(optionValue) {
9587
10010
  return values.some(function (v) {
@@ -9686,7 +10109,7 @@
9686
10109
  align: "start",
9687
10110
  closeOnClickOutside: true,
9688
10111
  bodyClassName: "p-0 overflow-hidden",
9689
- panelClassName: "min-w-[var(--trigger-width)] max-h-[200px] overflow-hidden",
10112
+ panelClassName: "min-w-[var(--trigger-width)] max-h-[45vh] overflow-hidden",
9690
10113
  onOpen: function () {
9691
10114
  if (disabled) {
9692
10115
  popover.hide();
@@ -9800,7 +10223,7 @@
9800
10223
 
9801
10224
 
9802
10225
  // ============================================
9803
- // File 19/37: components/enum-multiselect.js
10226
+ // File 19/41: components/enum-multiselect.js
9804
10227
  // ============================================
9805
10228
 
9806
10229
  /**
@@ -10061,7 +10484,7 @@
10061
10484
  var content = document.createElement("div");
10062
10485
  content.setAttribute("role", "listbox");
10063
10486
  content.setAttribute("aria-multiselectable", "true");
10064
- content.className = "w-full min-w-[200px] max-h-[30vh] overflow-hidden flex flex-col";
10487
+ content.className = "w-full min-w-[200px] max-h-[45vh] overflow-hidden flex flex-col";
10065
10488
 
10066
10489
  // Search input (using InputComponent like enum-select)
10067
10490
  var searchContainer = document.createElement("div");
@@ -10122,7 +10545,7 @@
10122
10545
  // Options list
10123
10546
  var optionsList = document.createElement("div");
10124
10547
  optionsList.className =
10125
- "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";
10126
10549
 
10127
10550
  content.appendChild(searchContainer);
10128
10551
  content.appendChild(optionsList);
@@ -10624,7 +11047,7 @@
10624
11047
 
10625
11048
 
10626
11049
  // ============================================
10627
- // File 20/37: components/record-multiselect.js
11050
+ // File 20/41: components/record-multiselect.js
10628
11051
  // ============================================
10629
11052
 
10630
11053
  /**
@@ -10780,6 +11203,7 @@
10780
11203
  * @param {string} [config.size] - 'default' | 'large' | 'small'
10781
11204
  * @param {number} [config.initialLimit] - Initial fetch limit (default 50)
10782
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: [...] })
10783
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)
10784
11208
  * @returns {HTMLElement} Record multiselect container element
10785
11209
  */
@@ -10794,6 +11218,7 @@
10794
11218
  var variant = config.variant || "default";
10795
11219
  var size = config.size || "default";
10796
11220
  var initialLimit = config.initialLimit != null ? config.initialLimit : 50;
11221
+ var initialFilter = config.initialFilter || null; // Can be array, object, or function returning either
10797
11222
  var displayFields = config.displayFields || [];
10798
11223
 
10799
11224
  var disabled = config.disabled === true;
@@ -10919,7 +11344,7 @@
10919
11344
  var content = document.createElement("div");
10920
11345
  content.setAttribute("role", "listbox");
10921
11346
  content.setAttribute("aria-multiselectable", "true");
10922
- content.className = "record-multiselect-content max-h-[30vh] overflow-hidden flex flex-col";
11347
+ content.className = "record-multiselect-content max-h-[45vh] overflow-hidden flex flex-col";
10923
11348
 
10924
11349
  var searchWrap = document.createElement("div");
10925
11350
  searchWrap.className = "p-8 pb-4 border-b-1/2 border-border-primary ";
@@ -10969,7 +11394,7 @@
10969
11394
  content.appendChild(searchWrap);
10970
11395
 
10971
11396
  var optionsList = document.createElement("div");
10972
- 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";
10973
11398
 
10974
11399
  // Add scroll listener for infinite scroll
10975
11400
  optionsList.addEventListener("scroll", function () {
@@ -10997,28 +11422,22 @@
10997
11422
  align: "start",
10998
11423
  closeOnClickOutside: true,
10999
11424
  bodyClassName: "p-0 overflow-hidden",
11000
- panelClassName: "max-h-[30vh] overflow-hidden",
11425
+ panelClassName: "min-w-[var(--trigger-width)] overflow-hidden",
11001
11426
  onOpen: function () {
11002
11427
  if (disabled) {
11003
11428
  popover.hide();
11004
11429
  return;
11005
11430
  }
11006
- document.querySelectorAll(".custom-select.open, .record-select.open, .custom-multiselect.open, .record-multiselect.open, .enum-select, .enum-multiselect").forEach(function (other) {
11007
- if (other !== container && other.popoverInstance) {
11008
- other.popoverInstance.hide();
11009
- }
11010
- });
11011
11431
  container.classList.add("open");
11012
11432
  trigger.setAttribute("aria-expanded", "true");
11013
11433
  searchTerm = "";
11014
11434
  if (searchInputWrapper) searchInputWrapper.setValue("");
11015
11435
  else if (searchInputEl) searchInputEl.value = "";
11016
- var triggerWidthPx = trigger.offsetWidth + "px";
11017
- content.style.minWidth = triggerWidthPx;
11018
- content.style.width = triggerWidthPx;
11019
11436
  if (popover.panel) {
11020
- popover.panel.style.width = triggerWidthPx;
11437
+ var triggerWidthPx = trigger.offsetWidth + "px";
11438
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
11021
11439
  popover.panel.style.minWidth = triggerWidthPx;
11440
+ popover.panel.style.width = triggerWidthPx;
11022
11441
  }
11023
11442
  loadInitialAndRender();
11024
11443
  setTimeout(function () {
@@ -11097,14 +11516,23 @@
11097
11516
  try {
11098
11517
  if (model && typeof model.select === "function") {
11099
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
+ }
11100
11525
  if (search && search.trim()) {
11101
- q = q.filterBy({
11526
+ filters.push({
11102
11527
  or: [
11103
11528
  { field: "name", operator: "contains", value: search.trim() },
11104
11529
  { field: "id", operator: "eq", value: search.trim() },
11105
11530
  ],
11106
11531
  });
11107
11532
  }
11533
+ if (filters.length > 0) {
11534
+ q = q.filterBy(filters.length === 1 ? filters[0] : { and: filters });
11535
+ }
11108
11536
  var orderBy = ["name"];
11109
11537
  if (objectSlug === "account") orderBy.push("-ParentId");
11110
11538
  return q
@@ -11363,6 +11791,16 @@
11363
11791
  }
11364
11792
  }
11365
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
+
11366
11804
  function loadInitialAndRender() {
11367
11805
  showLoading();
11368
11806
  currentPage = 1;
@@ -11381,9 +11819,11 @@
11381
11819
  } else {
11382
11820
  renderOptions();
11383
11821
  }
11822
+ scheduleUpdatePosition();
11384
11823
  }).catch(function () {
11385
11824
  showEmpty("Failed to load records");
11386
11825
  hasMoreRecords = false;
11826
+ scheduleUpdatePosition();
11387
11827
  });
11388
11828
  }
11389
11829
 
@@ -11435,9 +11875,11 @@
11435
11875
  } else {
11436
11876
  renderOptions();
11437
11877
  }
11878
+ scheduleUpdatePosition();
11438
11879
  }).catch(function () {
11439
11880
  showEmpty("Search failed");
11440
11881
  hasMoreRecords = false;
11882
+ scheduleUpdatePosition();
11441
11883
  });
11442
11884
  }, 500);
11443
11885
  }
@@ -11507,7 +11949,7 @@
11507
11949
 
11508
11950
 
11509
11951
  // ============================================
11510
- // File 21/37: components/input.js
11952
+ // File 21/41: components/input.js
11511
11953
  // ============================================
11512
11954
 
11513
11955
  /**
@@ -11534,7 +11976,7 @@
11534
11976
 
11535
11977
  var WRAPPER_CLASS = {
11536
11978
  base:
11537
- "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",
11538
11980
  default:
11539
11981
  "bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
11540
11982
  error:
@@ -11609,13 +12051,16 @@
11609
12051
  var isPassword = type === "password";
11610
12052
 
11611
12053
  var wrapper = document.createElement("div");
11612
- wrapper.className = join(
11613
- WRAPPER_CLASS.base,
11614
- WRAPPER_CLASS[variant] || WRAPPER_CLASS.default,
11615
- inputSize === "large" ? WRAPPER_CLASS.sizeLarge : inputSize === "small" ? WRAPPER_CLASS.sizeSmall : WRAPPER_CLASS.sizeDefault,
11616
- disabled ? WRAPPER_CLASS.disabled : "",
11617
- config.className || ""
11618
- );
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();
11619
12064
  wrapper.setAttribute("data-input-variant", variant);
11620
12065
 
11621
12066
  if (config.prefixNode) {
@@ -11745,19 +12190,12 @@
11745
12190
  wrapper.setVariant = function (v) {
11746
12191
  variant = v;
11747
12192
  wrapper.setAttribute("data-input-variant", v);
11748
- wrapper.className = join(
11749
- WRAPPER_CLASS.base,
11750
- WRAPPER_CLASS[variant] || WRAPPER_CLASS.default,
11751
- inputSize === "large" ? WRAPPER_CLASS.sizeLarge : inputSize === "small" ? WRAPPER_CLASS.sizeSmall : WRAPPER_CLASS.sizeDefault,
11752
- disabled ? WRAPPER_CLASS.disabled : "",
11753
- config.className || ""
11754
- );
12193
+ applyWrapperClasses();
11755
12194
  };
11756
12195
  wrapper.setDisabled = function (d) {
11757
12196
  disabled = !!d;
11758
12197
  input.disabled = disabled;
11759
- wrapper.classList.toggle("cursor-not-allowed", disabled);
11760
- wrapper.classList.toggle("opacity-60", disabled);
12198
+ applyWrapperClasses();
11761
12199
  };
11762
12200
 
11763
12201
  return wrapper;
@@ -11772,7 +12210,7 @@
11772
12210
 
11773
12211
 
11774
12212
  // ============================================
11775
- // File 22/37: components/currency.js
12213
+ // File 22/41: components/currency.js
11776
12214
  // ============================================
11777
12215
 
11778
12216
  /**
@@ -11802,7 +12240,7 @@
11802
12240
  sizeLarge: "",
11803
12241
  sizeSmall: "",
11804
12242
  disabled:
11805
- "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",
11806
12244
  };
11807
12245
 
11808
12246
  // Currency type label: fit-content, separator (border-r) on type only, full height
@@ -11989,7 +12427,12 @@
11989
12427
  wrapper.setDisabled = function (d) {
11990
12428
  disabled = !!d;
11991
12429
  input.disabled = disabled;
11992
- 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
+ );
11993
12436
  };
11994
12437
 
11995
12438
  return wrapper;
@@ -12005,7 +12448,7 @@
12005
12448
 
12006
12449
 
12007
12450
  // ============================================
12008
- // File 23/37: components/textarea.js
12451
+ // File 23/41: components/textarea.js
12009
12452
  // ============================================
12010
12453
 
12011
12454
  /**
@@ -12030,7 +12473,7 @@
12030
12473
  warning:
12031
12474
  "min-h-[80px] border-warning-base hover:border-warning-base focus:border-warning-base",
12032
12475
  disabled:
12033
- "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",
12034
12477
  };
12035
12478
 
12036
12479
  function join() {
@@ -12125,7 +12568,347 @@
12125
12568
 
12126
12569
 
12127
12570
  // ============================================
12128
- // File 24/37: components/checkbox.js
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;
12660
+ var disabled = !!config.disabled;
12661
+ var onChange = config.onChange;
12662
+
12663
+ var wrapper = document.createElement("div");
12664
+ wrapper.className = "w-full rounded-12 border border-borderColor-border-primary shadow-soft-2x-small";
12665
+
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"
12679
+ );
12680
+ editorEl.style.minHeight = minHeightPx + "px";
12681
+ if (value) editorEl.innerHTML = value;
12682
+ editorEl.setAttribute("data-placeholder", placeholder);
12683
+
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();
12700
+
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;
12885
+ };
12886
+ wrapper.getValue = function () {
12887
+ return getHtml();
12888
+ };
12889
+ wrapper.setValue = function (v) {
12890
+ setHtml(v);
12891
+ };
12892
+ wrapper.setDisabled = function (d) {
12893
+ disabled = !!d;
12894
+ editorEl.contentEditable = !disabled;
12895
+ toolbar.querySelectorAll("button").forEach(function (b) {
12896
+ b.disabled = disabled;
12897
+ });
12898
+ };
12899
+
12900
+ return wrapper;
12901
+ }
12902
+
12903
+ global.RichTextEditorComponent = {
12904
+ create: create,
12905
+ };
12906
+ })(typeof window !== "undefined" ? window : undefined);
12907
+
12908
+
12909
+
12910
+ // ============================================
12911
+ // File 25/41: components/checkbox.js
12129
12912
  // ============================================
12130
12913
 
12131
12914
  /**
@@ -12164,10 +12947,18 @@
12164
12947
  };
12165
12948
 
12166
12949
  var CHECKBOX_BASE_CLASS =
12167
- "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";
12168
12957
 
12169
12958
  var CHECKBOX_CHECKED_CLASS =
12170
- "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";
12171
12962
 
12172
12963
  var LABEL_BASE_CLASS =
12173
12964
  "cursor-pointer pb-0 text-reg-12 leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70";
@@ -12231,12 +13022,16 @@
12231
13022
 
12232
13023
  // Custom checkbox visual
12233
13024
  var checkboxBox = document.createElement("div");
12234
- checkboxBox.className = join(
12235
- CHECKBOX_BASE_CLASS,
12236
- CHECKBOX_CHECKED_CLASS,
12237
- CHECKBOX_SIZES[size] || CHECKBOX_SIZES.default,
12238
- "cursor-pointer"
12239
- );
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();
12240
13035
  checkboxBox.setAttribute("role", "checkbox");
12241
13036
  checkboxBox.setAttribute("tabindex", disabled ? "-1" : "0");
12242
13037
  checkboxBox.setAttribute("aria-checked", indeterminate ? "mixed" : checked ? "true" : "false");
@@ -12353,6 +13148,7 @@
12353
13148
  } else {
12354
13149
  checkboxBox.removeAttribute("aria-disabled");
12355
13150
  }
13151
+ applyCheckboxBoxClasses();
12356
13152
  updateCheckedState();
12357
13153
  };
12358
13154
 
@@ -12385,7 +13181,210 @@
12385
13181
 
12386
13182
 
12387
13183
  // ============================================
12388
- // 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
12389
13388
  // ============================================
12390
13389
 
12391
13390
  /**
@@ -12796,7 +13795,7 @@
12796
13795
 
12797
13796
 
12798
13797
  // ============================================
12799
- // File 26/37: components/enumeration.js
13798
+ // File 28/41: components/enumeration.js
12800
13799
  // ============================================
12801
13800
 
12802
13801
  /**
@@ -12808,7 +13807,7 @@
12808
13807
  (function (global) {
12809
13808
 
12810
13809
  var BASE_CLASS =
12811
- "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";
12812
13811
 
12813
13812
  var VARIANTS = {
12814
13813
  default:
@@ -12832,7 +13831,7 @@
12832
13831
  };
12833
13832
 
12834
13833
  var DISABLED_CLASS =
12835
- "pointer-events-none !cursor-not-allowed opacity-50";
13834
+ "pointer-events-none !cursor-not-allowed opacity-50 bg-fill-tertiary-fill-light-gray";
12836
13835
  var READONLY_CLASS = "pointer-events-none";
12837
13836
 
12838
13837
  var ITEM_BASE_CLASS =
@@ -12891,20 +13890,23 @@
12891
13890
  var iconSize = opts.iconSize || "default";
12892
13891
  var defaultValue = opts.defaultValue;
12893
13892
  var disabled = opts.disabled === true;
12894
- var readOnly = opts.readOnly === true;
12895
- var className = opts.className || "";
12896
- var onValueChange = opts.onValueChange;
12897
- var children = opts.children;
12898
-
12899
- var wrapper = document.createElement("div");
12900
- wrapper.className = join(
12901
- BASE_CLASS,
12902
- VARIANTS[variant] != null ? VARIANTS[variant] : VARIANTS.default,
12903
- SIZES[size] != null ? SIZES[size] : SIZES.default,
12904
- disabled ? DISABLED_CLASS : "",
12905
- readOnly ? READONLY_CLASS : "",
12906
- className
12907
- );
13893
+ var readOnly = opts.readOnly === true;
13894
+ var className = opts.className || "";
13895
+ var onValueChange = opts.onValueChange;
13896
+ var children = opts.children;
13897
+
13898
+ var wrapper = document.createElement("div");
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();
12908
13910
  wrapper.setAttribute("data-enumeration-variant", variant);
12909
13911
 
12910
13912
  var count =
@@ -12974,6 +13976,22 @@
12974
13976
  }
12975
13977
  }
12976
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
+
12977
13995
  return wrapper;
12978
13996
  }
12979
13997
 
@@ -13015,7 +14033,7 @@
13015
14033
 
13016
14034
 
13017
14035
  // ============================================
13018
- // File 27/37: components/time-picker.js
14036
+ // File 29/41: components/time-picker.js
13019
14037
  // ============================================
13020
14038
 
13021
14039
  /**
@@ -13376,7 +14394,7 @@
13376
14394
 
13377
14395
 
13378
14396
  // ============================================
13379
- // File 28/37: components/duration/duration-utils.js
14397
+ // File 30/41: components/duration/duration-utils.js
13380
14398
  // ============================================
13381
14399
 
13382
14400
  /**
@@ -13546,7 +14564,7 @@
13546
14564
 
13547
14565
 
13548
14566
  // ============================================
13549
- // File 29/37: components/duration/duration-constants.js
14567
+ // File 31/41: components/duration/duration-constants.js
13550
14568
  // ============================================
13551
14569
 
13552
14570
  /**
@@ -13598,7 +14616,7 @@
13598
14616
 
13599
14617
 
13600
14618
  // ============================================
13601
- // File 30/37: components/duration/duration.js
14619
+ // File 32/41: components/duration/duration.js
13602
14620
  // ============================================
13603
14621
 
13604
14622
  /**
@@ -13642,7 +14660,7 @@
13642
14660
  sizeLarge: "px-12 py-8",
13643
14661
  sizeSmall: "px-12 py-4",
13644
14662
  disabled:
13645
- "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",
13646
14664
  };
13647
14665
 
13648
14666
  function join() {
@@ -14035,8 +15053,13 @@
14035
15053
  };
14036
15054
  container.setDisabled = function (d) {
14037
15055
  disabled = !!d;
14038
- triggerWrapper.classList.toggle("cursor-not-allowed", disabled);
14039
- 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
+ );
14040
15063
  triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
14041
15064
  if (disabled) popoverApi.hide();
14042
15065
  };
@@ -14052,7 +15075,7 @@
14052
15075
 
14053
15076
 
14054
15077
  // ============================================
14055
- // 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
14056
15079
  // ============================================
14057
15080
 
14058
15081
  /**
@@ -14311,7 +15334,7 @@
14311
15334
 
14312
15335
 
14313
15336
  // ============================================
14314
- // File 32/37: components/date-time-picker/date-time-picker.js
15337
+ // File 34/41: components/date-time-picker/date-time-picker.js
14315
15338
  // ============================================
14316
15339
 
14317
15340
  /**
@@ -14556,13 +15579,15 @@
14556
15579
  var displayMonth = validDate ? new Date(validDate.getTime()) : new Date();
14557
15580
 
14558
15581
  var triggerWrapper = document.createElement("div");
14559
- var triggerClasses = join(
14560
- "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",
14561
- "bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
14562
- size === "large" ? "px-12 py-8" : size === "small" ? "px-12 py-4" : "px-12 py-6",
14563
- disabled ? "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary" : "cursor-pointer"
14564
- );
14565
- 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);
14566
15591
  triggerWrapper.setAttribute("role", "button");
14567
15592
  triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
14568
15593
  triggerWrapper.setAttribute("aria-haspopup", "dialog");
@@ -14830,8 +15855,7 @@
14830
15855
  };
14831
15856
  container.setDisabled = function (d) {
14832
15857
  disabled = !!d;
14833
- triggerWrapper.classList.toggle("cursor-not-allowed", disabled);
14834
- triggerWrapper.classList.toggle("opacity-60", disabled);
15858
+ triggerWrapper.className = getTriggerClassName(disabled);
14835
15859
  triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
14836
15860
  if (disabled) popoverApi.hide();
14837
15861
  };
@@ -14847,7 +15871,7 @@
14847
15871
 
14848
15872
 
14849
15873
  // ============================================
14850
- // File 33/37: components/phone-input/phone-utils.js
15874
+ // File 35/41: components/phone-input/phone-utils.js
14851
15875
  // ============================================
14852
15876
 
14853
15877
  /**
@@ -15010,7 +16034,7 @@
15010
16034
 
15011
16035
 
15012
16036
  // ============================================
15013
- // File 34/37: components/phone-input/phone-input.js
16037
+ // File 36/41: components/phone-input/phone-input.js
15014
16038
  // ============================================
15015
16039
 
15016
16040
  /**
@@ -15408,7 +16432,7 @@
15408
16432
 
15409
16433
 
15410
16434
  // ============================================
15411
- // File 35/37: components/file-input.js
16435
+ // File 37/41: components/file-input.js
15412
16436
  // ============================================
15413
16437
 
15414
16438
  /**
@@ -15452,6 +16476,15 @@
15452
16476
  return ICONS.file;
15453
16477
  }
15454
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
+
15455
16488
  /**
15456
16489
  * Upload file to S3
15457
16490
  * @param {File} file - File to upload
@@ -15463,26 +16496,31 @@
15463
16496
  formData.append("file", file, file.name);
15464
16497
  formData.append("is_private", String(!!isPrivate));
15465
16498
 
15466
- // Get upload URL - can be configured via global.S3_UPLOAD_URL
16499
+ // Get upload path - can be configured via global.S3_UPLOAD_URL
15467
16500
  const uploadUrl = global.S3_UPLOAD_URL || "/org/file/upload";
15468
- const baseUrl = global.SUPERLEAP_BASE_URL || "https://app.superleap.com/api/v1";
15469
- const fullUrl = uploadUrl.startsWith("http") ? uploadUrl : `${baseUrl}${uploadUrl}`;
15470
16501
 
15471
- // Get API key: use FlowUI._getComponent when globals were captured (index.js), else global.superleapClient
15472
- 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;
15473
16506
  try {
15474
- const client =
15475
- global.FlowUI && typeof global.FlowUI._getComponent === "function"
15476
- ? global.FlowUI._getComponent("superleapClient")
15477
- : global.superleapClient;
16507
+ if (client && typeof client.getBaseUrl === "function") {
16508
+ baseUrl = client.getBaseUrl();
16509
+ }
15478
16510
  if (client && typeof client.getSdk === "function") {
15479
- const sdk = client.getSdk();
15480
- apiKey = sdk?.apiKey;
16511
+ var sdk = client.getSdk();
16512
+ apiKey = sdk ? sdk.apiKey : null;
15481
16513
  }
15482
16514
  } catch (e) {
15483
- console.warn("[S3FileUpload] Could not get API key:", e);
16515
+ console.warn("[S3FileUpload] Could not get client:", e);
16516
+ }
16517
+
16518
+ if (!baseUrl) {
16519
+ throw new Error("SuperLeap client not initialized. Call superleapClient.init({ baseUrl, apiKey }) first.");
15484
16520
  }
15485
16521
 
16522
+ const fullUrl = uploadUrl.startsWith("http") ? uploadUrl : baseUrl.replace(/\/$/, "") + (uploadUrl.startsWith("/") ? uploadUrl : "/" + uploadUrl);
16523
+
15486
16524
  const headers = {};
15487
16525
  if (apiKey) {
15488
16526
  headers.Authorization = `Bearer ${apiKey}`;
@@ -15526,8 +16564,36 @@
15526
16564
  * @param {boolean} config.isPrivate - Whether files should be private
15527
16565
  * @param {number} config.maxFiles - Maximum number of files (for multiple mode)
15528
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
15529
16571
  * @returns {HTMLElement} Field element
15530
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
+
15531
16597
  function create(config) {
15532
16598
  const {
15533
16599
  label,
@@ -15539,7 +16605,14 @@
15539
16605
  isPrivate = false,
15540
16606
  maxFiles = null,
15541
16607
  maxFileSize = 10 * 1024 * 1024, // 10MB default
16608
+ disabled = false,
16609
+ variant = "default",
16610
+ inputSize = "default",
16611
+ className = "",
15542
16612
  } = config;
16613
+ let disabledState = !!disabled;
16614
+ let currentVariant = variant;
16615
+ let currentInputSize = inputSize;
15543
16616
 
15544
16617
  if (!global.FlowUI) {
15545
16618
  throw new Error("FlowUI not available");
@@ -15557,12 +16630,33 @@
15557
16630
 
15558
16631
  // Upload row: button + status + optional end spinner (match Select/MultiSelect trigger)
15559
16632
  const uploadWrapper = document.createElement("div");
15560
- 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");
15561
16651
 
15562
16652
  // Left content (button + status) – pointer-events-none so overlay input receives clicks
15563
16653
  const leftContent = document.createElement("div");
15564
16654
  leftContent.className = "pointer-events-none flex min-w-0 flex-1 items-center gap-8 truncate";
15565
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";
15566
16660
  const useButtonComponent = global.Button && typeof global.Button.create === "function";
15567
16661
  const initialButtonText = multiple ? "Choose files" : "Choose a file";
15568
16662
  const btn = useButtonComponent
@@ -15570,31 +16664,41 @@
15570
16664
  variant: "outline",
15571
16665
  size: "small",
15572
16666
  text: initialButtonText,
15573
- className: "shrink-0 truncate",
16667
+ className: "shrink-0 truncate " + disabledChildClasses,
15574
16668
  })
15575
16669
  : (function () {
15576
16670
  const el = document.createElement("div");
15577
- 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;
15578
16674
  el.textContent = initialButtonText;
15579
16675
  return el;
15580
16676
  })();
15581
16677
 
15582
16678
  // Status text: "No files chosen" (quaternary) or "X file(s) selected"
15583
16679
  const statusText = document.createElement("p");
15584
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-quaternary-text";
16680
+ statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
15585
16681
 
15586
16682
  // Hidden file input – overlays row, high z-index so it receives clicks
15587
16683
  const input = document.createElement("input");
15588
16684
  input.type = "file";
15589
- 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();
15590
16692
  input.multiple = multiple;
16693
+ input.disabled = disabledState;
15591
16694
  if (accept !== "*") {
15592
16695
  input.accept = accept;
15593
16696
  }
15594
16697
 
15595
16698
  // End icon slot: spinner when uploading (match Select chevron position)
15596
16699
  const endIconSlot = document.createElement("div");
15597
- 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";
15598
16702
  endIconSlot.style.display = "none";
15599
16703
 
15600
16704
  // Uploaded files: badge list (match UploadedFilePreviewer – flex-wrap gap-2)
@@ -15775,13 +16879,13 @@
15775
16879
 
15776
16880
  if (!filesChosen) {
15777
16881
  statusText.textContent = "No files chosen";
15778
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-quaternary-text";
16882
+ statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
15779
16883
  } else if (uploadingCount > 0) {
15780
16884
  statusText.textContent = "Uploading…";
15781
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-quaternary-text";
16885
+ statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
15782
16886
  } else {
15783
16887
  statusText.textContent = `${uploadedCount} file${uploadedCount !== 1 ? "s" : ""} selected`;
15784
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-primary-text";
16888
+ statusText.className = statusTextBaseClass + " text-typography-primary-text" + statusTextDisabledClass;
15785
16889
  }
15786
16890
 
15787
16891
  endIconSlot.style.display = uploadingCount > 0 ? "flex" : "none";
@@ -15918,6 +17022,32 @@
15918
17022
  loadExistingFiles();
15919
17023
  updateStatus();
15920
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
+
15921
17051
  return field;
15922
17052
  }
15923
17053
 
@@ -15932,7 +17062,7 @@
15932
17062
 
15933
17063
 
15934
17064
  // ============================================
15935
- // File 36/37: components/table.js
17065
+ // File 38/41: components/table.js
15936
17066
  // ============================================
15937
17067
 
15938
17068
  /**
@@ -16273,7 +17403,455 @@
16273
17403
 
16274
17404
 
16275
17405
  // ============================================
16276
- // 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
16277
17855
  // ============================================
16278
17856
 
16279
17857
  /**
@@ -16349,6 +17927,7 @@
16349
17927
  require("./components/input.js");
16350
17928
  require("./components/currency.js");
16351
17929
  require("./components/textarea.js");
17930
+ require("./components/richtext-editor.js");
16352
17931
  require("./components/duration/duration-utils.js");
16353
17932
  require("./components/duration/duration-constants.js");
16354
17933
  require("./components/duration/duration.js");
@@ -16364,8 +17943,11 @@
16364
17943
  require("./components/phone-input/phone-utils.js");
16365
17944
  require("./components/phone-input/phone-input.js");
16366
17945
  require("./components/checkbox.js");
17946
+ require("./components/checkbox-group.js");
16367
17947
  require("./components/radio-group.js");
16368
17948
  require("./components/table.js");
17949
+ require("./components/tabs.js");
17950
+ require("./components/steps.js");
16369
17951
 
16370
17952
  // Export FlowUI and SuperLeap from global scope
16371
17953
  if (typeof global !== "undefined" && global.FlowUI) {
@@ -16376,6 +17958,7 @@
16376
17958
  getSdk: global.superleapClient.getSdk,
16377
17959
  isAvailable: global.superleapClient.isAvailable,
16378
17960
  getDefaultConfig: global.superleapClient.getDefaultConfig,
17961
+ getBaseUrl: global.superleapClient.getBaseUrl,
16379
17962
  }
16380
17963
  : null;
16381
17964
 
@@ -16391,6 +17974,7 @@
16391
17974
  getSdk: window.superleapClient.getSdk,
16392
17975
  isAvailable: window.superleapClient.isAvailable,
16393
17976
  getDefaultConfig: window.superleapClient.getDefaultConfig,
17977
+ getBaseUrl: window.superleapClient.getBaseUrl,
16394
17978
  }
16395
17979
  : null;
16396
17980
 
@@ -16426,6 +18010,7 @@
16426
18010
  "InputComponent",
16427
18011
  "CurrencyComponent",
16428
18012
  "TextareaComponent",
18013
+ "RichTextEditorComponent",
16429
18014
  "Duration",
16430
18015
  "DateTimePicker",
16431
18016
  "Enumeration",
@@ -16437,8 +18022,11 @@
16437
18022
  "FileInput",
16438
18023
  "PhoneInput",
16439
18024
  "Checkbox",
18025
+ "CheckboxGroup",
16440
18026
  "RadioGroup",
16441
18027
  "SuperleapTable",
18028
+ "Tabs",
18029
+ "Steps",
16442
18030
  ];
16443
18031
 
16444
18032
  componentNames.forEach((name) => {
@@ -16468,6 +18056,7 @@
16468
18056
  getSdk: client.getSdk.bind(client),
16469
18057
  isAvailable: client.isAvailable.bind(client),
16470
18058
  getDefaultConfig: client.getDefaultConfig.bind(client),
18059
+ getBaseUrl: client.getBaseUrl.bind(client),
16471
18060
  // Bridge methods (from crm.js)
16472
18061
  connect: client.connect ? client.connect.bind(client) : undefined,
16473
18062
  isConnected: client.isConnected
@@ -16526,7 +18115,7 @@
16526
18115
  },
16527
18116
  });
16528
18117
  document.dispatchEvent(event);
16529
- console.log("[Superleap-Flow] Library ready - v2.3.4");
18118
+ console.log("[Superleap-Flow] Library ready - v2.5.1");
16530
18119
  }
16531
18120
 
16532
18121
  // Wait for DOM to be ready before dispatching event