@superleapai/flow-ui 2.5.3 → 2.5.5

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.3
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-25T17:41:37.089Z
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/42: 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/42: 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/42: 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/42: 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/42: 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/42: 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
@@ -4339,6 +4393,81 @@
4339
4393
  return field;
4340
4394
  }
4341
4395
 
4396
+ /**
4397
+ * Create a card select field (uses CardSelect component when available, else radio fallback)
4398
+ * @param {Object} config - Configuration object
4399
+ * @param {string} config.label - Field label
4400
+ * @param {string} config.fieldId - State key for this field
4401
+ * @param {Array} config.options - Array of { value, label, description?, icon?, disabled? }
4402
+ * @param {boolean} [config.required] - Whether field is required
4403
+ * @param {Function} [config.onChange] - Optional change handler (receives selected value)
4404
+ * @param {string} [config.helpText] - Optional help text for tooltip
4405
+ * @param {boolean} [config.disabled] - Whether all cards are disabled
4406
+ * @param {string} [config.className] - Extra CSS class on card container
4407
+ * @returns {HTMLElement} Field wrapper element
4408
+ */
4409
+ function createCardSelect(config) {
4410
+ const { label, fieldId, options = [], required = false, onChange, helpText = null, disabled = false, className } = config;
4411
+
4412
+ const field = createFieldWrapper(label, required, helpText);
4413
+
4414
+ if (getComponent("CardSelect") && getComponent("CardSelect").create) {
4415
+ const currentValue = get(fieldId);
4416
+ const cardSelectEl = getComponent("CardSelect").create({
4417
+ name: fieldId,
4418
+ options: options.map((opt) => ({
4419
+ value: opt.value,
4420
+ label: opt.label || opt.value,
4421
+ description: opt.description,
4422
+ icon: opt.icon,
4423
+ disabled: opt.disabled,
4424
+ })),
4425
+ value: currentValue,
4426
+ disabled,
4427
+ className,
4428
+ onChange: (value) => {
4429
+ set(fieldId, value);
4430
+ if (onChange) onChange(value);
4431
+ },
4432
+ });
4433
+ cardSelectEl._fieldId = fieldId;
4434
+ field.appendChild(cardSelectEl);
4435
+ return field;
4436
+ }
4437
+
4438
+ // Fallback: native radio buttons
4439
+ const radioGroup = document.createElement("div");
4440
+ radioGroup.className = "card-select-fallback";
4441
+
4442
+ options.forEach((opt) => {
4443
+ const wrapper = document.createElement("div");
4444
+ wrapper.className = "card-option";
4445
+
4446
+ const radio = document.createElement("input");
4447
+ radio.type = "radio";
4448
+ radio.name = fieldId;
4449
+ radio.value = opt.value;
4450
+ radio.id = `${fieldId}-${opt.value}`;
4451
+ radio.checked = get(fieldId) === opt.value;
4452
+ radio.disabled = disabled || !!opt.disabled;
4453
+ radio.addEventListener("change", () => {
4454
+ set(fieldId, opt.value);
4455
+ if (onChange) { onChange(opt.value); }
4456
+ });
4457
+
4458
+ const radioLabel = document.createElement("label");
4459
+ radioLabel.htmlFor = `${fieldId}-${opt.value}`;
4460
+ radioLabel.textContent = opt.label || opt.value;
4461
+
4462
+ wrapper.appendChild(radio);
4463
+ wrapper.appendChild(radioLabel);
4464
+ radioGroup.appendChild(wrapper);
4465
+ });
4466
+
4467
+ field.appendChild(radioGroup);
4468
+ return field;
4469
+ }
4470
+
4342
4471
  /**
4343
4472
  * Create a multi-select field (uses MultiSelect component when available, else checkbox group)
4344
4473
  * @param {Object} config - Configuration object
@@ -4448,6 +4577,7 @@
4448
4577
  size,
4449
4578
  canClear,
4450
4579
  initialLimit,
4580
+ initialFilter,
4451
4581
  helpText = null,
4452
4582
  } = config;
4453
4583
 
@@ -4467,6 +4597,7 @@
4467
4597
  size: size || "default",
4468
4598
  canClear: !!canClear,
4469
4599
  initialLimit,
4600
+ initialFilter,
4470
4601
  onChange: (value, record) => {
4471
4602
  set(fieldId, value);
4472
4603
  if (onChange) onChange(value, record);
@@ -4516,6 +4647,7 @@
4516
4647
  variant,
4517
4648
  size,
4518
4649
  initialLimit,
4650
+ initialFilter,
4519
4651
  displayFields,
4520
4652
  helpText = null,
4521
4653
  } = config;
@@ -4534,6 +4666,7 @@
4534
4666
  variant: variant || "default",
4535
4667
  size: size || "default",
4536
4668
  initialLimit,
4669
+ initialFilter,
4537
4670
  displayFields: displayFields || [],
4538
4671
  onValuesChange: (values, records) => {
4539
4672
  set(fieldId, values);
@@ -5091,6 +5224,40 @@
5091
5224
  return field;
5092
5225
  }
5093
5226
 
5227
+ /**
5228
+ * Create a checkbox group field (multiselect-like: options array, value array, onValuesChange)
5229
+ * @param {Object} config - { label, fieldId, options, required, helpText, variant, size, layout, disabled, onChange }
5230
+ * @returns {HTMLElement} Field wrapper containing checkbox group
5231
+ */
5232
+ function createCheckboxGroup(config) {
5233
+ const { label, fieldId, options = [], required = false, helpText = null, variant, size, layout = "vertical", disabled = false, onChange } = config;
5234
+
5235
+ const field = createFieldWrapper(label, required, helpText);
5236
+ field.setAttribute("data-field-id", fieldId);
5237
+
5238
+ if (getComponent("CheckboxGroup") && getComponent("CheckboxGroup").create) {
5239
+ const currentValues = get(fieldId) || [];
5240
+ const groupEl = getComponent("CheckboxGroup").create({
5241
+ fieldId,
5242
+ options,
5243
+ value: currentValues,
5244
+ variant: variant || "default",
5245
+ size: size || "default",
5246
+ layout,
5247
+ disabled,
5248
+ onValuesChange: (values) => {
5249
+ set(fieldId, values);
5250
+ if (onChange) onChange(values);
5251
+ },
5252
+ });
5253
+ groupEl._fieldId = fieldId;
5254
+ field.appendChild(groupEl);
5255
+ return field;
5256
+ }
5257
+
5258
+ return field;
5259
+ }
5260
+
5094
5261
  // ============================================================================
5095
5262
  // STEPPER COMPONENT
5096
5263
  // ============================================================================
@@ -5122,6 +5289,38 @@
5122
5289
  });
5123
5290
  }
5124
5291
 
5292
+ /**
5293
+ * Create a Tabs component (list + triggers + content panels)
5294
+ * @param {Object} config - { defaultValue?, value?, onChange?, tabs: [{ value, label, content }], size?, variant?, listClassName?, contentClassName? }
5295
+ * @returns {HTMLElement} Tabs root element
5296
+ */
5297
+ function createTabs(config) {
5298
+ const Tabs = getComponent("Tabs");
5299
+ if (Tabs && typeof Tabs.create === "function") {
5300
+ return Tabs.create(config);
5301
+ }
5302
+ const fallback = document.createElement("div");
5303
+ fallback.className = "tabs-root";
5304
+ fallback.textContent = "Tabs component not loaded.";
5305
+ return fallback;
5306
+ }
5307
+
5308
+ /**
5309
+ * Create a Steps component (numbered step triggers + optional content panels)
5310
+ * @param {Object} config - { steps: [{ id, label, content? }], defaultValue?, value?, onChange?, size?, variant?, listClassName?, contentClassName?, showContent? }
5311
+ * @returns {HTMLElement} Steps root element
5312
+ */
5313
+ function createSteps(config) {
5314
+ const Steps = getComponent("Steps");
5315
+ if (Steps && typeof Steps.create === "function") {
5316
+ return Steps.create(config);
5317
+ }
5318
+ const fallback = document.createElement("div");
5319
+ fallback.className = "steps-root";
5320
+ fallback.textContent = "Steps component not loaded.";
5321
+ return fallback;
5322
+ }
5323
+
5125
5324
  // ============================================================================
5126
5325
  // TABLE COMPONENT
5127
5326
  // ============================================================================
@@ -5284,6 +5483,34 @@
5284
5483
  return field;
5285
5484
  }
5286
5485
 
5486
+ // ============================================================================
5487
+ // ALERTS
5488
+ // ============================================================================
5489
+
5490
+ /**
5491
+ * Render multiple alert messages into a container
5492
+ * @param {HTMLElement} container - Container to append alerts to
5493
+ * @param {string[]} messages - Array of message strings
5494
+ * @param {string} [variant='default'] - 'default' | 'error' | 'warning' | 'success' | 'info' | 'destructive'
5495
+ */
5496
+ function renderAlerts(container, messages, variant = "default") {
5497
+ if (!container || !Array.isArray(messages)) return;
5498
+ const Alert = getComponent("Alert");
5499
+ if (Alert && typeof Alert.simple === "function") {
5500
+ messages.forEach((msg) => {
5501
+ const el = Alert.simple(msg, variant);
5502
+ if (el) container.appendChild(el);
5503
+ });
5504
+ } else {
5505
+ messages.forEach((msg) => {
5506
+ const div = document.createElement("div");
5507
+ 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");
5508
+ div.textContent = msg;
5509
+ container.appendChild(div);
5510
+ });
5511
+ }
5512
+ }
5513
+
5287
5514
  // ============================================================================
5288
5515
  // TOAST NOTIFICATIONS
5289
5516
  // ============================================================================
@@ -5420,10 +5647,12 @@
5420
5647
  // Form components
5421
5648
  createInput,
5422
5649
  createTextarea,
5650
+ createRichTextEditor,
5423
5651
  createSelect,
5424
5652
  createTimePicker,
5425
5653
  createDateTimePicker,
5426
5654
  createRadioGroup,
5655
+ createCardSelect,
5427
5656
  createMultiSelect,
5428
5657
  createRecordSelect,
5429
5658
  createRecordMultiSelect,
@@ -5435,6 +5664,7 @@
5435
5664
  createCurrency,
5436
5665
  createPhoneInput,
5437
5666
  createCheckbox,
5667
+ createCheckboxGroup,
5438
5668
 
5439
5669
  // Button (delegates to Button component when available; resolved at call time via getComponent)
5440
5670
  createButton: function (config) {
@@ -5455,8 +5685,11 @@
5455
5685
 
5456
5686
  // Stepper
5457
5687
  renderStepper,
5688
+ createTabs,
5689
+ createSteps,
5458
5690
 
5459
5691
  // Alerts
5692
+ renderAlerts,
5460
5693
  showToast,
5461
5694
 
5462
5695
  // Table
@@ -5484,7 +5717,7 @@
5484
5717
 
5485
5718
 
5486
5719
  // ============================================
5487
- // File 7/37: components/toast.js
5720
+ // File 7/42: components/toast.js
5488
5721
  // ============================================
5489
5722
 
5490
5723
  /**
@@ -5833,7 +6066,7 @@
5833
6066
 
5834
6067
 
5835
6068
  // ============================================
5836
- // File 8/37: components/alert.js
6069
+ // File 8/42: components/alert.js
5837
6070
  // ============================================
5838
6071
 
5839
6072
  /**
@@ -6121,7 +6354,7 @@
6121
6354
 
6122
6355
 
6123
6356
  // ============================================
6124
- // File 9/37: components/button.js
6357
+ // File 9/42: components/button.js
6125
6358
  // ============================================
6126
6359
 
6127
6360
  /**
@@ -6328,7 +6561,7 @@
6328
6561
 
6329
6562
 
6330
6563
  // ============================================
6331
- // File 10/37: components/spinner.js
6564
+ // File 10/42: components/spinner.js
6332
6565
  // ============================================
6333
6566
 
6334
6567
  /**
@@ -6470,7 +6703,7 @@
6470
6703
 
6471
6704
 
6472
6705
  // ============================================
6473
- // File 11/37: components/badge.js
6706
+ // File 11/42: components/badge.js
6474
6707
  // ============================================
6475
6708
 
6476
6709
  /**
@@ -6611,7 +6844,7 @@
6611
6844
 
6612
6845
 
6613
6846
  // ============================================
6614
- // File 12/37: components/avatar.js
6847
+ // File 12/42: components/avatar.js
6615
6848
  // ============================================
6616
6849
 
6617
6850
  /**
@@ -6812,7 +7045,7 @@
6812
7045
 
6813
7046
 
6814
7047
  // ============================================
6815
- // File 13/37: components/icon.js
7048
+ // File 13/42: components/icon.js
6816
7049
  // ============================================
6817
7050
 
6818
7051
  /**
@@ -6893,6 +7126,40 @@
6893
7126
  /** Filled circle for "just color" mode (IconOrColor when only icon_color is set) */
6894
7127
  IconCircleFilled:
6895
7128
  '<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>',
7129
+
7130
+ // Rich text editor / toolbar (Tabler Icons outline, stroke 2)
7131
+ IconBold:
7132
+ '<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>',
7133
+ IconItalic:
7134
+ '<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>',
7135
+ IconUnderline:
7136
+ '<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>',
7137
+ IconH1:
7138
+ '<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>',
7139
+ IconH2:
7140
+ '<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>',
7141
+ IconH3:
7142
+ '<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>',
7143
+ IconList:
7144
+ '<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>',
7145
+ IconListNumbers:
7146
+ '<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>',
7147
+ IconAlignLeft:
7148
+ '<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>',
7149
+ IconAlignCenter:
7150
+ '<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>',
7151
+ IconAlignRight:
7152
+ '<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>',
7153
+ IconCode:
7154
+ '<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>',
7155
+ IconLink:
7156
+ '<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>',
7157
+ IconPhoto:
7158
+ '<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>',
7159
+ IconArrowBackUp:
7160
+ '<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>',
7161
+ IconArrowForwardUp:
7162
+ '<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
7163
  };
6897
7164
 
6898
7165
  function join() {
@@ -7018,7 +7285,7 @@
7018
7285
 
7019
7286
 
7020
7287
  // ============================================
7021
- // File 14/37: components/popover.js
7288
+ // File 14/42: components/popover.js
7022
7289
  // ============================================
7023
7290
 
7024
7291
  /**
@@ -7041,6 +7308,7 @@
7041
7308
  * @param {Function} [config.onOpen] - Called when popover opens (before positioning)
7042
7309
  * @param {string} [config.bodyClassName] - Optional class for body wrapper (overrides default padding)
7043
7310
  * @param {string} [config.panelClassName] - Optional class to add to panel (e.g. for width)
7311
+ * @param {boolean} [config.modal=false] - If true, lock body scroll and show backdrop; only popover and trigger are interactive
7044
7312
  * @returns {Object} Popover API {show, hide, destroy, element}
7045
7313
  */
7046
7314
  function create(config = {}) {
@@ -7055,6 +7323,7 @@
7055
7323
  onOpen = null,
7056
7324
  bodyClassName = "",
7057
7325
  panelClassName = "",
7326
+ modal = true,
7058
7327
  } = config;
7059
7328
 
7060
7329
  const triggerEl =
@@ -7064,7 +7333,7 @@
7064
7333
  return { show: noop, hide: noop, destroy: noop, element: null };
7065
7334
  }
7066
7335
 
7067
- // Wrap trigger in a relative container so popover can be absolute relative to it (full width so parent controls it)
7336
+ // Wrap trigger in a relative container for layout (full width); popover is portaled to body
7068
7337
  const container = document.createElement("div");
7069
7338
  container.className = "relative w-full";
7070
7339
  const triggerParent = triggerEl.parentNode;
@@ -7075,7 +7344,7 @@
7075
7344
 
7076
7345
  const wrapper = document.createElement("div");
7077
7346
  wrapper.className =
7078
- "absolute z-50 pointer-events-none opacity-0 invisible transition-opacity duration-150 ease-out";
7347
+ "fixed z-50 pointer-events-none opacity-0 invisible transition-opacity duration-150 ease-out";
7079
7348
  wrapper.setAttribute("aria-hidden", "true");
7080
7349
 
7081
7350
  const panel = document.createElement("div");
@@ -7099,7 +7368,11 @@
7099
7368
  const body = document.createElement("div");
7100
7369
  body.className =
7101
7370
  bodyClassName ||
7102
- "text-reg-14 text-typography-secondary-text leading-5 [&_p]:mb-2 [&_p:last-child]:mb-0";
7371
+ "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";
7372
+ body.style.maxHeight = "90vh";
7373
+ body.style.overflowY = "auto";
7374
+ body.style.overflowX = "hidden";
7375
+ body.style.minHeight = "0";
7103
7376
  if (panelClassName) {
7104
7377
  panel.className = panel.className + " " + panelClassName;
7105
7378
  }
@@ -7111,7 +7384,40 @@
7111
7384
  panel.appendChild(body);
7112
7385
 
7113
7386
  wrapper.appendChild(panel);
7114
- container.appendChild(wrapper);
7387
+ document.body.appendChild(wrapper);
7388
+
7389
+ var backdropEl = null;
7390
+ var resizeObserver = null;
7391
+
7392
+ function onBackdropWheel(e) {
7393
+ e.preventDefault();
7394
+ }
7395
+
7396
+ function applyModalOpen() {
7397
+ if (!modal) return;
7398
+ if (!backdropEl) {
7399
+ backdropEl = document.createElement("div");
7400
+ backdropEl.className =
7401
+ "fixed inset-0 z-40 bg-transparent pointer-events-auto";
7402
+ backdropEl.setAttribute("aria-hidden", "true");
7403
+ backdropEl.addEventListener("click", function () {
7404
+ hide();
7405
+ });
7406
+ backdropEl.addEventListener("wheel", onBackdropWheel, {
7407
+ passive: false,
7408
+ });
7409
+ }
7410
+ document.body.appendChild(backdropEl);
7411
+ wrapper.style.zIndex = "999";
7412
+ }
7413
+
7414
+ function applyModalClose() {
7415
+ if (!modal) return;
7416
+ if (backdropEl && backdropEl.parentNode) {
7417
+ backdropEl.parentNode.removeChild(backdropEl);
7418
+ }
7419
+ wrapper.style.zIndex = "";
7420
+ }
7115
7421
 
7116
7422
  function noop() {}
7117
7423
 
@@ -7119,33 +7425,80 @@
7119
7425
  const triggerRect = triggerEl.getBoundingClientRect();
7120
7426
  const panelRect = panel.getBoundingClientRect();
7121
7427
  const gap = 8;
