@superleapai/flow-ui 2.4.4 → 2.4.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.
@@ -258,7 +258,7 @@
258
258
  var content = document.createElement("div");
259
259
  content.setAttribute("role", "listbox");
260
260
  content.setAttribute("aria-multiselectable", "true");
261
- content.className = "w-full min-w-[200px] max-h-[30vh] overflow-hidden flex flex-col";
261
+ content.className = "w-full min-w-[200px] max-h-[45vh] overflow-hidden flex flex-col";
262
262
 
263
263
  // Search input (using InputComponent like enum-select)
264
264
  var searchContainer = document.createElement("div");
@@ -319,7 +319,7 @@
319
319
  // Options list
320
320
  var optionsList = document.createElement("div");
321
321
  optionsList.className =
322
- "overflow-y-auto max-h-[30vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white flex-1 min-h-0";
322
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white flex-1 min-h-0";
323
323
 
324
324
  content.appendChild(searchContainer);
325
325
  content.appendChild(optionsList);
@@ -231,7 +231,7 @@
231
231
  // Create dropdown content
232
232
  var content = document.createElement("div");
233
233
  content.setAttribute("role", "listbox");
234
- content.className = "w-full min-w-[200px] max-h-[30vh] overflow-hidden flex flex-col";
234
+ content.className = "w-full min-w-[200px] max-h-[45vh] overflow-hidden flex flex-col";
235
235
 
236
236
  // Search input (using InputComponent like phone-input)
237
237
  var searchContainer = document.createElement("div");
@@ -292,7 +292,7 @@
292
292
  // Options list
293
293
  var optionsList = document.createElement("div");
294
294
  optionsList.className =
295
- "overflow-y-auto max-h-[30vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white flex-1 min-h-0";
295
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white flex-1 min-h-0";
296
296
 
297
297
  content.appendChild(searchContainer);
298
298
  content.appendChild(optionsList);
@@ -154,11 +154,11 @@
154
154
  var content = document.createElement("div");
155
155
  content.setAttribute("role", "listbox");
156
156
  content.setAttribute("aria-multiselectable", "true");
157
- content.className = "custom-multiselect-content w-full max-h-[200px] overflow-hidden flex flex-col";
157
+ content.className = "custom-multiselect-content w-full max-h-[45vh] overflow-hidden flex flex-col";
158
158
 
159
159
  var optionsList = document.createElement("div");
160
160
  optionsList.className =
161
- "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
161
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
162
162
 
