hs-uix 2.0.1 → 2.1.1

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.
@@ -198,8 +198,46 @@ var AutoStatusTag = ({
198
198
  );
199
199
  };
200
200
 
201
- // src/common-components/AvatarStack.js
201
+ // src/common-components/ActiveFilterChips.js
202
202
  import React3 from "react";
203
+ import { Button, Flex, Tag as Tag2 } from "@hubspot/ui-extensions";
204
+ var h = React3.createElement;
205
+ var ActiveFilterChips = ({
206
+ chips = [],
207
+ showBadges = true,
208
+ showClearAll = true,
209
+ clearAllLabel = "Clear all",
210
+ onRemove,
211
+ gap = "sm"
212
+ }) => {
213
+ if (!Array.isArray(chips) || chips.length === 0) return null;
214
+ if (!showBadges && !showClearAll) return null;
215
+ return h(
216
+ Flex,
217
+ { direction: "row", align: "center", gap, wrap: "wrap" },
218
+ ...showBadges ? chips.map((chip) => h(
219
+ Tag2,
220
+ {
221
+ key: chip.key,
222
+ variant: "default",
223
+ onDelete: () => onRemove == null ? void 0 : onRemove(chip.key)
224
+ },
225
+ chip.label
226
+ )) : [],
227
+ showClearAll ? h(
228
+ Button,
229
+ {
230
+ variant: "transparent",
231
+ size: "extra-small",
232
+ onClick: () => onRemove == null ? void 0 : onRemove("all")
233
+ },
234
+ clearAllLabel
235
+ ) : null
236
+ );
237
+ };
238
+
239
+ // src/common-components/AvatarStack.js
240
+ import React4 from "react";
203
241
  import { Image } from "@hubspot/ui-extensions";
204
242
 
205
243
  // src/common-components/svgDefaults.js
@@ -345,7 +383,7 @@ var AvatarStack = ({
345
383
  fontFamily
346
384
  });
347
385
  if (!stack) return null;