7122
- let top = 0;
7123
- let left = 0;
7428
+ const viewportHeight =
7429
+ window.innerHeight || document.documentElement.clientHeight;
7430
+ const viewportWidth =
7431
+ window.innerWidth || document.documentElement.clientWidth;
7432
+ const spaceBelow = viewportHeight - triggerRect.bottom;
7433
+ const spaceAbove = triggerRect.top;
7434
+ const spaceRight = viewportWidth - triggerRect.right;
7435
+ const spaceLeft = triggerRect.left;
7436
+
7437
+ // Flip placement when there is not enough space (prefer requested side, flip only when needed)
7438
+ let effectivePlacement = placement;
7439
+ if (
7440
+ placement === "bottom" &&
7441
+ spaceBelow < panelRect.height + gap &&
7442
+ spaceAbove >= panelRect.height + gap
7443
+ ) {
7444
+ effectivePlacement = "top";
7445
+ } else if (
7446
+ placement === "top" &&
7447
+ spaceAbove < panelRect.height + gap &&
7448
+ spaceBelow >= panelRect.height + gap
7449
+ ) {
7450
+ effectivePlacement = "bottom";
7451
+ } else if (
7452
+ placement === "right" &&
7453
+ spaceRight < panelRect.width + gap &&
7454
+ spaceLeft >= panelRect.width + gap
7455
+ ) {
7456
+ effectivePlacement = "left";
7457
+ } else if (
7458
+ placement === "left" &&
7459
+ spaceLeft < panelRect.width + gap &&
7460
+ spaceRight >= panelRect.width + gap
7461
+ ) {
7462
+ effectivePlacement = "right";
7463
+ }
7124
7464
 
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);
7465
+ panel.setAttribute("data-side", effectivePlacement);
7128
7466
 
7129
- switch (placement) {
7467
+ let top = 0;
7468
+ let left = 0;
7469
+ const alignLeft =
7470
+ align === "center"
7471
+ ? (triggerRect.width - panelRect.width) / 2
7472
+ : align === "end"
7473
+ ? triggerRect.width - panelRect.width
7474
+ : 0;
7475
+ const alignTop =
7476
+ align === "center"
7477
+ ? (triggerRect.height - panelRect.height) / 2
7478
+ : align === "end"
7479
+ ? triggerRect.height - panelRect.height
7480
+ : 0;
7481
+
7482
+ switch (effectivePlacement) {
7130
7483
  case "bottom":
7131
- top = triggerRect.height + gap;
7132
- left = alignLeft;
7484
+ top = triggerRect.bottom + gap;
7485
+ left = triggerRect.left + alignLeft;
7133
7486
  break;
7134
7487
  case "top":
7135
- top = -panelRect.height - gap;
7136
- left = alignLeft;
7488
+ top = triggerRect.top - panelRect.height - gap;
7489
+ left = triggerRect.left + alignLeft;
7137
7490
  break;
7138
7491
  case "right":
7139
- top = alignTop;
7140
- left = triggerRect.width + gap;
7492
+ top = triggerRect.top + alignTop;
7493
+ left = triggerRect.right + gap;
7141
7494
  break;
7142
7495
  case "left":
7143
- top = alignTop;
7144
- left = -panelRect.width - gap;
7496
+ top = triggerRect.top + alignTop;
7497
+ left = triggerRect.left - panelRect.width - gap;
7145
7498
  break;
7146
7499
  default:
7147
- top = triggerRect.height + gap;
7148
- left = alignLeft;
7500
+ top = triggerRect.bottom + gap;
7501
+ left = triggerRect.left + alignLeft;
7149
7502
  }
7150
7503
 
7151
7504
  wrapper.style.transform = "";
@@ -7159,16 +7512,41 @@
7159
7512
  wrapper.classList.add("invisible", "opacity-0", "pointer-events-none");
7160
7513
  wrapper.classList.remove("visible", "opacity-100", "pointer-events-auto");
7161
7514
  wrapper.setAttribute("aria-hidden", "true");
7515
+ window.removeEventListener("scroll", onScrollOrResize, true);
7516
+ window.removeEventListener("resize", onScrollOrResize);
7517
+ if (resizeObserver && panel) {
7518
+ resizeObserver.disconnect();
7519
+ resizeObserver = null;
7520
+ }
7521
+ applyModalClose();
7162
7522
  if (onClose) onClose();
7163
7523
  }
7164
7524
 
7525
+ function onScrollOrResize() {
7526
+ if (wrapper.classList.contains("visible")) position();
7527
+ }
7528
+
7165
7529
  function show() {
7166
7530
  if (onOpen) onOpen();
7531
+ applyModalOpen();
7167
7532
  requestAnimationFrame(function () {
7168
7533
  position();
7169
- wrapper.classList.remove("invisible", "opacity-0", "pointer-events-none");
7534
+ wrapper.classList.remove(
7535
+ "invisible",
7536
+ "opacity-0",
7537
+ "pointer-events-none",
7538
+ );
7170
7539
  wrapper.classList.add("visible", "opacity-100", "pointer-events-auto");
7171
7540
  wrapper.setAttribute("aria-hidden", "false");
7541
+ window.addEventListener("scroll", onScrollOrResize, true);
7542
+ window.addEventListener("resize", onScrollOrResize);
7543
+ // Re-position when panel content size changes (e.g. async record list load in record-select)
7544
+ if (typeof ResizeObserver !== "undefined" && !resizeObserver) {
7545
+ resizeObserver = new ResizeObserver(function () {
7546
+ if (wrapper.classList.contains("visible")) position();
7547
+ });
7548
+ resizeObserver.observe(panel);
7549
+ }
7172
7550
  requestAnimationFrame(function () {
7173
7551
  requestAnimationFrame(function () {
7174
7552
  panel.setAttribute("data-state", "open");
@@ -7179,6 +7557,16 @@
7179
7557
 
7180
7558
  function destroy() {
7181
7559
  hide();
7560
+ applyModalClose();
7561
+ if (resizeObserver && panel) {
7562
+ resizeObserver.disconnect();
7563
+ resizeObserver = null;
7564
+ }
7565
+ if (backdropEl && backdropEl.parentNode) {
7566
+ backdropEl.parentNode.removeChild(backdropEl);
7567
+ }
7568
+ window.removeEventListener("scroll", onScrollOrResize, true);
7569
+ window.removeEventListener("resize", onScrollOrResize);
7182
7570
  if (wrapper.parentNode) {
7183
7571
  wrapper.parentNode.removeChild(wrapper);
7184
7572
  }
@@ -7228,6 +7616,8 @@
7228
7616
  show,
7229
7617
  hide,
7230
7618
  destroy,
7619
+ /** Re-run positioning (e.g. after async content load). Call when panel size changes. */
7620
+ updatePosition: position,
7231
7621
  setContent(newContent) {
7232
7622
  body.innerHTML = "";
7233
7623
  if (typeof newContent === "string") {
@@ -7249,7 +7639,7 @@
7249
7639
 
7250
7640
 
7251
7641
  // ============================================
7252
- // File 15/37: components/select.js
7642
+ // File 15/42: components/select.js
7253
7643
  // ============================================
7254
7644
 
7255
7645
  /**
@@ -7446,11 +7836,11 @@
7446
7836
 
7447
7837
  var content = document.createElement("div");
7448
7838
  content.setAttribute("role", "listbox");
7449
- content.className = "custom-select-content w-full max-h-[200px] overflow-hidden flex flex-col";
7839
+ content.className = "custom-select-content w-full max-h-[45vh] overflow-hidden flex flex-col";
7450
7840
 
7451
7841
  var optionsList = document.createElement("div");
7452
7842
  optionsList.className =
7453
- "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
7843
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
7454
7844
 
7455
7845
  if (options.length === 0) {
7456
7846
  var noOpt = document.createElement("div");
@@ -7509,7 +7899,7 @@
7509
7899
  align: "start",
7510
7900
  closeOnClickOutside: true,
7511
7901
  bodyClassName: "p-0 overflow-hidden",
7512
- panelClassName: "min-w-[var(--trigger-width)] max-h-[200px] overflow-hidden",
7902
+ panelClassName: "min-w-[var(--trigger-width)] max-h-[45vh] overflow-hidden",
7513
7903
  onOpen: function () {
7514
7904
  if (disabled) {
7515
7905
  popover.hide();
@@ -7807,7 +8197,7 @@
7807
8197
 
7808
8198
 
7809
8199
  // ============================================
7810
- // File 16/37: components/enum-select.js
8200
+ // File 16/42: components/enum-select.js
7811
8201
  // ============================================
7812
8202
 
7813
8203
  /**
@@ -8041,7 +8431,7 @@
8041
8431
  // Create dropdown content
8042
8432
  var content = document.createElement("div");
8043
8433
  content.setAttribute("role", "listbox");
8044
- content.className = "w-full min-w-[200px] max-h-[30vh] overflow-hidden flex flex-col";
8434
+ content.className = "w-full min-w-[200px] max-h-[45vh] overflow-hidden flex flex-col";
8045
8435
 
8046
8436
  // Search input (using InputComponent like phone-input)
8047
8437
  var searchContainer = document.createElement("div");
@@ -8102,7 +8492,7 @@
8102
8492
  // Options list
8103
8493
  var optionsList = document.createElement("div");
8104
8494
  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";
8495
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white flex-1 min-h-0";
8106
8496
 
8107
8497
  content.appendChild(searchContainer);
8108
8498
  content.appendChild(optionsList);
@@ -8590,7 +8980,7 @@
8590
8980
 
8591
8981
 
8592
8982
  // ============================================
8593
- // File 17/37: components/record-select.js
8983
+ // File 17/42: components/record-select.js
8594
8984
  // ============================================
8595
8985
 
8596
8986
  /**
@@ -8604,9 +8994,20 @@
8604
8994
 
8605
8995
  (function (global) {
8606
8996
 
8607
- var Popover = global.Popover;
8608
- var InputComponent = global.InputComponent;
8609
- var Spinner = global.Spinner;
8997
+ function getDep(name) {
8998
+ if (
8999
+ typeof global.FlowUI !== "undefined" &&
9000
+ typeof global.FlowUI._getComponent === "function"
9001
+ ) {
9002
+ var c = global.FlowUI._getComponent(name);
9003
+ if (c) return c;
9004
+ }
9005
+ return global[name];
9006
+ }
9007
+
9008
+ var Popover = getDep("Popover");
9009
+ var InputComponent = getDep("InputComponent");
9010
+ var Spinner = getDep("Spinner");
8610
9011
 
8611
9012
  /** When objectSlug === USERS, show Vivid Avatar (name-based color) instead of static icon */
8612
9013
  var STANDARD_OBJECT_SLUGS_USERS = "user";
@@ -8628,7 +9029,7 @@
8628
9029
  '<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
9030
  var X_SVG =
8630
9031
  '<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
-
9032
+
8632
9033
  var SEARCH_ICON =
8633
9034
  '<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
9035
 
@@ -8643,7 +9044,8 @@
8643
9044
  "border-error-border hover:border-error-border-hover focus:border-1/2 focus:border-error-border-hover",
8644
9045
  warning:
8645
9046
  "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",
9047
+ borderless:
9048
+ "border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
8647
9049
  inline:
8648
9050
  "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
9051
  };
@@ -8652,7 +9054,9 @@
8652
9054
  large: "px-12 py-8",
8653
9055
  small: "px-8 py-4",
8654
9056
  };
8655
- var placeholderClass = placeholder ? " text-typography-quaternary-text" : "";
9057
+ var placeholderClass = placeholder
9058
+ ? " text-typography-quaternary-text"
9059
+ : "";
8656
9060
  var disabledClass = disabled
8657
9061
  ? " pointer-events-none cursor-not-allowed bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary"
8658
9062
  : "";
@@ -8708,15 +9112,27 @@
8708
9112
  function getObjectIconInfo(slug, objectSchema) {
8709
9113
  var color = "neutral";
8710
9114
  var iconStr = "IconDatabase";
8711
- if (objectSchema && objectSchema.properties && objectSchema.properties.icon_data) {
9115
+ if (
9116
+ objectSchema &&
9117
+ objectSchema.properties &&
9118
+ objectSchema.properties.icon_data
9119
+ ) {
8712
9120
  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;
9121
+ if (typeof iconData.color === "string" && iconData.color)
9122
+ color = iconData.color;
9123
+ if (typeof iconData.icon === "string" && iconData.icon)
9124
+ iconStr = iconData.icon;
8715
9125
  }
8716
- if (!objectSchema || !objectSchema.properties || !objectSchema.properties.icon_data || !objectSchema.properties.icon_data.icon) {
9126
+ if (
9127
+ !objectSchema ||
9128
+ !objectSchema.properties ||
9129
+ !objectSchema.properties.icon_data ||
9130
+ !objectSchema.properties.icon_data.icon
9131
+ ) {
8717
9132
  if (OBJECT_SLUG_TO_ICON[slug]) {
8718
9133
  iconStr = OBJECT_SLUG_TO_ICON[slug].iconStr;
8719
- if (OBJECT_SLUG_TO_ICON[slug].color) color = OBJECT_SLUG_TO_ICON[slug].color;
9134
+ if (OBJECT_SLUG_TO_ICON[slug].color)
9135
+ color = OBJECT_SLUG_TO_ICON[slug].color;
8720
9136
  } else {
8721
9137
  iconStr = "IconDatabase";
8722
9138
  color = "neutral";
@@ -8739,6 +9155,7 @@
8739
9155
  * @param {string} [config.size] - 'default' | 'large' | 'small'
8740
9156
  * @param {boolean} [config.canClear] - Show clear button when value is set
8741
9157
  * @param {number} [config.initialLimit] - Initial fetch limit (default 50)
9158
+ * @param {Object} [config.initialFilter] - Optional filter object to merge with search (e.g. { field: "status", operator: "exact", value: "active" } or { and: [...] })
8742
9159
  * @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
9160
  * @returns {HTMLElement} Record select container element
8744
9161
  */
@@ -8747,12 +9164,14 @@
8747
9164
  var objectSlug = config.objectSlug;
8748
9165
  var objectSchema = config.objectSchema || null;
8749
9166
  var placeholder = config.placeholder || "Select a record";
8750
- var searchPlaceholder = config.searchPlaceholder || "Search " + (objectSlug || "") + "...";
9167
+ var searchPlaceholder =
9168
+ config.searchPlaceholder || "Search " + (objectSlug || "") + "...";
8751
9169
  var onChange = config.onChange;
8752
9170
  var variant = config.variant || "default";
8753
9171
  var size = config.size || "default";
8754
9172
  var canClear = !!config.canClear;
8755
9173
  var initialLimit = config.initialLimit != null ? config.initialLimit : 50;
9174
+ var initialFilter = config.initialFilter || null; // Can be array, object, or function returning either
8756
9175
 
8757
9176
  var disabled = config.disabled === true;
8758
9177
  var value =
@@ -8800,7 +9219,7 @@
8800
9219
  size,
8801
9220
  disabled,
8802
9221
  !value,
8803
- canClear && !!value && !disabled
9222
+ canClear && !!value && !disabled,
8804
9223
  );
8805
9224
  trigger.disabled = disabled;
8806
9225
  trigger.setAttribute("aria-haspopup", "listbox");
@@ -8809,7 +9228,8 @@
8809
9228
  trigger.classList.add("record-select-trigger");
8810
9229
 
8811
9230
  var triggerContent = document.createElement("div");
8812
- triggerContent.className = "record-select-trigger-content flex items-center gap-8 flex-1 min-w-0";
9231
+ triggerContent.className =
9232
+ "record-select-trigger-content flex items-center gap-8 flex-1 min-w-0";
8813
9233
 
8814
9234
  var triggerIcon = document.createElement("span");
8815
9235
  triggerIcon.className = "record-select-trigger-icon shrink-0 hidden";
@@ -8852,7 +9272,9 @@
8852
9272
  // Dropdown content: search + list (same content pattern as Select)
8853
9273
  var content = document.createElement("div");
8854
9274
  content.setAttribute("role", "listbox");
8855
- content.className = "record-select-content max-h-[30vh] overflow-hidden flex flex-col";
9275
+ content.setAttribute("data-field-id", fieldId);
9276
+ content.className =
9277
+ "record-select-content max-h-[45vh] overflow-hidden flex flex-col";
8856
9278
 
8857
9279
  var searchWrap = document.createElement("div");
8858
9280
  searchWrap.className = "py-8 border-b-1/2 border-border-primary";
@@ -8883,12 +9305,12 @@
8883
9305
  } else {
8884
9306
  var fallbackWrapper = document.createElement("div");
8885
9307
  fallbackWrapper.className = "flex items-center gap-8 px-12";
8886
-
9308
+
8887
9309
  var searchIconSpan = document.createElement("span");
8888
9310
  searchIconSpan.className = "shrink-0 text-typography-tertiary-text";
8889
9311
  searchIconSpan.innerHTML = SEARCH_ICON;
8890
9312
  fallbackWrapper.appendChild(searchIconSpan);
8891
-
9313
+
8892
9314
  var searchInput = document.createElement("input");
8893
9315
  searchInput.type = "text";
8894
9316
  searchInput.className =
@@ -8902,21 +9324,22 @@
8902
9324
  content.appendChild(searchWrap);
8903
9325
 
8904
9326
  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
-
9327
+ optionsList.className =
9328
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-select-options";
9329
+
8907
9330
  // Add scroll listener for infinite scroll
8908
9331
  optionsList.addEventListener("scroll", function () {
8909
9332
  if (isFetchingMore || !hasMoreRecords) return;
8910
9333
  var scrollHeight = optionsList.scrollHeight;
8911
9334
  var scrollTop = optionsList.scrollTop;
8912
9335
  var clientHeight = optionsList.clientHeight;
8913
-
9336
+
8914
9337
  // Trigger load more when scrolled to bottom (with 50px threshold)
8915
9338
  if (scrollTop + clientHeight >= scrollHeight - 50) {
8916
9339
  loadMoreRecords();
8917
9340
  }
8918
9341
  });
8919
-
9342
+
8920
9343
  content.appendChild(optionsList);
8921
9344
 
8922
9345
  popover = Popover.create({
@@ -8926,30 +9349,41 @@
8926
9349
  align: "start",
8927
9350
  closeOnClickOutside: true,
8928
9351
  bodyClassName: "p-0 overflow-hidden",
8929
- panelClassName: "max-h-[30vh] overflow-hidden",
9352
+ panelClassName: "min-w-[var(--trigger-width)] overflow-hidden",
8930
9353
  onOpen: function () {
8931
9354
  if (disabled) {
8932
9355
  popover.hide();
8933
9356
  return;
8934
9357
  }
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
9358
  isOpen = true;
8943
9359
  container.classList.add("open");
8944
9360
  trigger.setAttribute("aria-expanded", "true");
8945
9361
  searchTerm = "";
8946
9362
  if (searchInputWrapper) searchInputWrapper.setValue("");
8947
9363
  else if (searchInputEl) searchInputEl.value = "";
8948
- content.style.minWidth = trigger.offsetWidth + "px";
9364
+ if (popover.panel) {
9365
+ var triggerWidthPx = trigger.offsetWidth + "px";
9366
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
9367
+ popover.panel.style.minWidth = triggerWidthPx;
9368
+ popover.panel.style.width = triggerWidthPx;
9369
+ }
8949
9370
  loadInitialAndRender();
8950
9371
  setTimeout(function () {
8951
9372
  if (searchInputEl) searchInputEl.focus();
8952
9373
  }, 0);
9374
+ // Let consumers (e.g. BANT Questions "Add Contact") inject content into the dropdown
9375
+ try {
9376
+ var doc =
9377
+ global.document ||
9378
+ (typeof document !== "undefined" ? document : null);
9379
+ if (doc && typeof global.CustomEvent !== "undefined") {
9380
+ doc.dispatchEvent(
9381
+ new global.CustomEvent("record-select:opened", {
9382
+ detail: { fieldId: fieldId, content: content },
9383
+ }),
9384
+ );
9385
+ }
9386
+ } catch (e) {}
8953
9387
  },
8954
9388
  onClose: function () {
8955
9389
  isOpen = false;
@@ -8965,7 +9399,8 @@
8965
9399
  },
8966
9400
  });
8967
9401
 
8968
- if (clearBtn) clearBtn.style.display = canClear && value && !disabled ? "" : "none";
9402
+ if (clearBtn)
9403
+ clearBtn.style.display = canClear && value && !disabled ? "" : "none";
8969
9404
 
8970
9405
  function setValue(newVal) {
8971
9406
  value = newVal !== undefined && newVal !== null ? newVal : "";
@@ -8979,16 +9414,18 @@
8979
9414
 
8980
9415
  function updateTriggerDisplay() {
8981
9416
  if (selectedRecord) {
8982
- triggerText.textContent = selectedRecord.name || selectedRecord.label || value;
9417
+ triggerText.textContent =
9418
+ selectedRecord.name || selectedRecord.label || value;
8983
9419
  trigger.classList.remove("placeholder");
8984
9420
  trigger.className = triggerClasses(
8985
9421
  variant,
8986
9422
  size,
8987
9423
  disabled,
8988
9424
  false,
8989
- canClear && !!value && !disabled
9425
+ canClear && !!value && !disabled,
8990
9426
  );
8991
- triggerIcon.className = "record-select-trigger-icon shrink-0 flex items-center justify-center size-20 rounded-4 overflow-hidden";
9427
+ triggerIcon.className =
9428
+ "record-select-trigger-icon shrink-0 flex items-center justify-center size-20 rounded-4 overflow-hidden";
8992
9429
  triggerIcon.innerHTML = "";
8993
9430
  if (objectSlug === STANDARD_OBJECT_SLUGS_USERS) {
8994
9431
  var Avatar = getAvatar();
@@ -9000,7 +9437,9 @@
9000
9437
  });
9001
9438
  triggerIcon.appendChild(vividEl);
9002
9439
  } else {
9003
- renderStaticIconPlaceholder(selectedRecord.name || selectedRecord.label);
9440
+ renderStaticIconPlaceholder(
9441
+ selectedRecord.name || selectedRecord.label,
9442
+ );
9004
9443
  }
9005
9444
  } else {
9006
9445
  renderStaticObjectIcon();
@@ -9013,12 +9452,13 @@
9013
9452
  size,
9014
9453
  disabled,
9015
9454
  true,
9016
- canClear && !!value && !disabled
9455
+ canClear && !!value && !disabled,
9017
9456
  );
9018
9457
  triggerIcon.className = "record-select-trigger-icon shrink-0 hidden";
9019
9458
  triggerIcon.innerHTML = "";
9020
9459
  }
9021
- if (clearBtn) clearBtn.style.display = canClear && value && !disabled ? "" : "none";
9460
+ if (clearBtn)
9461
+ clearBtn.style.display = canClear && value && !disabled ? "" : "none";
9022
9462
  }
9023
9463
 
9024
9464
  function renderStaticObjectIcon() {
@@ -9055,18 +9495,39 @@
9055
9495
  var fields = ["id", "name"];
9056
9496
  var actualLimit = limit || initialLimit;
9057
9497
  var offset = page ? (page - 1) * actualLimit : 0;
9058
-
9498
+
9059
9499
  try {
9060
9500
  if (model && typeof model.select === "function") {
9061
9501
  var q = model.select.apply(model, fields);
9502
+ var filters = [];
9503
+ var resolvedFilter =
9504
+ typeof initialFilter === "function"
9505
+ ? initialFilter()
9506
+ : initialFilter;
9507
+ console.log(
9508
+ "[RecordSelect] initialFilter:",
9509
+ resolvedFilter,
9510
+ "| search:",
9511
+ search,
9512
+ );
9513
+ if (resolvedFilter) {
9514
+ filters = filters.concat(
9515
+ Array.isArray(resolvedFilter) ? resolvedFilter : [resolvedFilter],
9516
+ );
9517
+ }
9062
9518
  if (search && search.trim()) {
9063
- q = q.filterBy({
9519
+ filters.push({
9064
9520
  or: [
9065
9521
  { field: "name", operator: "contains", value: search.trim() },
9066
9522
  { field: "id", operator: "eq", value: search.trim() },
9067
9523
  ],
9068
9524
  });
9069
9525
  }
9526
+ if (filters.length > 0) {
9527
+ q = q.filterBy(
9528
+ filters.length === 1 ? filters[0] : { and: filters },
9529
+ );
9530
+ }
9070
9531
  var orderBy = ["name"];
9071
9532
  if (objectSlug === "account") orderBy.push("-ParentId");
9072
9533
  return q
@@ -9129,7 +9590,12 @@
9129
9590
  updateTriggerDisplay();
9130
9591
  })
9131
9592
  .catch(function () {
9132
- selectedRecord = { id: value, value: value, name: value, label: value };
9593
+ selectedRecord = {
9594
+ id: value,
9595
+ value: value,
9596
+ name: value,
9597
+ label: value,
9598
+ };
9133
9599
  updateTriggerDisplay();
9134
9600
  });
9135
9601
  }
@@ -9140,7 +9606,9 @@
9140
9606
  loadWrap.className =
9141
9607
  "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
9608
  if (Spinner && typeof Spinner.create === "function") {
9143
- loadWrap.appendChild(Spinner.create({ size: "small", text: "Loading..." }));
9609
+ loadWrap.appendChild(
9610
+ Spinner.create({ size: "small", text: "Loading..." }),
9611
+ );
9144
9612
  } else {
9145
9613
  var loadText = document.createElement("span");
9146
9614
  loadText.textContent = "Loading...";
@@ -9148,12 +9616,12 @@
9148
9616
  }
9149
9617
  optionsList.appendChild(loadWrap);
9150
9618
  }
9151
-
9619
+
9152
9620
  function showLoadingMore() {
9153
9621
  // Remove existing loading more indicator
9154
9622
  var existing = optionsList.querySelector(".record-select-loading-more");
9155
9623
  if (existing) existing.remove();
9156
-
9624
+
9157
9625
  var loadWrap = document.createElement("div");
9158
9626
  loadWrap.className =
9159
9627
  "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 +9634,11 @@
9166
9634
  }
9167
9635
  optionsList.appendChild(loadWrap);
9168
9636
  }
9169
-
9637
+
9170
9638
  function removeLoadingMore() {
9171
- var loadingMore = optionsList.querySelector(".record-select-loading-more");
9639
+ var loadingMore = optionsList.querySelector(
9640
+ ".record-select-loading-more",
9641
+ );
9172
9642
  if (loadingMore) loadingMore.remove();
9173
9643
  }
9174
9644
 
@@ -9187,13 +9657,15 @@
9187
9657
  existingOptions.forEach(function (opt) {
9188
9658
  opt.remove();
9189
9659
  });
9190
-
9660
+
9191
9661
  // Remove old loading/empty states
9192
- var oldStates = optionsList.querySelectorAll(".record-select-loading, .record-select-empty");
9662
+ var oldStates = optionsList.querySelectorAll(
9663
+ ".record-select-loading, .record-select-empty",
9664
+ );
9193
9665
  oldStates.forEach(function (el) {
9194
9666
  el.remove();
9195
9667
  });
9196
-
9668
+
9197
9669
  filteredRecords.forEach(function (rec) {
9198
9670
  var optionValue = rec.id || rec.value;
9199
9671
  var optionLabel = rec.name || rec.label || rec.value;
@@ -9208,7 +9680,7 @@
9208
9680
  "hover:bg-fill-tertiary-fill-light-gray focus:bg-fill-tertiary-fill-light-gray",
9209
9681
  isSelected
9210
9682
  ? "bg-primary-surface hover:!bg-primary-surface-hover"
9211
- : ""
9683
+ : "",
9212
9684
  );
9213
9685
 
9214
9686
  var optContent = document.createElement("span");
@@ -9266,70 +9738,93 @@
9266
9738
 
9267
9739
  optionsList.appendChild(option);
9268
9740
  });
9269
-
9741
+
9270
9742
  // Add loading more indicator at the bottom if fetching
9271
9743
  if (isFetchingMore) {
9272
9744
  showLoadingMore();
9273
9745
  }
9274
9746
  }
9275
9747
 
9748
+ function scheduleUpdatePosition() {
9749
+ if (popover && typeof popover.updatePosition === "function") {
9750
+ requestAnimationFrame(function () {
9751
+ requestAnimationFrame(function () {
9752
+ popover.updatePosition();
9753
+ });
9754
+ });
9755
+ }
9756
+ }
9757
+
9276
9758
  function loadInitialAndRender() {
9277
9759
  showLoading();
9278
9760
  currentPage = 1;
9279
9761
  hasMoreRecords = true;
9280
9762
  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
- });
9763
+
9764
+ fetchRecords(searchTerm, initialLimit, 1)
9765
+ .then(function (result) {
9766
+ allRecords = result.records;
9767
+ filteredRecords = result.records;
9768
+ hasMoreRecords = result.hasMore;
9769
+ totalFetched = result.records.length;
9770
+ currentPage = 1;
9771
+
9772
+ if (
9773
+ value &&
9774
+ !result.records.some(function (r) {
9775
+ return (r.id || r.value) === value;
9776
+ })
9777
+ ) {
9778
+ loadSelectedRecord();
9779
+ } else if (value && result.records.length) {
9780
+ var sel = result.records.find(function (r) {
9781
+ return (r.id || r.value) === value;
9782
+ });
9783
+ if (sel) selectedRecord = sel;
9784
+ updateTriggerDisplay();
9785
+ }
9786
+ if (filteredRecords.length === 0) {
9787
+ showEmpty(searchTerm ? "No results found" : "No records available");
9788
+ } else {
9789
+ renderOptions();
9790
+ }
9791
+ scheduleUpdatePosition();
9792
+ })
9793
+ .catch(function () {
9794
+ showEmpty("Failed to load records");
9795
+ hasMoreRecords = false;
9796
+ scheduleUpdatePosition();
9797
+ });
9305
9798
  }
9306
-
9799
+
9307
9800
  function loadMoreRecords() {
9308
9801
  if (isFetchingMore || !hasMoreRecords) return;
9309
-
9802
+
9310
9803
  isFetchingMore = true;
9311
9804
  currentPage += 1;
9312
9805
  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 {
9806
+
9807
+ fetchRecords(searchTerm, initialLimit, currentPage)
9808
+ .then(function (result) {
9809
+ isFetchingMore = false;
9810
+ removeLoadingMore();
9811
+
9812
+ if (result.records.length > 0) {
9813
+ allRecords = allRecords.concat(result.records);
9814
+ filteredRecords = filteredRecords.concat(result.records);
9815
+ totalFetched += result.records.length;
9816
+ hasMoreRecords = result.hasMore;
9817
+ renderOptions();
9818
+ } else {
9819
+ hasMoreRecords = false;
9820
+ }
9821
+ })
9822
+ .catch(function (err) {
9823
+ console.error("[RecordSelect] loadMoreRecords error:", err);
9824
+ isFetchingMore = false;
9825
+ removeLoadingMore();
9325
9826
  hasMoreRecords = false;
9326
- }
9327
- }).catch(function (err) {
9328
- console.error("[RecordSelect] loadMoreRecords error:", err);
9329
- isFetchingMore = false;
9330
- removeLoadingMore();
9331
- hasMoreRecords = false;
9332
- });
9827
+ });
9333
9828
  }
9334
9829
 
9335
9830
  function debouncedSearch() {
@@ -9340,22 +9835,26 @@
9340
9835
  currentPage = 1;
9341
9836
  hasMoreRecords = true;
9342
9837
  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
- });
9838
+
9839
+ fetchRecords(searchTerm, initialLimit, 1)
9840
+ .then(function (result) {
9841
+ allRecords = result.records;
9842
+ filteredRecords = result.records;
9843
+ hasMoreRecords = result.hasMore;
9844
+ totalFetched = result.records.length;
9845
+
9846
+ if (result.records.length === 0) {
9847
+ showEmpty("No results found");
9848
+ } else {
9849
+ renderOptions();
9850
+ }
9851
+ scheduleUpdatePosition();
9852
+ })
9853
+ .catch(function () {
9854
+ showEmpty("Search failed");
9855
+ hasMoreRecords = false;
9856
+ scheduleUpdatePosition();
9857
+ });
9359
9858
  }, 500);
9360
9859
  }
