@superleapai/flow-ui 2.6.22 → 2.6.23

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.
@@ -168,19 +168,21 @@
168
168
  * @param {Array} config.value - Current selected values (array of record ids)
169
169
  * @param {Function} config.onValuesChange - Change handler (values: string[], records?: Array) => void
170
170
  * @param {boolean} [config.disabled] - Whether select is disabled
171
+ * @param {boolean} [config.showDisplayFields] - If true, fetch schema and show secondary fields from objectSchema.properties.display_fields
171
172
  * @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'borderless' | 'inline'
172
173
  * @param {string} [config.size] - 'default' | 'large' | 'small'
173
174
  * @param {number} [config.initialLimit] - Initial fetch limit (default 50)
174
- * @param {Array<string>} [config.displayFields] - Fields to display as secondary info (e.g. ["email", "phone"])
175
175
  * @param {Object} [config.initialFilter] - Optional filter object to merge with search (e.g. { field: "status", operator: "exact", value: "active" } or { and: [...] })
176
- * @param {Object} [config.objectSchema] - Optional ObjectType; display_plural_name/display_singular_name/slug used for placeholder/search/summary label; properties.icon_data { icon?, color? } for static icon (not used for user; user shows Vivid Avatar)
177
176
  * @returns {HTMLElement} Record multiselect container element
178
177
  */
179
178
  function createRecordMultiSelect(config) {
180
179
  var fieldId = config.fieldId;
181
180
  var objectSlug = config.objectSlug;
182
- var objectSchema = config.objectSchema || null;
181
+ var objectSchema = null;
183
182
  var displayPlural = getObjectDisplayName(objectSchema, objectSlug, "plural");
183
+ var hasCustomPlaceholder = typeof config.placeholder === "string" && config.placeholder.length > 0;
184
+ var hasCustomSearchPlaceholder = typeof config.searchPlaceholder === "string" && config.searchPlaceholder.length > 0;
185
+ var hasCustomSummaryLabel = typeof config.label === "string" && config.label.length > 0;
184
186
  var placeholder =
185
187
  config.placeholder ||
186
188
  (displayPlural ? "Select " + displayPlural : "Select records");
@@ -189,11 +191,11 @@
189
191
  "Search " + (displayPlural || objectSlug || "") + "...";
190
192
  var summaryLabel = config.label || (displayPlural || "selected");
191
193
  var onValuesChange = config.onValuesChange;
194
+ var showDisplayFields = config.showDisplayFields === true;
192
195
  var variant = config.variant || "default";
193
196
  var size = config.size || "default";
194
197
  var initialLimit = config.initialLimit != null ? config.initialLimit : 50;
195
198
  var initialFilter = config.initialFilter || null; // Can be array, object, or function returning either
196
- var displayFields = config.displayFields || [];
197
199
 
198
200
  var disabled = config.disabled === true;
199
201
  var values = Array.isArray(config.value)
@@ -224,6 +226,21 @@
224
226
  var currentPage = 1;
225
227
  var isFetchingMore = false;
226
228
  var totalFetched = 0;
229
+ var schemaLoaded = false;
230
+ var schemaLoading = false;
231
+
232
+ function getSchemaDisplayFields() {
233
+ if (!showDisplayFields) return [];
234
+ var fields =
235
+ objectSchema &&
236
+ objectSchema.properties &&
237
+ Array.isArray(objectSchema.properties.display_fields)
238
+ ? objectSchema.properties.display_fields
239
+ : [];
240
+ return fields.filter(function (field) {
241
+ return typeof field === "string" && field.length > 0;
242
+ });
243
+ }
227
244
 
228
245
  // Trigger wrapper + button
229
246
  var triggerWrapper = document.createElement("span");
@@ -413,6 +430,7 @@
413
430
  popover.panel.style.minWidth = triggerWidthPx;
414
431
  popover.panel.style.width = triggerWidthPx;
415
432
  }
433
+ loadObjectSchema();
416
434
  loadInitialAndRender();
417
435
  setTimeout(function () {
418
436
  if (searchInputEl) searchInputEl.focus();
@@ -432,6 +450,57 @@
432
450
  });
433
451
  container.popoverInstance = popover;
434
452
 