348
- return React3.createElement(Image, {
386
+ return React4.createElement(Image, {
349
387
  src: stack.src,
350
388
  width: stack.width,
351
389
  height: stack.height,
@@ -354,15 +392,15 @@ var AvatarStack = ({
354
392
  };
355
393
 
356
394
  // src/common-components/CrmLookupSelect.js
357
- import React8, { useMemo as useMemo4, useState as useState4 } from "react";
358
- import { MultiSelect as MultiSelect3, Select as Select3, useDebounce as useDebounce2 } from "@hubspot/ui-extensions";
395
+ import React14, { useMemo as useMemo4, useState as useState5 } from "react";
396
+ import { MultiSelect as MultiSelect3, Select as Select5, useDebounce as useDebounce2 } from "@hubspot/ui-extensions";
359
397
 
360
398
  // src/utils/crmSearchAdapters.js
361
- import React7, { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState3 } from "react";
362
- import { useCrmSearch, Flex as Flex3, Text as Text3 } from "@hubspot/ui-extensions";
399
+ import React13, { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
400
+ import { useCrmSearch, Flex as Flex6, Text as Text4 } from "@hubspot/ui-extensions";
363
401
 
364
- // packages/datatable/src/DataTable.jsx
365
- import React4, { useState, useMemo, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from "react";
402
+ // src/datatable/DataTable.jsx
403
+ import React9, { useState as useState2, useMemo, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from "react";
366
404
 
367
405
  // src/utils/query.js
368
406
  import Fuse from "fuse.js";
@@ -371,640 +409,61 @@ import Fuse from "fuse.js";
371
409
  import { useRef, useEffect, useCallback } from "react";
372
410
  import { useDebounce } from "@hubspot/ui-extensions";
373
411
 
374
- // packages/datatable/src/DataTable.jsx
412
+ // src/common-components/CollectionCount.js
413
+ import React5 from "react";
414
+ import { Text } from "@hubspot/ui-extensions";
415
+ var h2 = React5.createElement;
416
+ var resolveLabel = (label, count) => {
417
+ if (typeof label === "function") return label(count);
418
+ if (label && typeof label === "object") return count === 1 ? label.singular : label.plural;
419
+ return label || "items";
420
+ };
421
+ var formatCollectionCount = ({ shown, total, label = "items", formatter }) => {
422
+ const resolvedShown = Number(shown ?? total ?? 0);
423
+ const resolvedTotal = Number(total ?? resolvedShown);
424
+ if (typeof formatter === "function") return formatter(resolvedShown, resolvedTotal);
425
+ const resolvedLabel = resolveLabel(label, resolvedTotal);
426
+ return resolvedShown === resolvedTotal ? `${resolvedTotal} ${resolvedLabel}` : `${resolvedShown} of ${resolvedTotal} ${resolvedLabel}`;
427
+ };
428
+ var CollectionCount = ({
429
+ shown,
430
+ total,
431
+ label,
432
+ text,
433
+ formatter,
434
+ bold = false,
435
+ variant = "microcopy",
436
+ format
437
+ }) => h2(
438
+ Text,
439
+ {
440
+ variant,
441
+ format: format || (bold ? { fontWeight: "bold" } : void 0)
442
+ },
443
+ text ?? formatCollectionCount({ shown, total, label, formatter })
444
+ );
445
+
446
+ // src/common-components/CollectionToolbar.js
447
+ import React8, { useId, useState } from "react";
375
448
  import {
376
449
  Box,
377
- Button,
378
- Checkbox,
379
- CurrencyInput,
450
+ Button as Button2,
451
+ Flex as Flex3,
452
+ SearchInput
453
+ } from "@hubspot/ui-extensions";
454
+
455
+ // src/common-components/CollectionFilterControl.js
456
+ import React7 from "react";
457
+ import {
380
458
  DateInput,
381
- EmptyState,
382
- ErrorState,
383
- Flex,
384
- Icon,
385
- Input,
386
- Link,
459
+ Flex as Flex2,
387
460
  MultiSelect,
388
- NumberInput,
389
- SearchInput,
390
- Select,
391
- StepperInput,
392
- Table,
393
- TableBody,
394
- TableCell,
395
- TableFooter,
396
- TableHead,
397
- TableHeader,
398
- TableRow,
399
- Tag as Tag2,
400
- Tile,
401
- Text,
402
- TextArea,
403
- TimeInput,
404
- Toggle,
405
- Tooltip
461
+ Select
406
462
  } from "@hubspot/ui-extensions";
407
463
 
408
- // packages/kanban/src/Kanban.jsx
409
- import React6, { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState2 } from "react";
410
-
411
- // src/common-components/StyledText.js
412
- import React5 from "react";
413
- import { Image as Image2, Tag as Tag3 } from "@hubspot/ui-extensions";
414
- var VARIANT_PRESETS = {
415
- bodytext: { fontSize: 14, lineHeight: 24, fontWeight: 400 },
416
- microcopy: { fontSize: 12, lineHeight: 18, fontWeight: 400 }
417
- };
418
- var WEIGHT_ALIASES = {
419
- bold: 700,
420
- demibold: 600,
421
- regular: 400
422
- };
423
- var LINE_DECORATION = {
424
- strikethrough: "line-through",
425
- underline: "underline"
426
- };
427
- var ORIENTATION_ROTATION = {
428
- horizontal: 0,
429
- "vertical-up": -90,
430
- "vertical-down": 90
431
- };
432
- var BACKGROUND_PRESETS = {
433
- tag: {
434
- color: HS_SUBTLE_BG,
435
- borderColor: HS_TAG_SUBTLE_BORDER,
436
- borderWidth: HS_TAG_BORDER_WIDTH,
437
- radius: HS_TAG_BORDER_RADIUS,
438
- paddingX: HS_TAG_PADDING_X,
439
- paddingY: HS_TAG_PADDING_Y,
440
- height: HS_TAG_LINE_HEIGHT,
441
- textColor: HS_TAG_TEXT_COLOR,
442
- fontSize: HS_TAG_FONT_SIZE,
443
- canvasPaddingX: 0,
444
- canvasPaddingY: 0
445
- }
446
- };
447
- var TAG_VARIANTS = {
448
- default: {
449
- color: HS_SUBTLE_BG,
450
- borderColor: HS_TAG_SUBTLE_BORDER,
451
- textColor: HS_TAG_TEXT_COLOR
452
- },
453
- success: {
454
- color: "#E5F8F6",
455
- borderColor: "#00BDA5",
456
- textColor: "#00BDA5"
457
- },
458
- warning: {
459
- color: "#FEF8F0",
460
- borderColor: "#F5C26B",
461
- textColor: "#D39913"
462
- },
463
- error: {
464
- color: "#FDEDEE",
465
- borderColor: "#F2545B",
466
- textColor: "#F2545B"
467
- },
468
- danger: {
469
- color: "#FDEDEE",
470
- borderColor: "#F2545B",
471
- textColor: "#F2545B"
472
- },
473
- info: {
474
- color: "#E5F5F8",
475
- borderColor: "#00A4BD",
476
- textColor: "#00A4BD"
477
- }
478
- };
479
- var NATIVE_TAG_VARIANT_ALIASES = {
480
- danger: "error"
481
- };
482
- var escapeSvgText = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
483
- var applyTextTransform = (text, transform) => {
484
- if (!transform || transform === "none") return String(text);
485
- const s = String(text);
486
- switch (transform) {
487
- case "uppercase":
488
- return s.toUpperCase();
489
- case "lowercase":
490
- return s.toLowerCase();
491
- case "capitalize":
492
- return s.replace(/\b\w/g, (c) => c.toUpperCase());
493
- case "sentenceCase":
494
- return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
495
- default:
496
- return s;
497
- }
498
- };
499
- var estimateTextWidth = (text, fontSize) => Math.max(fontSize, Math.round(String(text).length * fontSize * 0.58));
500
- var resolveBackground = (background) => {
501
- if (!background) return null;
502
- const preset = background.preset ? BACKGROUND_PRESETS[background.preset] : null;
503
- const variant = background.preset === "tag" && background.variant ? TAG_VARIANTS[background.variant] || null : null;
504
- return {
505
- ...preset || {},
506
- ...variant || {},
507
- ...background
508
- };
509
- };
510
- var buildBackgroundRect = ({ background, x, y, width, height }) => {
511
- const radius = (background == null ? void 0 : background.radius) ?? 3;
512
- const fill = (background == null ? void 0 : background.color) ?? "transparent";
513
- const borderWidth = (background == null ? void 0 : background.borderWidth) ?? 0;
514
- const borderColor = background == null ? void 0 : background.borderColor;
515
- if (!borderColor || borderWidth <= 0) {
516
- return `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" />`;
517
- }
518
- const isTagPreset = (background == null ? void 0 : background.preset) === "tag";
519
- const fillRect = `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" />`;
520
- const strokeInset = borderWidth / 2;
521
- const strokeX = x + strokeInset;
522
- const strokeY = y + strokeInset;
523
- const strokeW = Math.max(0, width - borderWidth);
524
- const strokeH = Math.max(0, height - borderWidth);
525
- return fillRect + `<rect x="${strokeX}" y="${strokeY}" width="${strokeW}" height="${strokeH}" rx="${Math.max(
526
- 0,
527
- radius - strokeInset
528
- )}" fill="none" stroke="${borderColor}" stroke-width="${borderWidth}"${isTagPreset ? ` shape-rendering="crispEdges"` : ""} />`;
529
- };
530
- var canUseNativeTag = ({
531
- background,
532
- orientation,
533
- color,
534
- fontFamily,
535
- fontSize,
536
- width,
537
- height,
538
- paddingX,
539
- paddingY,
540
- format = {}
541
- }) => {
542
- if (!background || background.preset !== "tag") return false;
543
- const resolvedOrientation = typeof orientation === "number" ? orientation : ORIENTATION_ROTATION[orientation ?? "horizontal"] ?? 0;
544
- if (resolvedOrientation !== 0) return false;
545
- if (color != null || fontFamily != null || fontSize != null) return false;
546
- if (width != null || height != null || paddingX != null || paddingY != null) return false;
547
- if (background.color != null || background.textColor != null) return false;
548
- if (background.borderColor != null || background.borderWidth != null) return false;
549
- if (background.radius != null || background.height != null) return false;
550
- if (background.paddingX != null || background.paddingY != null) return false;
551
- if (background.canvasPaddingX != null || background.canvasPaddingY != null) return false;
552
- if (format.italic || format.lineDecoration) return false;
553
- if (format.textTransform && format.textTransform !== "none") return false;
554
- return true;
555
- };
556
- var makeStyledTextDataUri = (text, opts = {}) => {
557
- const {
558
- variant = "bodytext",
559
- format = {},
560
- orientation = "horizontal",
561
- color: colorProp = HS_TEXT_COLOR,
562
- fontFamily = HS_FONT_FAMILY,
563
- background: backgroundProp = null,
564
- paddingX: paddingXProp = 4,
565
- paddingY: paddingYProp = 2,
566
- width: widthOverride,
567
- height: heightOverride,
568
- fontSize: fontSizeOverride
569
- } = opts;
570
- const preset = VARIANT_PRESETS[variant] || VARIANT_PRESETS.bodytext;
571
- const background = resolveBackground(backgroundProp);
572
- const fontSize = fontSizeOverride ?? (background == null ? void 0 : background.fontSize) ?? preset.fontSize;
573
- const rawWeight = format.fontWeight;
574
- const fontWeight = rawWeight ? WEIGHT_ALIASES[rawWeight] ?? rawWeight : preset.fontWeight;
575
- const fontStyle = format.italic ? "italic" : "normal";
576
- const textDecoration = LINE_DECORATION[format.lineDecoration] || "none";
577
- const transformed = applyTextTransform(text, format.textTransform);
578
- const lineHeight = (background == null ? void 0 : background.height) ?? preset.lineHeight ?? fontSize;
579
- const color = (background == null ? void 0 : background.textColor) ?? colorProp;
580
- const paddingX = (background == null ? void 0 : background.canvasPaddingX) ?? paddingXProp;
581
- const paddingY = (background == null ? void 0 : background.canvasPaddingY) ?? paddingYProp;
582
- const rotate = typeof orientation === "number" ? orientation : ORIENTATION_ROTATION[orientation] ?? 0;
583
- const textW = estimateTextWidth(transformed, fontSize);
584
- let pillW = 0;
585
- let pillH = 0;
586
- if (background) {
587
- const bgPadX = background.paddingX ?? 6;
588
- const bgPadY = background.paddingY ?? 3;
589
- pillW = textW + bgPadX * 2;
590
- pillH = background.height ?? Math.max(lineHeight, fontSize + bgPadY * 2);
591
- }
592
- const intrinsicW = (background ? pillW : textW) + paddingX * 2;
593
- const intrinsicH = (background ? pillH : lineHeight) + paddingY * 2;
594
- const isOrthoRotation = rotate === 90 || rotate === -90 || rotate === 270;
595
- const canvasW = widthOverride ?? (isOrthoRotation ? intrinsicH : intrinsicW);
596
- const canvasH = heightOverride ?? (isOrthoRotation ? intrinsicW : intrinsicH);
597
- const cx = canvasW / 2;
598
- const cy = canvasH / 2;
599
- const rectX = cx - pillW / 2;
600
- const rectY = cy - pillH / 2;
601
- const group = (background ? buildBackgroundRect({
602
- background,
603
- x: rectX,
604
- y: rectY,
605
- width: pillW,
606
- height: pillH
607
- }) : "") + `<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>`;
608
- const wrapped = rotate ? `<g transform="rotate(${rotate} ${cx} ${cy})">${group}</g>` : group;
609
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${canvasW}" height="${canvasH}">` + wrapped + `</svg>`;
610
- return {
611
- src: `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,
612
- width: canvasW,
613
- height: canvasH
614
- };
615
- };
616
- var StyledText = ({
617
- children,
618
- text,
619
- alt,
620
- variant,
621
- format,
622
- orientation,
623
- color,
624
- background,
625
- fontFamily,
626
- fontSize,
627
- paddingX,
628
- paddingY,
629
- width,
630
- height
631
- }) => {
632
- const resolvedText = text ?? (typeof children === "string" ? children : "");
633
- if (canUseNativeTag({
634
- background,
635
- orientation,
636
- color,
637
- fontFamily,
638
- fontSize,
639
- width,
640
- height,
641
- paddingX,
642
- paddingY,
643
- format
644
- })) {
645
- const nativeVariant = NATIVE_TAG_VARIANT_ALIASES[background == null ? void 0 : background.variant] ?? (background == null ? void 0 : background.variant) ?? "default";
646
- return React5.createElement(Tag3, { variant: nativeVariant }, resolvedText);
647
- }
648
- const { src, width: w, height: h } = makeStyledTextDataUri(resolvedText, {
649
- variant,
650
- format,
651
- orientation,
652
- color,
653
- background,
654
- fontFamily,
655
- fontSize,
656
- paddingX,
657
- paddingY,
658
- width,
659
- height
660
- });
661
- return React5.createElement(Image2, {
662
- src,
663
- width: w,
664
- height: h,
665
- alt: alt ?? String(resolvedText)
666
- });
667
- };
668
-
669
- // packages/kanban/src/Kanban.jsx
670
- import {
671
- Alert,
672
- AutoGrid,
673
- Box as Box2,
674
- Button as Button2,
675
- Checkbox as Checkbox2,
676
- DateInput as DateInput2,
677
- DescriptionList,
678
- DescriptionListItem,
679
- Divider,
680
- Dropdown,
681
- EmptyState as EmptyState2,
682
- Flex as Flex2,
683
- Icon as Icon2,
684
- Image as Image3,
685
- Inline,
686
- Link as Link2,
687
- LoadingSpinner,
688
- Modal,
689
- ModalBody,
690
- MultiSelect as MultiSelect2,
691
- SearchInput as SearchInput2,
692
- Select as Select2,
693
- Statistics,
694
- StatisticsItem,
695
- StatisticsTrend,
696
- Tag as Tag4,
697
- Text as Text2,
698
- Tile as Tile2
699
- } from "@hubspot/ui-extensions";
700
-
701
- // src/utils/objectPath.js
702
- var getByPath = (obj, path) => {
703
- if (!path) return void 0;
704
- if (typeof path === "function") return path(obj);
705
- return String(path).split(".").reduce((acc, key) => acc == null ? void 0 : acc[key], obj);
706
- };
707
-
708
- // src/utils/crmSearchAdapters.js
709
- var EMPTY_ARRAY = [];
710
- var EMPTY_OBJECT = {};
711
- var isPlainObject = (value) => value != null && Object.prototype.toString.call(value) === "[object Object]";
712
- var coerceError = (error) => {
713
- if (!error) return false;
714
- if (typeof error === "string") return error;
715
- if (error.message) return error.message;
716
- return true;
717
- };
718
- var pickArray = (response) => {
719
- if (Array.isArray(response)) return response;
720
- if (!response) return EMPTY_ARRAY;
721
- return response.results || response.data || response.items || response.records || response.objects || EMPTY_ARRAY;
722
- };
723
- var pickTotal = (response, fallbackLength) => {
724
- var _a;
725
- if (!response || Array.isArray(response)) return fallbackLength;
726
- return response.total ?? response.totalCount ?? response.totalResults ?? ((_a = response.paging) == null ? void 0 : _a.total) ?? fallbackLength;
727
- };
728
- var normalizeCrmSearchRecord = (record, options = EMPTY_OBJECT) => {
729
- const {
730
- idField = "id",
731
- objectIdField = "objectId",
732
- propertiesKey = "properties",
733
- flattenProperties = true,
734
- propertyValueKey,
735
- mapRecord
736
- } = options;
737
- if (mapRecord) return mapRecord(record);
738
- 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`);
739
- const properties = (record == null ? void 0 : record[propertiesKey]) || EMPTY_OBJECT;
740
- const flattened = {};
741
- if (flattenProperties && isPlainObject(properties)) {
742
- for (const [key, value] of Object.entries(properties)) {
743
- flattened[key] = propertyValueKey && isPlainObject(value) ? value[propertyValueKey] : value;
744
- }
745
- }
746
- return {
747
- ...flattenProperties ? flattened : EMPTY_OBJECT,
748
- ...record,
749
- [idField]: objectId,
750
- [objectIdField]: objectId,
751
- [propertiesKey]: properties
752
- };
753
- };
754
- var normalizeCrmSearchRows = (response, options = EMPTY_OBJECT) => {
755
- const records = pickArray(response);
756
- return records.map((record) => normalizeCrmSearchRecord(record, options));
757
- };
758
- var STABLE_SORT_TIEBREAKER = { propertyName: "hs_object_id", direction: "ASCENDING" };
759
- var withStableSort = (sorts) => {
760
- const base = Array.isArray(sorts) ? sorts : [];
761
- if (base.some((s) => s && s.propertyName === STABLE_SORT_TIEBREAKER.propertyName)) return base;
762
- return [...base, STABLE_SORT_TIEBREAKER];
763
- };
764
- var buildCrmSearchConfig = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
765
- const {
766
- objectType,
767
- properties = EMPTY_ARRAY,
768
- query,
769
- filterGroups,
770
- sorts,
771
- pageLength,
772
- propertyMap = EMPTY_OBJECT,
773
- filterMap,
774
- sortMap,
775
- baseConfig = EMPTY_OBJECT
776
- } = options;
777
- const mappedFilters = filterMap ? filterMap(params.filters || EMPTY_OBJECT, params) : filterGroups;
778
- const mappedSorts = sortMap ? sortMap(params.sort || EMPTY_OBJECT, params) : sorts;
779
- const config = {
780
- ...baseConfig,
781
- objectType: objectType || baseConfig.objectType,
782
- properties: properties.length ? properties : baseConfig.properties,
783
- query: query ?? params.search ?? baseConfig.query,
784
- filterGroups: mappedFilters ?? baseConfig.filterGroups,
785
- sorts: withStableSort(mappedSorts ?? baseConfig.sorts),
786
- pageLength: pageLength ?? params.pageLength ?? baseConfig.pageLength
787
- };
788
- if (propertyMap && Object.keys(propertyMap).length && params.filters) {
789
- const filters = Object.entries(params.filters).filter(([, value]) => value !== void 0 && value !== null && value !== "" && !(Array.isArray(value) && value.length === 0)).map(([name, value]) => {
790
- const propertyName = propertyMap[name] || name;
791
- if (Array.isArray(value)) return { propertyName, operator: "IN", values: value };
792
- if (isPlainObject(value) && (value.from || value.to)) {
793
- const rangeFilters = [];
794
- if (value.from) rangeFilters.push({ propertyName, operator: "GTE", value: value.from });
795
- if (value.to) rangeFilters.push({ propertyName, operator: "LTE", value: value.to });
796
- return rangeFilters;
797
- }
798
- return { propertyName, operator: "EQ", value };
799
- }).flat();
800
- if (filters.length) config.filterGroups = [{ filters }];
801
- }
802
- Object.keys(config).forEach((key) => config[key] === void 0 && delete config[key]);
803
- return config;
804
- };
805
- var useCrmSearchDataSource = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
806
- const {
807
- format,
808
- row,
809
- rowIdField = "id",
810
- totalCount,
811
- loading,
812
- error,
813
- mapResponse
814
- } = options;
815
- const config = useMemo3(() => buildCrmSearchConfig(params, options), [params, options]);
816
- const response = useCrmSearch(config, format);
817
- return useMemo3(() => {
818
- const rows = mapResponse ? mapResponse(response) : normalizeCrmSearchRows(response, { idField: rowIdField, ...row || EMPTY_OBJECT });
819
- const resolvedTotal = typeof totalCount === "function" ? totalCount(response) : totalCount ?? pickTotal(response, rows.length);
820
- return {
821
- data: rows,
822
- rows,
823
- response,
824
- loading: typeof loading === "function" ? loading(response) : loading ?? !!(response == null ? void 0 : response.isLoading),
825
- isLoading: typeof loading === "function" ? loading(response) : loading ?? !!(response == null ? void 0 : response.isLoading),
826
- error: typeof error === "function" ? error(response) : error ?? coerceError(response == null ? void 0 : response.error),
827
- totalCount: resolvedTotal,
828
- rowIdField
829
- };
830
- }, [response, mapResponse, row, rowIdField, totalCount, loading, error]);
831
- };
832
- var crmSearchResultToOption = (row, options = EMPTY_OBJECT) => {
833
- const {
834
- label = "name",
835
- value = "objectId",
836
- description,
837
- fallbackLabel = "Untitled record",
838
- mapOption
839
- } = options;
840
- if (mapOption) return mapOption(row);
841
- const option = {
842
- label: getByPath(row, label) ?? getByPath(row, "properties.name") ?? fallbackLabel,
843
- value: getByPath(row, value) ?? getByPath(row, "id") ?? getByPath(row, "objectId")
844
- };
845
- const desc = getByPath(row, description);
846
- if (desc != null && desc !== "") option.description = desc;
847
- return option;
848
- };
849
- var useCrmSearchOptions = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
850
- const dataSource = useCrmSearchDataSource(params, options);
851
- const optionConfig = options.option || options;
852
- return useMemo3(() => ({
853
- ...dataSource,
854
- options: dataSource.rows.map((row) => crmSearchResultToOption(row, optionConfig))
855
- }), [dataSource, optionConfig]);
856
- };
857
- var CRM_OBJECT_TYPES = {
858
- contact: "0-1",
859
- contacts: "0-1",
860
- company: "0-2",
861
- companies: "0-2",
862
- deal: "0-3",
863
- deals: "0-3"
864
- };
865
- var resolveCrmObjectType = (objectType) => CRM_OBJECT_TYPES[objectType] || objectType;
866
-
867
- // src/common-components/CrmLookupSelect.js
868
- var EMPTY_ARRAY2 = [];
869
- var EMPTY_OBJECT2 = {};
870
- var makeOptionConfig = ({ option, labelProperty, valueProperty, descriptionProperty }) => ({
871
- label: labelProperty || (option == null ? void 0 : option.label) || "name",
872
- value: valueProperty || (option == null ? void 0 : option.value) || "objectId",
873
- description: descriptionProperty || (option == null ? void 0 : option.description),
874
- fallbackLabel: option == null ? void 0 : option.fallbackLabel,
875
- mapOption: option == null ? void 0 : option.mapOption
876
- });
877
- var mergeSelectedOptions = (options, selectedOptions, value) => {
878
- const selected = Array.isArray(selectedOptions) ? selectedOptions : selectedOptions ? [selectedOptions] : EMPTY_ARRAY2;
879
- if (!selected.length) return options;
880
- const values = new Set(options.map((opt) => opt.value));
881
- const activeValues = new Set(Array.isArray(value) ? value : value == null || value === "" ? [] : [value]);
882
- const missing = selected.filter((opt) => activeValues.has(opt.value) && !values.has(opt.value));
883
- return missing.length ? [...missing, ...options] : options;
884
- };
885
- var CrmLookupSelect = ({
886
- objectType,
887
- properties = EMPTY_ARRAY2,
888
- name,
889
- label,
890
- value,
891
- onChange,
892
- multiple = false,
893
- placeholder,
894
- description,
895
- tooltip,
896
- required,
897
- readOnly,
898
- error,
899
- validationMessage,
900
- variant,
901
- debounce = 300,
902
- minSearchLength = 0,
903
- pageLength = 20,
904
- option,
905
- labelProperty,
906
- valueProperty = "objectId",
907
- descriptionProperty,
908
- selectedOptions,
909
- format,
910
- row,
911
- baseConfig,
912
- query,
913
- onSearchChange,
914
- noResultsOption,
915
- loadingOption,
916
- selectProps = EMPTY_OBJECT2
917
- }) => {
918
- const [inputValue, setInputValue] = useState4(query || "");
919
- const [pickedOptions, setPickedOptions] = useState4(EMPTY_ARRAY2);
920
- const debouncedInput = useDebounce2(inputValue, debounce > 0 ? debounce : 1);
921
- const search = debounce > 0 ? debouncedInput : inputValue;
922
- const effectiveSearch = search && search.length >= minSearchLength ? search : "";
923
- const optionConfig = useMemo4(
924
- () => makeOptionConfig({ option, labelProperty, valueProperty, descriptionProperty }),
925
- [option, labelProperty, valueProperty, descriptionProperty]
926
- );
927
- const dataSource = useCrmSearchOptions(
928
- { search: effectiveSearch },
929
- {
930
- objectType: resolveCrmObjectType(objectType),
931
- properties,
932
- pageLength,
933
- format,
934
- baseConfig,
935
- row: row || { mapRecord: (record) => ({ objectId: record.objectId, ...record.properties }) },
936
- option: optionConfig
937
- }
938
- );
939
- const isSearching = dataSource.loading || inputValue.trim() !== (search || "").trim();
940
- const hasQuery = effectiveSearch.length > 0;
941
- const options = useMemo4(() => {
942
- const remembered = [...selectedOptions || EMPTY_ARRAY2, ...pickedOptions];
943
- const baseOptions = mergeSelectedOptions(dataSource.options || EMPTY_ARRAY2, remembered, value);
944
- if (isSearching && loadingOption) return [loadingOption, ...baseOptions];
945
- if (!isSearching && hasQuery && !baseOptions.length && noResultsOption) return [noResultsOption];
946
- return baseOptions;
947
- }, [dataSource.options, isSearching, hasQuery, selectedOptions, pickedOptions, value, loadingOption, noResultsOption]);
948
- const handleChange = (next) => {
949
- const nextValues = Array.isArray(next) ? next : next == null || next === "" ? EMPTY_ARRAY2 : [next];
950
- 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));
951
- if (picked.length) {
952
- setPickedOptions((prev) => {
953
- const byValue = new Map(prev.map((o) => [o.value, o]));
954
- picked.forEach((o) => byValue.set(o.value, o));
955
- return [...byValue.values()];
956
- });
957
- }
958
- if (onChange) onChange(next);
959
- };
960
- const commonProps = {
961
- name,
962
- label,
963
- value: multiple ? value || EMPTY_ARRAY2 : value,
964
- options,
965
- placeholder: placeholder || (dataSource.loading ? "Searching CRM..." : "Search CRM records..."),
966
- description,
967
- tooltip,
968
- required,
969
- readOnly,
970
- error: error || !!dataSource.error,
971
- validationMessage: validationMessage || (typeof dataSource.error === "string" ? dataSource.error : void 0),
972
- variant,
973
- onChange: handleChange,
974
- onInput: (next) => {
975
- setInputValue(next || "");
976
- if (onSearchChange) onSearchChange(next || "");
977
- },
978
- ...selectProps
979
- };
980
- return React8.createElement(multiple ? MultiSelect3 : Select3, commonProps);
981
- };
982
-
983
- // src/common-components/datePresets.js
984
- var HS_DATE_PRESETS = [
985
- { label: "Today", value: "today" },
986
- { label: "Yesterday", value: "yesterday" },
987
- { label: "Tomorrow", value: "tomorrow" },
988
- { label: "This week", value: "this_week" },
989
- { label: "Last week", value: "last_week" },
990
- { label: "Last 7 days", value: "7d" },
991
- { label: "Last 30 days", value: "30d" },
992
- { label: "Last 90 days", value: "90d" },
993
- { label: "This month", value: "this_month" },
994
- { label: "Last month", value: "last_month" },
995
- { label: "This quarter", value: "this_quarter" },
996
- { label: "Last quarter", value: "last_quarter" },
997
- { label: "This year", value: "this_year" },
998
- { label: "Last year", value: "last_year" }
999
- ];
1000
- var HS_DATE_DIRECTION_LABELS = {
1001
- asc: "Ascending",
1002
- desc: "Descending"
1003
- };
1004
-
1005
- // src/common-components/Icon.js
1006
- import React9 from "react";
1007
- import { Image as Image4, Icon as HsIcon } from "@hubspot/ui-extensions";
464
+ // src/common-components/Icon.js
465
+ import React6 from "react";
466
+ import { Image as Image2, Icon as HsIcon, Link } from "@hubspot/ui-extensions";
1008
467
 
1009
468
  // src/common-components/icons.generated.js
1010
469
  var GENERATED_ICONS = {
@@ -1257,361 +716,1246 @@ var GENERATED_ICONS = {
1257
716
  "ZoomIn": { "viewBox": "0 0 32 32", "paths": ["M14.42 26.75c2.85 0 5.47-.97 7.55-2.6l-.03.02 5.29 5.34a1.619 1.619 0 0 0 2.76-1.15c0-.45-.18-.85-.47-1.14l-5.33-5.33c1.59-2.06 2.55-4.68 2.55-7.52C26.74 7.54 21.2 2 14.37 2S2 7.55 2 14.38s5.54 12.37 12.37 12.37h.05m0-21.54c5.06 0 9.16 4.1 9.16 9.16s-4.1 9.16-9.16 9.16-9.16-4.1-9.16-9.16 4.1-9.15 9.16-9.16m-4.31 10.76h2.69v2.69c0 .89.72 1.62 1.62 1.62s1.62-.72 1.62-1.62v-2.69h2.69c.89 0 1.62-.72 1.62-1.62s-.72-1.62-1.62-1.62h-2.68V9.99c0-.89-.72-1.62-1.62-1.62s-1.62.72-1.62 1.62v2.69h-2.7c-.89 0-1.62.72-1.62 1.62s.72 1.62 1.62 1.62z"] },
1258
717
  "ZoomOut": { "viewBox": "0 0 32 32", "paths": ["M14.42 26.75c2.85 0 5.47-.97 7.56-2.6l-.03.02 5.28 5.34a1.619 1.619 0 0 0 2.76-1.15c0-.45-.18-.85-.47-1.14l-5.33-5.33c1.59-2.06 2.55-4.68 2.55-7.52C26.74 7.54 21.2 2 14.37 2S2 7.55 2 14.38s5.54 12.37 12.37 12.37h.05m0-21.55c5.06 0 9.16 4.1 9.16 9.16s-4.1 9.16-9.16 9.16-9.16-4.1-9.16-9.16c.01-5.05 4.11-9.14 9.16-9.15Zm-4.31 10.78h8.62c.89 0 1.62-.72 1.62-1.62s-.72-1.62-1.62-1.62h-8.62c-.89 0-1.62.72-1.62 1.62s.72 1.62 1.62 1.62"] }
1259
718
  };
1260
-
1261
- // src/common-components/Icon.js
1262
- var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
1263
- "add",
1264
- "appointment",
1265
- "approvals",
1266
- "artificialIntelligence",
1267
- "artificialIntelligenceEnhanced",
1268
- "attach",
1269
- "bank",
1270
- "block",
1271
- "book",
1272
- "bulb",
1273
- "calling",
1274
- "callingHangup",
1275
- "callingMade",
1276
- "callingMissed",
1277
- "callingVoicemail",
1278
- "callTranscript",
1279
- "campaigns",
1280
- "cap",
1281
- "checkCircle",
1282
- "circleFilled",
1283
- "circleHollow",
1284
- "clock",
1285
- "comment",
1286
- "contact",
1287
- "copy",
1288
- "crm",
1289
- "dataSync",
1290
- "date",
1291
- "delay",
1292
- "delete",
1293
- "description",
1294
- "developerProjects",
1295
- "documents",
1296
- "downCarat",
1297
- "download",
1298
- "edit",
1299
- "ellipses",
1300
- "email",
1301
- "emailOpen",
1302
- "emailThreadedReplies",
1303
- "enrichment",
1304
- "enroll",
1305
- "exclamation",
1306
- "exclamationCircle",
1307
- "facebook",
1308
- "faceHappy",
1309
- "faceHappyFilled",
1310
- "faceNeutral",
1311
- "faceNeutralFilled",
1312
- "faceSad",
1313
- "faceSadFilled",
1314
- "favoriteHollow",
1315
- "file",
1316
- "filledXCircleIcon",
1317
- "filter",
1318
- "flame",
1319
- "folder",
1320
- "folderOpen",
1321
- "forward",
1322
- "gauge",
1323
- "generateChart",
1324
- "gift",
1325
- "globe",
1326
- "globeLine",
1327
- "goal",
1328
- "googlePlus",
1329
- "guidedActions",
1330
- "hash",
1331
- "hide",
1332
- "home",
1333
- "hubDB",
1334
- "image",
1335
- "imageGallery",
1336
- "inbox",
1337
- "info",
1338
- "infoNoCircle",
1339
- "insertVideo",
1340
- "instagram",
1341
- "integrations",
1342
- "invoice",
1343
- "key",
1344
- "language",
1345
- "left",
1346
- "lessCircle",
1347
- "lesson",
1348
- "light",
1349
- "link",
1350
- "linkedin",
1351
- "listView",
1352
- "location",
1353
- "locked",
1354
- "mention",
1355
- "messages",
1356
- "mobile",
1357
- "moreCircle",
1358
- "notEditable",
1359
- "notification",
1360
- "notificationOff",
1361
- "objectAssociations",
1362
- "objectAssociationsManyToMany",
1363
- "objectAssociationsManyToOne",
1364
- "office365",
1365
- "order",
1366
- "paymentSubscriptions",
1367
- "pin",
1368
- "pinterest",
1369
- "powerPointFile",
1370
- "presentation",
1371
- "product",
1372
- "publish",
1373
- "question",
1374
- "questionAnswer",
1375
- "questionCircle",
1376
- "quickbooks",
1377
- "quote",
1378
- "readMore",
1379
- "readOnlyView",
1380
- "realEstateListing",
1381
- "recentlySelected",
1382
- "record",
1383
- "redo",
1384
- "refresh",
1385
- "registration",
1386
- "remove",
1387
- "replace",
1388
- "reports",
1389
- "right",
1390
- "robot",
1391
- "rotate",
1392
- "rss",
1393
- "salesQuote",
1394
- "salesTemplates",
1395
- "save",
1396
- "search",
1397
- "send",
1398
- "sequences",
1399
- "settings",
1400
- "shoppingCart",
1401
- "signal",
1402
- "signalPoor",
1403
- "signature",
1404
- "snooze",
1405
- "sortAlpAsc",
1406
- "sortAlpDesc",
1407
- "sortAmtAsc",
1408
- "sortAmtDesc",
1409
- "sortNumAsc",
1410
- "sortNumDesc",
1411
- "sortTableAsc",
1412
- "sortTableDesc",
1413
- "spellCheck",
1414
- "sprocket",
1415
- "star",
1416
- "stopRecord",
1417
- "strike",
1418
- "styles",
1419
- "success",
1420
- "tablet",
1421
- "tag",
1422
- "tasks",
1423
- "test",
1424
- "text",
1425
- "textBodyExpanded",
1426
- "textColor",
1427
- "textDataType",
1428
- "textSnippet",
1429
- "thumbsDown",
1430
- "thumbsUp",
1431
- "ticket",
1432
- "translate",
1433
- "trophy",
1434
- "twitter",
1435
- "undo",
1436
- "upCarat",
1437
- "upload",
1438
- "video",
1439
- "videoFile",
1440
- "videoPlayerSubtitles",
1441
- "view",
1442
- "viewDetails",
1443
- "warning",
1444
- "website",
1445
- "workflows",
1446
- "x",
1447
- "xCircle",
1448
- "xing",
1449
- "youtube",
1450
- "youtubePlay",
1451
- "zoomIn",
1452
- "zoomOut"
1453
- ]);
1454
- var NATIVE_COLORS = /* @__PURE__ */ new Set(["inherit", "alert", "warning", "success"]);
1455
- var NATIVE_SIZE_TOKENS = {
1456
- sm: "sm",
1457
- small: "sm",
1458
- md: "md",
1459
- medium: "md",
1460
- lg: "lg",
1461
- large: "lg"
719
+
720
+ // src/common-components/Icon.js
721
+ var NATIVE_ICON_NAMES = /* @__PURE__ */ new Set([
722
+ "add",
723
+ "appointment",
724
+ "approvals",
725
+ "artificialIntelligence",
726
+ "artificialIntelligenceEnhanced",
727
+ "attach",
728
+ "bank",
729
+ "block",
730
+ "book",
731
+ "bulb",
732
+ "calling",
733
+ "callingHangup",
734
+ "callingMade",
735
+ "callingMissed",
736
+ "callingVoicemail",
737
+ "callTranscript",
738
+ "campaigns",
739
+ "cap",
740
+ "checkCircle",
741
+ "circleFilled",
742
+ "circleHollow",
743
+ "clock",
744
+ "comment",
745
+ "contact",
746
+ "copy",
747
+ "crm",
748
+ "dataSync",
749
+ "date",
750
+ "delay",
751
+ "delete",
752
+ "description",
753
+ "developerProjects",
754
+ "documents",
755
+ "downCarat",
756
+ "download",
757
+ "edit",
758
+ "ellipses",
759
+ "email",
760
+ "emailOpen",
761
+ "emailThreadedReplies",
762
+ "enrichment",
763
+ "enroll",
764
+ "exclamation",
765
+ "exclamationCircle",
766
+ "facebook",
767
+ "faceHappy",
768
+ "faceHappyFilled",
769
+ "faceNeutral",
770
+ "faceNeutralFilled",
771
+ "faceSad",
772
+ "faceSadFilled",
773
+ "favoriteHollow",
774
+ "file",
775
+ "filledXCircleIcon",
776
+ "filter",
777
+ "flame",
778
+ "folder",
779
+ "folderOpen",
780
+ "forward",
781
+ "gauge",
782
+ "generateChart",
783
+ "gift",
784
+ "globe",
785
+ "globeLine",
786
+ "goal",
787
+ "googlePlus",
788
+ "guidedActions",
789
+ "hash",
790
+ "hide",
791
+ "home",
792
+ "hubDB",
793
+ "image",
794
+ "imageGallery",
795
+ "inbox",
796
+ "info",
797
+ "infoNoCircle",
798
+ "insertVideo",
799
+ "instagram",
800
+ "integrations",
801
+ "invoice",
802
+ "key",
803
+ "language",
804
+ "left",
805
+ "lessCircle",
806
+ "lesson",
807
+ "light",
808
+ "link",
809
+ "linkedin",
810
+ "listView",
811
+ "location",
812
+ "locked",
813
+ "mention",
814
+ "messages",
815
+ "mobile",
816
+ "moreCircle",
817
+ "notEditable",
818
+ "notification",
819
+ "notificationOff",
820
+ "objectAssociations",
821
+ "objectAssociationsManyToMany",
822
+ "objectAssociationsManyToOne",
823
+ "office365",
824
+ "order",
825
+ "paymentSubscriptions",
826
+ "pin",
827
+ "pinterest",
828
+ "powerPointFile",
829
+ "presentation",
830
+ "product",
831
+ "publish",
832
+ "question",
833
+ "questionAnswer",
834
+ "questionCircle",
835
+ "quickbooks",
836
+ "quote",
837
+ "readMore",
838
+ "readOnlyView",
839
+ "realEstateListing",
840
+ "recentlySelected",
841
+ "record",
842
+ "redo",
843
+ "refresh",
844
+ "registration",
845
+ "remove",
846
+ "replace",
847
+ "reports",
848
+ "right",
849
+ "robot",
850
+ "rotate",
851
+ "rss",
852
+ "salesQuote",
853
+ "salesTemplates",
854
+ "save",
855
+ "search",
856
+ "send",
857
+ "sequences",
858
+ "settings",
859
+ "shoppingCart",
860
+ "signal",
861
+ "signalPoor",
862
+ "signature",
863
+ "snooze",
864
+ "sortAlpAsc",
865
+ "sortAlpDesc",
866
+ "sortAmtAsc",
867
+ "sortAmtDesc",
868
+ "sortNumAsc",
869
+ "sortNumDesc",
870
+ "sortTableAsc",
871
+ "sortTableDesc",
872
+ "spellCheck",
873
+ "sprocket",
874
+ "star",
875
+ "stopRecord",
876
+ "strike",
877
+ "styles",
878
+ "success",
879
+ "tablet",
880
+ "tag",
881
+ "tasks",
882
+ "test",
883
+ "text",
884
+ "textBodyExpanded",
885
+ "textColor",
886
+ "textDataType",
887
+ "textSnippet",
888
+ "thumbsDown",
889
+ "thumbsUp",
890
+ "ticket",
891
+ "translate",
892
+ "trophy",
893
+ "twitter",
894
+ "undo",
895
+ "upCarat",
896
+ "upload",
897
+ "video",
898
+ "videoFile",
899
+ "videoPlayerSubtitles",
900
+ "view",
901
+ "viewDetails",
902
+ "warning",
903
+ "website",
904
+ "workflows",
905
+ "x",
906
+ "xCircle",
907
+ "xing",
908
+ "youtube",
909
+ "youtubePlay",
910
+ "zoomIn",
911
+ "zoomOut"
912
+ ]);
913
+ var NATIVE_COLORS = /* @__PURE__ */ new Set(["inherit", "alert", "warning", "success"]);
914
+ var NATIVE_SIZE_TOKENS = {
915
+ sm: "sm",
916
+ small: "sm",
917
+ md: "md",
918
+ medium: "md",
919
+ lg: "lg",
920
+ large: "lg"
921
+ };
922
+ var SEMANTIC_HEX = {
923
+ inherit: HS_TEXT_COLOR,
924
+ alert: "#F2545B",
925
+ warning: "#D39913",
926
+ success: "#00BDA5"
927
+ };
928
+ var SIZE_TOKENS2 = {
929
+ xs: 12,
930
+ "extra-small": 12,
931
+ sm: 14,
932
+ "small": 14,
933
+ md: 16,
934
+ "med": 16,
935
+ "medium": 16,
936
+ lg: 20,
937
+ "large": 20,
938
+ xl: 24,
939
+ "extra-large": 24
940
+ };
941
+ var resolveSize2 = (size) => {
942
+ if (typeof size === "number") return size;
943
+ if (typeof size === "string" && SIZE_TOKENS2[size] != null) return SIZE_TOKENS2[size];
944
+ return 16;
945
+ };
946
+ var escapeXmlAttr2 = (s) => String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
947
+ var PLUS_24 = "M11 5h2v6h6v2h-6v6h-2v-6H5v-2h6z";
948
+ var CURATED_ICONS = {
949
+ checkBare: { paths: ["M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z"] },
950
+ plusBare: { paths: [PLUS_24] },
951
+ // Close = a plus rotated 45° → a clean X (reuses transform support, no new
952
+ // path data to hand-author/get-wrong).
953
+ Close: { paths: [PLUS_24], transform: "rotate(45 12 12)" },
954
+ // Universal media-transport glyphs, authored at 32×32 to match the scraped
955
+ // canvas so they sit at the same optical size as the rest of the set.
956
+ Play: { viewBox: "0 0 32 32", paths: ["M9 6v20l17-10z"] },
957
+ Pause: { viewBox: "0 0 32 32", paths: ["M10 6h4v20h-4z M18 6h4v20h-4z"] },
958
+ Stop: { viewBox: "0 0 32 32", paths: ["M8 8h16v16H8z"] },
959
+ // Hand-authored to match HubSpot's filled/rounded weight (verified visually
960
+ // against the scraped set) for genuine gaps the scrape didn't cover.
961
+ Unlocked: {
962
+ viewBox: "0 0 32 32",
963
+ paths: [
964
+ "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",
965
+ { 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" }
966
+ ]
967
+ },
968
+ Print: {
969
+ viewBox: "0 0 32 32",
970
+ paths: [
971
+ "M9 4h14a1 1 0 0 1 1 1v6H8V5a1 1 0 0 1 1-1z",
972
+ { 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" },
973
+ "M10 21h12v6a1 1 0 0 1-1 1H11a1 1 0 0 1-1-1z"
974
+ ]
975
+ },
976
+ Shrink: {
977
+ viewBox: "0 0 32 32",
978
+ paths: [
979
+ "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",
980
+ "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"
981
+ ]
982
+ },
983
+ 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"] },
984
+ 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"] },
985
+ // Dark = crescent moon, pairs with the scraped Light (sun) for theme toggles.
986
+ 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"] },
987
+ 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"] },
988
+ 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"] },
989
+ // Backward = horizontal mirror of the scraped Forward arrow (x' = 32 - x).
990
+ // Derived so it stays in sync if Forward is ever re-scraped.
991
+ ...GENERATED_ICONS.Forward ? { Backward: { ...GENERATED_ICONS.Forward, transform: "translate(32 0) scale(-1 1)" } } : {}
992
+ };
993
+ var ICONS = { ...GENERATED_ICONS, ...CURATED_ICONS };
994
+ var svgToIconEntry = (raw) => {
995
+ const vb = /viewBox="([^"]+)"/i.exec(raw);
996
+ const viewBox = vb ? vb[1] : "0 0 24 24";
997
+ const rendered = raw.replace(
998
+ /<(mask|defs|clipPath|symbol)\b[^>]*>[\s\S]*?<\/\1>/gi,
999
+ ""
1000
+ );
1001
+ const paths = [];
1002
+ const pathRe = /<path\b([^>]*?)\/?>/gi;
1003
+ let m;
1004
+ while ((m = pathRe.exec(rendered)) !== null) {
1005
+ const attrs = m[1];
1006
+ const d = /\bd="([^"]+)"/i.exec(attrs);
1007
+ if (!d) continue;
1008
+ const fill = /\bfill="([^"]+)"/i.exec(attrs);
1009
+ const fillRule = /\bfill-rule="([^"]+)"/i.exec(attrs);
1010
+ const keepFill = fill && fill[1] !== "currentColor" && fill[1] !== "none";
1011
+ if (keepFill || fillRule) {
1012
+ paths.push({
1013
+ d: d[1],
1014
+ ...keepFill ? { fill: fill[1] } : {},
1015
+ ...fillRule ? { fillRule: fillRule[1] } : {}
1016
+ });
1017
+ } else {
1018
+ paths.push(d[1]);
1019
+ }
1020
+ }
1021
+ return { viewBox, paths };
1022
+ };
1023
+ var ICON_NAME_ALIASES = {
1024
+ call: "calling",
1025
+ phone: "calling",
1026
+ meeting: "appointment",
1027
+ note: "comment",
1028
+ notes: "comment",
1029
+ task: "tasks",
1030
+ open: "record",
1031
+ openRecord: "record",
1032
+ office: "record",
1033
+ company: "record",
1034
+ external: "link",
1035
+ externalLink: "link",
1036
+ down: "Down",
1037
+ up: "Up",
1038
+ pencil: "edit",
1039
+ trash: "delete"
1040
+ };
1041
+ var NATIVE_ICON_NAME_ALIASES = new Map([
1042
+ ...[...NATIVE_ICON_NAMES].map((nativeName) => [nativeName.toLowerCase(), nativeName]),
1043
+ ...Object.entries(ICON_NAME_ALIASES).map(([alias, nativeName]) => [alias.toLowerCase(), nativeName])
1044
+ ]);
1045
+ var resolveIconName = (name) => {
1046
+ if (typeof name !== "string") return name;
1047
+ if (NATIVE_ICON_NAMES.has(name)) return name;
1048
+ if (ICONS[name]) return name;
1049
+ const nativeOrAlias = NATIVE_ICON_NAME_ALIASES.get(name.toLowerCase());
1050
+ if (nativeOrAlias) return nativeOrAlias;
1051
+ return name;
1052
+ };
1053
+ 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);
1054
+ var makeIconDataUri = (nameOrEntry, opts = {}) => {
1055
+ const entry = nameOrEntry && typeof nameOrEntry === "object" ? nameOrEntry : ICONS[nameOrEntry];
1056
+ if (!entry || !entry.paths) return null;
1057
+ const size = resolveSize2(opts.size);
1058
+ const rawColor = opts.color;
1059
+ const color = rawColor && SEMANTIC_HEX[rawColor] || rawColor || HS_TEXT_COLOR;
1060
+ const viewBox = entry.viewBox ?? "0 0 24 24";
1061
+ const body = entry.paths.map((p) => {
1062
+ const d = typeof p === "string" ? p : p.d;
1063
+ const fill = typeof p === "object" && p.fill || color;
1064
+ const fillRule = typeof p === "object" && p.fillRule ? ` fill-rule="${p.fillRule}"` : "";
1065
+ return `<path d="${escapeXmlAttr2(d)}" fill="${fill}"${fillRule} />`;
1066
+ }).join("");
1067
+ const inner = entry.transform ? `<g transform="${escapeXmlAttr2(entry.transform)}">${body}</g>` : body;
1068
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="${viewBox}">` + inner + `</svg>`;
1069
+ return {
1070
+ src: `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,
1071
+ width: size,
1072
+ height: size
1073
+ };
1074
+ };
1075
+ var ICON_NAMES = Object.keys(ICONS);
1076
+ var NATIVE_ICON_NAME_LIST = [...NATIVE_ICON_NAMES].sort(
1077
+ (a, b) => a.localeCompare(b)
1078
+ );
1079
+ var Icon = ({ name, color, size, screenReaderText, onClick, href }) => {
1080
+ const resolvedName = resolveIconName(name);
1081
+ const renderNativeIcon = () => React6.createElement(HsIcon, {
1082
+ name: resolvedName,
1083
+ ...color != null ? { color } : {},
1084
+ ...size != null ? { size: NATIVE_SIZE_TOKENS[size] } : {},
1085
+ ...screenReaderText != null ? { screenReaderText } : {}
1086
+ });
1087
+ const renderNative = () => {
1088
+ const icon = renderNativeIcon();
1089
+ if (onClick == null && href == null) return icon;
1090
+ return React6.createElement(
1091
+ Link,
1092
+ {
1093
+ ...onClick != null ? { onClick } : {},
1094
+ ...href != null ? { href } : {}
1095
+ },
1096
+ icon
1097
+ );
1098
+ };
1099
+ if (canUseNative(name, color, size)) return renderNative();
1100
+ const result = makeIconDataUri(resolvedName, { size, color });
1101
+ if (!result) {
1102
+ return canUseNative(name, color, size) ? renderNative() : null;
1103
+ }
1104
+ return React6.createElement(Image2, {
1105
+ src: result.src,
1106
+ width: result.width,
1107
+ height: result.height,
1108
+ alt: screenReaderText ?? name,
1109
+ ...onClick != null ? { onClick } : {},
1110
+ ...href != null ? { href } : {}
1111
+ });
1112
+ };
1113
+
1114
+ // src/common-components/CollectionFilterControl.js
1115
+ var h3 = React7.createElement;
1116
+ var DEFAULT_LABELS = {
1117
+ all: "All",
1118
+ dateFrom: "From",
1119
+ dateTo: "To"
1120
+ };
1121
+ var CollectionFilterControl = ({
1122
+ filter,
1123
+ value,
1124
+ onChange,
1125
+ namePrefix = "collection-filter",
1126
+ labels: labelOverrides,
1127
+ selectVariant = "transparent",
1128
+ includeAll,
1129
+ allValue
1130
+ }) => {
1131
+ if (!filter) return null;
1132
+ const labels = { ...DEFAULT_LABELS, ...labelOverrides || {} };
1133
+ const type = filter.type || "select";
1134
+ const name = filter.name;
1135
+ const controlName = `${namePrefix}-${name}`;
1136
+ const handleChange = (next) => onChange == null ? void 0 : onChange(name, next);
1137
+ if (type === "multiselect") {
1138
+ return h3(MultiSelect, {
1139
+ key: name,
1140
+ name: controlName,
1141
+ placeholder: filter.placeholder || filter.label || labels.all,
1142
+ value: Array.isArray(value) ? value : [],
1143
+ options: filter.options || [],
1144
+ onChange: handleChange
1145
+ });
1146
+ }
1147
+ if (type === "dateRange") {
1148
+ const rangeValue = value || { from: null, to: null };
1149
+ return h3(
1150
+ Flex2,
1151
+ { key: name, direction: "row", align: "center", gap: "xs" },
1152
+ h3(DateInput, {
1153
+ name: `${controlName}-from`,
1154
+ placeholder: filter.fromLabel ?? labels.dateFrom,
1155
+ format: "medium",
1156
+ value: rangeValue.from ?? null,
1157
+ onChange: (next) => handleChange({ ...rangeValue, from: next })
1158
+ }),
1159
+ h3(Icon, { name: "right", size: "sm" }),
1160
+ h3(DateInput, {
1161
+ name: `${controlName}-to`,
1162
+ placeholder: filter.toLabel ?? labels.dateTo,
1163
+ format: "medium",
1164
+ value: rangeValue.to ?? null,
1165
+ onChange: (next) => handleChange({ ...rangeValue, to: next })
1166
+ })
1167
+ );
1168
+ }
1169
+ const resolvedIncludeAll = includeAll ?? filter.includeAll ?? true;
1170
+ const resolvedAllValue = filter.emptyValue ?? allValue ?? filter.allValue ?? "";
1171
+ const allLabel = filter.allLabel ?? filter.placeholder ?? filter.label ?? labels.all;
1172
+ const options = resolvedIncludeAll ? [{ label: allLabel, value: resolvedAllValue }, ...filter.options || []] : filter.options || [];
1173
+ return h3(Select, {
1174
+ key: name,
1175
+ name: controlName,
1176
+ variant: selectVariant,
1177
+ placeholder: filter.placeholder || filter.label || labels.all,
1178
+ value: value ?? resolvedAllValue,
1179
+ options,
1180
+ onChange: handleChange
1181
+ });
1182
+ };
1183
+
1184
+ // src/common-components/CollectionToolbar.js
1185
+ var h4 = React8.createElement;
1186
+ var DEFAULT_LABELS2 = {
1187
+ filtersButton: "Filters",
1188
+ clearAll: "Clear all"
1189
+ };
1190
+ var isVisible = (config) => config && (config.visible ?? config.show ?? true);
1191
+ var CollectionToolbar = ({
1192
+ search,
1193
+ filters,
1194
+ chips,
1195
+ right,
1196
+ footer,
1197
+ labels: labelOverrides,
1198
+ leftFlex = 3,
1199
+ rightFlex = 1,
1200
+ rightAlignSelf = "end",
1201
+ gap = "sm",
1202
+ idPrefix,
1203
+ uniqueNames = true
1204
+ }) => {
1205
+ const [showMoreFilters, setShowMoreFilters] = useState(false);
1206
+ const reactId = useId();
1207
+ const instanceId = String(idPrefix || reactId).replace(/[^a-zA-Z0-9_-]/g, "");
1208
+ const nameWithInstance = (name) => uniqueNames && name ? `${name}-${instanceId}` : name;
1209
+ const labels = { ...DEFAULT_LABELS2, ...labelOverrides || {} };
1210
+ const filterItems = Array.isArray(filters == null ? void 0 : filters.items) ? filters.items : [];
1211
+ const filterValues = (filters == null ? void 0 : filters.values) || {};
1212
+ const filterInlineLimit = (filters == null ? void 0 : filters.inlineLimit) ?? filterItems.length;
1213
+ const inlineFilters = filterItems.slice(0, filterInlineLimit);
1214
+ const overflowFilters = filterItems.slice(filterInlineLimit);
1215
+ const showSearch = isVisible(search);
1216
+ const hasSearch = showSearch && !!search;
1217
+ const hasFilters = inlineFilters.length > 0 || overflowFilters.length > 0;
1218
+ const hasChips = chips && Array.isArray(chips.items) && chips.items.length > 0;
1219
+ const hasLeft = hasSearch || hasFilters || hasChips;
1220
+ const hasRight = !!right;
1221
+ if (!hasLeft && !hasRight && !footer) return null;
1222
+ const filterLabels = { ...labels, ...(filters == null ? void 0 : filters.labels) || {} };
1223
+ const searchHandler = (search == null ? void 0 : search.onChange) || (search == null ? void 0 : search.onInput);
1224
+ const searchProps = hasSearch ? {
1225
+ name: nameWithInstance(search.name || "collection-search"),
1226
+ placeholder: search.placeholder,
1227
+ value: search.value,
1228
+ clearable: search.clearable !== false,
1229
+ ...search.onInput ? { onInput: search.onInput } : {},
1230
+ ...searchHandler ? { onChange: searchHandler } : {}
1231
+ } : null;
1232
+ const renderFilter = (filter) => h4(CollectionFilterControl, {
1233
+ key: filter.name,
1234
+ namePrefix: nameWithInstance((filters == null ? void 0 : filters.namePrefix) || "collection-filter"),
1235
+ filter,
1236
+ value: filterValues[filter.name],
1237
+ onChange: filters == null ? void 0 : filters.onChange,
1238
+ labels: filterLabels,
1239
+ includeAll: filters == null ? void 0 : filters.includeAll,
1240
+ allValue: filters == null ? void 0 : filters.allValue,
1241
+ selectVariant: filters == null ? void 0 : filters.selectVariant
1242
+ });
1243
+ const left = hasLeft ? h4(
1244
+ Box,
1245
+ { flex: leftFlex },
1246
+ h4(
1247
+ Flex3,
1248
+ { direction: "column", gap },
1249
+ hasSearch || hasFilters ? h4(
1250
+ Flex3,
1251
+ { direction: "row", align: "center", gap, wrap: "wrap" },
1252
+ hasSearch ? h4(SearchInput, searchProps) : null,
1253
+ ...inlineFilters.map(renderFilter),
1254
+ overflowFilters.length > 0 ? h4(
1255
+ Button2,
1256
+ {
1257
+ variant: "transparent",
1258
+ size: (filters == null ? void 0 : filters.overflowButtonSize) || "small",
1259
+ onClick: () => setShowMoreFilters((prev) => !prev)
1260
+ },
1261
+ h4(Icon, { name: "filter", size: "sm" }),
1262
+ " ",
1263
+ (filters == null ? void 0 : filters.filtersButtonLabel) || labels.filtersButton
1264
+ ) : null
1265
+ ) : null,
1266
+ showMoreFilters && overflowFilters.length > 0 ? h4(
1267
+ Flex3,
1268
+ { direction: "row", align: "center", gap, wrap: "wrap" },
1269
+ ...overflowFilters.map(renderFilter)
1270
+ ) : null,
1271
+ chips ? h4(ActiveFilterChips, {
1272
+ chips: chips.items,
1273
+ showBadges: chips.showBadges,
1274
+ showClearAll: chips.showClearAll,
1275
+ clearAllLabel: chips.clearAllLabel || labels.clearAll,
1276
+ onRemove: chips.onRemove,
1277
+ gap: chips.gap || gap
1278
+ }) : null
1279
+ )
1280
+ ) : null;
1281
+ const rightNode = hasRight ? h4(
1282
+ Box,
1283
+ { flex: rightFlex, alignSelf: rightAlignSelf },
1284
+ h4(Flex3, { direction: "row", align: "center", gap, justify: "end", wrap: "wrap" }, right)
1285
+ ) : null;
1286
+ return h4(
1287
+ Flex3,
1288
+ { direction: "column", gap: "xs" },
1289
+ h4(Flex3, { direction: "row", gap }, left, rightNode),
1290
+ footer || null
1291
+ );
1292
+ };
1293
+
1294
+ // src/datatable/DataTable.jsx
1295
+ import {
1296
+ Box as Box2,
1297
+ Button as Button3,
1298
+ Checkbox,
1299
+ CurrencyInput,
1300
+ DateInput as DateInput2,
1301
+ EmptyState,
1302
+ ErrorState,
1303
+ Flex as Flex4,
1304
+ Input,
1305
+ Link as Link2,
1306
+ MultiSelect as MultiSelect2,
1307
+ NumberInput,
1308
+ Select as Select2,
1309
+ StepperInput,
1310
+ Table,
1311
+ TableBody,
1312
+ TableCell,
1313
+ TableFooter,
1314
+ TableHead,
1315
+ TableHeader,
1316
+ TableRow,
1317
+ Tile,
1318
+ Text as Text2,
1319
+ TextArea,
1320
+ TimeInput,
1321
+ Toggle,
1322
+ Tooltip
1323
+ } from "@hubspot/ui-extensions";
1324
+
1325
+ // src/kanban/Kanban.jsx
1326
+ import React12, { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState3 } from "react";
1327
+
1328
+ // src/common-components/CollectionSortSelect.js
1329
+ import React10, { useId as useId2 } from "react";
1330
+ import { Select as Select3 } from "@hubspot/ui-extensions";
1331
+ var h5 = React10.createElement;
1332
+ var sanitizeId = (value) => String(value || "").replace(/[^a-zA-Z0-9_-]/g, "");
1333
+ var CollectionSortSelect = ({
1334
+ name = "collection-sort",
1335
+ value,
1336
+ options = [],
1337
+ placeholder = "Sort",
1338
+ onChange,
1339
+ includeEmpty = true,
1340
+ emptyValue = "",
1341
+ variant = "transparent",
1342
+ idPrefix,
1343
+ uniqueName = true
1344
+ }) => {
1345
+ const reactId = useId2();
1346
+ const instanceId = sanitizeId(idPrefix || reactId);
1347
+ const resolvedName = uniqueName && name ? `${name}-${instanceId}` : name;
1348
+ const mappedOptions = (options || []).map((option) => ({
1349
+ label: option.label,
1350
+ value: option.value
1351
+ }));
1352
+ return h5(Select3, {
1353
+ name: resolvedName,
1354
+ value: value ?? emptyValue,
1355
+ variant,
1356
+ placeholder,
1357
+ options: includeEmpty ? [{ label: placeholder, value: emptyValue }, ...mappedOptions] : mappedOptions,
1358
+ onChange
1359
+ });
1360
+ };
1361
+
1362
+ // src/common-components/StyledText.js
1363
+ import React11 from "react";
1364
+ import { Image as Image3, Tag as Tag3 } from "@hubspot/ui-extensions";
1365
+ var VARIANT_PRESETS = {
1366
+ bodytext: { fontSize: 14, lineHeight: 24, fontWeight: 400 },
1367
+ microcopy: { fontSize: 12, lineHeight: 18, fontWeight: 400 }
1368
+ };
1369
+ var WEIGHT_ALIASES = {
1370
+ bold: 700,
1371
+ demibold: 600,
1372
+ regular: 400
1373
+ };
1374
+ var LINE_DECORATION = {
1375
+ strikethrough: "line-through",
1376
+ underline: "underline"
1377
+ };
1378
+ var ORIENTATION_ROTATION = {
1379
+ horizontal: 0,
1380
+ "vertical-up": -90,
1381
+ "vertical-down": 90
1382
+ };
1383
+ var BACKGROUND_PRESETS = {
1384
+ tag: {
1385
+ color: HS_SUBTLE_BG,
1386
+ borderColor: HS_TAG_SUBTLE_BORDER,
1387
+ borderWidth: HS_TAG_BORDER_WIDTH,
1388
+ radius: HS_TAG_BORDER_RADIUS,
1389
+ paddingX: HS_TAG_PADDING_X,
1390
+ paddingY: HS_TAG_PADDING_Y,
1391
+ height: HS_TAG_LINE_HEIGHT,
1392
+ textColor: HS_TAG_TEXT_COLOR,
1393
+ fontSize: HS_TAG_FONT_SIZE,
1394
+ canvasPaddingX: 0,
1395
+ canvasPaddingY: 0
1396
+ }
1397
+ };
1398
+ var TAG_VARIANTS = {
1399
+ default: {
1400
+ color: HS_SUBTLE_BG,
1401
+ borderColor: HS_TAG_SUBTLE_BORDER,
1402
+ textColor: HS_TAG_TEXT_COLOR
1403
+ },
1404
+ success: {
1405
+ color: "#E5F8F6",
1406
+ borderColor: "#00BDA5",
1407
+ textColor: "#00BDA5"
1408
+ },
1409
+ warning: {
1410
+ color: "#FEF8F0",
1411
+ borderColor: "#F5C26B",
1412
+ textColor: "#D39913"
1413
+ },
1414
+ error: {
1415
+ color: "#FDEDEE",
1416
+ borderColor: "#F2545B",
1417
+ textColor: "#F2545B"
1418
+ },
1419
+ danger: {
1420
+ color: "#FDEDEE",
1421
+ borderColor: "#F2545B",
1422
+ textColor: "#F2545B"
1423
+ },
1424
+ info: {
1425
+ color: "#E5F5F8",
1426
+ borderColor: "#00A4BD",
1427
+ textColor: "#00A4BD"
1428
+ }
1429
+ };
1430
+ var NATIVE_TAG_VARIANT_ALIASES = {
1431
+ danger: "error"
1432
+ };
1433
+ var escapeSvgText = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1434
+ var applyTextTransform = (text, transform) => {
1435
+ if (!transform || transform === "none") return String(text);
1436
+ const s = String(text);
1437
+ switch (transform) {
1438
+ case "uppercase":
1439
+ return s.toUpperCase();
1440
+ case "lowercase":
1441
+ return s.toLowerCase();
1442
+ case "capitalize":
1443
+ return s.replace(/\b\w/g, (c) => c.toUpperCase());
1444
+ case "sentenceCase":
1445
+ return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
1446
+ default:
1447
+ return s;
1448
+ }
1449
+ };
1450
+ var estimateTextWidth = (text, fontSize) => Math.max(fontSize, Math.round(String(text).length * fontSize * 0.58));
1451
+ var resolveBackground = (background) => {
1452
+ if (!background) return null;
1453
+ const preset = background.preset ? BACKGROUND_PRESETS[background.preset] : null;
1454
+ const variant = background.preset === "tag" && background.variant ? TAG_VARIANTS[background.variant] || null : null;
1455
+ return {
1456
+ ...preset || {},
1457
+ ...variant || {},
1458
+ ...background
1459
+ };
1460
+ };
1461
+ var buildBackgroundRect = ({ background, x, y, width, height }) => {
1462
+ const radius = (background == null ? void 0 : background.radius) ?? 3;
1463
+ const fill = (background == null ? void 0 : background.color) ?? "transparent";
1464
+ const borderWidth = (background == null ? void 0 : background.borderWidth) ?? 0;
1465
+ const borderColor = background == null ? void 0 : background.borderColor;
1466
+ if (!borderColor || borderWidth <= 0) {
1467
+ return `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" />`;
1468
+ }
1469
+ const isTagPreset = (background == null ? void 0 : background.preset) === "tag";
1470
+ const fillRect = `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" />`;
1471
+ const strokeInset = borderWidth / 2;
1472
+ const strokeX = x + strokeInset;
1473
+ const strokeY = y + strokeInset;
1474
+ const strokeW = Math.max(0, width - borderWidth);
1475
+ const strokeH = Math.max(0, height - borderWidth);
1476
+ return fillRect + `<rect x="${strokeX}" y="${strokeY}" width="${strokeW}" height="${strokeH}" rx="${Math.max(
1477
+ 0,
1478
+ radius - strokeInset
1479
+ )}" fill="none" stroke="${borderColor}" stroke-width="${borderWidth}"${isTagPreset ? ` shape-rendering="crispEdges"` : ""} />`;
1480
+ };
1481
+ var canUseNativeTag = ({
1482
+ background,
1483
+ orientation,
1484
+ color,
1485
+ fontFamily,
1486
+ fontSize,
1487
+ width,
1488
+ height,
1489
+ paddingX,
1490
+ paddingY,
1491
+ format = {}
1492
+ }) => {
1493
+ if (!background || background.preset !== "tag") return false;
1494
+ const resolvedOrientation = typeof orientation === "number" ? orientation : ORIENTATION_ROTATION[orientation ?? "horizontal"] ?? 0;
1495
+ if (resolvedOrientation !== 0) return false;
1496
+ if (color != null || fontFamily != null || fontSize != null) return false;
1497
+ if (width != null || height != null || paddingX != null || paddingY != null) return false;
1498
+ if (background.color != null || background.textColor != null) return false;
1499
+ if (background.borderColor != null || background.borderWidth != null) return false;
1500
+ if (background.radius != null || background.height != null) return false;
1501
+ if (background.paddingX != null || background.paddingY != null) return false;
1502
+ if (background.canvasPaddingX != null || background.canvasPaddingY != null) return false;
1503
+ if (format.italic || format.lineDecoration) return false;
1504
+ if (format.textTransform && format.textTransform !== "none") return false;
1505
+ return true;
1506
+ };
1507
+ var makeStyledTextDataUri = (text, opts = {}) => {
1508
+ const {
1509
+ variant = "bodytext",
1510
+ format = {},
1511
+ orientation = "horizontal",
1512
+ color: colorProp = HS_TEXT_COLOR,
1513
+ fontFamily = HS_FONT_FAMILY,
1514
+ background: backgroundProp = null,
1515
+ paddingX: paddingXProp = 4,
1516
+ paddingY: paddingYProp = 2,
1517
+ width: widthOverride,
1518
+ height: heightOverride,
1519
+ fontSize: fontSizeOverride
1520
+ } = opts;
1521
+ const preset = VARIANT_PRESETS[variant] || VARIANT_PRESETS.bodytext;
1522
+ const background = resolveBackground(backgroundProp);
1523
+ const fontSize = fontSizeOverride ?? (background == null ? void 0 : background.fontSize) ?? preset.fontSize;
1524
+ const rawWeight = format.fontWeight;
1525
+ const fontWeight = rawWeight ? WEIGHT_ALIASES[rawWeight] ?? rawWeight : preset.fontWeight;
1526
+ const fontStyle = format.italic ? "italic" : "normal";
1527
+ const textDecoration = LINE_DECORATION[format.lineDecoration] || "none";
1528
+ const transformed = applyTextTransform(text, format.textTransform);
1529
+ const lineHeight = (background == null ? void 0 : background.height) ?? preset.lineHeight ?? fontSize;
1530
+ const color = (background == null ? void 0 : background.textColor) ?? colorProp;
1531
+ const paddingX = (background == null ? void 0 : background.canvasPaddingX) ?? paddingXProp;
1532
+ const paddingY = (background == null ? void 0 : background.canvasPaddingY) ?? paddingYProp;
1533
+ const rotate = typeof orientation === "number" ? orientation : ORIENTATION_ROTATION[orientation] ?? 0;
1534
+ const textW = estimateTextWidth(transformed, fontSize);
1535
+ let pillW = 0;
1536
+ let pillH = 0;
1537
+ if (background) {
1538
+ const bgPadX = background.paddingX ?? 6;
1539
+ const bgPadY = background.paddingY ?? 3;
1540
+ pillW = textW + bgPadX * 2;
1541
+ pillH = background.height ?? Math.max(lineHeight, fontSize + bgPadY * 2);
1542
+ }
1543
+ const intrinsicW = (background ? pillW : textW) + paddingX * 2;
1544
+ const intrinsicH = (background ? pillH : lineHeight) + paddingY * 2;
1545
+ const isOrthoRotation = rotate === 90 || rotate === -90 || rotate === 270;
1546
+ const canvasW = widthOverride ?? (isOrthoRotation ? intrinsicH : intrinsicW);
1547
+ const canvasH = heightOverride ?? (isOrthoRotation ? intrinsicW : intrinsicH);
1548
+ const cx = canvasW / 2;
1549
+ const cy = canvasH / 2;
1550
+ const rectX = cx - pillW / 2;
1551
+ const rectY = cy - pillH / 2;
1552
+ const group = (background ? buildBackgroundRect({
1553
+ background,
1554
+ x: rectX,
1555
+ y: rectY,
1556
+ width: pillW,
1557
+ height: pillH
1558
+ }) : "") + `<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>`;
1559
+ const wrapped = rotate ? `<g transform="rotate(${rotate} ${cx} ${cy})">${group}</g>` : group;
1560
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${canvasW}" height="${canvasH}">` + wrapped + `</svg>`;
1561
+ return {
1562
+ src: `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,
1563
+ width: canvasW,
1564
+ height: canvasH
1565
+ };
1462
1566
  };
1463
- var SEMANTIC_HEX = {
1464
- inherit: HS_TEXT_COLOR,
1465
- alert: "#F2545B",
1466
- warning: "#D39913",
1467
- success: "#00BDA5"
1567
+ var StyledText = ({
1568
+ children,
1569
+ text,
1570
+ alt,
1571
+ variant,
1572
+ format,
1573
+ orientation,
1574
+ color,
1575
+ background,
1576
+ fontFamily,
1577
+ fontSize,
1578
+ paddingX,
1579
+ paddingY,
1580
+ width,
1581
+ height
1582
+ }) => {
1583
+ const resolvedText = text ?? (typeof children === "string" ? children : "");
1584
+ if (canUseNativeTag({
1585
+ background,
1586
+ orientation,
1587
+ color,
1588
+ fontFamily,
1589
+ fontSize,
1590
+ width,
1591
+ height,
1592
+ paddingX,
1593
+ paddingY,
1594
+ format
1595
+ })) {
1596
+ const nativeVariant = NATIVE_TAG_VARIANT_ALIASES[background == null ? void 0 : background.variant] ?? (background == null ? void 0 : background.variant) ?? "default";
1597
+ return React11.createElement(Tag3, { variant: nativeVariant }, resolvedText);
1598
+ }
1599
+ const { src, width: w, height: h6 } = makeStyledTextDataUri(resolvedText, {
1600
+ variant,
1601
+ format,
1602
+ orientation,
1603
+ color,
1604
+ background,
1605
+ fontFamily,
1606
+ fontSize,
1607
+ paddingX,
1608
+ paddingY,
1609
+ width,
1610
+ height
1611
+ });
1612
+ return React11.createElement(Image3, {
1613
+ src,
1614
+ width: w,
1615
+ height: h6,
1616
+ alt: alt ?? String(resolvedText)
1617
+ });
1468
1618
  };
1469
- var SIZE_TOKENS2 = {
1470
- xs: 12,
1471
- "extra-small": 12,
1472
- sm: 14,
1473
- "small": 14,
1474
- md: 16,
1475
- "med": 16,
1476
- "medium": 16,
1477
- lg: 20,
1478
- "large": 20,
1479
- xl: 24,
1480
- "extra-large": 24
1619
+
1620
+ // src/kanban/Kanban.jsx
1621
+ import {
1622
+ Alert,
1623
+ AutoGrid,
1624
+ Box as Box3,
1625
+ Button as Button4,
1626
+ Checkbox as Checkbox2,
1627
+ DescriptionList,
1628
+ DescriptionListItem,
1629
+ Divider,
1630
+ Dropdown,
1631
+ EmptyState as EmptyState2,
1632
+ Flex as Flex5,
1633
+ Image as Image4,
1634
+ Inline,
1635
+ Link as Link3,
1636
+ LoadingSpinner,
1637
+ Select as Select4,
1638
+ Statistics,
1639
+ StatisticsItem,
1640
+ StatisticsTrend,
1641
+ Tag as Tag4,
1642
+ Text as Text3,
1643
+ Tile as Tile2
1644
+ } from "@hubspot/ui-extensions";
1645
+
1646
+ // src/utils/objectPath.js
1647
+ var getByPath = (obj, path) => {
1648
+ if (!path) return void 0;
1649
+ if (typeof path === "function") return path(obj);
1650
+ return String(path).split(".").reduce((acc, key) => acc == null ? void 0 : acc[key], obj);
1651
+ };
1652
+
1653
+ // src/utils/crmSearchAdapters.js
1654
+ var EMPTY_ARRAY = [];
1655
+ var EMPTY_OBJECT = {};
1656
+ var isPlainObject = (value) => value != null && Object.prototype.toString.call(value) === "[object Object]";
1657
+ var coerceError = (error) => {
1658
+ if (!error) return false;
1659
+ if (typeof error === "string") return error;
1660
+ if (error.message) return error.message;
1661
+ return true;
1662
+ };
1663
+ var pickArray = (response) => {
1664
+ if (Array.isArray(response)) return response;
1665
+ if (!response) return EMPTY_ARRAY;
1666
+ return response.results || response.data || response.items || response.records || response.objects || EMPTY_ARRAY;
1667
+ };
1668
+ var pickTotal = (response, fallbackLength) => {
1669
+ var _a;
1670
+ if (!response || Array.isArray(response)) return fallbackLength;
1671
+ return response.total ?? response.totalCount ?? response.totalResults ?? ((_a = response.paging) == null ? void 0 : _a.total) ?? fallbackLength;
1672
+ };
1673
+ var normalizeCrmSearchRecord = (record, options = EMPTY_OBJECT) => {
1674
+ const {
1675
+ idField = "id",
1676
+ objectIdField = "objectId",
1677
+ propertiesKey = "properties",
1678
+ flattenProperties = true,
1679
+ propertyValueKey,
1680
+ mapRecord
1681
+ } = options;
1682
+ if (mapRecord) return mapRecord(record);
1683
+ 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`);
1684
+ const properties = (record == null ? void 0 : record[propertiesKey]) || EMPTY_OBJECT;
1685
+ const flattened = {};
1686
+ if (flattenProperties && isPlainObject(properties)) {
1687
+ for (const [key, value] of Object.entries(properties)) {
1688
+ flattened[key] = propertyValueKey && isPlainObject(value) ? value[propertyValueKey] : value;
1689
+ }
1690
+ }
1691
+ return {
1692
+ ...flattenProperties ? flattened : EMPTY_OBJECT,
1693
+ ...record,
1694
+ [idField]: objectId,
1695
+ [objectIdField]: objectId,
1696
+ [propertiesKey]: properties
1697
+ };
1698
+ };
1699
+ var normalizeCrmSearchRows = (response, options = EMPTY_OBJECT) => {
1700
+ const records = pickArray(response);
1701
+ return records.map((record) => normalizeCrmSearchRecord(record, options));
1702
+ };
1703
+ var STABLE_SORT_TIEBREAKER = { propertyName: "hs_object_id", direction: "ASCENDING" };
1704
+ var withStableSort = (sorts) => {
1705
+ const base = Array.isArray(sorts) ? sorts : [];
1706
+ if (base.some((s) => s && s.propertyName === STABLE_SORT_TIEBREAKER.propertyName)) return base;
1707
+ return [...base, STABLE_SORT_TIEBREAKER];
1708
+ };
1709
+ var buildCrmSearchConfig = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
1710
+ const {
1711
+ objectType,
1712
+ properties = EMPTY_ARRAY,
1713
+ query,
1714
+ filterGroups,
1715
+ sorts,
1716
+ pageLength,
1717
+ propertyMap = EMPTY_OBJECT,
1718
+ filterMap,
1719
+ sortMap,
1720
+ baseConfig = EMPTY_OBJECT
1721
+ } = options;
1722
+ const mappedFilters = filterMap ? filterMap(params.filters || EMPTY_OBJECT, params) : filterGroups;
1723
+ const mappedSorts = sortMap ? sortMap(params.sort || EMPTY_OBJECT, params) : sorts;
1724
+ const config = {
1725
+ ...baseConfig,
1726
+ objectType: objectType || baseConfig.objectType,
1727
+ properties: properties.length ? properties : baseConfig.properties,
1728
+ query: query ?? params.search ?? baseConfig.query,
1729
+ filterGroups: mappedFilters ?? baseConfig.filterGroups,
1730
+ sorts: withStableSort(mappedSorts ?? baseConfig.sorts),
1731
+ pageLength: pageLength ?? params.pageLength ?? baseConfig.pageLength
1732
+ };
1733
+ if (!filterMap && propertyMap && Object.keys(propertyMap).length && params.filters) {
1734
+ const filters = Object.entries(params.filters).filter(([, value]) => value !== void 0 && value !== null && value !== "" && !(Array.isArray(value) && value.length === 0)).map(([name, value]) => {
1735
+ const propertyName = propertyMap[name] || name;
1736
+ if (Array.isArray(value)) return { propertyName, operator: "IN", values: value };
1737
+ if (isPlainObject(value) && (value.from || value.to)) {
1738
+ const rangeFilters = [];
1739
+ if (value.from) rangeFilters.push({ propertyName, operator: "GTE", value: value.from });
1740
+ if (value.to) rangeFilters.push({ propertyName, operator: "LTE", value: value.to });
1741
+ return rangeFilters;
1742
+ }
1743
+ return { propertyName, operator: "EQ", value };
1744
+ }).flat();
1745
+ if (filters.length) config.filterGroups = [{ filters }];
1746
+ }
1747
+ Object.keys(config).forEach((key) => config[key] === void 0 && delete config[key]);
1748
+ return config;
1749
+ };
1750
+ var useCrmSearchDataSource = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
1751
+ const {
1752
+ format,
1753
+ row,
1754
+ rowIdField = "id",
1755
+ totalCount,
1756
+ loading,
1757
+ error,
1758
+ mapResponse
1759
+ } = options;
1760
+ const config = useMemo3(() => buildCrmSearchConfig(params, options), [params, options]);
1761
+ const response = useCrmSearch(config, format);
1762
+ return useMemo3(() => {
1763
+ var _a;
1764
+ const rows = mapResponse ? mapResponse(response) : normalizeCrmSearchRows(response, { idField: rowIdField, ...row || EMPTY_OBJECT });
1765
+ const resolvedTotal = typeof totalCount === "function" ? totalCount(response) : totalCount ?? pickTotal(response, rows.length);
1766
+ return {
1767
+ data: rows,
1768
+ rows,
1769
+ response,
1770
+ pagination: response == null ? void 0 : response.pagination,
1771
+ hasMore: ((_a = response == null ? void 0 : response.pagination) == null ? void 0 : _a.hasNextPage) ?? (response == null ? void 0 : response.hasMore) ?? false,
1772
+ loading: typeof loading === "function" ? loading(response) : loading ?? !!(response == null ? void 0 : response.isLoading),
1773
+ isLoading: typeof loading === "function" ? loading(response) : loading ?? !!(response == null ? void 0 : response.isLoading),
1774
+ error: typeof error === "function" ? error(response) : error ?? coerceError(response == null ? void 0 : response.error),
1775
+ totalCount: resolvedTotal,
1776
+ rowIdField
1777
+ };
1778
+ }, [response, mapResponse, row, rowIdField, totalCount, loading, error]);
1779
+ };
1780
+ var crmSearchResultToOption = (row, options = EMPTY_OBJECT) => {
1781
+ const {
1782
+ label = "name",
1783
+ value = "objectId",
1784
+ description,
1785
+ fallbackLabel = "Untitled record",
1786
+ mapOption
1787
+ } = options;
1788
+ if (mapOption) return mapOption(row);
1789
+ const option = {
1790
+ label: getByPath(row, label) ?? getByPath(row, "properties.name") ?? fallbackLabel,
1791
+ value: getByPath(row, value) ?? getByPath(row, "id") ?? getByPath(row, "objectId")
1792
+ };
1793
+ const desc = getByPath(row, description);
1794
+ if (desc != null && desc !== "") option.description = desc;
1795
+ return option;
1481
1796
  };