9361
9860
 
@@ -9400,7 +9899,7 @@
9400
9899
  size,
9401
9900
  disabled,
9402
9901
  !value,
9403
- canClear && !!value && !disabled
9902
+ canClear && !!value && !disabled,
9404
9903
  );
9405
9904
  if (disabled && isOpen) closeDropdown();
9406
9905
  };
@@ -9419,7 +9918,7 @@
9419
9918
 
9420
9919
 
9421
9920
  // ============================================
9422
- // File 18/37: components/multiselect.js
9921
+ // File 18/42: components/multiselect.js
9423
9922
  // ============================================
9424
9923
 
9425
9924
  /**
@@ -9577,11 +10076,11 @@
9577
10076
  var content = document.createElement("div");
9578
10077
  content.setAttribute("role", "listbox");
9579
10078
  content.setAttribute("aria-multiselectable", "true");
9580
- content.className = "custom-multiselect-content w-full max-h-[200px] overflow-hidden flex flex-col";
10079
+ content.className = "custom-multiselect-content w-full max-h-[45vh] overflow-hidden flex flex-col";
9581
10080
 
9582
10081
  var optionsList = document.createElement("div");
9583
10082
  optionsList.className =
9584
- "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
10083
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
9585
10084
 
9586
10085
  function isSelected(optionValue) {
9587
10086
  return values.some(function (v) {
@@ -9686,7 +10185,7 @@
9686
10185
  align: "start",
9687
10186
  closeOnClickOutside: true,
9688
10187
  bodyClassName: "p-0 overflow-hidden",
9689
- panelClassName: "min-w-[var(--trigger-width)] max-h-[200px] overflow-hidden",
10188
+ panelClassName: "min-w-[var(--trigger-width)] max-h-[45vh] overflow-hidden",
9690
10189
  onOpen: function () {
9691
10190
  if (disabled) {
9692
10191
  popover.hide();
@@ -9800,7 +10299,7 @@
9800
10299
 
9801
10300
 
9802
10301
  // ============================================
9803
- // File 19/37: components/enum-multiselect.js
10302
+ // File 19/42: components/enum-multiselect.js
9804
10303
  // ============================================
9805
10304
 
9806
10305
  /**
@@ -10061,7 +10560,7 @@
10061
10560
  var content = document.createElement("div");
10062
10561
  content.setAttribute("role", "listbox");
10063
10562
  content.setAttribute("aria-multiselectable", "true");
10064
- content.className = "w-full min-w-[200px] max-h-[30vh] overflow-hidden flex flex-col";
10563
+ content.className = "w-full min-w-[200px] max-h-[45vh] overflow-hidden flex flex-col";
10065
10564
 
10066
10565
  // Search input (using InputComponent like enum-select)
10067
10566
  var searchContainer = document.createElement("div");
@@ -10122,7 +10621,7 @@
10122
10621
  // Options list
10123
10622
  var optionsList = document.createElement("div");
10124
10623
  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";
10624
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white flex-1 min-h-0";
10126
10625
 
10127
10626
  content.appendChild(searchContainer);
10128
10627
  content.appendChild(optionsList);
@@ -10624,7 +11123,7 @@
10624
11123
 
10625
11124
 
10626
11125
  // ============================================
10627
- // File 20/37: components/record-multiselect.js
11126
+ // File 20/42: components/record-multiselect.js
10628
11127
  // ============================================
10629
11128
 
10630
11129
  /**
@@ -10780,6 +11279,7 @@
10780
11279
  * @param {string} [config.size] - 'default' | 'large' | 'small'
10781
11280
  * @param {number} [config.initialLimit] - Initial fetch limit (default 50)
10782
11281
  * @param {Array<string>} [config.displayFields] - Fields to display as secondary info (e.g. ["email", "phone"])
11282
+ * @param {Object} [config.initialFilter] - Optional filter object to merge with search (e.g. { field: "status", operator: "exact", value: "active" } or { and: [...] })
10783
11283
  * @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
11284
  * @returns {HTMLElement} Record multiselect container element
10785
11285
  */
@@ -10794,6 +11294,7 @@
10794
11294
  var variant = config.variant || "default";
10795
11295
  var size = config.size || "default";
10796
11296
  var initialLimit = config.initialLimit != null ? config.initialLimit : 50;
11297
+ var initialFilter = config.initialFilter || null; // Can be array, object, or function returning either
10797
11298
  var displayFields = config.displayFields || [];
10798
11299
 
10799
11300
  var disabled = config.disabled === true;
@@ -10919,7 +11420,7 @@
10919
11420
  var content = document.createElement("div");
10920
11421
  content.setAttribute("role", "listbox");
10921
11422
  content.setAttribute("aria-multiselectable", "true");
10922
- content.className = "record-multiselect-content max-h-[30vh] overflow-hidden flex flex-col";
11423
+ content.className = "record-multiselect-content max-h-[45vh] overflow-hidden flex flex-col";
10923
11424
 