163
163
  function isSelected(optionValue) {
164
164
  return values.some(function (v) {
@@ -263,7 +263,7 @@
263
263
  align: "start",
264
264
  closeOnClickOutside: true,
265
265
  bodyClassName: "p-0 overflow-hidden",
266
- panelClassName: "min-w-[var(--trigger-width)] max-h-[200px] overflow-hidden",
266
+ panelClassName: "min-w-[var(--trigger-width)] max-h-[45vh] overflow-hidden",
267
267
  onOpen: function () {
268
268
  if (disabled) {
269
269
  popover.hide();
@@ -44,7 +44,7 @@
44
44
  return { show: noop, hide: noop, destroy: noop, element: null };
45
45
  }
46
46
 
47
- // Wrap trigger in a relative container so popover can be absolute relative to it (full width so parent controls it)
47
+ // Wrap trigger in a relative container for layout (full width); popover is portaled to body
48
48
  const container = document.createElement("div");
49
49
  container.className = "relative w-full";
50
50
  const triggerParent = triggerEl.parentNode;
@@ -55,7 +55,7 @@
55
55
 
56
56
  const wrapper = document.createElement("div");
57
57
  wrapper.className =
58
- "absolute z-50 pointer-events-none opacity-0 invisible transition-opacity duration-150 ease-out";
58
+ "fixed z-50 pointer-events-none opacity-0 invisible transition-opacity duration-150 ease-out";
59
59
  wrapper.setAttribute("aria-hidden", "true");
60
60
 
61
61
  const panel = document.createElement("div");
@@ -79,7 +79,11 @@
79
79
  const body = document.createElement("div");
80
80
  body.className =
81
81
  bodyClassName ||
82
- "text-reg-14 text-typography-secondary-text leading-5 [&_p]:mb-2 [&_p:last-child]:mb-0";
82
+ "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";
83
+ body.style.maxHeight = "90vh";
84
+ body.style.overflowY = "auto";
85
+ body.style.overflowX = "hidden";
86
+ body.style.minHeight = "0";
83
87
  if (panelClassName) {
84
88
  panel.className = panel.className + " " + panelClassName;
85
89
  }
@@ -91,7 +95,7 @@
91
95
  panel.appendChild(body);
92
96
 
93
97
  wrapper.appendChild(panel);
94
- container.appendChild(wrapper);
98
+ document.body.appendChild(wrapper);
95
99
 
96
100
  var backdropEl = null;
97
101
 
@@ -103,7 +107,7 @@
103
107
  if (!modal) return;
104
108
  if (!backdropEl) {
105
109
  backdropEl = document.createElement("div");
106
- backdropEl.className = "fixed inset-0 z-[9998] bg-transparent pointer-events-auto";
110
+ backdropEl.className = "fixed inset-0 z-40 bg-transparent pointer-events-auto";
107
111
  backdropEl.setAttribute("aria-hidden", "true");
108
112
  backdropEl.addEventListener("click", function () {
109
113
  hide();
@@ -111,7 +115,7 @@
111
115
  backdropEl.addEventListener("wheel", onBackdropWheel, { passive: false });
112
116
  }
113
117
  document.body.appendChild(backdropEl);
114
- container.style.zIndex = "9999";
118
+ wrapper.style.zIndex = "";
115
119
  }
116
120
 
117
121
  function applyModalClose() {
@@ -119,7 +123,7 @@
119
123
  if (backdropEl && backdropEl.parentNode) {
120
124
  backdropEl.parentNode.removeChild(backdropEl);
121
125
  }
122
- container.style.zIndex = "";
126
+ wrapper.style.zIndex = "";
123
127
  }
124
128
 
125
129
  function noop() {}
@@ -156,24 +160,24 @@
156
160
 
157
161
  switch (effectivePlacement) {
158
162
  case "bottom":
159
- top = triggerRect.height + gap;
160
- left = alignLeft;
163
+ top = triggerRect.bottom + gap;
164
+ left = triggerRect.left + alignLeft;
161
165
  break;
162
166
  case "top":
163
- top = -panelRect.height - gap;
164
- left = alignLeft;
167
+ top = triggerRect.top - panelRect.height - gap;
168
+ left = triggerRect.left + alignLeft;
165
169
  break;
166
170
  case "right":
167
- top = alignTop;
168
- left = triggerRect.width + gap;
171
+ top = triggerRect.top + alignTop;
172
+ left = triggerRect.right + gap;
169
173
  break;
170
174
  case "left":
171
- top = alignTop;
172
- left = -panelRect.width - gap;
175
+ top = triggerRect.top + alignTop;
176
+ left = triggerRect.left - panelRect.width - gap;
173
177
  break;
174
178
  default:
175
- top = triggerRect.height + gap;
176
- left = alignLeft;
179
+ top = triggerRect.bottom + gap;
180
+ left = triggerRect.left + alignLeft;
177
181
  }
178
182
 
179
183
  wrapper.style.transform = "";
@@ -187,10 +191,16 @@
187
191
  wrapper.classList.add("invisible", "opacity-0", "pointer-events-none");
188
192
  wrapper.classList.remove("visible", "opacity-100", "pointer-events-auto");
189
193
  wrapper.setAttribute("aria-hidden", "true");
194
+ window.removeEventListener("scroll", onScrollOrResize, true);
195
+ window.removeEventListener("resize", onScrollOrResize);
190
196
  applyModalClose();
191
197
  if (onClose) onClose();
192
198
  }
193
199
 
200
+ function onScrollOrResize() {
201
+ if (wrapper.classList.contains("visible")) position();
202
+ }
203
+
194
204
  function show() {
195
205
  if (onOpen) onOpen();
196
206
  applyModalOpen();
@@ -199,6 +209,8 @@
199
209
  wrapper.classList.remove("invisible", "opacity-0", "pointer-events-none");
200
210
  wrapper.classList.add("visible", "opacity-100", "pointer-events-auto");
201
211
  wrapper.setAttribute("aria-hidden", "false");
212
+ window.addEventListener("scroll", onScrollOrResize, true);
213
+ window.addEventListener("resize", onScrollOrResize);
202
214
  requestAnimationFrame(function () {
203
215
  requestAnimationFrame(function () {
204
216
  panel.setAttribute("data-state", "open");
@@ -213,6 +225,8 @@
213
225
  if (backdropEl && backdropEl.parentNode) {
214
226
  backdropEl.parentNode.removeChild(backdropEl);
215
227
  }
228
+ window.removeEventListener("scroll", onScrollOrResize, true);
229
+ window.removeEventListener("resize", onScrollOrResize);
216
230
  if (wrapper.parentNode) {
217
231
  wrapper.parentNode.removeChild(wrapper);
218
232
  }
@@ -262,6 +276,8 @@
262
276
  show,
263
277
  hide,
264
278
  destroy,
279
+ /** Re-run positioning (e.g. after async content load). Call when panel size changes. */
280
+ updatePosition: position,
265
281
  setContent(newContent) {
266
282
  body.innerHTML = "";
267
283
  if (typeof newContent === "string") {
@@ -152,7 +152,6 @@
152
152
  * @param {string} [config.size] - 'default' | 'large' | 'small'
153
153
  * @param {number} [config.initialLimit] - Initial fetch limit (default 50)
154
154
  * @param {Array<string>} [config.displayFields] - Fields to display as secondary info (e.g. ["email", "phone"])
155
- * @param {Object} [config.initialFilter] - Optional filter object to merge with search (e.g. { field: "status", operator: "exact", value: "active" } or { and: [...] })
156
155
  * @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)
157
156
  * @returns {HTMLElement} Record multiselect container element
158
157
  */
@@ -167,7 +166,6 @@
167
166
  var variant = config.variant || "default";
168
167
  var size = config.size || "default";
169
168
  var initialLimit = config.initialLimit != null ? config.initialLimit : 50;
170
- var initialFilter = config.initialFilter || null; // Can be array, object, or function returning either
171
169
  var displayFields = config.displayFields || [];
172
170
 
173
171
  var disabled = config.disabled === true;
@@ -293,7 +291,7 @@
293
291
  var content = document.createElement("div");
294
292
  content.setAttribute("role", "listbox");
295
293
  content.setAttribute("aria-multiselectable", "true");
296
- content.className = "record-multiselect-content max-h-[30vh] overflow-hidden flex flex-col";
294
+ content.className = "record-multiselect-content max-h-[45vh] overflow-hidden flex flex-col";
297
295
 
298
296
  var searchWrap = document.createElement("div");
299
297
  searchWrap.className = "p-8 pb-4 border-b-1/2 border-border-primary ";
@@ -343,7 +341,7 @@
343
341
  content.appendChild(searchWrap);
344
342
 
345
343
  var optionsList = document.createElement("div");
346
- optionsList.className = "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-multiselect-options";
344
+ optionsList.className = "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-multiselect-options";
347
345
 
348
346
  // Add scroll listener for infinite scroll
349
347
  optionsList.addEventListener("scroll", function () {
@@ -371,28 +369,22 @@
371
369
  align: "start",
372
370
  closeOnClickOutside: true,
373
371
  bodyClassName: "p-0 overflow-hidden",
374
- panelClassName: "max-h-[30vh] overflow-hidden",
372
+ panelClassName: "min-w-[var(--trigger-width)] overflow-hidden",
375
373
  onOpen: function () {
376
374
  if (disabled) {
377
375
  popover.hide();
378
376
  return;
379
377
  }
380
- document.querySelectorAll(".custom-select.open, .record-select.open, .custom-multiselect.open, .record-multiselect.open, .enum-select, .enum-multiselect").forEach(function (other) {
381
- if (other !== container && other.popoverInstance) {
382
- other.popoverInstance.hide();
383
- }
384
- });
385
378
  container.classList.add("open");
386
379
  trigger.setAttribute("aria-expanded", "true");
387
380
  searchTerm = "";
388
381
  if (searchInputWrapper) searchInputWrapper.setValue("");
389
382
  else if (searchInputEl) searchInputEl.value = "";
390
- var triggerWidthPx = trigger.offsetWidth + "px";
391
- content.style.minWidth = triggerWidthPx;
392
- content.style.width = triggerWidthPx;
393
383
  if (popover.panel) {
394
- popover.panel.style.width = triggerWidthPx;
384
+ var triggerWidthPx = trigger.offsetWidth + "px";
385
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
395
386
  popover.panel.style.minWidth = triggerWidthPx;
387
+ popover.panel.style.width = triggerWidthPx;
396
388
  }
397
389
  loadInitialAndRender();
398
390
  setTimeout(function () {
@@ -471,23 +463,14 @@
471
463
  try {
472
464
  if (model && typeof model.select === "function") {
473
465
  var q = model.select.apply(model, fields);
474
- var filters = [];
475
- var resolvedFilter = typeof initialFilter === 'function' ? initialFilter() : initialFilter;
476
- console.log('[RecordMultiselect] initialFilter:', resolvedFilter, '| search:', search);
477
- if (resolvedFilter) {
478
- filters = filters.concat(Array.isArray(resolvedFilter) ? resolvedFilter : [resolvedFilter]);
479
- }
480
466
  if (search && search.trim()) {
481
- filters.push({
467
+ q = q.filterBy({
482
468
  or: [
483
469
  { field: "name", operator: "contains", value: search.trim() },
484
470
  { field: "id", operator: "eq", value: search.trim() },
485
471
  ],
486
472
  });
487
473
  }
488
- if (filters.length > 0) {
489
- q = q.filterBy(filters.length === 1 ? filters[0] : { and: filters });
490
- }
491
474
  var orderBy = ["name"];
492
475
  if (objectSlug === "account") orderBy.push("-ParentId");
493
476
  return q
@@ -746,6 +729,16 @@
746
729
  }
747
730
  }
748
731
 
732
+ function scheduleUpdatePosition() {
733
+ if (popover && typeof popover.updatePosition === "function") {
734
+ requestAnimationFrame(function () {
735
+ requestAnimationFrame(function () {
736
+ popover.updatePosition();
737
+ });
738
+ });
739
+ }
740
+ }
741
+
749
742
  function loadInitialAndRender() {
750
743
  showLoading();
751
744
  currentPage = 1;
@@ -764,9 +757,11 @@
764
757
  } else {
765
758
  renderOptions();
766
759
  }
760
+ scheduleUpdatePosition();
767
761
  }).catch(function () {
768
762
  showEmpty("Failed to load records");
769
763
  hasMoreRecords = false;
764
+ scheduleUpdatePosition();
770
765
  });
771
766
  }
772
767
 
@@ -818,9 +813,11 @@
818
813
  } else {
819
814
  renderOptions();
820
815
  }
816
+ scheduleUpdatePosition();
821
817
  }).catch(function () {
822
818
  showEmpty("Search failed");
823
819
  hasMoreRecords = false;
820
+ scheduleUpdatePosition();
824
821
  });
825
822
  }, 500);
826
823
  }
@@ -145,7 +145,6 @@
145
145
  * @param {string} [config.size] - 'default' | 'large' | 'small'
146
146
  * @param {boolean} [config.canClear] - Show clear button when value is set
147
147
  * @param {number} [config.initialLimit] - Initial fetch limit (default 50)
148
- * @param {Object} [config.initialFilter] - Optional filter object to merge with search (e.g. { field: "status", operator: "exact", value: "active" } or { and: [...] })
149
148
  * @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)
150
149
  * @returns {HTMLElement} Record select container element
151
150
  */
@@ -160,7 +159,6 @@
160
159
  var size = config.size || "default";
161
160
  var canClear = !!config.canClear;
162
161
  var initialLimit = config.initialLimit != null ? config.initialLimit : 50;
163
- var initialFilter = config.initialFilter || null; // Can be array, object, or function returning either
164
162
 
165
163
  var disabled = config.disabled === true;
166
164
  var value =
@@ -260,7 +258,7 @@
260
258
  // Dropdown content: search + list (same content pattern as Select)
261
259
  var content = document.createElement("div");
262
260
  content.setAttribute("role", "listbox");
263
- content.className = "record-select-content max-h-[30vh] overflow-hidden flex flex-col";
261
+ content.className = "record-select-content max-h-[45vh] overflow-hidden flex flex-col";
264
262
 
265
263
  var searchWrap = document.createElement("div");
266
264
  searchWrap.className = "py-8 border-b-1/2 border-border-primary";
@@ -310,7 +308,7 @@
310
308
  content.appendChild(searchWrap);
311
309
 
312
310
  var optionsList = document.createElement("div");
313
- optionsList.className = "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-select-options";
311
+ optionsList.className = "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white record-select-options";
314
312
 
315
313
  // Add scroll listener for infinite scroll
316
314
  optionsList.addEventListener("scroll", function () {
@@ -334,26 +332,24 @@
334
332
  align: "start",
335
333
  closeOnClickOutside: true,
336
334
  bodyClassName: "p-0 overflow-hidden",
337
- panelClassName: "max-h-[30vh] overflow-hidden",
335
+ panelClassName: "min-w-[var(--trigger-width)] overflow-hidden",
338
336
  onOpen: function () {
339
337
  if (disabled) {
340
338
  popover.hide();
341
339
  return;
342
340
  }
343
- document.querySelectorAll(".custom-select.open, .record-select.open").forEach(function (other) {
344
- if (other !== container) {
345
- other.classList.remove("open");
346
- var t = other.querySelector("button, .custom-select-trigger, .record-select-trigger");
347
- if (t) t.setAttribute("aria-expanded", "false");
348
- }
349
- });
350
341
  isOpen = true;
351
342
  container.classList.add("open");
352
343
  trigger.setAttribute("aria-expanded", "true");
353
344
  searchTerm = "";
354
345
  if (searchInputWrapper) searchInputWrapper.setValue("");
355
346
  else if (searchInputEl) searchInputEl.value = "";
356
- content.style.minWidth = trigger.offsetWidth + "px";
347
+ if (popover.panel) {
348
+ var triggerWidthPx = trigger.offsetWidth + "px";
349
+ popover.panel.style.setProperty("--trigger-width", triggerWidthPx);
350
+ popover.panel.style.minWidth = triggerWidthPx;
351
+ popover.panel.style.width = triggerWidthPx;
352
+ }
357
353
  loadInitialAndRender();
358
354
  setTimeout(function () {
359
355
  if (searchInputEl) searchInputEl.focus();
@@ -486,23 +482,14 @@
486
482
  try {
487
483
  if (model && typeof model.select === "function") {
488
484
  var q = model.select.apply(model, fields);
489
- var filters = [];
490
- var resolvedFilter = typeof initialFilter === 'function' ? initialFilter() : initialFilter;
491
- console.log('[RecordSelect] initialFilter:', resolvedFilter, '| search:', search);
492
- if (resolvedFilter) {
493
- filters = filters.concat(Array.isArray(resolvedFilter) ? resolvedFilter : [resolvedFilter]);
494
- }
495
485
  if (search && search.trim()) {
496
- filters.push({
486
+ q = q.filterBy({
497
487
  or: [
498
488
  { field: "name", operator: "contains", value: search.trim() },
499
489
  { field: "id", operator: "eq", value: search.trim() },
500
490
  ],
501
491
  });
502
492
  }
503
- if (filters.length > 0) {
504
- q = q.filterBy(filters.length === 1 ? filters[0] : { and: filters });
505
- }
506
493
  var orderBy = ["name"];
507
494
  if (objectSlug === "account") orderBy.push("-ParentId");
508
495
  return q
@@ -709,6 +696,16 @@
709
696
  }
710
697
  }
711
698
 
699
+ function scheduleUpdatePosition() {
700
+ if (popover && typeof popover.updatePosition === "function") {
701
+ requestAnimationFrame(function () {
702
+ requestAnimationFrame(function () {
703
+ popover.updatePosition();
704
+ });
705
+ });
706
+ }
707
+ }
708
+
712
709
  function loadInitialAndRender() {
713
710
  showLoading();
714
711
  currentPage = 1;
@@ -734,9 +731,11 @@
734
731
  } else {
735
732
  renderOptions();
736
733
  }
734
+ scheduleUpdatePosition();
737
735
  }).catch(function () {
738
736
  showEmpty("Failed to load records");
739
737
  hasMoreRecords = false;
738
+ scheduleUpdatePosition();
740
739
  });
741
740
  }
742
741
 
@@ -788,9 +787,11 @@
788
787
  } else {
789
788
  renderOptions();
790
789
  }
790
+ scheduleUpdatePosition();
791
791
  }).catch(function () {
792
792
  showEmpty("Search failed");
793
793
  hasMoreRecords = false;
794
+ scheduleUpdatePosition();
794
795
  });
795
796
  }, 500);
796
797
  }
@@ -193,11 +193,11 @@
193
193
 
194
194
  var content = document.createElement("div");
195
195
  content.setAttribute("role", "listbox");
196
- content.className = "custom-select-content w-full max-h-[200px] overflow-hidden flex flex-col";
196
+ content.className = "custom-select-content w-full max-h-[45vh] overflow-hidden flex flex-col";
197
197
 
198
198
  var optionsList = document.createElement("div");
199
199
  optionsList.className =
200
- "overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
200
+ "overflow-y-auto max-h-[45vh] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
201
201
 
202
202
  if (options.length === 0) {
203
203
  var noOpt = document.createElement("div");
@@ -256,7 +256,7 @@
256
256
  align: "start",
257
257
  closeOnClickOutside: true,
258
258
  bodyClassName: "p-0 overflow-hidden",
259
- panelClassName: "min-w-[var(--trigger-width)] max-h-[200px] overflow-hidden",
259
+ panelClassName: "min-w-[var(--trigger-width)] max-h-[45vh] overflow-hidden",
260
260
  onOpen: function () {
261
261
  if (disabled) {
262
262
  popover.hide();
package/core/flow.js CHANGED
@@ -700,7 +700,6 @@
700
700
  size,
701
701
  canClear,
702
702
  initialLimit,
703
- initialFilter,
704
703
  helpText = null,
705
704
  } = config;
706
705
 
@@ -720,7 +719,6 @@
720
719
  size: size || "default",
721
720
  canClear: !!canClear,
722
721
  initialLimit,
723
- initialFilter,
724
722
  onChange: (value, record) => {
725
723
  set(fieldId, value);
726
724
  if (onChange) onChange(value, record);
@@ -770,7 +768,6 @@
770
768
  variant,
771
769
  size,
772
770
  initialLimit,
773
- initialFilter,
774
771
  displayFields,
775
772
  helpText = null,
776
773
  } = config;
@@ -789,7 +786,6 @@
789
786
  variant: variant || "default",
790
787
  size: size || "default",
791
788
  initialLimit,
792
- initialFilter,
793
789
  displayFields: displayFields || [],
794
790
  onValuesChange: (values, records) => {
795
791
  set(fieldId, values);