1482
- var resolveSize2 = (size) => {
1483
- if (typeof size === "number") return size;
1484
- if (typeof size === "string" && SIZE_TOKENS2[size] != null) return SIZE_TOKENS2[size];
1485
- return 16;
1797
+ var useCrmSearchOptions = (params = EMPTY_OBJECT, options = EMPTY_OBJECT) => {
1798
+ const dataSource = useCrmSearchDataSource(params, options);
1799
+ const optionConfig = options.option || options;
1800
+ return useMemo3(() => ({
1801
+ ...dataSource,
1802
+ options: dataSource.rows.map((row) => crmSearchResultToOption(row, optionConfig))
1803
+ }), [dataSource, optionConfig]);
1486
1804
  };
1487
- var escapeXmlAttr2 = (s) => String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1488
- var PLUS_24 = "M11 5h2v6h6v2h-6v6h-2v-6H5v-2h6z";
1489
- var CURATED_ICONS = {
1490
- checkBare: { paths: ["M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z"] },
1491
- plusBare: { paths: [PLUS_24] },
1492
- // Close = a plus rotated 45° → a clean X (reuses transform support, no new
1493
- // path data to hand-author/get-wrong).
1494
- Close: { paths: [PLUS_24], transform: "rotate(45 12 12)" },
1495
- // Universal media-transport glyphs, authored at 32×32 to match the scraped
1496
- // canvas so they sit at the same optical size as the rest of the set.
1497
- Play: { viewBox: "0 0 32 32", paths: ["M9 6v20l17-10z"] },
1498
- Pause: { viewBox: "0 0 32 32", paths: ["M10 6h4v20h-4z M18 6h4v20h-4z"] },
1499
- Stop: { viewBox: "0 0 32 32", paths: ["M8 8h16v16H8z"] },
1500
- // Hand-authored to match HubSpot's filled/rounded weight (verified visually
1501
- // against the scraped set) for genuine gaps the scrape didn't cover.
1502
- Unlocked: {
1503
- viewBox: "0 0 32 32",
1504
- paths: [
1505
- "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",
1506
- { 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" }
1507
- ]
1508
- },
1509
- Print: {
1510
- viewBox: "0 0 32 32",
1511
- paths: [
1512
- "M9 4h14a1 1 0 0 1 1 1v6H8V5a1 1 0 0 1 1-1z",
1513
- { 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" },
1514
- "M10 21h12v6a1 1 0 0 1-1 1H11a1 1 0 0 1-1-1z"
1515
- ]
1516
- },
1517
- Shrink: {
1518
- viewBox: "0 0 32 32",
1519
- paths: [
1520
- "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",
1521
- "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"
1522
- ]
1523
- },
1524
- 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"] },
1525
- 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"] },
1526
- // Dark = crescent moon, pairs with the scraped Light (sun) for theme toggles.
1527
- 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"] },
1528
- 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"] },
1529
- 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"] },
1530
- // Backward = horizontal mirror of the scraped Forward arrow (x' = 32 - x).
1531
- // Derived so it stays in sync if Forward is ever re-scraped.
1532
- ...GENERATED_ICONS.Forward ? { Backward: { ...GENERATED_ICONS.Forward, transform: "translate(32 0) scale(-1 1)" } } : {}
1805
+ var CRM_OBJECT_TYPES = {
1806
+ contact: "0-1",
1807
+ contacts: "0-1",
1808
+ company: "0-2",
1809
+ companies: "0-2",
1810
+ deal: "0-3",
1811
+ deals: "0-3"
1533
1812
  };
1534
- var ICONS = { ...GENERATED_ICONS, ...CURATED_ICONS };
1535
- var svgToIconEntry = (raw) => {
1536
- const vb = /viewBox="([^"]+)"/i.exec(raw);
1537
- const viewBox = vb ? vb[1] : "0 0 24 24";
1538
- const rendered = raw.replace(
1539
- /<(mask|defs|clipPath|symbol)\b[^>]*>[\s\S]*?<\/\1>/gi,
1540
- ""
1813
+ var resolveCrmObjectType = (objectType) => CRM_OBJECT_TYPES[objectType] || objectType;
1814
+
1815
+ // src/common-components/CrmLookupSelect.js
1816
+ var EMPTY_ARRAY2 = [];
1817
+ var EMPTY_OBJECT2 = {};
1818
+ var makeOptionConfig = ({ option, labelProperty, valueProperty, descriptionProperty }) => ({
1819
+ label: labelProperty || (option == null ? void 0 : option.label) || "name",
1820
+ value: valueProperty || (option == null ? void 0 : option.value) || "objectId",
1821
+ description: descriptionProperty || (option == null ? void 0 : option.description),
1822
+ fallbackLabel: option == null ? void 0 : option.fallbackLabel,
1823
+ mapOption: option == null ? void 0 : option.mapOption
1824
+ });
1825
+ var mergeSelectedOptions = (options, selectedOptions, value) => {
1826
+ const selected = Array.isArray(selectedOptions) ? selectedOptions : selectedOptions ? [selectedOptions] : EMPTY_ARRAY2;
1827
+ if (!selected.length) return options;
1828
+ const values = new Set(options.map((opt) => opt.value));
1829
+ const activeValues = new Set(Array.isArray(value) ? value : value == null || value === "" ? [] : [value]);
1830
+ const missing = selected.filter((opt) => activeValues.has(opt.value) && !values.has(opt.value));
1831
+ return missing.length ? [...missing, ...options] : options;
1832
+ };
1833
+ var CrmLookupSelect = ({
1834
+ objectType,
1835
+ properties = EMPTY_ARRAY2,
1836
+ name,
1837
+ label,
1838
+ value,
1839
+ onChange,
1840
+ multiple = false,
1841
+ placeholder,
1842
+ description,
1843
+ tooltip,
1844
+ required,
1845
+ readOnly,
1846
+ error,
1847
+ validationMessage,
1848
+ variant,
1849
+ debounce = 300,
1850
+ minSearchLength = 0,
1851
+ pageLength = 20,
1852
+ option,
1853
+ labelProperty,
1854
+ valueProperty = "objectId",
1855
+ descriptionProperty,
1856
+ selectedOptions,
1857
+ format,
1858
+ row,
1859
+ baseConfig,
1860
+ query,
1861
+ onSearchChange,
1862
+ noResultsOption,
1863
+ loadingOption,
1864
+ selectProps = EMPTY_OBJECT2
1865
+ }) => {
1866
+ const [inputValue, setInputValue] = useState5(query || "");
1867
+ const [pickedOptions, setPickedOptions] = useState5(EMPTY_ARRAY2);
1868
+ const debouncedInput = useDebounce2(inputValue, debounce > 0 ? debounce : 1);
1869
+ const search = debounce > 0 ? debouncedInput : inputValue;
1870
+ const effectiveSearch = search && search.length >= minSearchLength ? search : "";
1871
+ const optionConfig = useMemo4(
1872
+ () => makeOptionConfig({ option, labelProperty, valueProperty, descriptionProperty }),
1873
+ [option, labelProperty, valueProperty, descriptionProperty]
1541
1874
  );
1542
- const paths = [];
1543
- const pathRe = /<path\b([^>]*?)\/?>/gi;
1544
- let m;
1545
- while ((m = pathRe.exec(rendered)) !== null) {
1546
- const attrs = m[1];
1547
- const d = /\bd="([^"]+)"/i.exec(attrs);
1548
- if (!d) continue;
1549
- const fill = /\bfill="([^"]+)"/i.exec(attrs);
1550
- const fillRule = /\bfill-rule="([^"]+)"/i.exec(attrs);
1551
- const keepFill = fill && fill[1] !== "currentColor" && fill[1] !== "none";
1552
- if (keepFill || fillRule) {
1553
- paths.push({
1554
- d: d[1],
1555
- ...keepFill ? { fill: fill[1] } : {},
1556
- ...fillRule ? { fillRule: fillRule[1] } : {}
1875
+ const dataSource = useCrmSearchOptions(
1876
+ { search: effectiveSearch },
1877
+ {
1878
+ objectType: resolveCrmObjectType(objectType),
1879
+ properties,
1880
+ pageLength,
1881
+ format,
1882
+ baseConfig,
1883
+ row: row || { mapRecord: (record) => ({ objectId: record.objectId, ...record.properties }) },
1884
+ option: optionConfig
1885
+ }
1886
+ );
1887
+ const isSearching = dataSource.loading || inputValue.trim() !== (search || "").trim();
1888
+ const hasQuery = effectiveSearch.length > 0;
1889
+ const options = useMemo4(() => {
1890
+ const remembered = [...selectedOptions || EMPTY_ARRAY2, ...pickedOptions];
1891
+ const baseOptions = mergeSelectedOptions(dataSource.options || EMPTY_ARRAY2, remembered, value);
1892
+ if (isSearching && loadingOption) return [loadingOption, ...baseOptions];
1893
+ if (!isSearching && hasQuery && !baseOptions.length && noResultsOption) return [noResultsOption];
1894
+ return baseOptions;
1895
+ }, [dataSource.options, isSearching, hasQuery, selectedOptions, pickedOptions, value, loadingOption, noResultsOption]);
1896
+ const handleChange = (next) => {
1897
+ const nextValues = Array.isArray(next) ? next : next == null || next === "" ? EMPTY_ARRAY2 : [next];
1898
+ 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));
1899
+ if (picked.length) {
1900
+ setPickedOptions((prev) => {
1901
+ const byValue = new Map(prev.map((o) => [o.value, o]));
1902
+ picked.forEach((o) => byValue.set(o.value, o));
1903
+ return [...byValue.values()];
1557
1904
  });
1558
- } else {
1559
- paths.push(d[1]);
1560
1905
  }
1561
- }
1562
- return { viewBox, paths };
1563
- };
1564
- 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);
1565
- var makeIconDataUri = (nameOrEntry, opts = {}) => {
1566
- const entry = nameOrEntry && typeof nameOrEntry === "object" ? nameOrEntry : ICONS[nameOrEntry];
1567
- if (!entry || !entry.paths) return null;
1568
- const size = resolveSize2(opts.size);
1569
- const rawColor = opts.color;
1570
- const color = rawColor && SEMANTIC_HEX[rawColor] || rawColor || HS_TEXT_COLOR;
1571
- const viewBox = entry.viewBox ?? "0 0 24 24";
1572
- const body = entry.paths.map((p) => {
1573
- const d = typeof p === "string" ? p : p.d;
1574
- const fill = typeof p === "object" && p.fill || color;
1575
- const fillRule = typeof p === "object" && p.fillRule ? ` fill-rule="${p.fillRule}"` : "";
1576
- return `<path d="${escapeXmlAttr2(d)}" fill="${fill}"${fillRule} />`;
1577
- }).join("");
1578
- const inner = entry.transform ? `<g transform="${escapeXmlAttr2(entry.transform)}">${body}</g>` : body;
1579
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="${viewBox}">` + inner + `</svg>`;
1580
- return {
1581
- src: `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,
1582
- width: size,
1583
- height: size
1906
+ if (onChange) onChange(next);
1907
+ };
1908
+ const commonProps = {
1909
+ name,
1910
+ label,
1911
+ value: multiple ? value || EMPTY_ARRAY2 : value,
1912
+ options,
1913
+ placeholder: placeholder || (dataSource.loading ? "Searching CRM..." : "Search CRM records..."),
1914
+ description,
1915
+ tooltip,
1916
+ required,
1917
+ readOnly,
1918
+ error: error || !!dataSource.error,
1919
+ validationMessage: validationMessage || (typeof dataSource.error === "string" ? dataSource.error : void 0),
1920
+ variant,
1921
+ onChange: handleChange,
1922
+ onInput: (next) => {
1923
+ setInputValue(next || "");
1924
+ if (onSearchChange) onSearchChange(next || "");
1925
+ },
1926
+ ...selectProps
1584
1927
  };