10924
11425
  var searchWrap = document.createElement("div");
10925
11426
  searchWrap.className = "p-8 pb-4 border-b-1/2 border-border-primary ";
@@ -10969,7 +11470,7 @@
10969
11470
  content.appendChild(searchWrap);
10970
11471
 
10971
11472
  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";
11473
+ optionsList.className = "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-multiselect-options";
10973
11474
 
10974
11475
  // Add scroll listener for infinite scroll
10975
11476
  optionsList.addEventListener("scroll", function () {
@@ -10997,28 +11498,22 @@
10997
11498
  align: "start",
10998
11499
  closeOnClickOutside: true,
10999
11500
  bodyClassName: "p-0 overflow-hidden",
11000
- panelClassName: "max-h-[30vh] overflow-hidden",
11501
+ panelClassName: "min-w-[var(--trigger-width)] overflow-hidden",
11001
11502
  onOpen: function () {
11002
11503
  if (disabled) {
11003
11504
  popover.hide();
11004
11505
  return;
11005
11506
  }
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
11507
  container.classList.add("open");
11012
11508
  trigger.setAttribute("aria-expanded", "true");
11013
11509
  searchTerm = "";
11014
11510
  if (searchInputWrapper) searchInputWrapper.setValue("");
11015
11511
  else if (searchInputEl) searchInputEl.value = "";
11016
- var triggerWidthPx = trigger.offsetWidth + "px";
11017
- content.style.minWidth = triggerWidthPx;
11018
- content.style.width = triggerWidthPx;
11019
11512
  if (popover.panel) {
11020
- popover.panel.style.width = triggerWidthPx;
11513
+ var triggerWidthPx = trigger.offsetWidth + "px";
11514
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
11021
11515
  popover.panel.style.minWidth = triggerWidthPx;
11516
+ popover.panel.style.width = triggerWidthPx;
11022
11517
  }
11023
11518
  loadInitialAndRender();
11024
11519
  setTimeout(function () {
@@ -11097,14 +11592,23 @@
11097
11592
  try {
11098
11593
  if (model && typeof model.select === "function") {
11099
11594
  var q = model.select.apply(model, fields);
11595
+ var filters = [];
11596
+ var resolvedFilter = typeof initialFilter === 'function' ? initialFilter() : initialFilter;
11597
+ console.log('[RecordMultiselect] initialFilter:', resolvedFilter, '| search:', search);
11598
+ if (resolvedFilter) {
11599
+ filters = filters.concat(Array.isArray(resolvedFilter) ? resolvedFilter : [resolvedFilter]);
11600
+ }
11100
11601
  if (search && search.trim()) {
11101
- q = q.filterBy({
11602
+ filters.push({
11102
11603
  or: [
11103
11604
  { field: "name", operator: "contains", value: search.trim() },
11104
11605
  { field: "id", operator: "eq", value: search.trim() },
11105
11606
  ],
11106
11607
  });
11107
11608
  }
11609
+ if (filters.length > 0) {
11610
+ q = q.filterBy(filters.length === 1 ? filters[0] : { and: filters });
11611
+ }
11108
11612
  var orderBy = ["name"];
11109
11613
  if (objectSlug === "account") orderBy.push("-ParentId");
11110
11614
  return q
@@ -11363,6 +11867,16 @@
11363
11867
  }
11364
11868
  }
11365
11869
 
11870
+ function scheduleUpdatePosition() {
11871
+ if (popover && typeof popover.updatePosition === "function") {
11872
+ requestAnimationFrame(function () {
11873
+ requestAnimationFrame(function () {
11874
+ popover.updatePosition();
11875
+ });
11876
+ });
11877
+ }
11878
+ }
11879
+
11366
11880
  function loadInitialAndRender() {
11367
11881
  showLoading();
11368
11882
  currentPage = 1;
@@ -11381,9 +11895,11 @@
11381
11895
  } else {
11382
11896
  renderOptions();
11383
11897
  }
11898
+ scheduleUpdatePosition();
11384
11899
  }).catch(function () {
11385
11900
  showEmpty("Failed to load records");
11386
11901
  hasMoreRecords = false;
11902
+ scheduleUpdatePosition();
11387
11903
  });
11388
11904
  }
11389
11905
 
@@ -11435,9 +11951,11 @@
11435
11951
  } else {
11436
11952
  renderOptions();
11437
11953
  }
11954
+ scheduleUpdatePosition();
11438
11955
  }).catch(function () {
11439
11956
  showEmpty("Search failed");
11440
11957
  hasMoreRecords = false;
11958
+ scheduleUpdatePosition();
11441
11959
  });
11442
11960
  }, 500);
11443
11961
  }
@@ -11507,7 +12025,7 @@
11507
12025
 
11508
12026
 
11509
12027
  // ============================================
11510
- // File 21/37: components/input.js
12028
+ // File 21/42: components/input.js
11511
12029
  // ============================================
11512
12030
 
11513
12031
  /**
@@ -11534,7 +12052,7 @@
11534
12052
 
11535
12053
  var WRAPPER_CLASS = {
11536
12054
  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",
12055
+ "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
12056
  default:
11539
12057
  "bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
11540
12058
  error:
@@ -11609,13 +12127,16 @@
11609
12127
  var isPassword = type === "password";
11610
12128
 
11611
12129
  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
- );
12130
+ var sizeClass = inputSize === "large" ? WRAPPER_CLASS.sizeLarge : inputSize === "small" ? WRAPPER_CLASS.sizeSmall : WRAPPER_CLASS.sizeDefault;
12131
+ function applyWrapperClasses() {
12132
+ wrapper.className = join(
12133
+ WRAPPER_CLASS.base,
12134
+ disabled ? WRAPPER_CLASS.disabled : (WRAPPER_CLASS[variant] || WRAPPER_CLASS.default),
12135
+ sizeClass,
12136
+ config.className || ""
12137
+ );
12138
+ }
12139
+ applyWrapperClasses();
11619
12140
  wrapper.setAttribute("data-input-variant", variant);
11620
12141
 
11621
12142
  if (config.prefixNode) {
@@ -11745,19 +12266,12 @@
11745
12266
  wrapper.setVariant = function (v) {
11746
12267
  variant = v;
11747
12268
  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
- );
12269
+ applyWrapperClasses();
11755
12270
  };
11756
12271
  wrapper.setDisabled = function (d) {
11757
12272
  disabled = !!d;
11758
12273
  input.disabled = disabled;
11759
- wrapper.classList.toggle("cursor-not-allowed", disabled);
11760
- wrapper.classList.toggle("opacity-60", disabled);
12274
+ applyWrapperClasses();
11761
12275
  };
11762
12276
 
11763
12277
  return wrapper;
@@ -11772,7 +12286,7 @@
11772
12286
 
11773
12287
 
11774
12288
  // ============================================
11775
- // File 22/37: components/currency.js
12289
+ // File 22/42: components/currency.js
11776
12290
  // ============================================
11777
12291
 
11778
12292
  /**
@@ -11802,7 +12316,7 @@
11802
12316
  sizeLarge: "",
11803
12317
  sizeSmall: "",
11804
12318
  disabled:
11805
- "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
12319
+ "pointer-events-none cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
11806
12320
  };
11807
12321
 
11808
12322
  // Currency type label: fit-content, separator (border-r) on type only, full height
@@ -11989,7 +12503,12 @@
11989
12503
  wrapper.setDisabled = function (d) {
11990
12504
  disabled = !!d;
11991
12505
  input.disabled = disabled;
11992
- wrapper.classList.toggle("cursor-not-allowed", disabled);
12506
+ wrapper.className = join(
12507
+ WRAPPER_CLASS.base,
12508
+ WRAPPER_CLASS[variant] != null ? WRAPPER_CLASS[variant] : WRAPPER_CLASS.default,
12509
+ disabled ? WRAPPER_CLASS.disabled : "",
12510
+ config.className || ""
12511
+ );
11993
12512
  };
11994
12513
 
11995
12514
  return wrapper;
@@ -12005,7 +12524,7 @@
12005
12524
 
12006
12525
 
12007
12526
  // ============================================
12008
- // File 23/37: components/textarea.js
12527
+ // File 23/42: components/textarea.js
12009
12528
  // ============================================
12010
12529
 
12011
12530
  /**
@@ -12030,7 +12549,7 @@
12030
12549
  warning:
12031
12550
  "min-h-[80px] border-warning-base hover:border-warning-base focus:border-warning-base",
12032
12551
  disabled:
12033
- "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
12552
+ "pointer-events-none cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
12034
12553
  };
12035
12554
 
12036
12555
  function join() {
@@ -12125,7 +12644,347 @@
12125
12644
 
12126
12645
 
12127
12646
  // ============================================
12128
- // File 24/37: components/checkbox.js
12647
+ // File 24/42: components/richtext-editor.js
12648
+ // ============================================
12649
+
12650
+ /**
12651
+ * Rich Text Editor Component (vanilla JS)
12652
+ * Toolbar + contenteditable area with formatting (bold, italic, underline, headings, lists, alignment, link, image, code block, undo/redo).
12653
+ * Styling matches design: rounded-12, toolbar bg-fill-tertiary-fill-light-gray, content area with min-height.
12654
+ */
12655
+
12656
+ (function (global) {
12657
+
12658
+ var RICH_TEXT_CONTENT_STYLES =
12659
+ ".rich-text-editor-content ul{list-style-type:disc;padding-left:1.5em;margin:0.5em 0}" +
12660
+ ".rich-text-editor-content ol{list-style-type:decimal;padding-left:1.5em;margin:0.5em 0}" +
12661
+ ".rich-text-editor-content li{margin:0.25em 0}" +
12662
+ ".rich-text-editor-content li p{margin:0}" +
12663
+ ".rich-text-editor-content h1{font-size:1.5rem;font-weight:700;line-height:1.3;margin:0.75em 0 0.5em}" +
12664
+ ".rich-text-editor-content h2{font-size:1.25rem;font-weight:600;line-height:1.3;margin:0.75em 0 0.5em}" +
12665
+ ".rich-text-editor-content h3{font-size:1.125rem;font-weight:600;line-height:1.3;margin:0.75em 0 0.5em}" +
12666
+ ".rich-text-editor-content p{margin:0.5em 0}" +
12667
+ ".rich-text-editor-content a{color:var(--color-primary-500);text-decoration:underline;cursor:pointer}" +
12668
+ ".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}" +
12669
+ ".rich-text-editor-content code{font-family:ui-monospace,monospace;font-size:0.875em}" +
12670
+ ".rich-text-editor-content img{max-width:100%;height:auto;margin:0.5em 0}" +
12671
+ ".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)}" +
12672
+ ".rich-text-editor-content hr{border:none;border-top:1px solid var(--color-neutral-150);margin:1em 0}" +
12673
+ ".rich-text-editor-content .ProseMirror,.rich-text-editor-content [contenteditable]{outline:none}";
12674
+
12675
+ function join() {
12676
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
12677
+ }
12678
+
12679
+ function getDep(name) {
12680
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
12681
+ var c = global.FlowUI._getComponent(name);
12682
+ if (c) return c;
12683
+ }
12684
+ return global[name];
12685
+ }
12686
+
12687
+ /** Get Tabler icon element (16px) from Icon component for toolbar. */
12688
+ function getTablerIcon(iconName) {
12689
+ var Icon = getDep("Icon");
12690
+ if (!Icon || !Icon.iconMap || !Icon.iconMap[iconName]) return null;
12691
+ var svgStr = Icon.iconMap[iconName];
12692
+ var s16 = svgStr.replace(/width="24"/, 'width="16"').replace(/height="24"/, 'height="16"').replace(/width="20"/, 'width="16"').replace(/height="20"/, 'height="16"');
12693
+ var span = document.createElement("span");
12694
+ span.className = "flex items-center justify-center size-16";
12695
+ span.innerHTML = s16;
12696
+ return span;
12697
+ }
12698
+
12699
+ function createToolbarButton(opts) {
12700
+ var Button = getDep("Button");
12701
+ if (!Button || typeof Button.create !== "function") {
12702
+ throw new Error("RichTextEditor requires Button");
12703
+ }
12704
+ var icon = opts.iconStr ? getTablerIcon(opts.iconStr) : null;
12705
+ return Button.create({
12706
+ variant: "outline",
12707
+ size: "default",
12708
+ title: opts.title,
12709
+ icon: icon,
12710
+ onClick: opts.onClick,
12711
+ disabled: opts.disabled,
12712
+ });
12713
+ }
12714
+
12715
+ function createSeparator() {
12716
+ var sep = document.createElement("div");
12717
+ sep.className = "w-px h-16 bg-border-primary mx-4";
12718
+ sep.setAttribute("aria-hidden", "true");
12719
+ return sep;
12720
+ }
12721
+
12722
+ /**
12723
+ * Create a rich text editor
12724
+ * @param {Object} config
12725
+ * @param {string} [config.value] - Initial HTML content
12726
+ * @param {string} [config.placeholder] - Placeholder when empty
12727
+ * @param {number} [config.minHeightPx] - Min height of editor area (default 400)
12728
+ * @param {boolean} [config.disabled]
12729
+ * @param {Function} [config.onChange] - (html: string) => void
12730
+ * @returns {HTMLElement} Wrapper element with getValue/setValue/setDisabled/getInput
12731
+ */
12732
+ function create(config) {
12733
+ var value = config.value != null ? String(config.value) : "";
12734
+ var placeholder = config.placeholder != null ? config.placeholder : "";
12735
+ var minHeightPx = config.minHeightPx != null ? config.minHeightPx : 400;
12736
+ var disabled = !!config.disabled;
12737
+ var onChange = config.onChange;
12738
+
12739
+ var wrapper = document.createElement("div");
12740
+ wrapper.className = "w-full rounded-12 border border-borderColor-border-primary shadow-soft-2x-small";
12741
+
12742
+ var styleEl = document.createElement("style");
12743
+ styleEl.textContent = RICH_TEXT_CONTENT_STYLES;
12744
+ wrapper.appendChild(styleEl);
12745
+
12746
+ var toolbar = document.createElement("div");
12747
+ toolbar.className =
12748
+ "flex flex-wrap gap-4 rounded-t-12 border-borderColor-border-primary bg-fill-tertiary-fill-light-gray p-6";
12749
+ toolbar.setAttribute("role", "toolbar");
12750
+
12751
+ var editorEl = document.createElement("div");
12752
+ editorEl.contentEditable = !disabled;
12753
+ editorEl.className = join(
12754
+ "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"
12755
+ );
12756
+ editorEl.style.minHeight = minHeightPx + "px";
12757
+ if (value) editorEl.innerHTML = value;
12758
+ editorEl.setAttribute("data-placeholder", placeholder);
12759
+
12760
+ function isEmpty() {
12761
+ var text = (editorEl.textContent || "").trim();
12762
+ if (text) return false;
12763
+ var html = (editorEl.innerHTML || "").replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]+>/g, "");
12764
+ return !html.trim();
12765
+ }
12766
+ function updatePlaceholder() {
12767
+ if (placeholder && isEmpty()) {
12768
+ editorEl.classList.add("empty");
12769
+ editorEl.setAttribute("data-placeholder", placeholder);
12770
+ } else {
12771
+ editorEl.classList.remove("empty");
12772
+ editorEl.removeAttribute("data-placeholder");
12773
+ }
12774
+ }
12775
+ updatePlaceholder();
12776
+
12777
+ function getHtml() {
12778
+ return editorEl.innerHTML;
12779
+ }
12780
+ function setHtml(html) {
12781
+ editorEl.innerHTML = html || "";
12782
+ updatePlaceholder();
12783
+ }
12784
+ function notifyChange() {
12785
+ if (typeof onChange === "function") onChange(getHtml());
12786
+ }
12787
+
12788
+ function isActive(cmd, val) {
12789
+ try {
12790
+ return document.queryCommandState(cmd);
12791
+ } catch (e) {
12792
+ return false;
12793
+ }
12794
+ }
12795
+ function blockTag() {
12796
+ var sel = window.getSelection();
12797
+ if (!sel || sel.rangeCount === 0) return null;
12798
+ var node = sel.anchorNode;
12799
+ while (node && node !== editorEl) {
12800
+ if (node.nodeType === 1) {
12801
+ var n = node.nodeName.toLowerCase();
12802
+ if (["h1", "h2", "h3", "p", "div", "pre", "blockquote"].indexOf(n) !== -1) return n;
12803
+ }
12804
+ node = node.parentNode;
12805
+ }
12806
+ return null;
12807
+ }
12808
+ function isAlignment(align) {
12809
+ try {
12810
+ if (align === "left") return document.queryCommandState("justifyLeft");
12811
+ if (align === "center") return document.queryCommandState("justifyCenter");
12812
+ if (align === "right") return document.queryCommandState("justifyRight");
12813
+ } catch (e) {}
12814
+ return false;
12815
+ }
12816
+
12817
+ function refreshToolbar() {
12818
+ toolbar.querySelectorAll("button").forEach(function (btn) {
12819
+ var cmd = btn.getAttribute("data-command");
12820
+ var val = btn.getAttribute("data-value");
12821
+ if (!cmd) return;
12822
+ var active = false;
12823
+ if (cmd === "formatBlock") active = blockTag() === val;
12824
+ else if (cmd === "justifyLeft" && val === "left") active = isAlignment("left");
12825
+ else if (cmd === "justifyCenter" && val === "center") active = isAlignment("center");
12826
+ else if (cmd === "justifyRight" && val === "right") active = isAlignment("right");
12827
+ else active = isActive(cmd);
12828
+ btn.classList.toggle("bg-primary-base", active);
12829
+ btn.classList.toggle("border-primary-base", active);
12830
+ btn.classList.toggle("text-typography-invert-text", active);
12831
+ btn.classList.toggle("bg-fill-quarternary-fill-white", !active);
12832
+ btn.classList.toggle("border-border-primary", !active);
12833
+ btn.classList.toggle("text-typography-primary-text", !active);
12834
+ });
12835
+ }
12836
+
12837
+ function exec(cmd, value) {
12838
+ editorEl.focus();
12839
+ document.execCommand(cmd, false, value != null ? value : null);
12840
+ refreshToolbar();
12841
+ notifyChange();
12842
+ }
12843
+
12844
+ function insertCodeBlock() {
12845
+ editorEl.focus();
12846
+ var sel = window.getSelection();
12847
+ if (sel && sel.rangeCount) {
12848
+ var range = sel.getRangeAt(0);
12849
+ var pre = document.createElement("pre");
12850
+ var code = document.createElement("code");
12851
+ code.textContent = "code here";
12852
+ pre.appendChild(code);
12853
+ range.insertNode(pre);
12854
+ range.setStart(code, 0);
12855
+ range.setEnd(code, 0);
12856
+ sel.removeAllRanges();
12857
+ sel.addRange(range);
12858
+ }
12859
+ refreshToolbar();
12860
+ notifyChange();
12861
+ }
12862
+
12863
+ function addLink() {
12864
+ var url = window.prompt("Enter the URL:", "https://");
12865
+ if (url) {
12866
+ exec("createLink", url);
12867
+ }
12868
+ }
12869
+
12870
+ function addImage() {
12871
+ var input = document.createElement("input");
12872
+ input.type = "file";
12873
+ input.accept = "image/*";
12874
+ input.onchange = function (e) {
12875
+ var file = e.target && e.target.files && e.target.files[0];
12876
+ if (file) {
12877
+ var reader = new FileReader();
12878
+ reader.onload = function (ev) {
12879
+ var src = ev.target && ev.target.result;
12880
+ if (src) {
12881
+ editorEl.focus();
12882
+ document.execCommand("insertImage", false, src);
12883
+ notifyChange();
12884
+ }
12885
+ };
12886
+ reader.readAsDataURL(file);
12887
+ }
12888
+ };
12889
+ input.click();
12890
+ }
12891
+
12892
+ function undo() {
12893
+ editorEl.focus();
12894
+ document.execCommand("undo", false, null);
12895
+ refreshToolbar();
12896
+ notifyChange();
12897
+ }
12898
+ function redo() {
12899
+ editorEl.focus();
12900
+ document.execCommand("redo", false, null);
12901
+ refreshToolbar();
12902
+ notifyChange();
12903
+ }
12904
+
12905
+ function addBtn(iconStr, title, onClick, dataCommand, dataValue) {
12906
+ var btn = createToolbarButton({
12907
+ iconStr: iconStr,
12908
+ title: title,
12909
+ onClick: function () {
12910
+ if (disabled) return;
12911
+ onClick();
12912
+ }});
12913
+ if (dataCommand) btn.setAttribute("data-command", dataCommand);
12914
+ if (dataValue != null) btn.setAttribute("data-value", dataValue);
12915
+ toolbar.appendChild(btn);
12916
+ }
12917
+
12918
+ addBtn("IconBold", "Bold (Ctrl+B)", function () { exec("bold"); }, "bold");
12919
+ addBtn("IconItalic", "Italic (Ctrl+I)", function () { exec("italic"); }, "italic");
12920
+ addBtn("IconUnderline", "Underline (Ctrl+U)", function () { exec("underline"); }, "underline");
12921
+ toolbar.appendChild(createSeparator());
12922
+ addBtn("IconH1", "Heading 1", function () { exec("formatBlock", "h1"); }, "formatBlock", "h1");
12923
+ addBtn("IconH2", "Heading 2", function () { exec("formatBlock", "h2"); }, "formatBlock", "h2");
12924
+ addBtn("IconH3", "Heading 3", function () { exec("formatBlock", "h3"); }, "formatBlock", "h3");
12925
+ toolbar.appendChild(createSeparator());
12926
+ addBtn("IconList", "Bullet List", function () { exec("insertUnorderedList"); }, "insertUnorderedList");
12927
+ addBtn("IconListNumbers", "Ordered List", function () { exec("insertOrderedList"); }, "insertOrderedList");
12928
+ toolbar.appendChild(createSeparator());
12929
+ addBtn("IconAlignLeft", "Align Left", function () { exec("justifyLeft"); }, "justifyLeft", "left");
12930
+ addBtn("IconAlignCenter", "Align Center", function () { exec("justifyCenter"); }, "justifyCenter", "center");
12931
+ addBtn("IconAlignRight", "Align Right", function () { exec("justifyRight"); }, "justifyRight", "right");
12932
+ toolbar.appendChild(createSeparator());
12933
+ addBtn("IconCode", "Code Block", insertCodeBlock, "formatBlock", "pre");
12934
+ addBtn("IconLink", "Add Link", addLink, "link");
12935
+ addBtn("IconPhoto", "Insert Image", addImage);
12936
+ toolbar.appendChild(createSeparator());
12937
+ addBtn("IconArrowBackUp", "Undo", undo);
12938
+ addBtn("IconArrowForwardUp", "Redo", redo);
12939
+
12940
+ editorEl.addEventListener("input", function () {
12941
+ updatePlaceholder();
12942
+ refreshToolbar();
12943
+ notifyChange();
12944
+ });
12945
+ editorEl.addEventListener("keyup", refreshToolbar);
12946
+ editorEl.addEventListener("mouseup", refreshToolbar);
12947
+ editorEl.addEventListener("focus", refreshToolbar);
12948
+
12949
+ if (placeholder) {
12950
+ var placeholderStyles = document.createElement("style");
12951
+ placeholderStyles.textContent =
12952
+ ".rich-text-editor-content.empty:before{content:attr(data-placeholder);color:var(--color-neutral-400);pointer-events:none}";
12953
+ wrapper.appendChild(placeholderStyles);
12954
+ }
12955
+
12956
+ wrapper.appendChild(toolbar);
12957
+ wrapper.appendChild(editorEl);
12958
+
12959
+ wrapper.getInput = function () {
12960
+ return editorEl;
12961
+ };
12962
+ wrapper.getValue = function () {
12963
+ return getHtml();
12964
+ };
12965
+ wrapper.setValue = function (v) {
12966
+ setHtml(v);
12967
+ };
12968
+ wrapper.setDisabled = function (d) {
12969
+ disabled = !!d;
12970
+ editorEl.contentEditable = !disabled;
12971
+ toolbar.querySelectorAll("button").forEach(function (b) {
12972
+ b.disabled = disabled;
12973
+ });
12974
+ };
12975
+
12976
+ return wrapper;
12977
+ }
12978
+
12979
+ global.RichTextEditorComponent = {
12980
+ create: create,
12981
+ };
12982
+ })(typeof window !== "undefined" ? window : undefined);
12983
+
12984
+
12985
+
12986
+ // ============================================
12987
+ // File 25/42: components/checkbox.js
12129
12988
  // ============================================