453
+ function refreshSchemaDerivedText() {
454
+ displayPlural = getObjectDisplayName(objectSchema, objectSlug, "plural");
455
+ if (!hasCustomPlaceholder) {
456
+ placeholder = displayPlural
457
+ ? "Select " + displayPlural
458
+ : "Select records";
459
+ }
460
+ if (!hasCustomSearchPlaceholder) {
461
+ searchPlaceholder = "Search " + (displayPlural || objectSlug || "") + "...";
462
+ }
463
+ if (!hasCustomSummaryLabel) {
464
+ summaryLabel = displayPlural || "selected";
465
+ }
466
+
467
+ trigger.setAttribute("aria-label", placeholder);
468
+ renderTriggerContent();
469
+ if (searchInputWrapper && typeof searchInputWrapper.setPlaceholder === "function") {
470
+ searchInputWrapper.setPlaceholder(searchPlaceholder);
471
+ } else if (searchInputEl) {
472
+ searchInputEl.placeholder = searchPlaceholder;
473
+ }
474
+ if (searchInputEl) {
475
+ searchInputEl.setAttribute("aria-label", "Search " + (displayPlural || "records"));
476
+ }
477
+ }
478
+
479
+ function loadObjectSchema() {
480
+ if (schemaLoaded || schemaLoading) return;
481
+ if (!showDisplayFields) return;
482
+ var client = getClient();
483
+ if (!client || typeof client.getSdk !== "function") return;
484
+ var sdk = client.getSdk();
485
+ if (!sdk) return;
486
+ var model = sdk.model(objectSlug);
487
+ if (!model || typeof model.getSchema !== "function") return;
488
+
489
+ schemaLoading = true;
490
+ model
491
+ .getSchema()
492
+ .then(function (schema) {
493
+ objectSchema = schema && schema.data ? schema.data : schema;
494
+ schemaLoaded = true;
495
+ refreshSchemaDerivedText();
496
+ if (filteredRecords.length > 0) renderOptions();
497
+ })
498
+ .catch(function () {})
499
+ .finally(function () {
500
+ schemaLoading = false;
501
+ });
502
+ }
503
+
435
504
  function isSelected(recordId) {
436
505
  return values.some(function (v) {
437
506
  return v === recordId || String(v) === String(recordId);
@@ -483,7 +552,8 @@
483
552
  var sdk = client.getSdk();
484
553
  if (!sdk) return Promise.resolve({ records: [], hasMore: false });
485
554
  var model = sdk.model(objectSlug);
486
- var fields = ["id", "name"].concat(displayFields || []);
555
+ var schemaDisplayFields = getSchemaDisplayFields();
556
+ var fields = ["id", "name"].concat(schemaDisplayFields);
487
557
  var actualLimit = limit || initialLimit;
488
558
  var offset = page ? (page - 1) * actualLimit : 0;
489
559
 
@@ -517,9 +587,9 @@
517
587
  var mappedRecords = records.map(function (r) {
518
588
  var d = r.toJSON ? r.toJSON() : r;
519
589
  var iconUrl = d.icon || d.logo || d.icon_url || d.Icon__c || d.Logo__c || null;
520
- var displayLabels = displayFields ? displayFields.map(function (field) {
590
+ var displayLabels = schemaDisplayFields.map(function (field) {
521
591
  return d[field] || "";
522
- }) : [];
592
+ });
523
593
  return {
524
594
  id: d.id,
525
595
  value: d.id,
@@ -555,7 +625,8 @@
555
625
  return;
556
626
  }
557
627
  var model = sdk.model(objectSlug);
558
- var fields = ["id", "name"].concat(displayFields || []);
628
+ var schemaDisplayFields = getSchemaDisplayFields();
629
+ var fields = ["id", "name"].concat(schemaDisplayFields);
559
630
  model
560
631
  .select.apply(model, fields)
561
632
  .filterBy({
@@ -568,9 +639,9 @@
568
639
  selectedRecords = records.map(function (r) {
569
640
  var d = r.toJSON ? r.toJSON() : r;
570
641
  var iconUrl = d.icon || d.logo || d.icon_url || d.Icon__c || d.Logo__c || null;
571
- var displayLabels = displayFields ? displayFields.map(function (field) {
642
+ var displayLabels = schemaDisplayFields.map(function (field) {
572
643
  return d[field] || "";
573
- }) : [];
644
+ });
574
645
  return {
575
646
  id: d.id,
576
647
  value: d.id,
@@ -941,6 +1012,7 @@
941
1012
 
942
1013
  if (values && values.length > 0) loadSelectedRecords();
943
1014
  else renderTriggerContent();
1015
+ if (showDisplayFields) loadObjectSchema();
944
1016
 
945
1017
  return container;
946
1018
  }
@@ -187,18 +187,18 @@
187
187
  * @param {string} config.value - Current value (record id)
188
188
  * @param {Function} config.onChange - Change handler (value, record?) => void
189
189
  * @param {boolean} [config.disabled] - Whether select is disabled
190
+ * @param {boolean} [config.showDisplayFields] - If true, fetch schema and show secondary fields from objectSchema.properties.display_fields
190
191
  * @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'borderless' | 'inline'
191
192
  * @param {string} [config.size] - 'default' | 'large' | 'small'
192
193
  * @param {boolean} [config.canClear] - Show clear button when value is set
193
194
  * @param {number} [config.initialLimit] - Initial fetch limit (default 50)
194
195
  * @param {Object} [config.initialFilter] - Optional filter object to merge with search (e.g. { field: "status", operator: "exact", value: "active" } or { and: [...] })
195
- * @param {Object} [config.objectSchema] - Optional ObjectType; display_singular_name/display_plural_name/slug used for placeholder/search text; properties.icon_data { icon?, color? } for static icon (not used for user; user shows Vivid Avatar)
196
196
  * @returns {HTMLElement} Record select container element
197
197
  */
198
198
  function createRecordSelect(config) {
199
199
  var fieldId = config.fieldId;
200
200
  var objectSlug = config.objectSlug;
201
- var objectSchema = config.objectSchema || null;
201
+ var objectSchema = null;
202
202
  var displaySingular = getObjectDisplayName(objectSchema, objectSlug, "singular");
203
203
  var displayPlural = getObjectDisplayName(objectSchema, objectSlug, "plural");
204
204
  var placeholder =
@@ -208,6 +208,7 @@
208
208
  config.searchPlaceholder ||
209
209
  "Search " + (displayPlural || objectSlug || "") + "...";
210
210
  var onChange = config.onChange;
211
+ var showDisplayFields = config.showDisplayFields === true;
211
212
  var variant = config.variant || "default";
212
213
  var size = config.size || "default";
213
214
  var canClear = !!config.canClear;
@@ -247,6 +248,21 @@
247
248
  var currentPage = 1;
248
249
  var isFetchingMore = false;
249
250
  var totalFetched = 0;
251
+ var schemaLoaded = false;
252
+ var schemaLoading = false;
253
+
254
+ function getSchemaDisplayFields() {
255
+ if (!showDisplayFields) return [];
256
+ var fields =
257
+ objectSchema &&
258
+ objectSchema.properties &&
259
+ Array.isArray(objectSchema.properties.display_fields)
260
+ ? objectSchema.properties.display_fields
261
+ : [];
262
+ return fields.filter(function (field) {
263
+ return typeof field === "string" && field.length > 0;
264
+ });
265
+ }
250
266
 
251
267
  // Trigger wrapper + button (same structure as Select)
252
268
  var triggerWrapper = document.createElement("span");
@@ -408,6 +424,7 @@
408
424
  popover.panel.style.minWidth = triggerWidthPx;
409
425
  popover.panel.style.width = triggerWidthPx;
410
426
  }
427
+ if (showDisplayFields) loadObjectSchema();
411
428
  loadInitialAndRender();
412
429
  setTimeout(function () {
413
430
  if (searchInputEl) searchInputEl.focus();
@@ -443,6 +460,31 @@
443
460
  if (clearBtn)
444
461
  clearBtn.style.display = canClear && value && !disabled ? "" : "none";
445
462
 
463
+ function loadObjectSchema() {
464
+ if (schemaLoaded || schemaLoading) return;
465
+ if (!showDisplayFields) return;
466
+ var client = getClient();
467
+ if (!client || typeof client.getSdk !== "function") return;
468
+ var sdk = client.getSdk();
469
+ if (!sdk) return;
470
+ var model = sdk.model(objectSlug);
471
+ if (!model || typeof model.getSchema !== "function") return;
472
+
473
+ schemaLoading = true;
474
+ model
475
+ .getSchema()
476
+ .then(function (schema) {
477
+ objectSchema = schema && schema.data ? schema.data : schema;
478
+ schemaLoaded = true;
479
+ if (filteredRecords.length > 0) renderOptions();
480
+ else updateTriggerDisplay();
481
+ })
482
+ .catch(function () {})
483
+ .finally(function () {
484
+ schemaLoading = false;
485
+ });
486
+ }
487
+
446
488
  function setValue(newVal) {
447
489
  value = newVal !== undefined && newVal !== null ? newVal : "";
448
490
  selectedRecord = null;
@@ -552,7 +594,8 @@
552
594
  var sdk = client.getSdk();
553
595
  if (!sdk) return Promise.resolve({ records: [], hasMore: false });
554
596
  var model = sdk.model(objectSlug);
555
- var fields = ["id", "name"];
597
+ var schemaDisplayFields = getSchemaDisplayFields();
598
+ var fields = ["id", "name"].concat(schemaDisplayFields);
556
599
  var actualLimit = limit || initialLimit;
557
600
  var offset = page ? (page - 1) * actualLimit : 0;
558
601
 
@@ -602,6 +645,9 @@
602
645
  value: d.id,
603
646
  name: d.name || d.id,
604
647
  label: d.name || d.id,
648
+ displayLabels: schemaDisplayFields.map(function (field) {
649
+ return d[field] || "";
650
+ }),
605
651
  };
606
652
  });
607
653
  // If we got fewer records than the limit, there are no more
@@ -631,8 +677,9 @@
631
677
  return;
632
678
  }
633
679
  var model = sdk.model(objectSlug);
680
+ var schemaDisplayFields = getSchemaDisplayFields();
634
681
  model
635
- .select("id", "name")
682
+ .select.apply(model, ["id", "name"].concat(schemaDisplayFields))
636
683
  .where({ id: value })
637
684
  .limit(1)
638
685
  .first()
@@ -644,6 +691,9 @@
644
691
  value: d.id,
645
692
  name: d.name || d.id,
646
693
  label: d.name || d.id,
694
+ displayLabels: schemaDisplayFields.map(function (field) {
695
+ return d[field] || "";
696
+ }),
647
697
  };
648
698
  }
649
699
  updateTriggerDisplay();
@@ -654,6 +704,7 @@
654
704
  value: value,
655
705
  name: value,
656
706
  label: value,
707
+ displayLabels: [],
657
708
  };
658
709
  updateTriggerDisplay();
659
710
  });
@@ -747,7 +798,9 @@
747
798
  );
748
799
 
749
800
  var optContent = document.createElement("span");
750
- optContent.className = "flex items-center gap-8 flex-1 truncate";
801
+ optContent.className = "flex flex-col flex-1 truncate min-w-0";
802
+ var primaryRow = document.createElement("div");
803
+ primaryRow.className = "flex items-center gap-8 truncate";
751
804
  if (objectSlug === STANDARD_OBJECT_SLUGS_USERS) {
752
805
  var Avatar = getAvatar();
753
806
  if (Avatar && typeof Avatar.createVivid === "function") {
@@ -756,13 +809,13 @@
756
809
  size: "small",
757
810
  shape: "circle",
758
811
  });
759
- optContent.appendChild(avatarEl);
812
+ primaryRow.appendChild(avatarEl);
760
813
  } else {
761
814
  var pl = document.createElement("span");
762
815
  pl.className =
763
816
  "size-20 rounded-full bg-primary-surface text-primary-text flex items-center justify-center text-reg-12 font-semibold shrink-0";
764
817
  pl.textContent = (optionLabel || "?").charAt(0).toUpperCase();
765
- optContent.appendChild(pl);
818
+ primaryRow.appendChild(pl);
766
819
  }
767
820
  } else {
768
821
  var IconComponent = getIcon();
@@ -775,18 +828,39 @@
775
828
  defaultIcon: true,
776
829
  className: "size-20 shrink-0",
777
830
  });