1928
+ return React14.createElement(multiple ? MultiSelect3 : Select5, commonProps);
1585
1929
  };
1586
- var ICON_NAMES = Object.keys(ICONS);
1587
- var NATIVE_ICON_NAME_LIST = [...NATIVE_ICON_NAMES].sort(
1588
- (a, b) => a.localeCompare(b)
1589
- );
1590
- var Icon3 = ({ name, color, size, screenReaderText }) => {
1591
- if (canUseNative(name, color, size)) {
1592
- return React9.createElement(HsIcon, {
1593
- name,
1594
- ...color != null ? { color } : {},
1595
- ...size != null ? { size: NATIVE_SIZE_TOKENS[size] } : {},
1596
- ...screenReaderText != null ? { screenReaderText } : {}
1597
- });
1598
- }
1599
- const result = makeIconDataUri(name, { size, color });
1600
- if (!result) return null;
1601
- return React9.createElement(Image4, {
1602
- src: result.src,
1603
- width: result.width,
1604
- height: result.height,
1605
- alt: screenReaderText ?? name
1606
- });
1930
+
1931
+ // src/common-components/datePresets.js
1932
+ var HS_DATE_PRESETS = [
1933
+ { label: "Today", value: "today" },
1934
+ { label: "Yesterday", value: "yesterday" },
1935
+ { label: "Tomorrow", value: "tomorrow" },
1936
+ { label: "This week", value: "this_week" },
1937
+ { label: "Last week", value: "last_week" },
1938
+ { label: "Last 7 days", value: "7d" },
1939
+ { label: "Last 30 days", value: "30d" },
1940
+ { label: "Last 90 days", value: "90d" },
1941
+ { label: "This month", value: "this_month" },
1942
+ { label: "Last month", value: "last_month" },
1943
+ { label: "This quarter", value: "this_quarter" },
1944
+ { label: "Last quarter", value: "last_quarter" },
1945
+ { label: "This year", value: "this_year" },
1946
+ { label: "Last year", value: "last_year" }
1947
+ ];
1948
+ var HS_DATE_DIRECTION_LABELS = {
1949
+ asc: "Ascending",
1950
+ desc: "Descending"
1607
1951
  };
1608
1952
 
1609
1953
  // src/common-components/KeyValueList.js
1610
- import React10 from "react";
1611
- import { DescriptionList as DescriptionList2, DescriptionListItem as DescriptionListItem2, Flex as Flex4 } from "@hubspot/ui-extensions";
1954
+ import React15 from "react";
1955
+ import { DescriptionList as DescriptionList2, DescriptionListItem as DescriptionListItem2, Flex as Flex7 } from "@hubspot/ui-extensions";
1612
1956
  var KeyValueList = ({ items = [], direction = "row", gap = "sm" }) => {
1613
1957
  const rows = items.map(
1614
- (item, index) => React10.createElement(
1958
+ (item, index) => React15.createElement(
1615
1959
  DescriptionListItem2,
1616
1960
  {
1617
1961
  key: item.key ?? item.label ?? `kv-${index}`,
@@ -1620,16 +1964,16 @@ var KeyValueList = ({ items = [], direction = "row", gap = "sm" }) => {
1620
1964
  item.value
1621
1965
  )
1622
1966
  );
1623
- return React10.createElement(
1624
- Flex4,
1967
+ return React15.createElement(
1968
+ Flex7,
1625
1969
  { direction: "column", gap },
1626
- React10.createElement(DescriptionList2, { direction }, ...rows)
1970
+ React15.createElement(DescriptionList2, { direction }, ...rows)
1627
1971
  );
1628
1972
  };
1629
1973
 
1630
1974
  // src/common-components/SectionHeader.js
1631
- import React11 from "react";
1632
- import { Flex as Flex5, Heading, Text as Text4 } from "@hubspot/ui-extensions";
1975
+ import React16 from "react";
1976
+ import { Flex as Flex8, Heading, Text as Text5 } from "@hubspot/ui-extensions";
1633
1977
  var SectionHeader = ({
1634
1978
  title,
1635
1979
  description,
@@ -1640,12 +1984,12 @@ var SectionHeader = ({
1640
1984
  }) => {
1641
1985
  const body = [];
1642
1986
  if (title != null) {
1643
- body.push(React11.createElement(Heading, { key: "title", as: titleAs }, title));
1987
+ body.push(React16.createElement(Heading, { key: "title", as: titleAs }, title));
1644
1988
  }
1645
1989
  if (description != null) {
1646
1990
  body.push(
1647
- React11.createElement(
1648
- Text4,
1991
+ React16.createElement(
1992
+ Text5,
1649
1993
  { key: "description", variant: "microcopy" },
1650
1994
  description
1651
1995
  )
@@ -1654,10 +1998,10 @@ var SectionHeader = ({
1654
1998
  if (children != null) {
1655
1999
  body.push(children);
1656
2000
  }
1657
- const content = React11.createElement(Flex5, { direction: "column", gap }, ...body);
2001
+ const content = React16.createElement(Flex8, { direction: "column", gap }, ...body);
1658
2002
  if (actions == null) return content;
1659
- return React11.createElement(
1660
- Flex5,
2003
+ return React16.createElement(
2004
+ Flex8,
1661
2005
  { direction: "row", justify: "between", align: "start", gap: "sm" },
1662
2006
  content,
1663
2007
  actions
@@ -1665,8 +2009,8 @@ var SectionHeader = ({
1665
2009
  };
1666
2010
 
1667
2011
  // src/common-components/Spinner.js
1668
- import React12, { useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
1669
- import { Text as Text5 } from "@hubspot/ui-extensions";
2012
+ import React17, { useEffect as useEffect5, useRef as useRef5, useState as useState6 } from "react";
2013
+ import { Text as Text6 } from "@hubspot/ui-extensions";
1670
2014
 
1671
2015
  // src/common-components/spinners.js
1672
2016
  var BRAILLE_DOT_MAP = [
@@ -2006,7 +2350,7 @@ var Spinner = ({
2006
2350
  const preset = SPINNERS[name] || SPINNERS[DEFAULT_NAME];
2007
2351
  const resolvedFrames = Array.isArray(frames) && frames.length > 0 ? frames : preset.frames;
2008
2352
  const resolvedInterval = Number.isFinite(interval) ? interval : preset.interval;
2009
- const [index, setIndex] = useState5(0);
2353
+ const [index, setIndex] = useState6(0);
2010
2354
  const indexRef = useRef5(0);
2011
2355
  indexRef.current = index;
2012
2356
  useEffect5(() => {
@@ -2024,10 +2368,10 @@ var Spinner = ({
2024
2368
  const frame = resolvedFrames[index % resolvedFrames.length];
2025
2369
  const suffix = children != null ? children : label;
2026
2370
  if (suffix == null || suffix === "") {
2027
- return React12.createElement(Text5, rest, frame);
2371
+ return React17.createElement(Text6, rest, frame);
2028
2372
  }
2029
- return React12.createElement(
2030
- Text5,
2373
+ return React17.createElement(
2374
+ Text6,
2031
2375
  rest,
2032
2376
  frame,
2033
2377
  gap,
@@ -2035,9 +2379,14 @@ var Spinner = ({
2035
2379
  );
2036
2380
  };
2037
2381
  export {
2382
+ ActiveFilterChips,
2038
2383
  AutoStatusTag,
2039
2384
  AutoTag,
2040
2385
  AvatarStack,
2386
+ CollectionCount,
2387
+ CollectionFilterControl,
2388
+ CollectionSortSelect,
2389
+ CollectionToolbar,
2041
2390
  CrmLookupSelect,
2042
2391
  DEFAULT_SVG_FONT_WEIGHT,
2043
2392
  HS_DATE_DIRECTION_LABELS,
@@ -2057,7 +2406,7 @@ export {
2057
2406
  HS_TEXT_COLOR,
2058
2407
  ICONS,
2059
2408
  ICON_NAMES,
2060
- Icon3 as Icon,
2409
+ Icon,
2061
2410
  KeyValueList,
2062
2411
  NATIVE_ICON_NAME_LIST,
2063
2412
  SPINNERS,
@@ -2065,6 +2414,7 @@ export {
2065
2414
  SectionHeader,
2066
2415
  Spinner,
2067
2416
  StyledText,
2417
+ formatCollectionCount,
2068
2418
  gridToBraille,
2069
2419
  makeAvatarStackDataUri,
2070
2420
  makeGrid,