12130
12989
 
12131
12990
  /**
@@ -12164,10 +13023,18 @@
12164
13023
  };
12165
13024
 
12166
13025
  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";
13026
+ "flex items-center justify-center rounded-2 border-1/2 border-borderColor-border-primary bg-fill-quarternary-fill-white p-4 transition-all";
13027
+
13028
+ var CHECKBOX_ACTIVE_CLASS =
13029
+ "hover:border-primary-base hover:shadow-primary-focused cursor-pointer";
13030
+
13031
+ var CHECKBOX_DISABLED_CLASS =
13032
+ "cursor-not-allowed opacity-50";
12168
13033
 
12169
13034
  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";
13035
+ "data-checked:border-transparent data-checked:bg-primary-base";
13036
+ var CHECKBOX_CHECKED_ACTIVE_CLASS =
13037
+ "data-checked:hover:border-primary-base data-checked:hover:shadow-primary-focused";
12171
13038
 
12172
13039
  var LABEL_BASE_CLASS =
12173
13040
  "cursor-pointer pb-0 text-reg-12 leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70";
@@ -12231,12 +13098,16 @@
12231
13098
 
12232
13099
  // Custom checkbox visual
12233
13100
  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
- );
13101
+ function applyCheckboxBoxClasses() {
13102
+ var sizeClass = CHECKBOX_SIZES[size] || CHECKBOX_SIZES.default;
13103
+ checkboxBox.className = join(
13104
+ CHECKBOX_BASE_CLASS,
13105
+ disabled ? CHECKBOX_DISABLED_CLASS : join(CHECKBOX_ACTIVE_CLASS, CHECKBOX_CHECKED_ACTIVE_CLASS),
13106
+ CHECKBOX_CHECKED_CLASS,
13107
+ sizeClass
13108
+ );
13109
+ }
13110
+ applyCheckboxBoxClasses();
12240
13111
  checkboxBox.setAttribute("role", "checkbox");
12241
13112
  checkboxBox.setAttribute("tabindex", disabled ? "-1" : "0");
12242
13113
  checkboxBox.setAttribute("aria-checked", indeterminate ? "mixed" : checked ? "true" : "false");
@@ -12353,6 +13224,7 @@
12353
13224
  } else {
12354
13225
  checkboxBox.removeAttribute("aria-disabled");
12355
13226
  }
13227
+ applyCheckboxBoxClasses();
12356
13228
  updateCheckedState();
12357
13229
  };
12358
13230
 
@@ -12385,7 +13257,210 @@
12385
13257
 
12386
13258
 
12387
13259
  // ============================================
12388
- // File 25/37: components/radio-group.js
13260
+ // File 26/42: components/checkbox-group.js
13261
+ // ============================================
13262
+
13263
+ /**
13264
+ * CheckboxGroup Component (vanilla JS)
13265
+ * Multi-select via checkboxes; same API as MultiSelect (options, value array, onValuesChange).
13266
+ * Uses input.js-style variants and sizes for the group wrapper.
13267
+ */
13268
+
13269
+ (function (global) {
13270
+
13271
+ // Wrapper classes aligned with input.js variants
13272
+ var WRAPPER_CLASS = {
13273
+ base:
13274
+ "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",
13275
+ default:
13276
+ "bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
13277
+ error:
13278
+ "border-error-base bg-fill-quarternary-fill-white hover:border-error-base focus-within:border-error-base",
13279
+ warning:
13280
+ "border-warning-base bg-fill-quarternary-fill-white hover:border-warning-base focus-within:border-warning-base",
13281
+ success:
13282
+ "border-success-base bg-fill-quarternary-fill-white hover:border-success-base focus-within:border-success-base",
13283
+ borderless:
13284
+ "border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
13285
+ inline:
13286
+ "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",
13287
+ sizeDefault: "px-12 py-6 gap-6",
13288
+ sizeLarge: "px-12 py-8 gap-8",
13289
+ sizeSmall: "px-12 py-4 gap-4",
13290
+ disabled:
13291
+ "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
13292
+ };
13293
+
13294
+ function join() {
13295
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
13296
+ }
13297
+
13298
+ function getOptionValue(opt) {
13299
+ return opt.value !== undefined && opt.value !== null
13300
+ ? opt.value
13301
+ : opt.slug || opt.id;
13302
+ }
13303
+
13304
+ function getOptionLabel(opt) {
13305
+ return opt.label || opt.name || opt.display_name || opt.value;
13306
+ }
13307
+
13308
+ function getDep(name) {
13309
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
13310
+ var c = global.FlowUI._getComponent(name);
13311
+ if (c) return c;
13312
+ }
13313
+ return global[name];
13314
+ }
13315
+
13316
+ /**
13317
+ * Create a checkbox group component (multiselect-like: multiple values, options array)
13318
+ * @param {Object} config
13319
+ * @param {string} [config.fieldId] - Field ID for state management
13320
+ * @param {Array} config.options - Array of { value, label } or { slug, display_name }
13321
+ * @param {Array} [config.value] - Current selected values (array)
13322
+ * @param {Function} config.onValuesChange - Change handler (values: string[])
13323
+ * @param {boolean} [config.disabled] - Whether all checkboxes are disabled
13324
+ * @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'success' | 'borderless' | 'inline'
13325
+ * @param {string} [config.size] - 'default' | 'large' | 'small'
13326
+ * @param {string} [config.layout] - 'vertical' | 'horizontal'
13327
+ * @param {string} [config.className] - Extra class on wrapper
13328
+ * @returns {HTMLElement} CheckboxGroup container element
13329
+ */
13330
+ function createCheckboxGroup(config) {
13331
+ var fieldId = config.fieldId;
13332
+ var options = config.options || [];
13333
+ var onValuesChange = config.onValuesChange;
13334
+ var variant = config.variant || "default";
13335
+ var size = config.size || "default";
13336
+ var disabled = config.disabled === true;
13337
+ var layout = config.layout || "vertical";
13338
+ var className = config.className || "";
13339
+
13340
+ var values = Array.isArray(config.value)
13341
+ ? config.value.slice()
13342
+ : Array.isArray(config.values)
13343
+ ? config.values.slice()
13344
+ : [];
13345
+
13346
+ var Checkbox = getDep("Checkbox");
13347
+ if (!Checkbox || typeof Checkbox.create !== "function") {
13348
+ throw new Error("CheckboxGroup requires the Checkbox component. Load checkbox.js before checkbox-group.js.");
13349
+ }
13350
+
13351
+ var container = document.createElement("div");
13352
+ container.setAttribute("role", "group");
13353
+ container.setAttribute("aria-label", config.ariaLabel || "Checkbox group");
13354
+ if (fieldId) container.setAttribute("data-field-id", fieldId);
13355
+
13356
+ var sizeClass = size === "large" ? WRAPPER_CLASS.sizeLarge : size === "small" ? WRAPPER_CLASS.sizeSmall : WRAPPER_CLASS.sizeDefault;
13357
+ function applyWrapperClasses() {
13358
+ container.className = join(
13359
+ WRAPPER_CLASS.base,
13360
+ disabled ? WRAPPER_CLASS.disabled : (WRAPPER_CLASS[variant] || WRAPPER_CLASS.default),
13361
+ sizeClass,
13362
+ layout === "horizontal" ? "flex-row flex-wrap" : "flex-col",
13363
+ "custom-checkbox-group",
13364
+ className
13365
+ );
13366
+ }
13367
+ applyWrapperClasses();
13368
+ container.setAttribute("data-checkbox-group-variant", variant);
13369
+
13370
+ function isSelected(optionValue) {
13371
+ return values.some(function (v) {
13372
+ return v === optionValue || String(v) === String(optionValue);
13373
+ });
13374
+ }
13375
+
13376
+ var optionsContainer = document.createElement("div");
13377
+ optionsContainer.className = join(
13378
+ "flex gap-8",
13379
+ layout === "horizontal" ? "flex-row flex-wrap" : "flex-col"
13380
+ );
13381
+
13382
+ function buildOptions() {
13383
+ optionsContainer.innerHTML = "";
13384
+ if (options.length === 0) {
13385
+ var empty = document.createElement("div");
13386
+ empty.className = "!text-reg-13 text-typography-quaternary-text py-4";
13387
+ empty.textContent = "No options available";
13388
+ optionsContainer.appendChild(empty);
13389
+ return;
13390
+ }
13391
+
13392
+ options.forEach(function (opt, index) {
13393
+ var optionValue = getOptionValue(opt);
13394
+ var optionLabel = getOptionLabel(opt);
13395
+ var optionDisabled = disabled || !!opt.disabled;
13396
+ var checked = isSelected(optionValue);
13397
+
13398
+ var cb = Checkbox.create({
13399
+ id: (fieldId || "cbg") + "-" + index,
13400
+ name: fieldId ? fieldId + "[]" : "checkbox-group-" + index,
13401
+ checked: checked,
13402
+ disabled: optionDisabled,
13403
+ label: optionLabel,
13404
+ align: "left",
13405
+ size: size === "large" ? "large" : size === "small" ? "small" : "default",
13406
+ onChange: function (isChecked) {
13407
+ if (optionDisabled) return;
13408
+ if (isChecked) {
13409
+ if (!values.some(function (v) { return v === optionValue || String(v) === String(optionValue); })) {
13410
+ values.push(optionValue);
13411
+ }
13412
+ } else {
13413
+ var idx = values.findIndex(function (v) {
13414
+ return v === optionValue || String(v) === String(optionValue);
13415
+ });
13416
+ if (idx >= 0) values.splice(idx, 1);
13417
+ }
13418
+ if (onValuesChange) onValuesChange(values.slice());
13419
+ },
13420
+ });
13421
+ optionsContainer.appendChild(cb);
13422
+ });
13423
+ }
13424
+
13425
+ buildOptions();
13426
+ container.appendChild(optionsContainer);
13427
+
13428
+ container.updateValues = function (newValues) {
13429
+ values = Array.isArray(newValues) ? newValues.slice() : [];
13430
+ buildOptions();
13431
+ };
13432
+
13433
+ container.setDisabled = function (isDisabled) {
13434
+ disabled = !!isDisabled;
13435
+ applyWrapperClasses();
13436
+ var wrappers = optionsContainer.querySelectorAll(":scope > div");
13437
+ for (var i = 0; i < wrappers.length; i++) {
13438
+ if (typeof wrappers[i].setDisabled === "function") wrappers[i].setDisabled(disabled);
13439
+ }
13440
+ };
13441
+
13442
+ container.setVariant = function (v) {
13443
+ variant = v;
13444
+ container.setAttribute("data-checkbox-group-variant", v);
13445
+ applyWrapperClasses();
13446
+ };
13447
+
13448
+ container.getValues = function () {
13449
+ return values.slice();
13450
+ };
13451
+
13452
+ return container;
13453
+ }
13454
+
13455
+ global.CheckboxGroup = {
13456
+ create: createCheckboxGroup,
13457
+ };
13458
+ })(typeof window !== "undefined" ? window : undefined);
13459
+
13460
+
13461
+
13462
+ // ============================================
13463
+ // File 27/42: components/radio-group.js
12389
13464
  // ============================================
12390
13465
 