778
- if (iconEl) optContent.appendChild(iconEl);
831
+ if (iconEl) primaryRow.appendChild(iconEl);
779
832
  } else {
780
833
  var pl = document.createElement("span");
781
834
  pl.className =
782
835
  "size-20 rounded-4 flex items-center justify-center shrink-0 bg-neutral-surface-hover text-neutral-text-base";
783
836
  pl.textContent = "?";
784
- optContent.appendChild(pl);
837
+ primaryRow.appendChild(pl);
785
838
  }
786
839
  }
787
840
  var labelSpan = document.createElement("span");
841
+ labelSpan.className = "truncate";
788
842
  labelSpan.textContent = optionLabel;
789
- optContent.appendChild(labelSpan);
843
+ primaryRow.appendChild(labelSpan);
844
+ optContent.appendChild(primaryRow);
845
+
846
+ if (showDisplayFields && rec.displayLabels && rec.displayLabels.length > 0 && rec.displayLabels.some(function (l) { return l; })) {
847
+ var secondaryRow = document.createElement("div");
848
+ secondaryRow.className = "flex items-center gap-4 truncate text-reg-10 text-typography-tertiary-text mt-2 ml-28";
849
+ rec.displayLabels.forEach(function (label, index) {
850
+ if (label) {
851
+ var secondaryLabel = document.createElement("span");
852
+ secondaryLabel.className = "truncate";
853
+ secondaryLabel.textContent = label;
854
+ secondaryRow.appendChild(secondaryLabel);
855
+ if (index < rec.displayLabels.length - 1 && rec.displayLabels[index + 1]) {
856
+ var separator = document.createElement("span");
857
+ separator.textContent = " • ";
858
+ secondaryRow.appendChild(separator);
859
+ }
860
+ }
861
+ });
862
+ optContent.appendChild(secondaryRow);
863
+ }
790
864
  option.appendChild(optContent);
