hs-uix 2.0.0 → 2.1.0

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.
@@ -29,9 +29,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // src/common-components/index.js
30
30
  var common_components_exports = {};
31
31
  __export(common_components_exports, {
32
+ ActiveFilterChips: () => ActiveFilterChips,
32
33
  AutoStatusTag: () => AutoStatusTag,
33
34
  AutoTag: () => AutoTag,
34
35
  AvatarStack: () => AvatarStack,
36
+ CollectionCount: () => CollectionCount,
37
+ CollectionFilterControl: () => CollectionFilterControl,
38
+ CollectionSortSelect: () => CollectionSortSelect,
39
+ CollectionToolbar: () => CollectionToolbar,
35
40
  CrmLookupSelect: () => CrmLookupSelect,
36
41
  DEFAULT_SVG_FONT_WEIGHT: () => DEFAULT_SVG_FONT_WEIGHT,
37
42
  HS_DATE_DIRECTION_LABELS: () => HS_DATE_DIRECTION_LABELS,
@@ -51,7 +56,7 @@ __export(common_components_exports, {
51
56
  HS_TEXT_COLOR: () => HS_TEXT_COLOR,
52
57
  ICONS: () => ICONS,
53
58
  ICON_NAMES: () => ICON_NAMES,
54
- Icon: () => Icon3,
59
+ Icon: () => Icon,
55
60
  KeyValueList: () => KeyValueList,
56
61
  NATIVE_ICON_NAME_LIST: () => NATIVE_ICON_NAME_LIST,
57
62
  SPINNERS: () => SPINNERS,
@@ -59,6 +64,7 @@ __export(common_components_exports, {
59
64
  SectionHeader: () => SectionHeader,
60
65
  Spinner: () => Spinner,
61
66
  StyledText: () => StyledText,
67
+ formatCollectionCount: () => formatCollectionCount,
62
68
  gridToBraille: () => gridToBraille,
63
69
  makeAvatarStackDataUri: () => makeAvatarStackDataUri,
64
70
  makeGrid: () => makeGrid,
@@ -268,9 +274,47 @@ var AutoStatusTag = ({
268
274
  );
269
275
  };
270
276
 
271
- // src/common-components/AvatarStack.js
277
+ // src/common-components/ActiveFilterChips.js
272
278
  var import_react3 = __toESM(require("react"));
273
279
  var import_ui_extensions3 = require("@hubspot/ui-extensions");
280
+ var h = import_react3.default.createElement;
281
+ var ActiveFilterChips = ({
282
+ chips = [],
283
+ showBadges = true,
284
+ showClearAll = true,
285
+ clearAllLabel = "Clear all",
286
+ onRemove,
287
+ gap = "sm"
288
+ }) => {
289
+ if (!Array.isArray(chips) || chips.length === 0) return null;
290
+ if (!showBadges && !showClearAll) return null;
291
+ return h(
292
+ import_ui_extensions3.Flex,
293
+ { direction: "row", align: "center", gap, wrap: "wrap" },
294
+ ...showBadges ? chips.map((chip) => h(
295
+ import_ui_extensions3.Tag,
296
+ {
297
+ key: chip.key,
298
+ variant: "default",
299
+ onDelete: () => onRemove == null ? void 0 : onRemove(chip.key)
300
+ },
301
+ chip.label
302
+ )) : [],
303
+ showClearAll ? h(
304
+ import_ui_extensions3.Button,
305
+ {
306
+ variant: "transparent",
307
+ size: "extra-small",
308
+ onClick: () => onRemove == null ? void 0 : onRemove("all")
309
+ },
310
+ clearAllLabel
311
+ ) : null
312
+ );
313
+ };
314
+
315
+ // src/common-components/AvatarStack.js
316
+ var import_react4 = __toESM(require("react"));
317
+ var import_ui_extensions4 = require("@hubspot/ui-extensions");
274
318
 
275
319
  // src/common-components/svgDefaults.js
276
320
  var HS_FONT_FAMILY = '"Lexend Deca", Helvetica, Arial, sans-serif';
@@ -415,7 +459,7 @@ var AvatarStack = ({
415
459
  fontFamily
416
460
  });
417
461
  if (!stack) return null;
418
- return import_react3.default.createElement(import_ui_extensions3.Image, {
462
+ return import_react4.default.createElement(import_ui_extensions4.Image, {
419
463
  src: stack.src,
420
464
  width: stack.width,
421
465
  height: stack.height,
@@ -424,597 +468,68 @@ var AvatarStack = ({
424
468
  };
425
469
 
426
470
  // src/common-components/CrmLookupSelect.js
427
- var import_react9 = __toESM(require("react"));
428
- var import_ui_extensions9 = require("@hubspot/ui-extensions");
471
+ var import_react15 = __toESM(require("react"));
472
+ var import_ui_extensions15 = require("@hubspot/ui-extensions");
429
473
 
430
474
  // src/utils/crmSearchAdapters.js
431
- var import_react8 = __toESM(require("react"));
432
- var import_ui_extensions8 = require("@hubspot/ui-extensions");
475
+ var import_react14 = __toESM(require("react"));
476
+ var import_ui_extensions14 = require("@hubspot/ui-extensions");
433
477
 
434
- // packages/datatable/src/DataTable.jsx
435
- var import_react5 = __toESM(require("react"));
478
+ // src/datatable/DataTable.jsx
479
+ var import_react10 = __toESM(require("react"));
436
480
 
437
481
  // src/utils/query.js
438
482
  var import_fuse = __toESM(require("fuse.js"));
439
483
 
440
484
  // src/utils/interactionHooks.js
441
- var import_react4 = require("react");
442
- var import_ui_extensions4 = require("@hubspot/ui-extensions");
443
-
444
- // packages/datatable/src/DataTable.jsx
485
+ var import_react5 = require("react");
445
486
  var import_ui_extensions5 = require("@hubspot/ui-extensions");
446
487
 
447
- // packages/kanban/src/Kanban.jsx
448
- var import_react7 = __toESM(require("react"));
449
-
450
- // src/common-components/StyledText.js
488
+ // src/common-components/CollectionCount.js
451
489
  var import_react6 = __toESM(require("react"));
452
490
  var import_ui_extensions6 = require("@hubspot/ui-extensions");
453
- var VARIANT_PRESETS = {
454
- bodytext: { fontSize: 14, lineHeight: 24, fontWeight: 400 },
455
- microcopy: { fontSize: 12, lineHeight: 18, fontWeight: 400 }
456
- };
457
- var WEIGHT_ALIASES = {
458
- bold: 700,
459
- demibold: 600,
460
- regular: 400
461
- };
462
- var LINE_DECORATION = {
463
- strikethrough: "line-through",
464
- underline: "underline"
465
- };
466
- var ORIENTATION_ROTATION = {
467
- horizontal: 0,
468
- "vertical-up": -90,
469
- "vertical-down": 90
470
- };
471
- var BACKGROUND_PRESETS = {
472
- tag: {
473
- color: HS_SUBTLE_BG,
474
- borderColor: HS_TAG_SUBTLE_BORDER,
475
- borderWidth: HS_TAG_BORDER_WIDTH,
476
- radius: HS_TAG_BORDER_RADIUS,
477
- paddingX: HS_TAG_PADDING_X,
478
- paddingY: HS_TAG_PADDING_Y,
479
- height: HS_TAG_LINE_HEIGHT,
480
- textColor: HS_TAG_TEXT_COLOR,
481
- fontSize: HS_TAG_FONT_SIZE,
482
- canvasPaddingX: 0,
483
- canvasPaddingY: 0
484
- }
485
- };
486
- var TAG_VARIANTS = {
487
- default: {
488
- color: HS_SUBTLE_BG,
489
- borderColor: HS_TAG_SUBTLE_BORDER,
490
- textColor: HS_TAG_TEXT_COLOR
491
- },
492
- success: {
493
- color: "#E5F8F6",
494
- borderColor: "#00BDA5",
495
- textColor: "#00BDA5"
496
- },
497
- warning: {
498
- color: "#FEF8F0",
499
- borderColor: "#F5C26B",
500
- textColor: "#D39913"
501
- },
502
- error: {
503
- color: "#FDEDEE",
504
- borderColor: "#F2545B",
505
- textColor: "#F2545B"
506
- },
507
- danger: {
508
- color: "#FDEDEE",
509
- borderColor: "#F2545B",
510
- textColor: "#F2545B"
511
- },
512
- info: {
513
- color: "#E5F5F8",
514
- borderColor: "#00A4BD",
515
- textColor: "#00A4BD"
516
- }
517
- };
518
- var NATIVE_TAG_VARIANT_ALIASES = {
519
- danger: "error"
520
- };
521
- var escapeSvgText = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
522
- var applyTextTransform = (text, transform) => {
523
- if (!transform || transform === "none") return String(text);
524
- const s = String(text);
525
- switch (transform) {
526
- case "uppercase":
527
- return s.toUpperCase();
528
- case "lowercase":
529
- return s.toLowerCase();
530
- case "capitalize":
531
- return s.replace(/\b\w/g, (c) => c.toUpperCase());
532
- case "sentenceCase":
533
- return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
534
- default:
535
- return s;
536
- }
537
- };
538
- var estimateTextWidth = (text, fontSize) => Math.max(fontSize, Math.round(String(text).length * fontSize * 0.58));
539
- var resolveBackground = (background) => {
540
- if (!background) return null;
541
- const preset = background.preset ? BACKGROUND_PRESETS[background.preset] : null;
542
- const variant = background.preset === "tag" && background.variant ? TAG_VARIANTS[background.variant] || null : null;
543
- return {
544
- ...preset || {},
545
- ...variant || {},
546
- ...background
547
- };
548
- };
549
- var buildBackgroundRect = ({ background, x, y, width, height }) => {
550
- const radius = (background == null ? void 0 : background.radius) ?? 3;
551
- const fill = (background == null ? void 0 : background.color) ?? "transparent";
552
- const borderWidth = (background == null ? void 0 : background.borderWidth) ?? 0;
553
- const borderColor = background == null ? void 0 : background.borderColor;
554
- if (!borderColor || borderWidth <= 0) {
555
- return `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" />`;
556
- }
557
- const isTagPreset = (background == null ? void 0 : background.preset) === "tag";
558
- const fillRect = `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" />`;
559
- const strokeInset = borderWidth / 2;
560
- const strokeX = x + strokeInset;
561
- const strokeY = y + strokeInset;
562
- const strokeW = Math.max(0, width - borderWidth);
563
- const strokeH = Math.max(0, height - borderWidth);
564
- return fillRect + `<rect x="${strokeX}" y="${strokeY}" width="${strokeW}" height="${strokeH}" rx="${Math.max(
565
- 0,
566
- radius - strokeInset
567
- )}" fill="none" stroke="${borderColor}" stroke-width="${borderWidth}"${isTagPreset ? ` shape-rendering="crispEdges"` : ""} />`;
568
- };
569
- var canUseNativeTag = ({
570
- background,
571
- orientation,
572
- color,
573
- fontFamily,
574
- fontSize,
575
- width,
576
- height,
577
- paddingX,
578
- paddingY,
579
- format = {}
580
- }) => {
581
- if (!background || background.preset !== "tag") return false;
582
- const resolvedOrientation = typeof orientation === "number" ? orientation : ORIENTATION_ROTATION[orientation ?? "horizontal"] ?? 0;
583
- if (resolvedOrientation !== 0) return false;
584
- if (color != null || fontFamily != null || fontSize != null) return false;
585
- if (width != null || height != null || paddingX != null || paddingY != null) return false;
586
- if (background.color != null || background.textColor != null) return false;
587
- if (background.borderColor != null || background.borderWidth != null) return false;
588
- if (background.radius != null || background.height != null) return false;
589
- if (background.paddingX != null || background.paddingY != null) return false;
590
- if (background.canvasPaddingX != null || background.canvasPaddingY != null) return false;
591
- if (format.italic || format.lineDecoration) return false;
592
- if (format.textTransform && format.textTransform !== "none") return false;
593
- return true;
491
+ var h2 = import_react6.default.createElement;
492
+ var resolveLabel = (label, count) => {
493
+ if (typeof label === "function") return label(count);
494
+ if (label && typeof label === "object") return count === 1 ? label.singular : label.plural;
495
+ return label || "items";
594
496
  };
595
- var makeStyledTextDataUri = (text, opts = {}) => {
596
- const {
597
- variant = "bodytext",
598
- format = {},
599
- orientation = "horizontal",
600
- color: colorProp = HS_TEXT_COLOR,
601
- fontFamily = HS_FONT_FAMILY,
602
- background: backgroundProp = null,
603
- paddingX: paddingXProp = 4,
604
- paddingY: paddingYProp = 2,
605
- width: widthOverride,
606
- height: heightOverride,
607
- fontSize: fontSizeOverride
608
- } = opts;
609
- const preset = VARIANT_PRESETS[variant] || VARIANT_PRESETS.bodytext;
610
- const background = resolveBackground(backgroundProp);
611
- const fontSize = fontSizeOverride ?? (background == null ? void 0 : background.fontSize) ?? preset.fontSize;
612
- const rawWeight = format.fontWeight;
613
- const fontWeight = rawWeight ? WEIGHT_ALIASES[rawWeight] ?? rawWeight : preset.fontWeight;
614
- const fontStyle = format.italic ? "italic" : "normal";
615
- const textDecoration = LINE_DECORATION[format.lineDecoration] || "none";
616
- const transformed = applyTextTransform(text, format.textTransform);
617
- const lineHeight = (background == null ? void 0 : background.height) ?? preset.lineHeight ?? fontSize;
618
- const color = (background == null ? void 0 : background.textColor) ?? colorProp;
619
- const paddingX = (background == null ? void 0 : background.canvasPaddingX) ?? paddingXProp;
620
- const paddingY = (background == null ? void 0 : background.canvasPaddingY) ?? paddingYProp;
621
- const rotate = typeof orientation === "number" ? orientation : ORIENTATION_ROTATION[orientation] ?? 0;
622
- const textW = estimateTextWidth(transformed, fontSize);
623
- let pillW = 0;
624
- let pillH = 0;
625
- if (background) {
626
- const bgPadX = background.paddingX ?? 6;
627
- const bgPadY = background.paddingY ?? 3;
628
- pillW = textW + bgPadX * 2;
629
- pillH = background.height ?? Math.max(lineHeight, fontSize + bgPadY * 2);
630
- }
631
- const intrinsicW = (background ? pillW : textW) + paddingX * 2;
632
- const intrinsicH = (background ? pillH : lineHeight) + paddingY * 2;
633
- const isOrthoRotation = rotate === 90 || rotate === -90 || rotate === 270;
634
- const canvasW = widthOverride ?? (isOrthoRotation ? intrinsicH : intrinsicW);
635
- const canvasH = heightOverride ?? (isOrthoRotation ? intrinsicW : intrinsicH);
636
- const cx = canvasW / 2;
637
- const cy = canvasH / 2;
638
- const rectX = cx - pillW / 2;
639
- const rectY = cy - pillH / 2;
640
- const group = (background ? buildBackgroundRect({
641
- background,
642
- x: rectX,
643
- y: rectY,
644
- width: pillW,
645
- height: pillH
646
- }) : "") + `<text x="${cx}" y="${cy}" text-anchor="middle" dominant-baseline="central" font-family="${fontFamily.replace(/"/g, "&quot;")}" font-size="${fontSize}" font-weight="${fontWeight}" font-style="${fontStyle}" text-decoration="${textDecoration}" fill="${color}">${escapeSvgText(transformed)}</text>`;
647
- const wrapped = rotate ? `<g transform="rotate(${rotate} ${cx} ${cy})">${group}</g>` : group;
648
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${canvasW}" height="${canvasH}">` + wrapped + `</svg>`;
649
- return {
650
- src: `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,
651
- width: canvasW,
652
- height: canvasH
653
- };
497
+ var formatCollectionCount = ({ shown, total, label = "items", formatter }) => {
498
+ const resolvedShown = Number(shown ?? total ?? 0);
499
+ const resolvedTotal = Number(total ?? resolvedShown);
500
+ if (typeof formatter === "function") return formatter(resolvedShown, resolvedTotal);
501
+ const resolvedLabel = resolveLabel(label, resolvedTotal);
502
+ return resolvedShown === resolvedTotal ? `${resolvedTotal} ${resolvedLabel}` : `${resolvedShown} of ${resolvedTotal} ${resolvedLabel}`;
654
503
  };
655
- var StyledText = ({
656
- children,
504
+ var CollectionCount = ({
505
+ shown,
506
+ total,
507
+ label,
657
508
  text,
658
- alt,
659
- variant,
660
- format,
661
- orientation,
662
- color,
663
- background,
664
- fontFamily,
665
- fontSize,
666
- paddingX,
667
- paddingY,
668
- width,
669
- height
670
- }) => {
671
- const resolvedText = text ?? (typeof children === "string" ? children : "");
672
- if (canUseNativeTag({
673
- background,
674
- orientation,
675
- color,
676
- fontFamily,
677
- fontSize,
678
- width,
679
- height,
680
- paddingX,
681
- paddingY,
682
- format
683
- })) {
684
- const nativeVariant = NATIVE_TAG_VARIANT_ALIASES[background == null ? void 0 : background.variant] ?? (background == null ? void 0 : background.variant) ?? "default";
685
- return import_react6.default.createElement(import_ui_extensions6.Tag, { variant: nativeVariant }, resolvedText);
686
- }
687
- const { src, width: w, height: h } = makeStyledTextDataUri(resolvedText, {
509
+ formatter,
510
+ bold = false,
511
+ variant = "microcopy",
512
+ format
513
+ }) => h2(
514
+ import_ui_extensions6.Text,
515
+ {
688
516
  variant,
689
- format,
690
- orientation,
691
- color,
692
- background,
693
- fontFamily,
694
- fontSize,
695
- paddingX,
696
- paddingY,
697
- width,
698
- height
699
- });
700
- return import_react6.default.createElement(import_ui_extensions6.Image, {
701
- src,
702
- width: w,
703
- height: h,
704
- alt: alt ?? String(resolvedText)
705
- });
706
- };
517
+ format: format || (bold ? { fontWeight: "bold" } : void 0)
518
+ },
519
+ text ?? formatCollectionCount({ shown, total, label, formatter })
520
+ );
707
521
 
708
- // packages/kanban/src/Kanban.jsx
709
- var import_ui_extensions7 = require("@hubspot/ui-extensions");
522
+ // src/common-components/CollectionToolbar.js
523
+ var import_react9 = __toESM(require("react"));
524
+ var import_ui_extensions9 = require("@hubspot/ui-extensions");
710
525
 
711
- // src/utils/objectPath.js
712
- var getByPath = (obj, path) => {
713
- if (!path) return void 0;
714
- if (typeof path === "function") return path(obj);
715
- return String(path).split(".").reduce((acc, key) => acc == null ? void 0 : acc[key], obj);
716
- };
526
+ // src/common-components/CollectionFilterControl.js
527
+ var import_react8 = __toESM(require("react"));
528
+ var import_ui_extensions8 = require("@hubspot/ui-extensions");
717
529
 
718
- // src/utils/crmSearchAdapters.js
719
- var EMPTY_ARRAY = [];
720
- var EMPTY_OBJECT = {};
721
- var isPlainObject = (value) => value != null && Object.prototype.toString.call(value) === "[object Object]";
722
- var coerceError = (error) => {
723
- if (!error) return false;
724
- if (typeof error === "string") return error;
725
- if (error.message) return error.message;
726
- return true;
727
- };
728
- var pickArray = (response) => {
729
- if (Array.isArray(response)) return response;
730
- if (!response) return EMPTY_ARRAY;
731
- return response.results || response.data || response.items || response.records || response.objects || EMPTY_ARRAY;
732
- };
733
- var pickTotal = (response, fallbackLength) => {
734
- var _a;
735
- if (!response || Array.isArray(response)) return fallbackLength;
736
- return response.total ?? response.totalCount ?? response.totalResults ?? ((_a = response.paging) == null ? void 0 : _a.total) ?? fallbackLength;
737
- };
738
- var normalizeCrmSearchRecord = (record, options = EMPTY_OBJECT) => {
739
- const {
740
- idField = "id",
741
- objectIdField = "objectId",
742
- propertiesKey = "properties",
743
- flattenProperties = true,
744
- propertyValueKey,
745
- mapRecord
746
- } = options;
747
- if (mapRecord) return mapRecord(record);
748
- const objectId = (record == null ? void 0 : record.objectId) ?? (record == null ? void 0 : record.id) ?? (record == null ? void 0 : record.hs_object_id) ?? getByPath(record, `${propertiesKey}.hs_object_id`);
749
- const properties = (record == null ? void 0 : record[propertiesKey]) || EMPTY_OBJECT;
750
- const flattened = {};
751
- if (flattenProperties && isPlainObject(properties)) {
752
- for (const [key, value] of Object.entries(properties)) {
753
- flattened[key] = propertyValueKey && isPlainObject(value) ? value[propertyValueKey] : value;
754
- }
755
- }
756
- return {
757
- ...flattenProperties ? flattened : EMPTY_OBJECT,
758
- ...record,
759
- [idField]: objectId,
760
- [objectIdField]: objectId,
761
- [propertiesKey]: properties
762
- };
763
- };
764
- var normalizeCrmSearchRows = (response, options = EMPTY_OBJECT) => {
765
- const records = pickArray(response);
766
- return records.map((record) => normalizeCrmSearchRecord(record, options));
767
- };
768
- var STABLE_SORT_TIEBREAKER = { propertyName: "hs_object_id", direction: "ASCENDING" };
769
- var withStableSort = (sorts) => {
770
- const base = Array.isArray(sorts) ? sorts : [];
771
- if (base.some((s) => s && s.propertyName === STABLE_SORT_TIEBREAKER.propertyName)) return base;
772
- return [...base, STABLE_SORT_TIEBREAKER];
773
- };
774
- var buildCrmSearchConfig = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
775
- const {
776
- objectType,
777
- properties = EMPTY_ARRAY,
778
- query,
779
- filterGroups,
780
- sorts,
781
- pageLength,
782
- propertyMap = EMPTY_OBJECT,
783
- filterMap,
784
- sortMap,
785
- baseConfig = EMPTY_OBJECT
786
- } = options;
787
- const mappedFilters = filterMap ? filterMap(params.filters || EMPTY_OBJECT, params) : filterGroups;
788
- const mappedSorts = sortMap ? sortMap(params.sort || EMPTY_OBJECT, params) : sorts;
789
- const config = {
790
- ...baseConfig,
791
- objectType: objectType || baseConfig.objectType,
792
- properties: properties.length ? properties : baseConfig.properties,
793
- query: query ?? params.search ?? baseConfig.query,
794
- filterGroups: mappedFilters ?? baseConfig.filterGroups,
795
- sorts: withStableSort(mappedSorts ?? baseConfig.sorts),
796
- pageLength: pageLength ?? params.pageLength ?? baseConfig.pageLength
797
- };
798
- if (propertyMap && Object.keys(propertyMap).length && params.filters) {
799
- const filters = Object.entries(params.filters).filter(([, value]) => value !== void 0 && value !== null && value !== "" && !(Array.isArray(value) && value.length === 0)).map(([name, value]) => {
800
- const propertyName = propertyMap[name] || name;
801
- if (Array.isArray(value)) return { propertyName, operator: "IN", values: value };
802
- if (isPlainObject(value) && (value.from || value.to)) {
803
- const rangeFilters = [];
804
- if (value.from) rangeFilters.push({ propertyName, operator: "GTE", value: value.from });
805
- if (value.to) rangeFilters.push({ propertyName, operator: "LTE", value: value.to });
806
- return rangeFilters;
807
- }
808
- return { propertyName, operator: "EQ", value };
809
- }).flat();
810
- if (filters.length) config.filterGroups = [{ filters }];
811
- }
812
- Object.keys(config).forEach((key) => config[key] === void 0 && delete config[key]);
813
- return config;
814
- };
815
- var useCrmSearchDataSource = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
816
- const {
817
- format,
818
- row,
819
- rowIdField = "id",
820
- totalCount,
821
- loading,
822
- error,
823
- mapResponse
824
- } = options;
825
- const config = (0, import_react8.useMemo)(() => buildCrmSearchConfig(params, options), [params, options]);
826
- const response = (0, import_ui_extensions8.useCrmSearch)(config, format);
827
- return (0, import_react8.useMemo)(() => {
828
- const rows = mapResponse ? mapResponse(response) : normalizeCrmSearchRows(response, { idField: rowIdField, ...row || EMPTY_OBJECT });
829
- const resolvedTotal = typeof totalCount === "function" ? totalCount(response) : totalCount ?? pickTotal(response, rows.length);
830
- return {
831
- data: rows,
832
- rows,
833
- response,
834
- loading: typeof loading === "function" ? loading(response) : loading ?? !!(response == null ? void 0 : response.isLoading),
835
- isLoading: typeof loading === "function" ? loading(response) : loading ?? !!(response == null ? void 0 : response.isLoading),
836
- error: typeof error === "function" ? error(response) : error ?? coerceError(response == null ? void 0 : response.error),
837
- totalCount: resolvedTotal,
838
- rowIdField
839
- };
840
- }, [response, mapResponse, row, rowIdField, totalCount, loading, error]);
841
- };
842
- var crmSearchResultToOption = (row, options = EMPTY_OBJECT) => {
843
- const {
844
- label = "name",
845
- value = "objectId",
846
- description,
847
- fallbackLabel = "Untitled record",
848
- mapOption
849
- } = options;
850
- if (mapOption) return mapOption(row);
851
- const option = {
852
- label: getByPath(row, label) ?? getByPath(row, "properties.name") ?? fallbackLabel,
853
- value: getByPath(row, value) ?? getByPath(row, "id") ?? getByPath(row, "objectId")
854
- };
855
- const desc = getByPath(row, description);
856
- if (desc != null && desc !== "") option.description = desc;
857
- return option;
858
- };
859
- var useCrmSearchOptions = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
860
- const dataSource = useCrmSearchDataSource(params, options);
861
- const optionConfig = options.option || options;
862
- return (0, import_react8.useMemo)(() => ({
863
- ...dataSource,
864
- options: dataSource.rows.map((row) => crmSearchResultToOption(row, optionConfig))
865
- }), [dataSource, optionConfig]);
866
- };
867
- var CRM_OBJECT_TYPES = {
868
- contact: "0-1",
869
- contacts: "0-1",
870
- company: "0-2",
871
- companies: "0-2",
872
- deal: "0-3",
873
- deals: "0-3"
874
- };
875
- var resolveCrmObjectType = (objectType) => CRM_OBJECT_TYPES[objectType] || objectType;
876
-
877
- // src/common-components/CrmLookupSelect.js
878
- var EMPTY_ARRAY2 = [];
879
- var EMPTY_OBJECT2 = {};
880
- var makeOptionConfig = ({ option, labelProperty, valueProperty, descriptionProperty }) => ({
881
- label: labelProperty || (option == null ? void 0 : option.label) || "name",
882
- value: valueProperty || (option == null ? void 0 : option.value) || "objectId",
883
- description: descriptionProperty || (option == null ? void 0 : option.description),
884
- fallbackLabel: option == null ? void 0 : option.fallbackLabel,
885
- mapOption: option == null ? void 0 : option.mapOption
886
- });
887
- var mergeSelectedOptions = (options, selectedOptions, value) => {
888
- const selected = Array.isArray(selectedOptions) ? selectedOptions : selectedOptions ? [selectedOptions] : EMPTY_ARRAY2;
889
- if (!selected.length) return options;
890
- const values = new Set(options.map((opt) => opt.value));
891
- const activeValues = new Set(Array.isArray(value) ? value : value == null || value === "" ? [] : [value]);
892
- const missing = selected.filter((opt) => activeValues.has(opt.value) && !values.has(opt.value));
893
- return missing.length ? [...missing, ...options] : options;
894
- };
895
- var CrmLookupSelect = ({
896
- objectType,
897
- properties = EMPTY_ARRAY2,
898
- name,
899
- label,
900
- value,
901
- onChange,
902
- multiple = false,
903
- placeholder,
904
- description,
905
- tooltip,
906
- required,
907
- readOnly,
908
- error,
909
- validationMessage,
910
- variant,
911
- debounce = 300,
912
- minSearchLength = 0,
913
- pageLength = 20,
914
- option,
915
- labelProperty,
916
- valueProperty = "objectId",
917
- descriptionProperty,
918
- selectedOptions,
919
- format,
920
- row,
921
- baseConfig,
922
- query,
923
- onSearchChange,
924
- noResultsOption,
925
- loadingOption,
926
- selectProps = EMPTY_OBJECT2
927
- }) => {
928
- const [inputValue, setInputValue] = (0, import_react9.useState)(query || "");
929
- const [pickedOptions, setPickedOptions] = (0, import_react9.useState)(EMPTY_ARRAY2);
930
- const debouncedInput = (0, import_ui_extensions9.useDebounce)(inputValue, debounce > 0 ? debounce : 1);
931
- const search = debounce > 0 ? debouncedInput : inputValue;
932
- const effectiveSearch = search && search.length >= minSearchLength ? search : "";
933
- const optionConfig = (0, import_react9.useMemo)(
934
- () => makeOptionConfig({ option, labelProperty, valueProperty, descriptionProperty }),
935
- [option, labelProperty, valueProperty, descriptionProperty]
936
- );
937
- const dataSource = useCrmSearchOptions(
938
- { search: effectiveSearch },
939
- {
940
- objectType: resolveCrmObjectType(objectType),
941
- properties,
942
- pageLength,
943
- format,
944
- baseConfig,
945
- row: row || { mapRecord: (record) => ({ objectId: record.objectId, ...record.properties }) },
946
- option: optionConfig
947
- }
948
- );
949
- const isSearching = dataSource.loading || inputValue.trim() !== (search || "").trim();
950
- const hasQuery = effectiveSearch.length > 0;
951
- const options = (0, import_react9.useMemo)(() => {
952
- const remembered = [...selectedOptions || EMPTY_ARRAY2, ...pickedOptions];
953
- const baseOptions = mergeSelectedOptions(dataSource.options || EMPTY_ARRAY2, remembered, value);
954
- if (isSearching && loadingOption) return [loadingOption, ...baseOptions];
955
- if (!isSearching && hasQuery && !baseOptions.length && noResultsOption) return [noResultsOption];
956
- return baseOptions;
957
- }, [dataSource.options, isSearching, hasQuery, selectedOptions, pickedOptions, value, loadingOption, noResultsOption]);
958
- const handleChange = (next) => {
959
- const nextValues = Array.isArray(next) ? next : next == null || next === "" ? EMPTY_ARRAY2 : [next];
960
- const picked = nextValues.map((v) => options.find((o) => o.value === v)).filter((o) => o && o.value !== (loadingOption == null ? void 0 : loadingOption.value) && o.value !== (noResultsOption == null ? void 0 : noResultsOption.value));
961
- if (picked.length) {
962
- setPickedOptions((prev) => {
963
- const byValue = new Map(prev.map((o) => [o.value, o]));
964
- picked.forEach((o) => byValue.set(o.value, o));
965
- return [...byValue.values()];
966
- });
967
- }
968
- if (onChange) onChange(next);
969
- };
970
- const commonProps = {
971
- name,
972
- label,
973
- value: multiple ? value || EMPTY_ARRAY2 : value,
974
- options,
975
- placeholder: placeholder || (dataSource.loading ? "Searching CRM..." : "Search CRM records..."),
976
- description,
977
- tooltip,
978
- required,
979
- readOnly,
980
- error: error || !!dataSource.error,
981
- validationMessage: validationMessage || (typeof dataSource.error === "string" ? dataSource.error : void 0),
982
- variant,
983
- onChange: handleChange,
984
- onInput: (next) => {
985
- setInputValue(next || "");
986
- if (onSearchChange) onSearchChange(next || "");
987
- },
988
- ...selectProps
989
- };
990
- return import_react9.default.createElement(multiple ? import_ui_extensions9.MultiSelect : import_ui_extensions9.Select, commonProps);
991
- };
992
-
993
- // src/common-components/datePresets.js
994
- var HS_DATE_PRESETS = [
995
- { label: "Today", value: "today" },
996
- { label: "Yesterday", value: "yesterday" },
997
- { label: "Tomorrow", value: "tomorrow" },
998
- { label: "This week", value: "this_week" },
999
- { label: "Last week", value: "last_week" },
1000
- { label: "Last 7 days", value: "7d" },
1001
- { label: "Last 30 days", value: "30d" },
1002
- { label: "Last 90 days", value: "90d" },
1003
- { label: "This month", value: "this_month" },
1004
- { label: "Last month", value: "last_month" },
1005
- { label: "This quarter", value: "this_quarter" },
1006
- { label: "Last quarter", value: "last_quarter" },
1007
- { label: "This year", value: "this_year" },
1008
- { label: "Last year", value: "last_year" }
1009
- ];
1010
- var HS_DATE_DIRECTION_LABELS = {
1011
- asc: "Ascending",
1012
- desc: "Descending"
1013
- };
1014
-
1015
- // src/common-components/Icon.js
1016
- var import_react10 = __toESM(require("react"));
1017
- var import_ui_extensions10 = require("@hubspot/ui-extensions");
530
+ // src/common-components/Icon.js
531
+ var import_react7 = __toESM(require("react"));
532
+ var import_ui_extensions7 = require("@hubspot/ui-extensions");
1018
533
 
1019
534
  // src/common-components/icons.generated.js
1020
535
  var GENERATED_ICONS = {
@@ -1489,140 +1004,974 @@ var SIZE_TOKENS2 = {
1489
1004
  xl: 24,
1490
1005
  "extra-large": 24
1491
1006
  };
1492
- var resolveSize2 = (size) => {
1493
- if (typeof size === "number") return size;
1494
- if (typeof size === "string" && SIZE_TOKENS2[size] != null) return SIZE_TOKENS2[size];
1495
- return 16;
1007
+ var resolveSize2 = (size) => {
1008
+ if (typeof size === "number") return size;
1009
+ if (typeof size === "string" && SIZE_TOKENS2[size] != null) return SIZE_TOKENS2[size];
1010
+ return 16;
1011
+ };
1012
+ var escapeXmlAttr2 = (s) => String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1013
+ var PLUS_24 = "M11 5h2v6h6v2h-6v6h-2v-6H5v-2h6z";
1014
+ var CURATED_ICONS = {
1015
+ checkBare: { paths: ["M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z"] },
1016
+ plusBare: { paths: [PLUS_24] },
1017
+ // Close = a plus rotated 45° → a clean X (reuses transform support, no new
1018
+ // path data to hand-author/get-wrong).
1019
+ Close: { paths: [PLUS_24], transform: "rotate(45 12 12)" },
1020
+ // Universal media-transport glyphs, authored at 32×32 to match the scraped
1021
+ // canvas so they sit at the same optical size as the rest of the set.
1022
+ Play: { viewBox: "0 0 32 32", paths: ["M9 6v20l17-10z"] },
1023
+ Pause: { viewBox: "0 0 32 32", paths: ["M10 6h4v20h-4z M18 6h4v20h-4z"] },
1024
+ Stop: { viewBox: "0 0 32 32", paths: ["M8 8h16v16H8z"] },
1025
+ // Hand-authored to match HubSpot's filled/rounded weight (verified visually
1026
+ // against the scraped set) for genuine gaps the scrape didn't cover.
1027
+ Unlocked: {
1028
+ viewBox: "0 0 32 32",
1029
+ paths: [
1030
+ "M9.5 14v-3.4c0-3.57 2.9-6.46 6.46-6.46 2.66 0 5.02 1.64 5.97 4.07a1.6 1.6 0 1 1-2.98 1.16 3.23 3.23 0 0 0-2.99-2.03c-1.78 0-3.23 1.45-3.23 3.23V14z",
1031
+ { d: "M6.3 14h19.4a3.2 3.2 0 0 1 3.2 3.2v8.6a3.2 3.2 0 0 1-3.2 3.2H6.3a3.2 3.2 0 0 1-3.2-3.2v-8.6A3.2 3.2 0 0 1 6.3 14Zm9.7 4.4a2.1 2.1 0 0 0-1.05 3.92V25a1.05 1.05 0 0 0 2.1 0v-2.68A2.1 2.1 0 0 0 16 18.4Z", fillRule: "evenodd" }
1032
+ ]
1033
+ },
1034
+ Print: {
1035
+ viewBox: "0 0 32 32",
1036
+ paths: [
1037
+ "M9 4h14a1 1 0 0 1 1 1v6H8V5a1 1 0 0 1 1-1z",
1038
+ { d: "M5 12h22a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-3v-3H8v3H5a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2zm18 3.2a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4z", fillRule: "evenodd" },
1039
+ "M10 21h12v6a1 1 0 0 1-1 1H11a1 1 0 0 1-1-1z"
1040
+ ]
1041
+ },
1042
+ Shrink: {
1043
+ viewBox: "0 0 32 32",
1044
+ paths: [
1045
+ "M14.05 4.1c1.08 0 1.95.87 1.95 1.95v8a1.95 1.95 0 0 1-1.95 1.95h-8a1.95 1.95 0 1 1 0-3.9h3.28L3.17 5.93A1.95 1.95 0 0 1 5.93 3.17l6.17 6.17V6.05c0-1.08.87-1.95 1.95-1.95Z",
1046
+ "M17.95 27.9a1.95 1.95 0 0 1-1.95-1.95v-8a1.95 1.95 0 0 1 1.95-1.95h8a1.95 1.95 0 1 1 0 3.9h-3.28l6.18 6.17a1.95 1.95 0 0 1-2.76 2.76l-6.17-6.17v3.29c0 1.08-.87 1.95-1.95 1.95Z"
1047
+ ]
1048
+ },
1049
+ Heart: { viewBox: "0 0 32 32", paths: ["M16 26.5C9 21 5 17 5 12.4 5 9.1 7.6 6.5 11 6.5c2 0 3.8 1 5 2.6 1.2-1.6 3-2.6 5-2.6 3.4 0 6 2.6 6 5.9 0 4.6-4 8.6-11 14.1Z"] },
1050
+ CircleFilled: { viewBox: "0 0 32 32", paths: ["M16 2C8.27 2 2 8.27 2 16s6.27 14 14 14 14-6.27 14-14S23.73 2 16 2z"] },
1051
+ // Dark = crescent moon, pairs with the scraped Light (sun) for theme toggles.
1052
+ Dark: { viewBox: "0 0 32 32", paths: ["M19 3.5A12 12 0 1 0 28.5 19 9.6 9.6 0 0 1 19 3.5Z"] },
1053
+ Export: { viewBox: "0 0 32 32", paths: ["M16 3l5 5h-3.5v7h-3V8H11z", "M6 16h4v6h12v-6h4v8a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2z"] },
1054
+ Import: { viewBox: "0 0 32 32", paths: ["M16 16l5-5h-3.5V4h-3v7H11z", "M6 17h4v5h12v-5h4v7a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2z"] },
1055
+ // Backward = horizontal mirror of the scraped Forward arrow (x' = 32 - x).
1056
+ // Derived so it stays in sync if Forward is ever re-scraped.
1057
+ ...GENERATED_ICONS.Forward ? { Backward: { ...GENERATED_ICONS.Forward, transform: "translate(32 0) scale(-1 1)" } } : {}
1058
+ };
1059
+ var ICONS = { ...GENERATED_ICONS, ...CURATED_ICONS };
1060
+ var svgToIconEntry = (raw) => {
1061
+ const vb = /viewBox="([^"]+)"/i.exec(raw);
1062
+ const viewBox = vb ? vb[1] : "0 0 24 24";
1063
+ const rendered = raw.replace(
1064
+ /<(mask|defs|clipPath|symbol)\b[^>]*>[\s\S]*?<\/\1>/gi,
1065
+ ""
1066
+ );
1067
+ const paths = [];
1068
+ const pathRe = /<path\b([^>]*?)\/?>/gi;
1069
+ let m;
1070
+ while ((m = pathRe.exec(rendered)) !== null) {
1071
+ const attrs = m[1];
1072
+ const d = /\bd="([^"]+)"/i.exec(attrs);
1073
+ if (!d) continue;
1074
+ const fill = /\bfill="([^"]+)"/i.exec(attrs);
1075
+ const fillRule = /\bfill-rule="([^"]+)"/i.exec(attrs);
1076
+ const keepFill = fill && fill[1] !== "currentColor" && fill[1] !== "none";
1077
+ if (keepFill || fillRule) {
1078
+ paths.push({
1079
+ d: d[1],
1080
+ ...keepFill ? { fill: fill[1] } : {},
1081
+ ...fillRule ? { fillRule: fillRule[1] } : {}
1082
+ });
1083
+ } else {
1084
+ paths.push(d[1]);
1085
+ }
1086
+ }
1087
+ return { viewBox, paths };
1088
+ };
1089
+ var ICON_NAME_ALIASES = {
1090
+ call: "calling",
1091
+ phone: "calling",
1092
+ meeting: "appointment",
1093
+ note: "comment",
1094
+ notes: "comment",
1095
+ task: "tasks",
1096
+ open: "record",
1097
+ openRecord: "record",
1098
+ office: "record",
1099
+ company: "record",
1100
+ external: "link",
1101
+ externalLink: "link",
1102
+ down: "Down",
1103
+ up: "Up",
1104
+ pencil: "edit",
1105
+ trash: "delete"
1106
+ };
1107
+ var NATIVE_ICON_NAME_ALIASES = new Map([
1108
+ ...[...NATIVE_ICON_NAMES].map((nativeName) => [nativeName.toLowerCase(), nativeName]),
1109
+ ...Object.entries(ICON_NAME_ALIASES).map(([alias, nativeName]) => [alias.toLowerCase(), nativeName])
1110
+ ]);
1111
+ var resolveIconName = (name) => {
1112
+ if (typeof name !== "string") return name;
1113
+ if (NATIVE_ICON_NAMES.has(name)) return name;
1114
+ if (ICONS[name]) return name;
1115
+ const nativeOrAlias = NATIVE_ICON_NAME_ALIASES.get(name.toLowerCase());
1116
+ if (nativeOrAlias) return nativeOrAlias;
1117
+ return name;
1118
+ };
1119
+ var canUseNative = (name, color, size) => NATIVE_ICON_NAMES.has(resolveIconName(name)) && (color == null || NATIVE_COLORS.has(color)) && (size == null || typeof size === "string" && NATIVE_SIZE_TOKENS[size] != null);
1120
+ var makeIconDataUri = (nameOrEntry, opts = {}) => {
1121
+ const entry = nameOrEntry && typeof nameOrEntry === "object" ? nameOrEntry : ICONS[nameOrEntry];
1122
+ if (!entry || !entry.paths) return null;
1123
+ const size = resolveSize2(opts.size);
1124
+ const rawColor = opts.color;
1125
+ const color = rawColor && SEMANTIC_HEX[rawColor] || rawColor || HS_TEXT_COLOR;
1126
+ const viewBox = entry.viewBox ?? "0 0 24 24";
1127
+ const body = entry.paths.map((p) => {
1128
+ const d = typeof p === "string" ? p : p.d;
1129
+ const fill = typeof p === "object" && p.fill || color;
1130
+ const fillRule = typeof p === "object" && p.fillRule ? ` fill-rule="${p.fillRule}"` : "";
1131
+ return `<path d="${escapeXmlAttr2(d)}" fill="${fill}"${fillRule} />`;
1132
+ }).join("");
1133
+ const inner = entry.transform ? `<g transform="${escapeXmlAttr2(entry.transform)}">${body}</g>` : body;
1134
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="${viewBox}">` + inner + `</svg>`;
1135
+ return {
1136
+ src: `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,
1137
+ width: size,
1138
+ height: size
1139
+ };
1140
+ };
1141
+ var ICON_NAMES = Object.keys(ICONS);
1142
+ var NATIVE_ICON_NAME_LIST = [...NATIVE_ICON_NAMES].sort(
1143
+ (a, b) => a.localeCompare(b)
1144
+ );
1145
+ var Icon = ({ name, color, size, screenReaderText, onClick, href }) => {
1146
+ const resolvedName = resolveIconName(name);
1147
+ const renderNativeIcon = () => import_react7.default.createElement(import_ui_extensions7.Icon, {
1148
+ name: resolvedName,
1149
+ ...color != null ? { color } : {},
1150
+ ...size != null ? { size: NATIVE_SIZE_TOKENS[size] } : {},
1151
+ ...screenReaderText != null ? { screenReaderText } : {}
1152
+ });
1153
+ const renderNative = () => {
1154
+ const icon = renderNativeIcon();
1155
+ if (onClick == null && href == null) return icon;
1156
+ return import_react7.default.createElement(
1157
+ import_ui_extensions7.Link,
1158
+ {
1159
+ ...onClick != null ? { onClick } : {},
1160
+ ...href != null ? { href } : {}
1161
+ },
1162
+ icon
1163
+ );
1164
+ };
1165
+ if (canUseNative(name, color, size)) return renderNative();
1166
+ const result = makeIconDataUri(resolvedName, { size, color });
1167
+ if (!result) {
1168
+ return canUseNative(name, color, size) ? renderNative() : null;
1169
+ }
1170
+ return import_react7.default.createElement(import_ui_extensions7.Image, {
1171
+ src: result.src,
1172
+ width: result.width,
1173
+ height: result.height,
1174
+ alt: screenReaderText ?? name,
1175
+ ...onClick != null ? { onClick } : {},
1176
+ ...href != null ? { href } : {}
1177
+ });
1178
+ };
1179
+
1180
+ // src/common-components/CollectionFilterControl.js
1181
+ var h3 = import_react8.default.createElement;
1182
+ var DEFAULT_LABELS = {
1183
+ all: "All",
1184
+ dateFrom: "From",
1185
+ dateTo: "To"
1186
+ };
1187
+ var CollectionFilterControl = ({
1188
+ filter,
1189
+ value,
1190
+ onChange,
1191
+ namePrefix = "collection-filter",
1192
+ labels: labelOverrides,
1193
+ selectVariant = "transparent",
1194
+ includeAll,
1195
+ allValue
1196
+ }) => {
1197
+ if (!filter) return null;
1198
+ const labels = { ...DEFAULT_LABELS, ...labelOverrides || {} };
1199
+ const type = filter.type || "select";
1200
+ const name = filter.name;
1201
+ const controlName = `${namePrefix}-${name}`;
1202
+ const handleChange = (next) => onChange == null ? void 0 : onChange(name, next);
1203
+ if (type === "multiselect") {
1204
+ return h3(import_ui_extensions8.MultiSelect, {
1205
+ key: name,
1206
+ name: controlName,
1207
+ placeholder: filter.placeholder || filter.label || labels.all,
1208
+ value: Array.isArray(value) ? value : [],
1209
+ options: filter.options || [],
1210
+ onChange: handleChange
1211
+ });
1212
+ }
1213
+ if (type === "dateRange") {
1214
+ const rangeValue = value || { from: null, to: null };
1215
+ return h3(
1216
+ import_ui_extensions8.Flex,
1217
+ { key: name, direction: "row", align: "center", gap: "xs" },
1218
+ h3(import_ui_extensions8.DateInput, {
1219
+ name: `${controlName}-from`,
1220
+ placeholder: filter.fromLabel ?? labels.dateFrom,
1221
+ format: "medium",
1222
+ value: rangeValue.from ?? null,
1223
+ onChange: (next) => handleChange({ ...rangeValue, from: next })
1224
+ }),
1225
+ h3(Icon, { name: "right", size: "sm" }),
1226
+ h3(import_ui_extensions8.DateInput, {
1227
+ name: `${controlName}-to`,
1228
+ placeholder: filter.toLabel ?? labels.dateTo,
1229
+ format: "medium",
1230
+ value: rangeValue.to ?? null,
1231
+ onChange: (next) => handleChange({ ...rangeValue, to: next })
1232
+ })
1233
+ );
1234
+ }
1235
+ const resolvedIncludeAll = includeAll ?? filter.includeAll ?? true;
1236
+ const resolvedAllValue = filter.emptyValue ?? allValue ?? filter.allValue ?? "";
1237
+ const allLabel = filter.allLabel ?? filter.placeholder ?? filter.label ?? labels.all;
1238
+ const options = resolvedIncludeAll ? [{ label: allLabel, value: resolvedAllValue }, ...filter.options || []] : filter.options || [];
1239
+ return h3(import_ui_extensions8.Select, {
1240
+ key: name,
1241
+ name: controlName,
1242
+ variant: selectVariant,
1243
+ placeholder: filter.placeholder || filter.label || labels.all,
1244
+ value: value ?? resolvedAllValue,
1245
+ options,
1246
+ onChange: handleChange
1247
+ });
1248
+ };
1249
+
1250
+ // src/common-components/CollectionToolbar.js
1251
+ var h4 = import_react9.default.createElement;
1252
+ var DEFAULT_LABELS2 = {
1253
+ filtersButton: "Filters",
1254
+ clearAll: "Clear all"
1255
+ };
1256
+ var isVisible = (config) => config && (config.visible ?? config.show ?? true);
1257
+ var CollectionToolbar = ({
1258
+ search,
1259
+ filters,
1260
+ chips,
1261
+ right,
1262
+ footer,
1263
+ labels: labelOverrides,
1264
+ leftFlex = 3,
1265
+ rightFlex = 1,
1266
+ rightAlignSelf = "end",
1267
+ gap = "sm",
1268
+ idPrefix,
1269
+ uniqueNames = true
1270
+ }) => {
1271
+ const [showMoreFilters, setShowMoreFilters] = (0, import_react9.useState)(false);
1272
+ const reactId = (0, import_react9.useId)();
1273
+ const instanceId = String(idPrefix || reactId).replace(/[^a-zA-Z0-9_-]/g, "");
1274
+ const nameWithInstance = (name) => uniqueNames && name ? `${name}-${instanceId}` : name;
1275
+ const labels = { ...DEFAULT_LABELS2, ...labelOverrides || {} };
1276
+ const filterItems = Array.isArray(filters == null ? void 0 : filters.items) ? filters.items : [];
1277
+ const filterValues = (filters == null ? void 0 : filters.values) || {};
1278
+ const filterInlineLimit = (filters == null ? void 0 : filters.inlineLimit) ?? filterItems.length;
1279
+ const inlineFilters = filterItems.slice(0, filterInlineLimit);
1280
+ const overflowFilters = filterItems.slice(filterInlineLimit);
1281
+ const showSearch = isVisible(search);
1282
+ const hasSearch = showSearch && !!search;
1283
+ const hasFilters = inlineFilters.length > 0 || overflowFilters.length > 0;
1284
+ const hasChips = chips && Array.isArray(chips.items) && chips.items.length > 0;
1285
+ const hasLeft = hasSearch || hasFilters || hasChips;
1286
+ const hasRight = !!right;
1287
+ if (!hasLeft && !hasRight && !footer) return null;
1288
+ const filterLabels = { ...labels, ...(filters == null ? void 0 : filters.labels) || {} };
1289
+ const searchHandler = (search == null ? void 0 : search.onChange) || (search == null ? void 0 : search.onInput);
1290
+ const searchProps = hasSearch ? {
1291
+ name: nameWithInstance(search.name || "collection-search"),
1292
+ placeholder: search.placeholder,
1293
+ value: search.value,
1294
+ clearable: search.clearable !== false,
1295
+ ...search.onInput ? { onInput: search.onInput } : {},
1296
+ ...searchHandler ? { onChange: searchHandler } : {}
1297
+ } : null;
1298
+ const renderFilter = (filter) => h4(CollectionFilterControl, {
1299
+ key: filter.name,
1300
+ namePrefix: nameWithInstance((filters == null ? void 0 : filters.namePrefix) || "collection-filter"),
1301
+ filter,
1302
+ value: filterValues[filter.name],
1303
+ onChange: filters == null ? void 0 : filters.onChange,
1304
+ labels: filterLabels,
1305
+ includeAll: filters == null ? void 0 : filters.includeAll,
1306
+ allValue: filters == null ? void 0 : filters.allValue,
1307
+ selectVariant: filters == null ? void 0 : filters.selectVariant
1308
+ });
1309
+ const left = hasLeft ? h4(
1310
+ import_ui_extensions9.Box,
1311
+ { flex: leftFlex },
1312
+ h4(
1313
+ import_ui_extensions9.Flex,
1314
+ { direction: "column", gap },
1315
+ hasSearch || hasFilters ? h4(
1316
+ import_ui_extensions9.Flex,
1317
+ { direction: "row", align: "center", gap, wrap: "wrap" },
1318
+ hasSearch ? h4(import_ui_extensions9.SearchInput, searchProps) : null,
1319
+ ...inlineFilters.map(renderFilter),
1320
+ overflowFilters.length > 0 ? h4(
1321
+ import_ui_extensions9.Button,
1322
+ {
1323
+ variant: "transparent",
1324
+ size: (filters == null ? void 0 : filters.overflowButtonSize) || "small",
1325
+ onClick: () => setShowMoreFilters((prev) => !prev)
1326
+ },
1327
+ h4(Icon, { name: "filter", size: "sm" }),
1328
+ " ",
1329
+ (filters == null ? void 0 : filters.filtersButtonLabel) || labels.filtersButton
1330
+ ) : null
1331
+ ) : null,
1332
+ showMoreFilters && overflowFilters.length > 0 ? h4(
1333
+ import_ui_extensions9.Flex,
1334
+ { direction: "row", align: "center", gap, wrap: "wrap" },
1335
+ ...overflowFilters.map(renderFilter)
1336
+ ) : null,
1337
+ chips ? h4(ActiveFilterChips, {
1338
+ chips: chips.items,
1339
+ showBadges: chips.showBadges,
1340
+ showClearAll: chips.showClearAll,
1341
+ clearAllLabel: chips.clearAllLabel || labels.clearAll,
1342
+ onRemove: chips.onRemove,
1343
+ gap: chips.gap || gap
1344
+ }) : null
1345
+ )
1346
+ ) : null;
1347
+ const rightNode = hasRight ? h4(
1348
+ import_ui_extensions9.Box,
1349
+ { flex: rightFlex, alignSelf: rightAlignSelf },
1350
+ h4(import_ui_extensions9.Flex, { direction: "row", align: "center", gap, justify: "end", wrap: "wrap" }, right)
1351
+ ) : null;
1352
+ return h4(
1353
+ import_ui_extensions9.Flex,
1354
+ { direction: "column", gap: "xs" },
1355
+ h4(import_ui_extensions9.Flex, { direction: "row", gap }, left, rightNode),
1356
+ footer || null
1357
+ );
1358
+ };
1359
+
1360
+ // src/datatable/DataTable.jsx
1361
+ var import_ui_extensions10 = require("@hubspot/ui-extensions");
1362
+
1363
+ // src/kanban/Kanban.jsx
1364
+ var import_react13 = __toESM(require("react"));
1365
+
1366
+ // src/common-components/CollectionSortSelect.js
1367
+ var import_react11 = __toESM(require("react"));
1368
+ var import_ui_extensions11 = require("@hubspot/ui-extensions");
1369
+ var h5 = import_react11.default.createElement;
1370
+ var sanitizeId = (value) => String(value || "").replace(/[^a-zA-Z0-9_-]/g, "");
1371
+ var CollectionSortSelect = ({
1372
+ name = "collection-sort",
1373
+ value,
1374
+ options = [],
1375
+ placeholder = "Sort",
1376
+ onChange,
1377
+ includeEmpty = true,
1378
+ emptyValue = "",
1379
+ variant = "transparent",
1380
+ idPrefix,
1381
+ uniqueName = true
1382
+ }) => {
1383
+ const reactId = (0, import_react11.useId)();
1384
+ const instanceId = sanitizeId(idPrefix || reactId);
1385
+ const resolvedName = uniqueName && name ? `${name}-${instanceId}` : name;
1386
+ const mappedOptions = (options || []).map((option) => ({
1387
+ label: option.label,
1388
+ value: option.value
1389
+ }));
1390
+ return h5(import_ui_extensions11.Select, {
1391
+ name: resolvedName,
1392
+ value: value ?? emptyValue,
1393
+ variant,
1394
+ placeholder,
1395
+ options: includeEmpty ? [{ label: placeholder, value: emptyValue }, ...mappedOptions] : mappedOptions,
1396
+ onChange
1397
+ });
1398
+ };
1399
+
1400
+ // src/common-components/StyledText.js
1401
+ var import_react12 = __toESM(require("react"));
1402
+ var import_ui_extensions12 = require("@hubspot/ui-extensions");
1403
+ var VARIANT_PRESETS = {
1404
+ bodytext: { fontSize: 14, lineHeight: 24, fontWeight: 400 },
1405
+ microcopy: { fontSize: 12, lineHeight: 18, fontWeight: 400 }
1406
+ };
1407
+ var WEIGHT_ALIASES = {
1408
+ bold: 700,
1409
+ demibold: 600,
1410
+ regular: 400
1411
+ };
1412
+ var LINE_DECORATION = {
1413
+ strikethrough: "line-through",
1414
+ underline: "underline"
1415
+ };
1416
+ var ORIENTATION_ROTATION = {
1417
+ horizontal: 0,
1418
+ "vertical-up": -90,
1419
+ "vertical-down": 90
1420
+ };
1421
+ var BACKGROUND_PRESETS = {
1422
+ tag: {
1423
+ color: HS_SUBTLE_BG,
1424
+ borderColor: HS_TAG_SUBTLE_BORDER,
1425
+ borderWidth: HS_TAG_BORDER_WIDTH,
1426
+ radius: HS_TAG_BORDER_RADIUS,
1427
+ paddingX: HS_TAG_PADDING_X,
1428
+ paddingY: HS_TAG_PADDING_Y,
1429
+ height: HS_TAG_LINE_HEIGHT,
1430
+ textColor: HS_TAG_TEXT_COLOR,
1431
+ fontSize: HS_TAG_FONT_SIZE,
1432
+ canvasPaddingX: 0,
1433
+ canvasPaddingY: 0
1434
+ }
1435
+ };
1436
+ var TAG_VARIANTS = {
1437
+ default: {
1438
+ color: HS_SUBTLE_BG,
1439
+ borderColor: HS_TAG_SUBTLE_BORDER,
1440
+ textColor: HS_TAG_TEXT_COLOR
1441
+ },
1442
+ success: {
1443
+ color: "#E5F8F6",
1444
+ borderColor: "#00BDA5",
1445
+ textColor: "#00BDA5"
1446
+ },
1447
+ warning: {
1448
+ color: "#FEF8F0",
1449
+ borderColor: "#F5C26B",
1450
+ textColor: "#D39913"
1451
+ },
1452
+ error: {
1453
+ color: "#FDEDEE",
1454
+ borderColor: "#F2545B",
1455
+ textColor: "#F2545B"
1456
+ },
1457
+ danger: {
1458
+ color: "#FDEDEE",
1459
+ borderColor: "#F2545B",
1460
+ textColor: "#F2545B"
1461
+ },
1462
+ info: {
1463
+ color: "#E5F5F8",
1464
+ borderColor: "#00A4BD",
1465
+ textColor: "#00A4BD"
1466
+ }
1467
+ };
1468
+ var NATIVE_TAG_VARIANT_ALIASES = {
1469
+ danger: "error"
1470
+ };
1471
+ var escapeSvgText = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1472
+ var applyTextTransform = (text, transform) => {
1473
+ if (!transform || transform === "none") return String(text);
1474
+ const s = String(text);
1475
+ switch (transform) {
1476
+ case "uppercase":
1477
+ return s.toUpperCase();
1478
+ case "lowercase":
1479
+ return s.toLowerCase();
1480
+ case "capitalize":
1481
+ return s.replace(/\b\w/g, (c) => c.toUpperCase());
1482
+ case "sentenceCase":
1483
+ return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
1484
+ default:
1485
+ return s;
1486
+ }
1487
+ };
1488
+ var estimateTextWidth = (text, fontSize) => Math.max(fontSize, Math.round(String(text).length * fontSize * 0.58));
1489
+ var resolveBackground = (background) => {
1490
+ if (!background) return null;
1491
+ const preset = background.preset ? BACKGROUND_PRESETS[background.preset] : null;
1492
+ const variant = background.preset === "tag" && background.variant ? TAG_VARIANTS[background.variant] || null : null;
1493
+ return {
1494
+ ...preset || {},
1495
+ ...variant || {},
1496
+ ...background
1497
+ };
1498
+ };
1499
+ var buildBackgroundRect = ({ background, x, y, width, height }) => {
1500
+ const radius = (background == null ? void 0 : background.radius) ?? 3;
1501
+ const fill = (background == null ? void 0 : background.color) ?? "transparent";
1502
+ const borderWidth = (background == null ? void 0 : background.borderWidth) ?? 0;
1503
+ const borderColor = background == null ? void 0 : background.borderColor;
1504
+ if (!borderColor || borderWidth <= 0) {
1505
+ return `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" />`;
1506
+ }
1507
+ const isTagPreset = (background == null ? void 0 : background.preset) === "tag";
1508
+ const fillRect = `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" />`;
1509
+ const strokeInset = borderWidth / 2;
1510
+ const strokeX = x + strokeInset;
1511
+ const strokeY = y + strokeInset;
1512
+ const strokeW = Math.max(0, width - borderWidth);
1513
+ const strokeH = Math.max(0, height - borderWidth);
1514
+ return fillRect + `<rect x="${strokeX}" y="${strokeY}" width="${strokeW}" height="${strokeH}" rx="${Math.max(
1515
+ 0,
1516
+ radius - strokeInset
1517
+ )}" fill="none" stroke="${borderColor}" stroke-width="${borderWidth}"${isTagPreset ? ` shape-rendering="crispEdges"` : ""} />`;
1518
+ };
1519
+ var canUseNativeTag = ({
1520
+ background,
1521
+ orientation,
1522
+ color,
1523
+ fontFamily,
1524
+ fontSize,
1525
+ width,
1526
+ height,
1527
+ paddingX,
1528
+ paddingY,
1529
+ format = {}
1530
+ }) => {
1531
+ if (!background || background.preset !== "tag") return false;
1532
+ const resolvedOrientation = typeof orientation === "number" ? orientation : ORIENTATION_ROTATION[orientation ?? "horizontal"] ?? 0;
1533
+ if (resolvedOrientation !== 0) return false;
1534
+ if (color != null || fontFamily != null || fontSize != null) return false;
1535
+ if (width != null || height != null || paddingX != null || paddingY != null) return false;
1536
+ if (background.color != null || background.textColor != null) return false;
1537
+ if (background.borderColor != null || background.borderWidth != null) return false;
1538
+ if (background.radius != null || background.height != null) return false;
1539
+ if (background.paddingX != null || background.paddingY != null) return false;
1540
+ if (background.canvasPaddingX != null || background.canvasPaddingY != null) return false;
1541
+ if (format.italic || format.lineDecoration) return false;
1542
+ if (format.textTransform && format.textTransform !== "none") return false;
1543
+ return true;
1544
+ };
1545
+ var makeStyledTextDataUri = (text, opts = {}) => {
1546
+ const {
1547
+ variant = "bodytext",
1548
+ format = {},
1549
+ orientation = "horizontal",
1550
+ color: colorProp = HS_TEXT_COLOR,
1551
+ fontFamily = HS_FONT_FAMILY,
1552
+ background: backgroundProp = null,
1553
+ paddingX: paddingXProp = 4,
1554
+ paddingY: paddingYProp = 2,
1555
+ width: widthOverride,
1556
+ height: heightOverride,
1557
+ fontSize: fontSizeOverride
1558
+ } = opts;
1559
+ const preset = VARIANT_PRESETS[variant] || VARIANT_PRESETS.bodytext;
1560
+ const background = resolveBackground(backgroundProp);
1561
+ const fontSize = fontSizeOverride ?? (background == null ? void 0 : background.fontSize) ?? preset.fontSize;
1562
+ const rawWeight = format.fontWeight;
1563
+ const fontWeight = rawWeight ? WEIGHT_ALIASES[rawWeight] ?? rawWeight : preset.fontWeight;
1564
+ const fontStyle = format.italic ? "italic" : "normal";
1565
+ const textDecoration = LINE_DECORATION[format.lineDecoration] || "none";
1566
+ const transformed = applyTextTransform(text, format.textTransform);
1567
+ const lineHeight = (background == null ? void 0 : background.height) ?? preset.lineHeight ?? fontSize;
1568
+ const color = (background == null ? void 0 : background.textColor) ?? colorProp;
1569
+ const paddingX = (background == null ? void 0 : background.canvasPaddingX) ?? paddingXProp;
1570
+ const paddingY = (background == null ? void 0 : background.canvasPaddingY) ?? paddingYProp;
1571
+ const rotate = typeof orientation === "number" ? orientation : ORIENTATION_ROTATION[orientation] ?? 0;
1572
+ const textW = estimateTextWidth(transformed, fontSize);
1573
+ let pillW = 0;
1574
+ let pillH = 0;
1575
+ if (background) {
1576
+ const bgPadX = background.paddingX ?? 6;
1577
+ const bgPadY = background.paddingY ?? 3;
1578
+ pillW = textW + bgPadX * 2;
1579
+ pillH = background.height ?? Math.max(lineHeight, fontSize + bgPadY * 2);
1580
+ }
1581
+ const intrinsicW = (background ? pillW : textW) + paddingX * 2;
1582
+ const intrinsicH = (background ? pillH : lineHeight) + paddingY * 2;
1583
+ const isOrthoRotation = rotate === 90 || rotate === -90 || rotate === 270;
1584
+ const canvasW = widthOverride ?? (isOrthoRotation ? intrinsicH : intrinsicW);
1585
+ const canvasH = heightOverride ?? (isOrthoRotation ? intrinsicW : intrinsicH);
1586
+ const cx = canvasW / 2;
1587
+ const cy = canvasH / 2;
1588
+ const rectX = cx - pillW / 2;
1589
+ const rectY = cy - pillH / 2;
1590
+ const group = (background ? buildBackgroundRect({
1591
+ background,
1592
+ x: rectX,
1593
+ y: rectY,
1594
+ width: pillW,
1595
+ height: pillH
1596
+ }) : "") + `<text x="${cx}" y="${cy}" text-anchor="middle" dominant-baseline="central" font-family="${fontFamily.replace(/"/g, "&quot;")}" font-size="${fontSize}" font-weight="${fontWeight}" font-style="${fontStyle}" text-decoration="${textDecoration}" fill="${color}">${escapeSvgText(transformed)}</text>`;
1597
+ const wrapped = rotate ? `<g transform="rotate(${rotate} ${cx} ${cy})">${group}</g>` : group;
1598
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${canvasW}" height="${canvasH}">` + wrapped + `</svg>`;
1599
+ return {
1600
+ src: `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,
1601
+ width: canvasW,
1602
+ height: canvasH
1603
+ };
1604
+ };
1605
+ var StyledText = ({
1606
+ children,
1607
+ text,
1608
+ alt,
1609
+ variant,
1610
+ format,
1611
+ orientation,
1612
+ color,
1613
+ background,
1614
+ fontFamily,
1615
+ fontSize,
1616
+ paddingX,
1617
+ paddingY,
1618
+ width,
1619
+ height
1620
+ }) => {
1621
+ const resolvedText = text ?? (typeof children === "string" ? children : "");
1622
+ if (canUseNativeTag({
1623
+ background,
1624
+ orientation,
1625
+ color,
1626
+ fontFamily,
1627
+ fontSize,
1628
+ width,
1629
+ height,
1630
+ paddingX,
1631
+ paddingY,
1632
+ format
1633
+ })) {
1634
+ const nativeVariant = NATIVE_TAG_VARIANT_ALIASES[background == null ? void 0 : background.variant] ?? (background == null ? void 0 : background.variant) ?? "default";
1635
+ return import_react12.default.createElement(import_ui_extensions12.Tag, { variant: nativeVariant }, resolvedText);
1636
+ }
1637
+ const { src, width: w, height: h6 } = makeStyledTextDataUri(resolvedText, {
1638
+ variant,
1639
+ format,
1640
+ orientation,
1641
+ color,
1642
+ background,
1643
+ fontFamily,
1644
+ fontSize,
1645
+ paddingX,
1646
+ paddingY,
1647
+ width,
1648
+ height
1649
+ });
1650
+ return import_react12.default.createElement(import_ui_extensions12.Image, {
1651
+ src,
1652
+ width: w,
1653
+ height: h6,
1654
+ alt: alt ?? String(resolvedText)
1655
+ });
1656
+ };
1657
+
1658
+ // src/kanban/Kanban.jsx
1659
+ var import_ui_extensions13 = require("@hubspot/ui-extensions");
1660
+
1661
+ // src/utils/objectPath.js
1662
+ var getByPath = (obj, path) => {
1663
+ if (!path) return void 0;
1664
+ if (typeof path === "function") return path(obj);
1665
+ return String(path).split(".").reduce((acc, key) => acc == null ? void 0 : acc[key], obj);
1666
+ };
1667
+
1668
+ // src/utils/crmSearchAdapters.js
1669
+ var EMPTY_ARRAY = [];
1670
+ var EMPTY_OBJECT = {};
1671
+ var isPlainObject = (value) => value != null && Object.prototype.toString.call(value) === "[object Object]";
1672
+ var coerceError = (error) => {
1673
+ if (!error) return false;
1674
+ if (typeof error === "string") return error;
1675
+ if (error.message) return error.message;
1676
+ return true;
1677
+ };
1678
+ var pickArray = (response) => {
1679
+ if (Array.isArray(response)) return response;
1680
+ if (!response) return EMPTY_ARRAY;
1681
+ return response.results || response.data || response.items || response.records || response.objects || EMPTY_ARRAY;
1682
+ };
1683
+ var pickTotal = (response, fallbackLength) => {
1684
+ var _a;
1685
+ if (!response || Array.isArray(response)) return fallbackLength;
1686
+ return response.total ?? response.totalCount ?? response.totalResults ?? ((_a = response.paging) == null ? void 0 : _a.total) ?? fallbackLength;
1687
+ };
1688
+ var normalizeCrmSearchRecord = (record, options = EMPTY_OBJECT) => {
1689
+ const {
1690
+ idField = "id",
1691
+ objectIdField = "objectId",
1692
+ propertiesKey = "properties",
1693
+ flattenProperties = true,
1694
+ propertyValueKey,
1695
+ mapRecord
1696
+ } = options;
1697
+ if (mapRecord) return mapRecord(record);
1698
+ const objectId = (record == null ? void 0 : record.objectId) ?? (record == null ? void 0 : record.id) ?? (record == null ? void 0 : record.hs_object_id) ?? getByPath(record, `${propertiesKey}.hs_object_id`);
1699
+ const properties = (record == null ? void 0 : record[propertiesKey]) || EMPTY_OBJECT;
1700
+ const flattened = {};
1701
+ if (flattenProperties && isPlainObject(properties)) {
1702
+ for (const [key, value] of Object.entries(properties)) {
1703
+ flattened[key] = propertyValueKey && isPlainObject(value) ? value[propertyValueKey] : value;
1704
+ }
1705
+ }
1706
+ return {
1707
+ ...flattenProperties ? flattened : EMPTY_OBJECT,
1708
+ ...record,
1709
+ [idField]: objectId,
1710
+ [objectIdField]: objectId,
1711
+ [propertiesKey]: properties
1712
+ };
1713
+ };
1714
+ var normalizeCrmSearchRows = (response, options = EMPTY_OBJECT) => {
1715
+ const records = pickArray(response);
1716
+ return records.map((record) => normalizeCrmSearchRecord(record, options));
1717
+ };
1718
+ var STABLE_SORT_TIEBREAKER = { propertyName: "hs_object_id", direction: "ASCENDING" };
1719
+ var withStableSort = (sorts) => {
1720
+ const base = Array.isArray(sorts) ? sorts : [];
1721
+ if (base.some((s) => s && s.propertyName === STABLE_SORT_TIEBREAKER.propertyName)) return base;
1722
+ return [...base, STABLE_SORT_TIEBREAKER];
1723
+ };
1724
+ var buildCrmSearchConfig = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
1725
+ const {
1726
+ objectType,
1727
+ properties = EMPTY_ARRAY,
1728
+ query,
1729
+ filterGroups,
1730
+ sorts,
1731
+ pageLength,
1732
+ propertyMap = EMPTY_OBJECT,
1733
+ filterMap,
1734
+ sortMap,
1735
+ baseConfig = EMPTY_OBJECT
1736
+ } = options;
1737
+ const mappedFilters = filterMap ? filterMap(params.filters || EMPTY_OBJECT, params) : filterGroups;
1738
+ const mappedSorts = sortMap ? sortMap(params.sort || EMPTY_OBJECT, params) : sorts;
1739
+ const config = {
1740
+ ...baseConfig,
1741
+ objectType: objectType || baseConfig.objectType,
1742
+ properties: properties.length ? properties : baseConfig.properties,
1743
+ query: query ?? params.search ?? baseConfig.query,
1744
+ filterGroups: mappedFilters ?? baseConfig.filterGroups,
1745
+ sorts: withStableSort(mappedSorts ?? baseConfig.sorts),
1746
+ pageLength: pageLength ?? params.pageLength ?? baseConfig.pageLength
1747
+ };
1748
+ if (!filterMap && propertyMap && Object.keys(propertyMap).length && params.filters) {
1749
+ const filters = Object.entries(params.filters).filter(([, value]) => value !== void 0 && value !== null && value !== "" && !(Array.isArray(value) && value.length === 0)).map(([name, value]) => {
1750
+ const propertyName = propertyMap[name] || name;
1751
+ if (Array.isArray(value)) return { propertyName, operator: "IN", values: value };
1752
+ if (isPlainObject(value) && (value.from || value.to)) {
1753
+ const rangeFilters = [];
1754
+ if (value.from) rangeFilters.push({ propertyName, operator: "GTE", value: value.from });
1755
+ if (value.to) rangeFilters.push({ propertyName, operator: "LTE", value: value.to });
1756
+ return rangeFilters;
1757
+ }
1758
+ return { propertyName, operator: "EQ", value };
1759
+ }).flat();
1760
+ if (filters.length) config.filterGroups = [{ filters }];
1761
+ }
1762
+ Object.keys(config).forEach((key) => config[key] === void 0 && delete config[key]);
1763
+ return config;
1764
+ };
1765
+ var useCrmSearchDataSource = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
1766
+ const {
1767
+ format,
1768
+ row,
1769
+ rowIdField = "id",
1770
+ totalCount,
1771
+ loading,
1772
+ error,
1773
+ mapResponse
1774
+ } = options;
1775
+ const config = (0, import_react14.useMemo)(() => buildCrmSearchConfig(params, options), [params, options]);
1776
+ const response = (0, import_ui_extensions14.useCrmSearch)(config, format);
1777
+ return (0, import_react14.useMemo)(() => {
1778
+ var _a;
1779
+ const rows = mapResponse ? mapResponse(response) : normalizeCrmSearchRows(response, { idField: rowIdField, ...row || EMPTY_OBJECT });
1780
+ const resolvedTotal = typeof totalCount === "function" ? totalCount(response) : totalCount ?? pickTotal(response, rows.length);
1781
+ return {
1782
+ data: rows,
1783
+ rows,
1784
+ response,
1785
+ pagination: response == null ? void 0 : response.pagination,
1786
+ hasMore: ((_a = response == null ? void 0 : response.pagination) == null ? void 0 : _a.hasNextPage) ?? (response == null ? void 0 : response.hasMore) ?? false,
1787
+ loading: typeof loading === "function" ? loading(response) : loading ?? !!(response == null ? void 0 : response.isLoading),
1788
+ isLoading: typeof loading === "function" ? loading(response) : loading ?? !!(response == null ? void 0 : response.isLoading),
1789
+ error: typeof error === "function" ? error(response) : error ?? coerceError(response == null ? void 0 : response.error),
1790
+ totalCount: resolvedTotal,
1791
+ rowIdField
1792
+ };
1793
+ }, [response, mapResponse, row, rowIdField, totalCount, loading, error]);
1794
+ };
1795
+ var crmSearchResultToOption = (row, options = EMPTY_OBJECT) => {
1796
+ const {
1797
+ label = "name",
1798
+ value = "objectId",
1799
+ description,
1800
+ fallbackLabel = "Untitled record",
1801
+ mapOption
1802
+ } = options;
1803
+ if (mapOption) return mapOption(row);
1804
+ const option = {
1805
+ label: getByPath(row, label) ?? getByPath(row, "properties.name") ?? fallbackLabel,
1806
+ value: getByPath(row, value) ?? getByPath(row, "id") ?? getByPath(row, "objectId")
1807
+ };
1808
+ const desc = getByPath(row, description);
1809
+ if (desc != null && desc !== "") option.description = desc;
1810
+ return option;
1811
+ };
1812
+ var useCrmSearchOptions = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
1813
+ const dataSource = useCrmSearchDataSource(params, options);
1814
+ const optionConfig = options.option || options;
1815
+ return (0, import_react14.useMemo)(() => ({
1816
+ ...dataSource,
1817
+ options: dataSource.rows.map((row) => crmSearchResultToOption(row, optionConfig))
1818
+ }), [dataSource, optionConfig]);
1496
1819
  };
1497
- var escapeXmlAttr2 = (s) => String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1498
- var PLUS_24 = "M11 5h2v6h6v2h-6v6h-2v-6H5v-2h6z";
1499
- var CURATED_ICONS = {
1500
- checkBare: { paths: ["M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z"] },
1501
- plusBare: { paths: [PLUS_24] },
1502
- // Close = a plus rotated 45° → a clean X (reuses transform support, no new
1503
- // path data to hand-author/get-wrong).
1504
- Close: { paths: [PLUS_24], transform: "rotate(45 12 12)" },
1505
- // Universal media-transport glyphs, authored at 32×32 to match the scraped
1506
- // canvas so they sit at the same optical size as the rest of the set.
1507
- Play: { viewBox: "0 0 32 32", paths: ["M9 6v20l17-10z"] },
1508
- Pause: { viewBox: "0 0 32 32", paths: ["M10 6h4v20h-4z M18 6h4v20h-4z"] },
1509
- Stop: { viewBox: "0 0 32 32", paths: ["M8 8h16v16H8z"] },
1510
- // Hand-authored to match HubSpot's filled/rounded weight (verified visually
1511
- // against the scraped set) for genuine gaps the scrape didn't cover.
1512
- Unlocked: {
1513
- viewBox: "0 0 32 32",
1514
- paths: [
1515
- "M9.5 14v-3.4c0-3.57 2.9-6.46 6.46-6.46 2.66 0 5.02 1.64 5.97 4.07a1.6 1.6 0 1 1-2.98 1.16 3.23 3.23 0 0 0-2.99-2.03c-1.78 0-3.23 1.45-3.23 3.23V14z",
1516
- { d: "M6.3 14h19.4a3.2 3.2 0 0 1 3.2 3.2v8.6a3.2 3.2 0 0 1-3.2 3.2H6.3a3.2 3.2 0 0 1-3.2-3.2v-8.6A3.2 3.2 0 0 1 6.3 14Zm9.7 4.4a2.1 2.1 0 0 0-1.05 3.92V25a1.05 1.05 0 0 0 2.1 0v-2.68A2.1 2.1 0 0 0 16 18.4Z", fillRule: "evenodd" }
1517
- ]
1518
- },
1519
- Print: {
1520
- viewBox: "0 0 32 32",
1521
- paths: [
1522
- "M9 4h14a1 1 0 0 1 1 1v6H8V5a1 1 0 0 1 1-1z",
1523
- { d: "M5 12h22a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-3v-3H8v3H5a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2zm18 3.2a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4z", fillRule: "evenodd" },
1524
- "M10 21h12v6a1 1 0 0 1-1 1H11a1 1 0 0 1-1-1z"
1525
- ]
1526
- },
1527
- Shrink: {
1528
- viewBox: "0 0 32 32",
1529
- paths: [
1530
- "M14.05 4.1c1.08 0 1.95.87 1.95 1.95v8a1.95 1.95 0 0 1-1.95 1.95h-8a1.95 1.95 0 1 1 0-3.9h3.28L3.17 5.93A1.95 1.95 0 0 1 5.93 3.17l6.17 6.17V6.05c0-1.08.87-1.95 1.95-1.95Z",
1531
- "M17.95 27.9a1.95 1.95 0 0 1-1.95-1.95v-8a1.95 1.95 0 0 1 1.95-1.95h8a1.95 1.95 0 1 1 0 3.9h-3.28l6.18 6.17a1.95 1.95 0 0 1-2.76 2.76l-6.17-6.17v3.29c0 1.08-.87 1.95-1.95 1.95Z"
1532
- ]
1533
- },
1534
- Heart: { viewBox: "0 0 32 32", paths: ["M16 26.5C9 21 5 17 5 12.4 5 9.1 7.6 6.5 11 6.5c2 0 3.8 1 5 2.6 1.2-1.6 3-2.6 5-2.6 3.4 0 6 2.6 6 5.9 0 4.6-4 8.6-11 14.1Z"] },
1535
- CircleFilled: { viewBox: "0 0 32 32", paths: ["M16 2C8.27 2 2 8.27 2 16s6.27 14 14 14 14-6.27 14-14S23.73 2 16 2z"] },
1536
- // Dark = crescent moon, pairs with the scraped Light (sun) for theme toggles.
1537
- Dark: { viewBox: "0 0 32 32", paths: ["M19 3.5A12 12 0 1 0 28.5 19 9.6 9.6 0 0 1 19 3.5Z"] },
1538
- Export: { viewBox: "0 0 32 32", paths: ["M16 3l5 5h-3.5v7h-3V8H11z", "M6 16h4v6h12v-6h4v8a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2z"] },
1539
- Import: { viewBox: "0 0 32 32", paths: ["M16 16l5-5h-3.5V4h-3v7H11z", "M6 17h4v5h12v-5h4v7a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2z"] },
1540
- // Backward = horizontal mirror of the scraped Forward arrow (x' = 32 - x).
1541
- // Derived so it stays in sync if Forward is ever re-scraped.
1542
- ...GENERATED_ICONS.Forward ? { Backward: { ...GENERATED_ICONS.Forward, transform: "translate(32 0) scale(-1 1)" } } : {}
1820
+ var CRM_OBJECT_TYPES = {
1821
+ contact: "0-1",
1822
+ contacts: "0-1",
1823
+ company: "0-2",
1824
+ companies: "0-2",
1825
+ deal: "0-3",
1826
+ deals: "0-3"
1543
1827
  };
1544
- var ICONS = { ...GENERATED_ICONS, ...CURATED_ICONS };
1545
- var svgToIconEntry = (raw) => {
1546
- const vb = /viewBox="([^"]+)"/i.exec(raw);
1547
- const viewBox = vb ? vb[1] : "0 0 24 24";
1548
- const rendered = raw.replace(
1549
- /<(mask|defs|clipPath|symbol)\b[^>]*>[\s\S]*?<\/\1>/gi,
1550
- ""
1828
+ var resolveCrmObjectType = (objectType) => CRM_OBJECT_TYPES[objectType] || objectType;
1829
+
1830
+ // src/common-components/CrmLookupSelect.js
1831
+ var EMPTY_ARRAY2 = [];
1832
+ var EMPTY_OBJECT2 = {};
1833
+ var makeOptionConfig = ({ option, labelProperty, valueProperty, descriptionProperty }) => ({
1834
+ label: labelProperty || (option == null ? void 0 : option.label) || "name",
1835
+ value: valueProperty || (option == null ? void 0 : option.value) || "objectId",
1836
+ description: descriptionProperty || (option == null ? void 0 : option.description),
1837
+ fallbackLabel: option == null ? void 0 : option.fallbackLabel,
1838
+ mapOption: option == null ? void 0 : option.mapOption
1839
+ });
1840
+ var mergeSelectedOptions = (options, selectedOptions, value) => {
1841
+ const selected = Array.isArray(selectedOptions) ? selectedOptions : selectedOptions ? [selectedOptions] : EMPTY_ARRAY2;
1842
+ if (!selected.length) return options;
1843
+ const values = new Set(options.map((opt) => opt.value));
1844
+ const activeValues = new Set(Array.isArray(value) ? value : value == null || value === "" ? [] : [value]);
1845
+ const missing = selected.filter((opt) => activeValues.has(opt.value) && !values.has(opt.value));
1846
+ return missing.length ? [...missing, ...options] : options;
1847
+ };
1848
+ var CrmLookupSelect = ({
1849
+ objectType,
1850
+ properties = EMPTY_ARRAY2,
1851
+ name,
1852
+ label,
1853
+ value,
1854
+ onChange,
1855
+ multiple = false,
1856
+ placeholder,
1857
+ description,
1858
+ tooltip,
1859
+ required,
1860
+ readOnly,
1861
+ error,
1862
+ validationMessage,
1863
+ variant,
1864
+ debounce = 300,
1865
+ minSearchLength = 0,
1866
+ pageLength = 20,
1867
+ option,
1868
+ labelProperty,
1869
+ valueProperty = "objectId",
1870
+ descriptionProperty,
1871
+ selectedOptions,
1872
+ format,
1873
+ row,
1874
+ baseConfig,
1875
+ query,
1876
+ onSearchChange,
1877
+ noResultsOption,
1878
+ loadingOption,
1879
+ selectProps = EMPTY_OBJECT2
1880
+ }) => {
1881
+ const [inputValue, setInputValue] = (0, import_react15.useState)(query || "");
1882
+ const [pickedOptions, setPickedOptions] = (0, import_react15.useState)(EMPTY_ARRAY2);
1883
+ const debouncedInput = (0, import_ui_extensions15.useDebounce)(inputValue, debounce > 0 ? debounce : 1);
1884
+ const search = debounce > 0 ? debouncedInput : inputValue;
1885
+ const effectiveSearch = search && search.length >= minSearchLength ? search : "";
1886
+ const optionConfig = (0, import_react15.useMemo)(
1887
+ () => makeOptionConfig({ option, labelProperty, valueProperty, descriptionProperty }),
1888
+ [option, labelProperty, valueProperty, descriptionProperty]
1551
1889
  );
1552
- const paths = [];
1553
- const pathRe = /<path\b([^>]*?)\/?>/gi;
1554
- let m;
1555
- while ((m = pathRe.exec(rendered)) !== null) {
1556
- const attrs = m[1];
1557
- const d = /\bd="([^"]+)"/i.exec(attrs);
1558
- if (!d) continue;
1559
- const fill = /\bfill="([^"]+)"/i.exec(attrs);
1560
- const fillRule = /\bfill-rule="([^"]+)"/i.exec(attrs);
1561
- const keepFill = fill && fill[1] !== "currentColor" && fill[1] !== "none";
1562
- if (keepFill || fillRule) {
1563
- paths.push({
1564
- d: d[1],
1565
- ...keepFill ? { fill: fill[1] } : {},
1566
- ...fillRule ? { fillRule: fillRule[1] } : {}
1890
+ const dataSource = useCrmSearchOptions(
1891
+ { search: effectiveSearch },
1892
+ {
1893
+ objectType: resolveCrmObjectType(objectType),
1894
+ properties,
1895
+ pageLength,
1896
+ format,
1897
+ baseConfig,
1898
+ row: row || { mapRecord: (record) => ({ objectId: record.objectId, ...record.properties }) },
1899
+ option: optionConfig
1900
+ }
1901
+ );
1902
+ const isSearching = dataSource.loading || inputValue.trim() !== (search || "").trim();
1903
+ const hasQuery = effectiveSearch.length > 0;
1904
+ const options = (0, import_react15.useMemo)(() => {
1905
+ const remembered = [...selectedOptions || EMPTY_ARRAY2, ...pickedOptions];
1906
+ const baseOptions = mergeSelectedOptions(dataSource.options || EMPTY_ARRAY2, remembered, value);
1907
+ if (isSearching && loadingOption) return [loadingOption, ...baseOptions];
1908
+ if (!isSearching && hasQuery && !baseOptions.length && noResultsOption) return [noResultsOption];
1909
+ return baseOptions;
1910
+ }, [dataSource.options, isSearching, hasQuery, selectedOptions, pickedOptions, value, loadingOption, noResultsOption]);
1911
+ const handleChange = (next) => {
1912
+ const nextValues = Array.isArray(next) ? next : next == null || next === "" ? EMPTY_ARRAY2 : [next];
1913
+ const picked = nextValues.map((v) => options.find((o) => o.value === v)).filter((o) => o && o.value !== (loadingOption == null ? void 0 : loadingOption.value) && o.value !== (noResultsOption == null ? void 0 : noResultsOption.value));
1914
+ if (picked.length) {
1915
+ setPickedOptions((prev) => {
1916
+ const byValue = new Map(prev.map((o) => [o.value, o]));
1917
+ picked.forEach((o) => byValue.set(o.value, o));
1918
+ return [...byValue.values()];
1567
1919
  });
1568
- } else {
1569
- paths.push(d[1]);
1570
1920
  }
1571
- }
1572
- return { viewBox, paths };
1573
- };
1574
- var canUseNative = (name, color, size) => NATIVE_ICON_NAMES.has(name) && (color == null || NATIVE_COLORS.has(color)) && (size == null || typeof size === "string" && NATIVE_SIZE_TOKENS[size] != null);
1575
- var makeIconDataUri = (nameOrEntry, opts = {}) => {
1576
- const entry = nameOrEntry && typeof nameOrEntry === "object" ? nameOrEntry : ICONS[nameOrEntry];
1577
- if (!entry || !entry.paths) return null;
1578
- const size = resolveSize2(opts.size);
1579
- const rawColor = opts.color;
1580
- const color = rawColor && SEMANTIC_HEX[rawColor] || rawColor || HS_TEXT_COLOR;
1581
- const viewBox = entry.viewBox ?? "0 0 24 24";
1582
- const body = entry.paths.map((p) => {
1583
- const d = typeof p === "string" ? p : p.d;
1584
- const fill = typeof p === "object" && p.fill || color;
1585
- const fillRule = typeof p === "object" && p.fillRule ? ` fill-rule="${p.fillRule}"` : "";
1586
- return `<path d="${escapeXmlAttr2(d)}" fill="${fill}"${fillRule} />`;
1587
- }).join("");
1588
- const inner = entry.transform ? `<g transform="${escapeXmlAttr2(entry.transform)}">${body}</g>` : body;
1589
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="${viewBox}">` + inner + `</svg>`;
1590
- return {
1591
- src: `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,
1592
- width: size,
1593
- height: size
1921
+ if (onChange) onChange(next);
1922
+ };
1923
+ const commonProps = {
1924
+ name,
1925
+ label,
1926
+ value: multiple ? value || EMPTY_ARRAY2 : value,
1927
+ options,
1928
+ placeholder: placeholder || (dataSource.loading ? "Searching CRM..." : "Search CRM records..."),
1929
+ description,
1930
+ tooltip,
1931
+ required,
1932
+ readOnly,
1933
+ error: error || !!dataSource.error,
1934
+ validationMessage: validationMessage || (typeof dataSource.error === "string" ? dataSource.error : void 0),
1935
+ variant,
1936
+ onChange: handleChange,
1937
+ onInput: (next) => {
1938
+ setInputValue(next || "");
1939
+ if (onSearchChange) onSearchChange(next || "");
1940
+ },
1941
+ ...selectProps
1594
1942
  };
1943
+ return import_react15.default.createElement(multiple ? import_ui_extensions15.MultiSelect : import_ui_extensions15.Select, commonProps);
1595
1944
  };
1596
- var ICON_NAMES = Object.keys(ICONS);
1597
- var NATIVE_ICON_NAME_LIST = [...NATIVE_ICON_NAMES].sort(
1598
- (a, b) => a.localeCompare(b)
1599
- );
1600
- var Icon3 = ({ name, color, size, screenReaderText }) => {
1601
- if (canUseNative(name, color, size)) {
1602
- return import_react10.default.createElement(import_ui_extensions10.Icon, {
1603
- name,
1604
- ...color != null ? { color } : {},
1605
- ...size != null ? { size: NATIVE_SIZE_TOKENS[size] } : {},
1606
- ...screenReaderText != null ? { screenReaderText } : {}
1607
- });
1608
- }
1609
- const result = makeIconDataUri(name, { size, color });
1610
- if (!result) return null;
1611
- return import_react10.default.createElement(import_ui_extensions10.Image, {
1612
- src: result.src,
1613
- width: result.width,
1614
- height: result.height,
1615
- alt: screenReaderText ?? name
1616
- });
1945
+
1946
+ // src/common-components/datePresets.js
1947
+ var HS_DATE_PRESETS = [
1948
+ { label: "Today", value: "today" },
1949
+ { label: "Yesterday", value: "yesterday" },
1950
+ { label: "Tomorrow", value: "tomorrow" },
1951
+ { label: "This week", value: "this_week" },
1952
+ { label: "Last week", value: "last_week" },
1953
+ { label: "Last 7 days", value: "7d" },
1954
+ { label: "Last 30 days", value: "30d" },
1955
+ { label: "Last 90 days", value: "90d" },
1956
+ { label: "This month", value: "this_month" },
1957
+ { label: "Last month", value: "last_month" },
1958
+ { label: "This quarter", value: "this_quarter" },
1959
+ { label: "Last quarter", value: "last_quarter" },
1960
+ { label: "This year", value: "this_year" },
1961
+ { label: "Last year", value: "last_year" }
1962
+ ];
1963
+ var HS_DATE_DIRECTION_LABELS = {
1964
+ asc: "Ascending",
1965
+ desc: "Descending"
1617
1966
  };
1618
1967
 
1619
1968
  // src/common-components/KeyValueList.js
1620
- var import_react11 = __toESM(require("react"));
1621
- var import_ui_extensions11 = require("@hubspot/ui-extensions");
1969
+ var import_react16 = __toESM(require("react"));
1970
+ var import_ui_extensions16 = require("@hubspot/ui-extensions");
1622
1971
  var KeyValueList = ({ items = [], direction = "row", gap = "sm" }) => {
1623
1972
  const rows = items.map(
1624
- (item, index) => import_react11.default.createElement(
1625
- import_ui_extensions11.DescriptionListItem,
1973
+ (item, index) => import_react16.default.createElement(
1974
+ import_ui_extensions16.DescriptionListItem,
1626
1975
  {
1627
1976
  key: item.key ?? item.label ?? `kv-${index}`,
1628
1977
  label: item.label
@@ -1630,16 +1979,16 @@ var KeyValueList = ({ items = [], direction = "row", gap = "sm" }) => {
1630
1979
  item.value
1631
1980
  )
1632
1981
  );
1633
- return import_react11.default.createElement(
1634
- import_ui_extensions11.Flex,
1982
+ return import_react16.default.createElement(
1983
+ import_ui_extensions16.Flex,
1635
1984
  { direction: "column", gap },
1636
- import_react11.default.createElement(import_ui_extensions11.DescriptionList, { direction }, ...rows)
1985
+ import_react16.default.createElement(import_ui_extensions16.DescriptionList, { direction }, ...rows)
1637
1986
  );
1638
1987
  };
1639
1988
 
1640
1989
  // src/common-components/SectionHeader.js
1641
- var import_react12 = __toESM(require("react"));
1642
- var import_ui_extensions12 = require("@hubspot/ui-extensions");
1990
+ var import_react17 = __toESM(require("react"));
1991
+ var import_ui_extensions17 = require("@hubspot/ui-extensions");
1643
1992
  var SectionHeader = ({
1644
1993
  title,
1645
1994
  description,
@@ -1650,12 +1999,12 @@ var SectionHeader = ({
1650
1999
  }) => {
1651
2000
  const body = [];
1652
2001
  if (title != null) {
1653
- body.push(import_react12.default.createElement(import_ui_extensions12.Heading, { key: "title", as: titleAs }, title));
2002
+ body.push(import_react17.default.createElement(import_ui_extensions17.Heading, { key: "title", as: titleAs }, title));
1654
2003
  }
1655
2004
  if (description != null) {
1656
2005
  body.push(
1657
- import_react12.default.createElement(
1658
- import_ui_extensions12.Text,
2006
+ import_react17.default.createElement(
2007
+ import_ui_extensions17.Text,
1659
2008
  { key: "description", variant: "microcopy" },
1660
2009
  description
1661
2010
  )
@@ -1664,10 +2013,10 @@ var SectionHeader = ({
1664
2013
  if (children != null) {
1665
2014
  body.push(children);
1666
2015
  }
1667
- const content = import_react12.default.createElement(import_ui_extensions12.Flex, { direction: "column", gap }, ...body);
2016
+ const content = import_react17.default.createElement(import_ui_extensions17.Flex, { direction: "column", gap }, ...body);
1668
2017
  if (actions == null) return content;
1669
- return import_react12.default.createElement(
1670
- import_ui_extensions12.Flex,
2018
+ return import_react17.default.createElement(
2019
+ import_ui_extensions17.Flex,
1671
2020
  { direction: "row", justify: "between", align: "start", gap: "sm" },
1672
2021
  content,
1673
2022
  actions
@@ -1675,8 +2024,8 @@ var SectionHeader = ({
1675
2024
  };
1676
2025
 
1677
2026
  // src/common-components/Spinner.js
1678
- var import_react13 = __toESM(require("react"));
1679
- var import_ui_extensions13 = require("@hubspot/ui-extensions");
2027
+ var import_react18 = __toESM(require("react"));
2028
+ var import_ui_extensions18 = require("@hubspot/ui-extensions");
1680
2029
 
1681
2030
  // src/common-components/spinners.js
1682
2031
  var BRAILLE_DOT_MAP = [
@@ -2016,10 +2365,10 @@ var Spinner = ({
2016
2365
  const preset = SPINNERS[name] || SPINNERS[DEFAULT_NAME];
2017
2366
  const resolvedFrames = Array.isArray(frames) && frames.length > 0 ? frames : preset.frames;
2018
2367
  const resolvedInterval = Number.isFinite(interval) ? interval : preset.interval;
2019
- const [index, setIndex] = (0, import_react13.useState)(0);
2020
- const indexRef = (0, import_react13.useRef)(0);
2368
+ const [index, setIndex] = (0, import_react18.useState)(0);
2369
+ const indexRef = (0, import_react18.useRef)(0);
2021
2370
  indexRef.current = index;
2022
- (0, import_react13.useEffect)(() => {
2371
+ (0, import_react18.useEffect)(() => {
2023
2372
  if (paused || resolvedFrames.length <= 1) return void 0;
2024
2373
  const id = setInterval(() => {
2025
2374
  indexRef.current = (indexRef.current + 1) % resolvedFrames.length;
@@ -2027,17 +2376,17 @@ var Spinner = ({
2027
2376
  }, Math.max(16, resolvedInterval));
2028
2377
  return () => clearInterval(id);
2029
2378
  }, [paused, resolvedFrames, resolvedInterval]);
2030
- (0, import_react13.useEffect)(() => {
2379
+ (0, import_react18.useEffect)(() => {
2031
2380
  indexRef.current = 0;
2032
2381
  setIndex(0);
2033
2382
  }, [resolvedFrames]);
2034
2383
  const frame = resolvedFrames[index % resolvedFrames.length];
2035
2384
  const suffix = children != null ? children : label;
2036
2385
  if (suffix == null || suffix === "") {
2037
- return import_react13.default.createElement(import_ui_extensions13.Text, rest, frame);
2386
+ return import_react18.default.createElement(import_ui_extensions18.Text, rest, frame);
2038
2387
  }
2039
- return import_react13.default.createElement(
2040
- import_ui_extensions13.Text,
2388
+ return import_react18.default.createElement(
2389
+ import_ui_extensions18.Text,
2041
2390
  rest,
2042
2391
  frame,
2043
2392
  gap,
@@ -2046,9 +2395,14 @@ var Spinner = ({
2046
2395
  };
2047
2396
  // Annotate the CommonJS export names for ESM import in node:
2048
2397
  0 && (module.exports = {
2398
+ ActiveFilterChips,
2049
2399
  AutoStatusTag,
2050
2400
  AutoTag,
2051
2401
  AvatarStack,
2402
+ CollectionCount,
2403
+ CollectionFilterControl,
2404
+ CollectionSortSelect,
2405
+ CollectionToolbar,
2052
2406
  CrmLookupSelect,
2053
2407
  DEFAULT_SVG_FONT_WEIGHT,
2054
2408
  HS_DATE_DIRECTION_LABELS,
@@ -2076,6 +2430,7 @@ var Spinner = ({
2076
2430
  SectionHeader,
2077
2431
  Spinner,
2078
2432
  StyledText,
2433
+ formatCollectionCount,
2079
2434
  gridToBraille,
2080
2435
  makeAvatarStackDataUri,
2081
2436
  makeGrid,