12391
13466
  /**
@@ -12796,7 +13871,296 @@
12796
13871
 
12797
13872
 
12798
13873
  // ============================================
12799
- // File 26/37: components/enumeration.js
13874
+ // File 28/42: components/card-select.js
13875
+ // ============================================
13876
+
13877
+ /**
13878
+ * CardSelect Component (vanilla JS)
13879
+ * Full-width clickable card selection with icon, title, description, and check indicator.
13880
+ * Drop-in replacement / upgrade to RadioGroup when visual card UI is preferred.
13881
+ */
13882
+
13883
+ (function (global) {
13884
+
13885
+ var COLORS = {
13886
+ selectedBorder: "#175259",
13887
+ unselectedBorder: "#e5e7eb",
13888
+ selectedBg: "#f0f9f8",
13889
+ unselectedBg: "#ffffff",
13890
+ selectedShadow: "0px 0px 0px 2px #e9f7f5",
13891
+ unselectedShadow: "0px 1.5px 4px -1px rgba(10,9,11,0.07)",
13892
+ hoverBorder: "#9ca3af",
13893
+ hoverShadow: "0px 5px 13px -5px rgba(10,9,11,0.05), 0px 2px 4px -1px rgba(10,9,11,0.02)",
13894
+ iconSelectedBg: "#d0ede9",
13895
+ iconUnselectedBg: "#f3f4f6",
13896
+ iconSelectedColor: "#175259",
13897
+ iconUnselectedColor: "#6b7280",
13898
+ titleSelected: "#175259",
13899
+ titleUnselected: "#111827",
13900
+ descSelected: "#35b18b",
13901
+ descUnselected: "#6b7280",
13902
+ checkBorderSelected: "#175259",
13903
+ checkBorderUnselected: "#d1d5db",
13904
+ checkBgSelected: "#175259",
13905
+ checkBgUnselected: "transparent",
13906
+ };
13907
+
13908
+ var CHECK_ICON =
13909
+ '<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 5l2.5 2.5L8 3" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>';
13910
+
13911
+ function join() {
13912
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
13913
+ }
13914
+
13915
+ function applyCardStyles(card, isSelected) {
13916
+ card.style.borderColor = isSelected ? COLORS.selectedBorder : COLORS.unselectedBorder;
13917
+ card.style.background = isSelected ? COLORS.selectedBg : COLORS.unselectedBg;
13918
+ card.style.boxShadow = isSelected ? COLORS.selectedShadow : COLORS.unselectedShadow;
13919
+ }
13920
+
13921
+ function applyIconStyles(iconWrapper, isSelected) {
13922
+ iconWrapper.style.background = isSelected ? COLORS.iconSelectedBg : COLORS.iconUnselectedBg;
13923
+ iconWrapper.style.color = isSelected ? COLORS.iconSelectedColor : COLORS.iconUnselectedColor;
13924
+ }
13925
+
13926
+ function applyTitleStyles(titleEl, isSelected) {
13927
+ titleEl.style.color = isSelected ? COLORS.titleSelected : COLORS.titleUnselected;
13928
+ }
13929
+
13930
+ function applyDescStyles(descEl, isSelected) {
13931
+ descEl.style.color = isSelected ? COLORS.descSelected : COLORS.descUnselected;
13932
+ }
13933
+
13934
+ function applyCheckStyles(checkEl, isSelected) {
13935
+ checkEl.style.borderColor = isSelected ? COLORS.checkBorderSelected : COLORS.checkBorderUnselected;
13936
+ checkEl.style.background = isSelected ? COLORS.checkBgSelected : COLORS.checkBgUnselected;
13937
+ checkEl.innerHTML = isSelected ? CHECK_ICON : "";
13938
+ }
13939
+
13940
+ /**
13941
+ * Create a card select component
13942
+ * @param {Object} config
13943
+ * @param {string} [config.name] - name attribute for the group (used for id generation)
13944
+ * @param {Array} config.options - array of { value, label, description?, icon?, disabled? }
13945
+ * @param {string} [config.defaultValue] - initial selected value
13946
+ * @param {string} [config.value] - controlled value (takes priority over defaultValue)
13947
+ * @param {boolean} [config.disabled] - disable all cards
13948
+ * @param {string} [config.className] - extra class on wrapper
13949
+ * @param {Function} [config.onChange] - change handler (receives selected value)
13950
+ * @returns {HTMLElement} wrapper element with getValue/setValue/setDisabled API
13951
+ */
13952
+ function create(config) {
13953
+ var opts = config || {};
13954
+ var name = opts.name || "card-select-" + Math.random().toString(36).substr(2, 9);
13955
+ var options = opts.options || [];
13956
+ var defaultValue = opts.defaultValue;
13957
+ var selectedValue = opts.value !== undefined ? opts.value : defaultValue;
13958
+ var disabled = !!opts.disabled;
13959
+ var className = opts.className || "";
13960
+ var onChange = opts.onChange;
13961
+
13962
+ // Wrapper container
13963
+ var wrapper = document.createElement("div");
13964
+ wrapper.setAttribute("role", "radiogroup");
13965
+ wrapper.setAttribute("dir", "ltr");
13966
+ wrapper.className = join("flex flex-col gap-3 w-full", className);
13967
+
13968
+ function updateAllCards(newValue) {
13969
+ var cards = wrapper.querySelectorAll("[data-card-value]");
13970
+ cards.forEach(function (card) {
13971
+ var cv = card.dataset.cardValue;
13972
+ var active = cv === newValue;
13973
+ applyCardStyles(card, active);
13974
+ card.setAttribute("aria-checked", active ? "true" : "false");
13975
+ var iw = card.querySelector("[data-icon]");
13976
+ var titleEl = card.querySelector("[data-title]");
13977
+ var descEl = card.querySelector("[data-desc]");
13978
+ var checkEl = card.querySelector("[data-check]");
13979
+ if (iw) applyIconStyles(iw, active);
13980
+ if (titleEl) applyTitleStyles(titleEl, active);
13981
+ if (descEl) applyDescStyles(descEl, active);
13982
+ if (checkEl) applyCheckStyles(checkEl, active);
13983
+ });
13984
+ }
13985
+
13986
+ options.forEach(function (option, index) {
13987
+ var optionValue = option.value;
13988
+ var optionLabel = option.label || option.value;
13989
+ var optionDesc = option.description || "";
13990
+ var optionIcon = option.icon || "";
13991
+ var optionDisabled = disabled || !!option.disabled;
13992
+ var isSelected = optionValue === selectedValue;
13993
+
13994
+ // Card element
13995
+ var card = document.createElement("div");
13996
+ card.dataset.cardValue = optionValue;
13997
+ card.id = name + "-card-" + index;
13998
+ card.setAttribute("role", "radio");
13999
+ card.setAttribute("aria-checked", isSelected ? "true" : "false");
14000
+ card.setAttribute("tabindex", optionDisabled ? "-1" : "0");
14001
+
14002
+ card.style.cssText = [
14003
+ "display: flex",
14004
+ "align-items: flex-start",
14005
+ "gap: 16px",
14006
+ "padding: 18px 20px",
14007
+ "border-radius: 10px",
14008
+ "border: 1.5px solid " + (isSelected ? COLORS.selectedBorder : COLORS.unselectedBorder),
14009
+ "background: " + (isSelected ? COLORS.selectedBg : COLORS.unselectedBg),
14010
+ "cursor: " + (optionDisabled ? "not-allowed" : "pointer"),
14011
+ "transition: border-color 0.15s, background 0.15s, box-shadow 0.15s",
14012
+ "box-shadow: " + (isSelected ? COLORS.selectedShadow : COLORS.unselectedShadow),
14013
+ "user-select: none",
14014
+ optionDisabled ? "opacity: 0.5" : "",
14015
+ ].filter(Boolean).join("; ");
14016
+
14017
+ // Icon wrapper (only rendered when icon is provided)
14018
+ if (optionIcon) {
14019
+ var iconWrapper = document.createElement("div");
14020
+ iconWrapper.dataset.icon = "";
14021
+ iconWrapper.style.cssText = [
14022
+ "flex-shrink: 0",
14023
+ "width: 44px",
14024
+ "height: 44px",
14025
+ "border-radius: 8px",
14026
+ "background: " + (isSelected ? COLORS.iconSelectedBg : COLORS.iconUnselectedBg),
14027
+ "display: flex",
14028
+ "align-items: center",
14029
+ "justify-content: center",
14030
+ "color: " + (isSelected ? COLORS.iconSelectedColor : COLORS.iconUnselectedColor),
14031
+ "transition: background 0.15s, color 0.15s",
14032
+ ].join("; ");
14033
+ iconWrapper.innerHTML = optionIcon;
14034
+ card.appendChild(iconWrapper);
14035
+ }
14036
+
14037
+ // Text wrapper
14038
+ var textWrapper = document.createElement("div");
14039
+ textWrapper.style.cssText = "display: flex; flex-direction: column; gap: 4px; flex: 1;";
14040
+
14041
+ var titleEl = document.createElement("span");
14042
+ titleEl.dataset.title = "";
14043
+ titleEl.textContent = optionLabel;
14044
+ titleEl.style.cssText = [
14045
+ "font-size: 14px",
14046
+ "font-weight: 600",
14047
+ "color: " + (isSelected ? COLORS.titleSelected : COLORS.titleUnselected),
14048
+ "line-height: 1.4",
14049
+ "transition: color 0.15s",
14050
+ ].join("; ");
14051
+ textWrapper.appendChild(titleEl);
14052
+
14053
+ if (optionDesc) {
14054
+ var descEl = document.createElement("span");
14055
+ descEl.dataset.desc = "";
14056
+ descEl.textContent = optionDesc;
14057
+ descEl.style.cssText = [
14058
+ "font-size: 12px",
14059
+ "color: " + (isSelected ? COLORS.descSelected : COLORS.descUnselected),
14060
+ "line-height: 1.5",
14061
+ "transition: color 0.15s",
14062
+ ].join("; ");
14063
+ textWrapper.appendChild(descEl);
14064
+ }
14065
+
14066
+ card.appendChild(textWrapper);
14067
+
14068
+ // Check indicator (radio circle in top-right)
14069
+ var checkEl = document.createElement("div");
14070
+ checkEl.dataset.check = "";
14071
+ checkEl.style.cssText = [
14072
+ "flex-shrink: 0",
14073
+ "width: 18px",
14074
+ "height: 18px",
14075
+ "border-radius: 50%",
14076
+ "border: 2px solid " + (isSelected ? COLORS.checkBorderSelected : COLORS.checkBorderUnselected),
14077
+ "background: " + (isSelected ? COLORS.checkBgSelected : COLORS.checkBgUnselected),
14078
+ "display: flex",
14079
+ "align-items: center",
14080
+ "justify-content: center",
14081
+ "margin-top: 2px",
14082
+ "transition: all 0.15s",
14083
+ ].join("; ");
14084
+ if (isSelected) {
14085
+ checkEl.innerHTML = CHECK_ICON;
14086
+ }
14087
+ card.appendChild(checkEl);
14088
+
14089
+ // Hover and focus styles
14090
+ if (!optionDisabled) {
14091
+ card.addEventListener("mouseenter", function () {
14092
+ if (card.getAttribute("aria-checked") !== "true") {
14093
+ card.style.borderColor = COLORS.hoverBorder;
14094
+ card.style.boxShadow = COLORS.hoverShadow;
14095
+ }
14096
+ });
14097
+ card.addEventListener("mouseleave", function () {
14098
+ if (card.getAttribute("aria-checked") !== "true") {
14099
+ card.style.borderColor = COLORS.unselectedBorder;
14100
+ card.style.boxShadow = COLORS.unselectedShadow;
14101
+ }
14102
+ });
14103
+
14104
+ // Click handler
14105
+ card.addEventListener("click", function () {
14106
+ if (optionDisabled || disabled) return;
14107
+ selectedValue = optionValue;
14108
+ updateAllCards(selectedValue);
14109
+ if (typeof onChange === "function") {
14110
+ onChange(selectedValue);
14111
+ }
14112
+ });
14113
+
14114
+ // Keyboard support
14115
+ card.addEventListener("keydown", function (e) {
14116
+ if (optionDisabled || disabled) return;
14117
+ if (e.key === " " || e.key === "Enter") {
14118
+ e.preventDefault();
14119
+ card.click();
14120
+ }
14121
+ });
14122
+ }
14123
+
14124
+ wrapper.appendChild(card);
14125
+ });
14126
+
14127
+ // Public API
14128
+ wrapper.getValue = function () {
14129
+ return selectedValue !== undefined ? selectedValue : null;
14130
+ };
14131
+
14132
+ wrapper.setValue = function (newValue) {
14133
+ selectedValue = newValue;
14134
+ updateAllCards(newValue);
14135
+ };
14136
+
14137
+ wrapper.setDisabled = function (isDisabled) {
14138
+ disabled = !!isDisabled;
14139
+ wrapper.querySelectorAll("[data-card-value]").forEach(function (card) {
14140
+ card.style.cursor = disabled ? "not-allowed" : "pointer";
14141
+ card.style.opacity = disabled ? "0.5" : "1";
14142
+ card.setAttribute("tabindex", disabled ? "-1" : "0");
14143
+ });
14144
+ };
14145
+
14146
+ return wrapper;
14147
+ }
14148
+
14149
+ var CardSelect = {
14150
+ create: create,
14151
+ };
14152
+
14153
+ if (typeof module !== "undefined" && module.exports) {
14154
+ module.exports = CardSelect;
14155
+ } else {
14156
+ global.CardSelect = CardSelect;
14157
+ }
14158
+ })(typeof window !== "undefined" ? window : undefined);
14159
+
14160
+
14161
+
14162
+ // ============================================
14163
+ // File 29/42: components/enumeration.js
12800
14164
  // ============================================
12801
14165
 
12802
14166
  /**
@@ -12808,7 +14172,7 @@
12808
14172
  (function (global) {
12809
14173
 
12810
14174
  var BASE_CLASS =
12811
- "flex items-center border rounded-4 text-typography-primary-text gap-4 !text-reg-13";
14175
+ "flex items-center border-1/2 rounded-4 text-typography-primary-text gap-4 !text-reg-13";
12812
14176
 
12813
14177
  var VARIANTS = {
12814
14178
  default:
@@ -12832,7 +14196,7 @@
12832
14196
  };
12833
14197
 
12834
14198
  var DISABLED_CLASS =
12835
- "pointer-events-none !cursor-not-allowed opacity-50";
14199
+ "pointer-events-none !cursor-not-allowed opacity-50 bg-fill-tertiary-fill-light-gray";
12836
14200
  var READONLY_CLASS = "pointer-events-none";
12837
14201
 
12838
14202
  var ITEM_BASE_CLASS =
@@ -12891,20 +14255,23 @@
12891
14255
  var iconSize = opts.iconSize || "default";
12892
14256
  var defaultValue = opts.defaultValue;
12893
14257
  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
- );
14258
+ var readOnly = opts.readOnly === true;
14259
+ var className = opts.className || "";
14260
+ var onValueChange = opts.onValueChange;
14261
+ var children = opts.children;
14262
+
14263
+ var wrapper = document.createElement("div");
14264
+ function applyWrapperClasses() {
14265
+ wrapper.className = join(
14266
+ BASE_CLASS,
14267
+ VARIANTS[variant] != null ? VARIANTS[variant] : VARIANTS.default,
14268
+ SIZES[size] != null ? SIZES[size] : SIZES.default,
14269
+ disabled ? DISABLED_CLASS : "",
14270
+ readOnly ? READONLY_CLASS : "",
14271
+ className
14272
+ );
14273
+ }
14274
+ applyWrapperClasses();
12908
14275
  wrapper.setAttribute("data-enumeration-variant", variant);
12909
14276
 
12910
14277
  var count =
@@ -12974,6 +14341,22 @@
12974
14341
  }
12975
14342
  }
12976
14343
 
14344
+ wrapper.setDisabled = function (d) {
14345
+ disabled = d === true;
14346
+ for (var j = 0; j < itemElements.length; j++) {
14347
+ itemElements[j].setAttribute("tabindex", disabled || readOnly ? "-1" : "0");
14348
+ }
14349
+ applyWrapperClasses();
14350
+ };
14351
+
14352
+ wrapper.setReadOnly = function (r) {
14353
+ readOnly = r === true;
14354
+ for (var j = 0; j < itemElements.length; j++) {
14355
+ itemElements[j].setAttribute("tabindex", disabled || readOnly ? "-1" : "0");
14356
+ }
14357
+ applyWrapperClasses();
14358
+ };
14359
+
12977
14360
  return wrapper;
12978
14361
  }
12979
14362
 
@@ -13015,7 +14398,7 @@
13015
14398
 
13016
14399
 
13017
14400
  // ============================================
13018
- // File 27/37: components/time-picker.js
14401
+ // File 30/42: components/time-picker.js
13019
14402
  // ============================================
13020
14403
 
13021
14404
  /**
@@ -13376,7 +14759,7 @@
13376
14759
 
13377
14760
 
13378
14761
  // ============================================
13379
- // File 28/37: components/duration/duration-utils.js
14762
+ // File 31/42: components/duration/duration-utils.js
13380
14763
  // ============================================
13381
14764
 
13382
14765
  /**
@@ -13546,7 +14929,7 @@
13546
14929
 
13547
14930
 
13548
14931
  // ============================================
13549
- // File 29/37: components/duration/duration-constants.js
14932
+ // File 32/42: components/duration/duration-constants.js
13550
14933
  // ============================================
13551
14934
 
13552
14935
  /**
@@ -13598,7 +14981,7 @@
13598
14981
 
13599
14982
 
13600
14983
  // ============================================
13601
- // File 30/37: components/duration/duration.js
14984
+ // File 33/42: components/duration/duration.js
13602
14985
  // ============================================
13603
14986
 
13604
14987
  /**
@@ -13642,7 +15025,7 @@
13642
15025
  sizeLarge: "px-12 py-8",
13643
15026
  sizeSmall: "px-12 py-4",
13644
15027
  disabled:
13645
- "cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary opacity-60",
15028
+ "pointer-events-none cursor-not-allowed border-border-primary bg-fill-tertiary-fill-light-gray text-typography-quaternary-text hover:border-border-primary",
13646
15029
  };
13647
15030
 
13648
15031
  function join() {
@@ -14035,8 +15418,13 @@
14035
15418
  };
14036
15419
  container.setDisabled = function (d) {
14037
15420
  disabled = !!d;
14038
- triggerWrapper.classList.toggle("cursor-not-allowed", disabled);
14039
- triggerWrapper.classList.toggle("opacity-60", disabled);
15421
+ triggerWrapper.className = join(
15422
+ TRIGGER_CLASS.base,
15423
+ TRIGGER_CLASS[variant] != null ? TRIGGER_CLASS[variant] : TRIGGER_CLASS.default,
15424
+ size === "large" ? TRIGGER_CLASS.sizeLarge : size === "small" ? TRIGGER_CLASS.sizeSmall : TRIGGER_CLASS.sizeDefault,
15425
+ disabled ? TRIGGER_CLASS.disabled : "",
15426
+ className
15427
+ );
14040
15428
  triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
14041
15429
  if (disabled) popoverApi.hide();
14042
15430
  };
@@ -14052,7 +15440,7 @@
14052
15440
 
14053
15441
 
14054
15442
  // ============================================
14055
- // File 31/37: components/date-time-picker/date-time-picker-utils.js
15443
+ // File 34/42: components/date-time-picker/date-time-picker-utils.js
14056
15444
  // ============================================
14057
15445
 
14058
15446
  /**
@@ -14311,7 +15699,7 @@
14311
15699
 
14312
15700
 
14313
15701
  // ============================================
14314
- // File 32/37: components/date-time-picker/date-time-picker.js
15702
+ // File 35/42: components/date-time-picker/date-time-picker.js
14315
15703
  // ============================================
14316
15704
 
14317
15705
  /**
@@ -14556,13 +15944,15 @@
14556
15944
  var displayMonth = validDate ? new Date(validDate.getTime()) : new Date();
14557
15945
 
14558
15946
  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;
15947
+ function getTriggerClassName(disabledState) {
15948
+ return join(
15949
+ "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",
15950
+ "bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
15951
+ size === "large" ? "px-12 py-8" : size === "small" ? "px-12 py-4" : "px-12 py-6",
15952
+ 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"
15953
+ );
15954
+ }
15955
+ triggerWrapper.className = getTriggerClassName(disabled);
14566
15956
  triggerWrapper.setAttribute("role", "button");
14567
15957
  triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
14568
15958
  triggerWrapper.setAttribute("aria-haspopup", "dialog");
@@ -14830,8 +16220,7 @@
14830
16220
  };
14831
16221
  container.setDisabled = function (d) {
14832
16222
  disabled = !!d;
14833
- triggerWrapper.classList.toggle("cursor-not-allowed", disabled);
14834
- triggerWrapper.classList.toggle("opacity-60", disabled);
16223
+ triggerWrapper.className = getTriggerClassName(disabled);
14835
16224
  triggerWrapper.setAttribute("tabindex", disabled ? "-1" : "0");
14836
16225
  if (disabled) popoverApi.hide();
14837
16226
  };
@@ -14847,7 +16236,7 @@
14847
16236
 
14848
16237
 
14849
16238
  // ============================================
14850
- // File 33/37: components/phone-input/phone-utils.js
16239
+ // File 36/42: components/phone-input/phone-utils.js
14851
16240
  // ============================================
14852
16241
 
14853
16242
  /**
@@ -15010,7 +16399,7 @@
15010
16399
 
15011
16400
 
15012
16401
  // ============================================
15013
- // File 34/37: components/phone-input/phone-input.js
16402
+ // File 37/42: components/phone-input/phone-input.js
15014
16403
  // ============================================
15015
16404
 
15016
16405
  /**
@@ -15408,7 +16797,7 @@
15408
16797
 
15409
16798
 
15410
16799
  // ============================================
15411
- // File 35/37: components/file-input.js
16800
+ // File 38/42: components/file-input.js
15412
16801
  // ============================================
15413
16802
 
15414
16803
  /**
@@ -15452,6 +16841,15 @@
15452
16841
  return ICONS.file;
15453
16842
  }
15454
16843
 
16844
+ /** Resolve client: use FlowUI._getComponent when bundle has captured globals, else global.superleapClient (same as enum-select) */
16845
+ function getClient() {
16846
+ if (global.FlowUI && typeof global.FlowUI._getComponent === "function") {
16847
+ var c = global.FlowUI._getComponent("superleapClient");
16848
+ if (c) return c;
16849
+ }
16850
+ return global.superleapClient;
16851
+ }
16852
+
15455
16853
  /**
15456
16854
  * Upload file to S3
15457
16855
  * @param {File} file - File to upload
@@ -15463,26 +16861,31 @@
15463
16861
  formData.append("file", file, file.name);
15464
16862
  formData.append("is_private", String(!!isPrivate));
15465
16863
 
15466
- // Get upload URL - can be configured via global.S3_UPLOAD_URL
16864
+ // Get upload path - can be configured via global.S3_UPLOAD_URL
15467
16865
  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
16866
 
15471
- // Get API key: use FlowUI._getComponent when globals were captured (index.js), else global.superleapClient
15472
- let apiKey = null;
16867
+ // Base URL and API key from superleapClient only (same pattern as enum-select)
16868
+ var client = getClient();
16869
+ var baseUrl = null;
16870
+ var apiKey = null;
15473
16871
  try {
15474
- const client =
15475
- global.FlowUI && typeof global.FlowUI._getComponent === "function"
15476
- ? global.FlowUI._getComponent("superleapClient")
15477
- : global.superleapClient;
16872
+ if (client && typeof client.getBaseUrl === "function") {
16873
+ baseUrl = client.getBaseUrl();
16874
+ }
15478
16875
  if (client && typeof client.getSdk === "function") {
15479
- const sdk = client.getSdk();
15480
- apiKey = sdk?.apiKey;
16876
+ var sdk = client.getSdk();
16877
+ apiKey = sdk ? sdk.apiKey : null;
15481
16878
  }
15482
16879
  } catch (e) {
15483
- console.warn("[S3FileUpload] Could not get API key:", e);
16880
+ console.warn("[S3FileUpload] Could not get client:", e);
16881
+ }
16882
+
16883
+ if (!baseUrl) {
16884
+ throw new Error("SuperLeap client not initialized. Call superleapClient.init({ baseUrl, apiKey }) first.");
15484
16885
  }
15485
16886
 
16887
+ const fullUrl = uploadUrl.startsWith("http") ? uploadUrl : baseUrl.replace(/\/$/, "") + (uploadUrl.startsWith("/") ? uploadUrl : "/" + uploadUrl);
16888
+
15486
16889
  const headers = {};
15487
16890
  if (apiKey) {
15488
16891
  headers.Authorization = `Bearer ${apiKey}`;
@@ -15526,8 +16929,36 @@
15526
16929
  * @param {boolean} config.isPrivate - Whether files should be private
15527
16930
  * @param {number} config.maxFiles - Maximum number of files (for multiple mode)
15528
16931
  * @param {number} config.maxFileSize - Maximum file size in bytes
16932
+ * @param {boolean} [config.disabled] - Whether the file upload is disabled
16933
+ * @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'success' | 'borderless' | 'inline'
16934
+ * @param {string} [config.inputSize] - 'default' | 'large' | 'small'
16935
+ * @param {string} [config.className] - Extra class on upload wrapper
15529
16936
  * @returns {HTMLElement} Field element
15530
16937
  */