791
865
 
792
866
  option.addEventListener("click", function (e) {
@@ -983,6 +1057,7 @@
983
1057
 
984
1058
  if (value) loadSelectedRecord();
985
1059
  else updateTriggerDisplay();
1060
+ if (showDisplayFields) loadObjectSchema();
986
1061
 
987
1062
  return container;
988
1063
  }
package/core/flow.js CHANGED
@@ -887,6 +887,7 @@
887
887
  * @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'borderless' | 'inline'
888
888
  * @param {string} [config.size] - 'default' | 'large' | 'small'
889
889
  * @param {boolean} [config.canClear] - Show clear button when value is set
890
+ * @param {boolean} [config.showDisplayFields=false] - Show secondary fields from object schema display_fields
890
891
  * @param {number} [config.initialLimit] - Initial fetch limit
891
892
  * @param {string} [config.helpText] - Optional help text for tooltip
892
893
  * @returns {HTMLElement} Field element
@@ -904,6 +905,7 @@
904
905
  variant,
905
906
  size,
906
907
  canClear,
908
+ showDisplayFields = false,
907
909
  initialLimit,
908
910
  initialFilter,
909
911
  helpText = null,
@@ -924,6 +926,7 @@
924
926
  variant: variant || "default",
925
927
  size: size || "default",
926
928
  canClear: !!canClear,
929
+ showDisplayFields: !!showDisplayFields,
927
930
  initialLimit,
928
931
  initialFilter,
929
932
  onChange: (value, record) => {
@@ -958,8 +961,8 @@
958
961
  * @param {boolean} [config.disabled=false]
959
962
  * @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'borderless' | 'inline'
960
963
  * @param {string} [config.size] - 'default' | 'large' | 'small'
964
+ * @param {boolean} [config.showDisplayFields=false] - Show secondary fields from object schema display_fields
961
965
  * @param {number} [config.initialLimit] - Initial fetch limit
962
- * @param {Array<string>} [config.displayFields] - Fields to show as secondary info
963
966
  * @param {string} [config.helpText] - Optional help text for tooltip
964
967
  * @returns {HTMLElement} Field element
965
968
  */
@@ -975,9 +978,9 @@
975
978
  disabled = false,
976
979
  variant,
977
980
  size,
981
+ showDisplayFields = false,
978
982
  initialLimit,
979
983
  initialFilter,
980
- displayFields,
981
984
  helpText = null,
982
985
  } = config;
983
986
 
@@ -997,9 +1000,9 @@
997
1000
  disabled,
998
1001
  variant: variant || "default",
999
1002
  size: size || "default",
1003
+ showDisplayFields: !!showDisplayFields,
1000
1004
  initialLimit,
1001
1005
  initialFilter,
1002
- displayFields: displayFields || [],
1003
1006
  onValuesChange: (values, records) => {
1004
1007
  set(fieldId, values);
1005
1008
  if (onChange) onChange(values, records);