16938
+ var UPLOAD_WRAPPER_CLASS = {
16939
+ base:
16940
+ "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",
16941
+ default:
16942
+ "border-border-primary bg-fill-quarternary-fill-white hover:border-primary-base focus-within:border-primary-base",
16943
+ error:
16944
+ "border-error-base bg-fill-quarternary-fill-white hover:border-error-base focus-within:border-error-base",
16945
+ warning:
16946
+ "border-warning-base bg-fill-quarternary-fill-white hover:border-warning-base focus-within:border-warning-base",
16947
+ success:
16948
+ "border-success-base bg-fill-quarternary-fill-white hover:border-success-base focus-within:border-success-base",
16949
+ borderless:
16950
+ "border-none shadow-none rounded-0 bg-fill-quarternary-fill-white",
16951
+ inline:
16952
+ "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",
16953
+ sizeDefault: "px-12 py-4",
16954
+ sizeLarge: "px-12 py-6",
16955
+ sizeSmall: "px-12 py-2",
16956
+ };
16957
+
16958
+ function joinClasses() {
16959
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
16960
+ }
16961
+
15531
16962
  function create(config) {
15532
16963
  const {
15533
16964
  label,
@@ -15539,7 +16970,14 @@
15539
16970
  isPrivate = false,
15540
16971
  maxFiles = null,
15541
16972
  maxFileSize = 10 * 1024 * 1024, // 10MB default
16973
+ disabled = false,
16974
+ variant = "default",
16975
+ inputSize = "default",
16976
+ className = "",
15542
16977
  } = config;
16978
+ let disabledState = !!disabled;
16979
+ let currentVariant = variant;
16980
+ let currentInputSize = inputSize;
15543
16981
 
15544
16982
  if (!global.FlowUI) {
15545
16983
  throw new Error("FlowUI not available");
@@ -15557,12 +16995,33 @@
15557
16995
 
15558
16996
  // Upload row: button + status + optional end spinner (match Select/MultiSelect trigger)
15559
16997
  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";
16998
+ var sizeClass =
16999
+ currentInputSize === "large"
17000
+ ? UPLOAD_WRAPPER_CLASS.sizeLarge
17001
+ : currentInputSize === "small"
17002
+ ? UPLOAD_WRAPPER_CLASS.sizeSmall
17003
+ : UPLOAD_WRAPPER_CLASS.sizeDefault;
17004
+ function applyWrapperClasses() {
17005
+ uploadWrapper.className = joinClasses(
17006
+ UPLOAD_WRAPPER_CLASS.base,
17007
+ UPLOAD_WRAPPER_CLASS[currentVariant] || UPLOAD_WRAPPER_CLASS.default,
17008
+ sizeClass,
17009
+ className
17010
+ );
17011
+ }
17012
+ applyWrapperClasses();
17013
+ uploadWrapper.setAttribute("data-file-input-variant", currentVariant);
17014
+ uploadWrapper.setAttribute("data-file-input-size", currentInputSize);
17015
+ uploadWrapper.setAttribute("data-disabled", disabledState ? "true" : "false");
15561
17016
 
15562
17017
  // Left content (button + status) – pointer-events-none so overlay input receives clicks
15563
17018
  const leftContent = document.createElement("div");
15564
17019
  leftContent.className = "pointer-events-none flex min-w-0 flex-1 items-center gap-8 truncate";
15565
17020
 
17021
+ var disabledChildClasses =
17022
+ "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";
17023
+ var statusTextBaseClass = "text-reg-13 min-w-0 flex-1 truncate";
17024
+ var statusTextDisabledClass = " group-has-[:disabled]:text-typography-quaternary-text";
15566
17025
  const useButtonComponent = global.Button && typeof global.Button.create === "function";
15567
17026
  const initialButtonText = multiple ? "Choose files" : "Choose a file";
15568
17027
  const btn = useButtonComponent
@@ -15570,31 +17029,41 @@
15570
17029
  variant: "outline",
15571
17030
  size: "small",
15572
17031
  text: initialButtonText,
15573
- className: "shrink-0 truncate",
17032
+ className: "shrink-0 truncate " + disabledChildClasses,
15574
17033
  })
15575
17034
  : (function () {
15576
17035
  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";
17036
+ el.className =
17037
+ "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 " +
17038
+ disabledChildClasses;
15578
17039
  el.textContent = initialButtonText;
15579
17040
  return el;
15580
17041
  })();
15581
17042
 
15582
17043
  // Status text: "No files chosen" (quaternary) or "X file(s) selected"
15583
17044
  const statusText = document.createElement("p");
15584
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-quaternary-text";
17045
+ statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
15585
17046
 
15586
17047
  // Hidden file input – overlays row, high z-index so it receives clicks
15587
17048
  const input = document.createElement("input");
15588
17049
  input.type = "file";
15589
- input.className = "absolute inset-0 z-10 w-full cursor-pointer opacity-0";
17050
+ const inputBaseClass = "absolute inset-0 z-10 w-full opacity-0";
17051
+ function applyInputClasses() {
17052
+ input.className =
17053
+ inputBaseClass +
17054
+ (disabledState ? " !pointer-events-none !cursor-not-allowed" : " cursor-pointer");
17055
+ }
17056
+ applyInputClasses();
15590
17057
  input.multiple = multiple;
17058
+ input.disabled = disabledState;
15591
17059
  if (accept !== "*") {
15592
17060
  input.accept = accept;
15593
17061
  }
15594
17062
 
15595
17063
  // End icon slot: spinner when uploading (match Select chevron position)
15596
17064
  const endIconSlot = document.createElement("div");
15597
- endIconSlot.className = "ml-4 flex size-16 items-center justify-center shrink-0 text-typography-quaternary-text";
17065
+ endIconSlot.className =
17066
+ "ml-4 flex size-16 items-center justify-center shrink-0 text-typography-quaternary-text group-has-[:disabled]:text-typography-quaternary-text";
15598
17067
  endIconSlot.style.display = "none";
15599
17068
 
15600
17069
  // Uploaded files: badge list (match UploadedFilePreviewer – flex-wrap gap-2)
@@ -15775,13 +17244,13 @@
15775
17244
 
15776
17245
  if (!filesChosen) {
15777
17246
  statusText.textContent = "No files chosen";
15778
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-quaternary-text";
17247
+ statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
15779
17248
  } else if (uploadingCount > 0) {
15780
17249
  statusText.textContent = "Uploading…";
15781
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-quaternary-text";
17250
+ statusText.className = statusTextBaseClass + " text-typography-quaternary-text" + statusTextDisabledClass;
15782
17251
  } else {
15783
17252
  statusText.textContent = `${uploadedCount} file${uploadedCount !== 1 ? "s" : ""} selected`;
15784
- statusText.className = "text-reg-13 min-w-0 flex-1 truncate text-typography-primary-text";
17253
+ statusText.className = statusTextBaseClass + " text-typography-primary-text" + statusTextDisabledClass;
15785
17254
  }
15786
17255
 
15787
17256
  endIconSlot.style.display = uploadingCount > 0 ? "flex" : "none";
@@ -15918,6 +17387,32 @@
15918
17387
  loadExistingFiles();
15919
17388
  updateStatus();
15920
17389
 
17390
+ field.setDisabled = function (d) {
17391
+ disabledState = !!d;
17392
+ input.disabled = disabledState;
17393
+ applyInputClasses();
17394
+ applyWrapperClasses();
17395
+ uploadWrapper.setAttribute("data-disabled", disabledState ? "true" : "false");
17396
+ };
17397
+
17398
+ field.setVariant = function (v) {
17399
+ currentVariant = v || "default";
17400
+ uploadWrapper.setAttribute("data-file-input-variant", currentVariant);
17401
+ applyWrapperClasses();
17402
+ };
17403
+
17404
+ field.setInputSize = function (s) {
17405
+ currentInputSize = s || "default";
17406
+ sizeClass =
17407
+ currentInputSize === "large"
17408
+ ? UPLOAD_WRAPPER_CLASS.sizeLarge
17409
+ : currentInputSize === "small"
17410
+ ? UPLOAD_WRAPPER_CLASS.sizeSmall
17411
+ : UPLOAD_WRAPPER_CLASS.sizeDefault;
17412
+ uploadWrapper.setAttribute("data-file-input-size", currentInputSize);
17413
+ applyWrapperClasses();
17414
+ };
17415
+
15921
17416
  return field;
15922
17417
  }
15923
17418
 
@@ -15932,7 +17427,7 @@
15932
17427
 
15933
17428
 
15934
17429
  // ============================================
15935
- // File 36/37: components/table.js
17430
+ // File 39/42: components/table.js
15936
17431
  // ============================================
15937
17432
 
15938
17433
  /**
@@ -16273,7 +17768,455 @@
16273
17768
 
16274
17769
 
16275
17770
  // ============================================
16276
- // File 37/37: index.js
17771
+ // File 40/42: components/tabs.js
17772
+ // ============================================
17773
+
17774
+ /**
17775
+ * Tabs Component (vanilla JS)
17776
+ * Tabbed interface with list, triggers, and content panels.
17777
+ * Ref: Radix-style Tabs; design tokens match design system.
17778
+ */
17779
+
17780
+ (function (global) {
17781
+
17782
+ function getComponent(name) {
17783
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
17784
+ var c = global.FlowUI._getComponent(name);
17785
+ if (c) return c;
17786
+ }
17787
+ return global[name];
17788
+ }
17789
+
17790
+ var LIST_BASE_CLASS =
17791
+ "inline-flex items-center justify-center gap-2 rounded-4 bg-fill-tertiary-fill-light-gray p-4";
17792
+
17793
+ /** Button variant classes for active (outline) vs inactive (ghost) */
17794
+ var BUTTON_OUTLINE =
17795
+ "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";
17796
+ var BUTTON_GHOST =
17797
+ "group text-typography-primary-text hover:bg-fill-tertiary-fill-light-gray active:bg-fill-secondary-fill-gray disabled:text-typography-quaternary-text";
17798
+ var BUTTON_BASE =
17799
+ "inline-flex items-center justify-center whitespace-nowrap !text-med-12 transition-colors disabled:pointer-events-none hover:cursor-pointer h-fit";
17800
+ var BUTTON_SIZES = {
17801
+ default: "px-8 py-4 gap-4 rounded-4",
17802
+ small: "px-8 py-4 gap-4 rounded-4",
17803
+ large: "px-16 py-8 gap-4 rounded-4",
17804
+ };
17805
+
17806
+ var CONTENT_BASE_CLASS =
17807
+ "ring-offset-background focus-visible:ring-ring h-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2";
17808
+
17809
+ function join() {
17810
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
17811
+ }
17812
+
17813
+ /**
17814
+ * Create a full Tabs component (list + triggers + content panels)
17815
+ * @param {Object} config
17816
+ * @param {string} [config.defaultValue] - initial active tab value
17817
+ * @param {string} [config.value] - controlled active value
17818
+ * @param {Function} [config.onChange] - (value) => void when tab changes
17819
+ * @param {Array<{value: string, label: string, content: HTMLElement|string}>} config.tabs - tab definitions
17820
+ * @param {string} [config.size] - 'default' | 'small' | 'large'
17821
+ * @param {string} [config.listClassName] - extra class on list
17822
+ * @param {string} [config.contentClassName] - extra class on content wrapper
17823
+ * @returns {HTMLElement} root element (wrapper containing list + content area)
17824
+ */
17825
+ function create(config) {
17826
+ var opts = config || {};
17827
+ var defaultValue = opts.defaultValue;
17828
+ var controlledValue = opts.value;
17829
+ var onChange = opts.onChange;
17830
+ var tabs = opts.tabs || [];
17831
+ var size = opts.size || "default";
17832
+ var listClassName = opts.listClassName || "";
17833
+ var contentClassName = opts.contentClassName || "";
17834
+
17835
+ var sizeClass = BUTTON_SIZES[size] || BUTTON_SIZES.default;
17836
+
17837
+ var root = document.createElement("div");
17838
+ root.className = "tabs-root w-full";
17839
+
17840
+ var activeValue = controlledValue !== undefined ? controlledValue : defaultValue !== undefined ? defaultValue : (tabs[0] && tabs[0].value) || "";
17841
+
17842
+ // List container
17843
+ var list = document.createElement("div");
17844
+ list.setAttribute("role", "tablist");
17845
+ list.className = join(LIST_BASE_CLASS, listClassName);
17846
+
17847
+ // Content container (holds all panels; we show/hide by value)
17848
+ var contentWrapper = document.createElement("div");
17849
+ contentWrapper.className = join("tabs-content-wrapper mt-4", contentClassName);
17850
+
17851
+ var triggerEls = [];
17852
+ var contentPanels = [];
17853
+
17854
+ var Button = getComponent("Button");
17855
+
17856
+ tabs.forEach(function (tab, index) {
17857
+ var value = tab.value;
17858
+ var label = tab.label != null ? tab.label : value;
17859
+ var content = tab.content;
17860
+
17861
+ var isActive = value === activeValue;
17862
+ var trigger = Button.create({
17863
+ variant: isActive ? "outline" : "ghost",
17864
+ size: size === "small" ? "small" : size === "large" ? "large" : "default",
17865
+ text: label,
17866
+ type: "button",
17867
+ className: "mx-2",
17868
+ onClick: function () {
17869
+ if (activeValue === value) return;
17870
+ if (controlledValue === undefined) {
17871
+ activeValue = value;
17872
+ updateActiveState();
17873
+ }
17874
+ if (typeof onChange === "function") {
17875
+ onChange(value);
17876
+ }
17877
+ },
17878
+ });
17879
+
17880
+ trigger.setAttribute("role", "tab");
17881
+ trigger.setAttribute("aria-selected", value === activeValue ? "true" : "false");
17882
+ trigger.setAttribute("data-state", value === activeValue ? "active" : "inactive");
17883
+ trigger.setAttribute("data-value", value);
17884
+ trigger.tabIndex = value === activeValue ? 0 : -1;
17885
+
17886
+ trigger.addEventListener("keydown", function (e) {
17887
+ if (e.key === "Enter" || e.key === " ") {
17888
+ e.preventDefault();
17889
+ trigger.click();
17890
+ }
17891
+ if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
17892
+ e.preventDefault();
17893
+ var nextIndex = e.key === "ArrowRight" ? index + 1 : index - 1;
17894
+ if (nextIndex >= 0 && nextIndex < tabs.length) {
17895
+ var nextTrigger = triggerEls[nextIndex];
17896
+ if (nextTrigger) {
17897
+ nextTrigger.focus();
17898
+ nextTrigger.click();
17899
+ }
17900
+ }
17901
+ }
17902
+ });
17903
+
17904
+ list.appendChild(trigger);
17905
+ triggerEls.push(trigger);
17906
+
17907
+ var panel = document.createElement("div");
17908
+ panel.setAttribute("role", "tabpanel");
17909
+ panel.setAttribute("aria-hidden", value !== activeValue ? "true" : "false");
17910
+ panel.setAttribute("data-value", value);
17911
+ panel.className = join(CONTENT_BASE_CLASS, value !== activeValue ? "hidden" : "");
17912
+ if (typeof content === "string") {
17913
+ panel.innerHTML = content;
17914
+ } else if (content && content.nodeType === 1) {
17915
+ panel.appendChild(content);
17916
+ }
17917
+ contentWrapper.appendChild(panel);
17918
+ contentPanels.push(panel);
17919
+ });
17920
+
17921
+ function updateActiveState() {
17922
+ triggerEls.forEach(function (t, i) {
17923
+ var val = tabs[i] && tabs[i].value;
17924
+ var isActive = val === activeValue;
17925
+ t.setAttribute("aria-selected", isActive ? "true" : "false");
17926
+ t.setAttribute("data-state", isActive ? "active" : "inactive");
17927
+ t.tabIndex = isActive ? 0 : -1;
17928
+ t.className = join(BUTTON_BASE, isActive ? BUTTON_OUTLINE : BUTTON_GHOST, sizeClass, "mx-2");
17929
+ });
17930
+ contentPanels.forEach(function (p, i) {
17931
+ var val = tabs[i] && tabs[i].value;
17932
+ var isActive = val === activeValue;
17933
+ p.setAttribute("aria-hidden", isActive ? "false" : "true");
17934
+ p.classList.toggle("hidden", !isActive);
17935
+ });
17936
+ }
17937
+
17938
+ root.appendChild(list);
17939
+ root.appendChild(contentWrapper);
17940
+
17941
+ root.getValue = function () {
17942
+ return activeValue;
17943
+ };
17944
+
17945
+ root.setValue = function (newValue) {
17946
+ if (newValue === activeValue) return;
17947
+ activeValue = newValue;
17948
+ updateActiveState();
17949
+ };
17950
+
17951
+ return root;
17952
+ }
17953
+
17954
+ var Tabs = {
17955
+ create: create,
17956
+ };
17957
+
17958
+ if (typeof module !== "undefined" && module.exports) {
17959
+ module.exports = Tabs;
17960
+ } else {
17961
+ global.Tabs = Tabs;
17962
+ }
17963
+ })(typeof window !== "undefined" ? window : undefined);
17964
+
17965
+
17966
+
17967
+ // ============================================
17968
+ // File 41/42: components/steps.js
17969
+ // ============================================
17970
+
17971
+ /**
17972
+ * Steps Component (vanilla JS)
17973
+ * Multi-step flow with numbered step triggers and optional content panels.
17974
+ * Use for wizards / step-by-step forms (e.g. "1 Step One", "2 Step Two").
17975
+ */
17976
+
17977
+ (function (global) {
17978
+
17979
+ function getComponent(name) {
17980
+ if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
17981
+ var c = global.FlowUI._getComponent(name);
17982
+ if (c) return c;
17983
+ }
17984
+ return global[name];
17985
+ }
17986
+
17987
+ var LIST_BASE_CLASS =
17988
+ "flex items-center flex-wrap gap-0 rounded-4 border border-border-primary bg-fill-quarternary-fill-white p-4";
17989
+
17990
+ /** Inactive step: muted label color only (button stays ghost large) */
17991
+ var INACTIVE_LABEL_CLASS = "!text-typography-quaternary-text";
17992
+
17993
+ /** Box around step number: active = dark fill + invert text, inactive = dark gray fill + tertiary text */
17994
+ var NUMBER_BOX_ACTIVE =
17995
+ "flex h-16 w-16 items-center justify-center rounded-4 bg-typography-primary-text text-typography-invert-text shrink-0";
17996
+ var NUMBER_BOX_INACTIVE =
17997
+ "flex h-16 w-16 items-center justify-center rounded-4 bg-fill-primary-fill-dark-gray text-typography-tertiary-text shrink-0";
17998
+
17999
+ /** Horizontal line between steps (step1 ——— step2) */
18000
+ var CONNECTOR_CLASS = "flex-1 min-w-12 max-w-32 h-0 border-t border-border-primary shrink-0 mx-2 self-center";
18001
+
18002
+ var CONTENT_BASE_CLASS =
18003
+ "ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2 h-full";
18004
+
18005
+ function join() {
18006
+ return Array.prototype.filter.call(arguments, Boolean).join(" ");
18007
+ }
18008
+
18009
+ /**
18010
+ * Create a Steps component (numbered step triggers + optional content)
18011
+ * Design: ghost large Button for all steps; inactive steps use muted label color. Connectors between steps. Optional title/description above.
18012
+ * @param {Object} config
18013
+ * @param {Array<{id: string, label: string, content?: HTMLElement|string}>} config.steps - step definitions
18014
+ * @param {string} [config.defaultValue] - initial active step id
18015
+ * @param {string} [config.value] - controlled active step id
18016
+ * @param {Function} [config.onChange] - (stepId) => void when step changes
18017
+ * @param {string} [config.title] - optional title above steps
18018
+ * @param {string} [config.description] - optional description above steps
18019
+ * @param {string} [config.listClassName] - extra class on step list
18020
+ * @param {string} [config.contentClassName] - extra class on content wrapper
18021
+ * @param {boolean} [config.showContent=true] - whether to render content panels (if steps have content)
18022
+ * @returns {HTMLElement} root element
18023
+ */
18024
+ function create(config) {
18025
+ var opts = config || {};
18026
+ var steps = opts.steps || [];
18027
+ var defaultValue = opts.defaultValue;
18028
+ var controlledValue = opts.value;
18029
+ var onChange = opts.onChange;
18030
+ var title = opts.title;
18031
+ var description = opts.description;
18032
+ var listClassName = opts.listClassName || "";
18033
+ var contentClassName = opts.contentClassName || "";
18034
+ var showContent = opts.showContent !== false;
18035
+
18036
+ var root = document.createElement("div");
18037
+ root.className = "steps-root w-full";
18038
+
18039
+ var activeId =
18040
+ controlledValue !== undefined
18041
+ ? controlledValue
18042
+ : defaultValue !== undefined
18043
+ ? defaultValue
18044
+ : (steps[0] && steps[0].id) || "";
18045
+
18046
+ if (title != null && title !== "") {
18047
+ var titleEl = document.createElement("h3");
18048
+ titleEl.className = "text-typography-primary-text";
18049
+ titleEl.textContent = title;
18050
+ root.appendChild(titleEl);
18051
+ }
18052
+ if (description != null && description !== "") {
18053
+ var descEl = document.createElement("p");
18054
+ descEl.className = "text-typography-secondary-text";
18055
+ descEl.textContent = description;
18056
+ root.appendChild(descEl);
18057
+ }
18058
+
18059
+ // Step list (horizontal: step, connector, step, connector, ...)
18060
+ var list = document.createElement("div");
18061
+ list.setAttribute("role", "tablist");
18062
+ list.setAttribute("aria-label", "Steps");
18063
+ list.className = join(LIST_BASE_CLASS, listClassName);
18064
+
18065
+ var triggerEls = [];
18066
+ var numberBoxEls = [];
18067
+ var contentPanels = [];
18068
+ var contentWrapper = null;
18069
+
18070
+ if (showContent && steps.some(function (s) { return s.content != null; })) {
18071
+ contentWrapper = document.createElement("div");
18072
+ contentWrapper.className = join("steps-content-wrapper mt-4", contentClassName);
18073
+ }
18074
+
18075
+ var Button = getComponent("Button");
18076
+
18077
+ steps.forEach(function (step, index) {
18078
+ var stepId = step.id;
18079
+ var label = step.label != null ? step.label : stepId;
18080
+ var content = step.content;
18081
+ var stepNumber = index + 1;
18082
+ var isActive = stepId === activeId;
18083
+
18084
+ var trigger = Button.create({
18085
+ variant: "ghost",
18086
+ size: "large",
18087
+ text: "\u00A0",
18088
+ type: "button",
18089
+ className: isActive ? "" : INACTIVE_LABEL_CLASS,
18090
+ onClick: function () {
18091
+ if (activeId === stepId) return;
18092
+ if (controlledValue === undefined) {
18093
+ activeId = stepId;
18094
+ updateActiveState();
18095
+ }
18096
+ if (typeof onChange === "function") {
18097
+ onChange(stepId);
18098
+ }
18099
+ },
18100
+ });
18101
+
18102
+ var numberBox = document.createElement("span");
18103
+ numberBox.setAttribute("aria-hidden", "true");
18104
+ numberBox.className = isActive ? NUMBER_BOX_ACTIVE : NUMBER_BOX_INACTIVE;
18105
+ numberBox.textContent = String(stepNumber);
18106
+
18107
+ trigger.innerHTML = "";
18108
+ trigger.appendChild(numberBox);
18109
+ trigger.appendChild(document.createTextNode(" " + label));
18110
+
18111
+ trigger.setAttribute("role", "tab");
18112
+ trigger.setAttribute("aria-selected", isActive ? "true" : "false");
18113
+ trigger.setAttribute("data-state", isActive ? "active" : "inactive");
18114
+ trigger.setAttribute("data-step-id", stepId);
18115
+ trigger.tabIndex = isActive ? 0 : -1;
18116
+
18117
+ numberBoxEls.push(numberBox);
18118
+
18119
+ trigger.addEventListener("keydown", function (e) {
18120
+ if (e.key === "Enter" || e.key === " ") {
18121
+ e.preventDefault();
18122
+ trigger.click();
18123
+ }
18124
+ if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
18125
+ e.preventDefault();
18126
+ var nextIndex = e.key === "ArrowRight" ? index + 1 : index - 1;
18127
+ if (nextIndex >= 0 && nextIndex < steps.length) {
18128
+ var nextTrigger = triggerEls[nextIndex];
18129
+ if (nextTrigger) {
18130
+ nextTrigger.focus();
18131
+ nextTrigger.click();
18132
+ }
18133
+ }
18134
+ }
18135
+ });
18136
+
18137
+ list.appendChild(trigger);
18138
+ triggerEls.push(trigger);
18139
+
18140
+ if (index < steps.length - 1) {
18141
+ var connector = document.createElement("span");
18142
+ connector.className = CONNECTOR_CLASS;
18143
+ connector.setAttribute("aria-hidden", "true");
18144
+ list.appendChild(connector);
18145
+ }
18146
+
18147
+ if (contentWrapper && content != null) {
18148
+ var panel = document.createElement("div");
18149
+ panel.setAttribute("role", "tabpanel");
18150
+ panel.setAttribute("aria-hidden", stepId !== activeId ? "true" : "false");
18151
+ panel.setAttribute("data-step-id", stepId);
18152
+ panel.className = join(CONTENT_BASE_CLASS, stepId !== activeId ? "hidden" : "");
18153
+ if (typeof content === "string") {
18154
+ panel.innerHTML = content;
18155
+ } else if (content && content.nodeType === 1) {
18156
+ panel.appendChild(content);
18157
+ }
18158
+ contentWrapper.appendChild(panel);
18159
+ contentPanels.push(panel);
18160
+ }
18161
+ });
18162
+
18163
+ function updateActiveState() {
18164
+ triggerEls.forEach(function (t, i) {
18165
+ var step = steps[i];
18166
+ var id = step && step.id;
18167
+ var isActive = id === activeId;
18168
+ t.setAttribute("aria-selected", isActive ? "true" : "false");
18169
+ t.setAttribute("data-state", isActive ? "active" : "inactive");
18170
+ t.tabIndex = isActive ? 0 : -1;
18171
+ if (isActive) {
18172
+ t.classList.remove(INACTIVE_LABEL_CLASS);
18173
+ } else {
18174
+ t.classList.add(INACTIVE_LABEL_CLASS);
18175
+ }
18176
+ numberBoxEls[i].className = isActive ? NUMBER_BOX_ACTIVE : NUMBER_BOX_INACTIVE;
18177
+ });
18178
+ contentPanels.forEach(function (p, i) {
18179
+ var step = steps[i];
18180
+ var id = step && step.id;
18181
+ var isActive = id === activeId;
18182
+ p.setAttribute("aria-hidden", isActive ? "false" : "true");
18183
+ p.classList.toggle("hidden", !isActive);
18184
+ });
18185
+ }
18186
+
18187
+ root.appendChild(list);
18188
+ if (contentWrapper) {
18189
+ root.appendChild(contentWrapper);
18190
+ }
18191
+
18192
+ root.getValue = function () {
18193
+ return activeId;
18194
+ };
18195
+
18196
+ root.setValue = function (newId) {
18197
+ if (newId === activeId) return;
18198
+ activeId = newId;
18199
+ updateActiveState();
18200
+ };
18201
+
18202
+ return root;
18203
+ }
18204
+
18205
+ var Steps = {
18206
+ create: create,
18207
+ };
18208
+
18209
+ if (typeof module !== "undefined" && module.exports) {
18210
+ module.exports = Steps;
18211
+ } else {
18212
+ global.Steps = Steps;
18213
+ }
18214
+ })(typeof window !== "undefined" ? window : undefined);
18215
+
18216
+
18217
+
18218
+ // ============================================
18219
+ // File 42/42: index.js
16277
18220
  // ============================================
16278
18221
 
16279
18222
  /**
@@ -16349,6 +18292,7 @@
16349
18292
  require("./components/input.js");
16350
18293
  require("./components/currency.js");
16351
18294
  require("./components/textarea.js");
18295
+ require("./components/richtext-editor.js");
16352
18296
  require("./components/duration/duration-utils.js");
16353
18297
  require("./components/duration/duration-constants.js");
16354
18298
  require("./components/duration/duration.js");
@@ -16364,8 +18308,12 @@
16364
18308
  require("./components/phone-input/phone-utils.js");
16365
18309
  require("./components/phone-input/phone-input.js");
16366
18310
  require("./components/checkbox.js");
18311
+ require("./components/checkbox-group.js");
16367
18312
  require("./components/radio-group.js");
18313
+ require("./components/card-select.js");
16368
18314
  require("./components/table.js");
18315
+ require("./components/tabs.js");
18316
+ require("./components/steps.js");
16369
18317
 
16370
18318
  // Export FlowUI and SuperLeap from global scope
16371
18319
  if (typeof global !== "undefined" && global.FlowUI) {
@@ -16376,6 +18324,7 @@
16376
18324
  getSdk: global.superleapClient.getSdk,
16377
18325
  isAvailable: global.superleapClient.isAvailable,
16378
18326
  getDefaultConfig: global.superleapClient.getDefaultConfig,
18327
+ getBaseUrl: global.superleapClient.getBaseUrl,
16379
18328
  }
16380
18329
  : null;
16381
18330
 
@@ -16391,6 +18340,7 @@
16391
18340
  getSdk: window.superleapClient.getSdk,
16392
18341
  isAvailable: window.superleapClient.isAvailable,
16393
18342
  getDefaultConfig: window.superleapClient.getDefaultConfig,
18343
+ getBaseUrl: window.superleapClient.getBaseUrl,
16394
18344
  }
16395
18345
  : null;
16396
18346
 
@@ -16426,6 +18376,7 @@
16426
18376
  "InputComponent",
16427
18377
  "CurrencyComponent",
16428
18378
  "TextareaComponent",
18379
+ "RichTextEditorComponent",
16429
18380
  "Duration",
16430
18381
  "DateTimePicker",
16431
18382
  "Enumeration",
@@ -16437,8 +18388,12 @@
16437
18388
  "FileInput",
16438
18389
  "PhoneInput",
16439
18390
  "Checkbox",
18391
+ "CheckboxGroup",
16440
18392
  "RadioGroup",
18393
+ "CardSelect",
16441
18394
  "SuperleapTable",
18395
+ "Tabs",
18396
+ "Steps",
16442
18397
  ];
16443
18398
 
16444
18399
  componentNames.forEach((name) => {
@@ -16468,6 +18423,7 @@
16468
18423
  getSdk: client.getSdk.bind(client),
16469
18424
  isAvailable: client.isAvailable.bind(client),
16470
18425
  getDefaultConfig: client.getDefaultConfig.bind(client),
18426
+ getBaseUrl: client.getBaseUrl.bind(client),
16471
18427
  // Bridge methods (from crm.js)
16472
18428
  connect: client.connect ? client.connect.bind(client) : undefined,
16473
18429
  isConnected: client.isConnected
@@ -16526,7 +18482,7 @@
16526
18482
  },
16527
18483
  });
16528
18484
  document.dispatchEvent(event);
16529
- console.log("[Superleap-Flow] Library ready - v2.3.4");
18485
+ console.log("[Superleap-Flow] Library ready - v2.5.3");
16530
18486
  }
16531
18487
 
16532
18488
  // Wait for DOM to be ready before dispatching event