hs-uix 1.5.1 → 1.6.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.
package/dist/index.mjs CHANGED
@@ -943,7 +943,7 @@ var DataTable = ({
943
943
  }
944
944
  return isEmpty ? "--" : content;
945
945
  };
946
- const renderFilterControl = (filter) => {
946
+ const renderFilterControl2 = (filter) => {
947
947
  const type = filter.type || "select";
948
948
  if (type === "multiselect") {
949
949
  return /* @__PURE__ */ React.createElement(
@@ -1008,7 +1008,7 @@ var DataTable = ({
1008
1008
  value: internalSearchTerm,
1009
1009
  onChange: handleSearchChange
1010
1010
  }
1011
- ), filters.slice(0, filterInlineLimit).map(renderFilterControl), filters.length > filterInlineLimit && /* @__PURE__ */ React.createElement(
1011
+ ), filters.slice(0, filterInlineLimit).map(renderFilterControl2), filters.length > filterInlineLimit && /* @__PURE__ */ React.createElement(
1012
1012
  Button,
1013
1013
  {
1014
1014
  variant: "transparent",
@@ -1018,7 +1018,7 @@ var DataTable = ({
1018
1018
  /* @__PURE__ */ React.createElement(Icon, { name: "filter", size: "sm" }),
1019
1019
  " ",
1020
1020
  resolvedFiltersButtonLabel
1021
- )), showMoreFilters && filters.length > filterInlineLimit && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, filters.slice(filterInlineLimit).map(renderFilterControl)), activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showFilterBadges && activeChips.map((chip) => /* @__PURE__ */ React.createElement(Tag, { key: chip.key, variant: "default", onDelete: () => handleFilterRemove(chip.key) }, chip.label)), showClearFiltersButton && /* @__PURE__ */ React.createElement(
1021
+ )), showMoreFilters && filters.length > filterInlineLimit && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, filters.slice(filterInlineLimit).map(renderFilterControl2)), activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showFilterBadges && activeChips.map((chip) => /* @__PURE__ */ React.createElement(Tag, { key: chip.key, variant: "default", onDelete: () => handleFilterRemove(chip.key) }, chip.label)), showClearFiltersButton && /* @__PURE__ */ React.createElement(
1022
1022
  Button,
1023
1023
  {
1024
1024
  variant: "transparent",
@@ -3155,6 +3155,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3155
3155
  }
3156
3156
  };
3157
3157
  const getFieldColSpan = (field) => {
3158
+ if (field.colSpan === "full") return columns;
3158
3159
  if (field.colSpan != null) return Math.min(field.colSpan, columns);
3159
3160
  if (field.width === "full" && columns > 1) return columns;
3160
3161
  if (columns > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return columns;
@@ -3180,6 +3181,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3180
3181
  let currentRowSpan = 0;
3181
3182
  const gridColumnWidth = 200;
3182
3183
  const colSpan = (field) => {
3184
+ if (field.colSpan === "full") return cols;
3183
3185
  if (field.colSpan != null) return Math.min(field.colSpan, cols);
3184
3186
  if (field.width === "full" && cols > 1) return cols;
3185
3187
  if (cols > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return cols;
@@ -3313,8 +3315,16 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3313
3315
  }
3314
3316
  batch = [];
3315
3317
  };
3318
+ const isFullSpan = (f) => f.colSpan === "full" || typeof f.colSpan === "number" && f.colSpan > 1;
3316
3319
  for (const field of fieldList) {
3317
3320
  if (isDependent(field)) continue;
3321
+ if (isFullSpan(field)) {
3322
+ flushBatch();
3323
+ elements.push(
3324
+ /* @__PURE__ */ React2.createElement(React2.Fragment, { key: `full-${field.name}` }, renderField(field))
3325
+ );
3326
+ continue;
3327
+ }
3318
3328
  batch.push(field);
3319
3329
  const dependents = getDependents(field);
3320
3330
  if (dependents.length > 0) {
@@ -3326,6 +3336,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3326
3336
  return elements;
3327
3337
  };
3328
3338
  const wrapWithGroups = (fieldList, renderFn) => {
3339
+ const formatGroupLabel = (groupName) => String(groupName || "").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().replace(/\b\w/g, (char) => char.toUpperCase());
3329
3340
  const hasGroups = fieldList.some((f) => f.group);
3330
3341
  if (!hasGroups) return renderFn(fieldList);
3331
3342
  const chunks = [];
@@ -3347,6 +3358,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3347
3358
  for (let i = 0; i < chunks.length; i++) {
3348
3359
  const chunk = chunks[i];
3349
3360
  const opts = chunk.group && groups && groups[chunk.group] || {};
3361
+ const resolvedGroupLabel = opts.label || formatGroupLabel(chunk.group);
3350
3362
  const showDivider = opts.showDivider !== false;
3351
3363
  const showLabel = opts.showLabel !== false;
3352
3364
  if (i > 0 && showDivider) {
@@ -3360,8 +3372,13 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3360
3372
  );
3361
3373
  } else {
3362
3374
  elements.push(
3363
- /* @__PURE__ */ React2.createElement(Text2, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, opts.label || chunk.group)
3375
+ /* @__PURE__ */ React2.createElement(Text2, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, resolvedGroupLabel)
3364
3376
  );
3377
+ if (opts.description) {
3378
+ elements.push(
3379
+ /* @__PURE__ */ React2.createElement(Text2, { key: `group-description-${i}`, variant: "microcopy" }, opts.description)
3380
+ );
3381
+ }
3365
3382
  }
3366
3383
  }
3367
3384
  const chunkElements = renderFn(chunk.fields);
@@ -3509,9 +3526,1498 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3509
3526
  );
3510
3527
  });
3511
3528
 
3512
- // src/common-components/AutoTag.js
3529
+ // packages/kanban/src/Kanban.jsx
3530
+ import React4, { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
3531
+ import Fuse2 from "fuse.js";
3532
+
3533
+ // src/common-components/StyledText.js
3513
3534
  import React3 from "react";
3514
- import { Tag as Tag2 } from "@hubspot/ui-extensions";
3535
+ import { Image, Tag as Tag2 } from "@hubspot/ui-extensions";
3536
+
3537
+ // src/common-components/svgDefaults.js
3538
+ var HS_FONT_FAMILY = '"Lexend Deca", Helvetica, Arial, sans-serif';
3539
+ var HS_TEXT_COLOR = "#33475b";
3540
+ var HS_SUBTLE_BG = "#F5F8FA";
3541
+ var HS_MUTED_TEXT = "#7C98B6";
3542
+ var HS_NEUTRAL_CHIP = "#CBD6E2";
3543
+ var HS_TAG_SUBTLE_BORDER = "#7C98B6";
3544
+ var HS_TAG_TEXT_COLOR = HS_TEXT_COLOR;
3545
+ var HS_TAG_FONT_SIZE = 12;
3546
+ var HS_TAG_LINE_HEIGHT = 22;
3547
+ var HS_TAG_PADDING_X = 8;
3548
+ var HS_TAG_PADDING_Y = 0;
3549
+ var HS_TAG_BORDER_RADIUS = 0;
3550
+ var HS_TAG_BORDER_WIDTH = 1;
3551
+ var DEFAULT_SVG_FONT_WEIGHT = 600;
3552
+
3553
+ // src/common-components/StyledText.js
3554
+ var VARIANT_PRESETS = {
3555
+ bodytext: { fontSize: 14, lineHeight: 24, fontWeight: 400 },
3556
+ microcopy: { fontSize: 12, lineHeight: 18, fontWeight: 400 }
3557
+ };
3558
+ var WEIGHT_ALIASES = {
3559
+ bold: 700,
3560
+ demibold: 600,
3561
+ regular: 400
3562
+ };
3563
+ var LINE_DECORATION = {
3564
+ strikethrough: "line-through",
3565
+ underline: "underline"
3566
+ };
3567
+ var ORIENTATION_ROTATION = {
3568
+ horizontal: 0,
3569
+ "vertical-up": -90,
3570
+ "vertical-down": 90
3571
+ };
3572
+ var BACKGROUND_PRESETS = {
3573
+ tag: {
3574
+ color: HS_SUBTLE_BG,
3575
+ borderColor: HS_TAG_SUBTLE_BORDER,
3576
+ borderWidth: HS_TAG_BORDER_WIDTH,
3577
+ radius: HS_TAG_BORDER_RADIUS,
3578
+ paddingX: HS_TAG_PADDING_X,
3579
+ paddingY: HS_TAG_PADDING_Y,
3580
+ height: HS_TAG_LINE_HEIGHT,
3581
+ textColor: HS_TAG_TEXT_COLOR,
3582
+ fontSize: HS_TAG_FONT_SIZE,
3583
+ canvasPaddingX: 0,
3584
+ canvasPaddingY: 0
3585
+ }
3586
+ };
3587
+ var TAG_VARIANTS = {
3588
+ default: {
3589
+ color: HS_SUBTLE_BG,
3590
+ borderColor: HS_TAG_SUBTLE_BORDER,
3591
+ textColor: HS_TAG_TEXT_COLOR
3592
+ },
3593
+ success: {
3594
+ color: "#E5F8F6",
3595
+ borderColor: "#00BDA5",
3596
+ textColor: "#00BDA5"
3597
+ },
3598
+ warning: {
3599
+ color: "#FEF8F0",
3600
+ borderColor: "#F5C26B",
3601
+ textColor: "#D39913"
3602
+ },
3603
+ error: {
3604
+ color: "#FDEDEE",
3605
+ borderColor: "#F2545B",
3606
+ textColor: "#F2545B"
3607
+ },
3608
+ danger: {
3609
+ color: "#FDEDEE",
3610
+ borderColor: "#F2545B",
3611
+ textColor: "#F2545B"
3612
+ },
3613
+ info: {
3614
+ color: "#E5F5F8",
3615
+ borderColor: "#00A4BD",
3616
+ textColor: "#00A4BD"
3617
+ }
3618
+ };
3619
+ var NATIVE_TAG_VARIANT_ALIASES = {
3620
+ danger: "error"
3621
+ };
3622
+ var escapeSvgText = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3623
+ var applyTextTransform = (text, transform) => {
3624
+ if (!transform || transform === "none") return String(text);
3625
+ const s = String(text);
3626
+ switch (transform) {
3627
+ case "uppercase":
3628
+ return s.toUpperCase();
3629
+ case "lowercase":
3630
+ return s.toLowerCase();
3631
+ case "capitalize":
3632
+ return s.replace(/\b\w/g, (c) => c.toUpperCase());
3633
+ case "sentenceCase":
3634
+ return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
3635
+ default:
3636
+ return s;
3637
+ }
3638
+ };
3639
+ var estimateTextWidth = (text, fontSize) => Math.max(fontSize, Math.round(String(text).length * fontSize * 0.58));
3640
+ var resolveBackground = (background) => {
3641
+ if (!background) return null;
3642
+ const preset = background.preset ? BACKGROUND_PRESETS[background.preset] : null;
3643
+ const variant = background.preset === "tag" && background.variant ? TAG_VARIANTS[background.variant] || null : null;
3644
+ return {
3645
+ ...preset || {},
3646
+ ...variant || {},
3647
+ ...background
3648
+ };
3649
+ };
3650
+ var buildBackgroundRect = ({ background, x, y, width, height }) => {
3651
+ const radius = (background == null ? void 0 : background.radius) ?? 3;
3652
+ const fill = (background == null ? void 0 : background.color) ?? "transparent";
3653
+ const borderWidth = (background == null ? void 0 : background.borderWidth) ?? 0;
3654
+ const borderColor = background == null ? void 0 : background.borderColor;
3655
+ if (!borderColor || borderWidth <= 0) {
3656
+ return `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" />`;
3657
+ }
3658
+ const isTagPreset = (background == null ? void 0 : background.preset) === "tag";
3659
+ const fillRect = `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" />`;
3660
+ const strokeInset = borderWidth / 2;
3661
+ const strokeX = x + strokeInset;
3662
+ const strokeY = y + strokeInset;
3663
+ const strokeW = Math.max(0, width - borderWidth);
3664
+ const strokeH = Math.max(0, height - borderWidth);
3665
+ return fillRect + `<rect x="${strokeX}" y="${strokeY}" width="${strokeW}" height="${strokeH}" rx="${Math.max(
3666
+ 0,
3667
+ radius - strokeInset
3668
+ )}" fill="none" stroke="${borderColor}" stroke-width="${borderWidth}"${isTagPreset ? ` shape-rendering="crispEdges"` : ""} />`;
3669
+ };
3670
+ var canUseNativeTag = ({
3671
+ background,
3672
+ orientation,
3673
+ color,
3674
+ fontFamily,
3675
+ fontSize,
3676
+ width,
3677
+ height,
3678
+ paddingX,
3679
+ paddingY,
3680
+ format = {}
3681
+ }) => {
3682
+ if (!background || background.preset !== "tag") return false;
3683
+ const resolvedOrientation = typeof orientation === "number" ? orientation : ORIENTATION_ROTATION[orientation ?? "horizontal"] ?? 0;
3684
+ if (resolvedOrientation !== 0) return false;
3685
+ if (color != null || fontFamily != null || fontSize != null) return false;
3686
+ if (width != null || height != null || paddingX != null || paddingY != null) return false;
3687
+ if (background.color != null || background.textColor != null) return false;
3688
+ if (background.borderColor != null || background.borderWidth != null) return false;
3689
+ if (background.radius != null || background.height != null) return false;
3690
+ if (background.paddingX != null || background.paddingY != null) return false;
3691
+ if (background.canvasPaddingX != null || background.canvasPaddingY != null) return false;
3692
+ if (format.italic || format.lineDecoration) return false;
3693
+ if (format.textTransform && format.textTransform !== "none") return false;
3694
+ return true;
3695
+ };
3696
+ var makeStyledTextDataUri = (text, opts = {}) => {
3697
+ const {
3698
+ variant = "bodytext",
3699
+ format = {},
3700
+ orientation = "horizontal",
3701
+ color: colorProp = HS_TEXT_COLOR,
3702
+ fontFamily = HS_FONT_FAMILY,
3703
+ background: backgroundProp = null,
3704
+ paddingX: paddingXProp = 4,
3705
+ paddingY: paddingYProp = 2,
3706
+ width: widthOverride,
3707
+ height: heightOverride,
3708
+ fontSize: fontSizeOverride
3709
+ } = opts;
3710
+ const preset = VARIANT_PRESETS[variant] || VARIANT_PRESETS.bodytext;
3711
+ const background = resolveBackground(backgroundProp);
3712
+ const fontSize = fontSizeOverride ?? (background == null ? void 0 : background.fontSize) ?? preset.fontSize;
3713
+ const rawWeight = format.fontWeight;
3714
+ const fontWeight = rawWeight ? WEIGHT_ALIASES[rawWeight] ?? rawWeight : preset.fontWeight;
3715
+ const fontStyle = format.italic ? "italic" : "normal";
3716
+ const textDecoration = LINE_DECORATION[format.lineDecoration] || "none";
3717
+ const transformed = applyTextTransform(text, format.textTransform);
3718
+ const lineHeight = (background == null ? void 0 : background.height) ?? preset.lineHeight ?? fontSize;
3719
+ const color = (background == null ? void 0 : background.textColor) ?? colorProp;
3720
+ const paddingX = (background == null ? void 0 : background.canvasPaddingX) ?? paddingXProp;
3721
+ const paddingY = (background == null ? void 0 : background.canvasPaddingY) ?? paddingYProp;
3722
+ const rotate = typeof orientation === "number" ? orientation : ORIENTATION_ROTATION[orientation] ?? 0;
3723
+ const textW = estimateTextWidth(transformed, fontSize);
3724
+ let pillW = 0;
3725
+ let pillH = 0;
3726
+ if (background) {
3727
+ const bgPadX = background.paddingX ?? 6;
3728
+ const bgPadY = background.paddingY ?? 3;
3729
+ pillW = textW + bgPadX * 2;
3730
+ pillH = background.height ?? Math.max(lineHeight, fontSize + bgPadY * 2);
3731
+ }
3732
+ const intrinsicW = (background ? pillW : textW) + paddingX * 2;
3733
+ const intrinsicH = (background ? pillH : lineHeight) + paddingY * 2;
3734
+ const isOrthoRotation = rotate === 90 || rotate === -90 || rotate === 270;
3735
+ const canvasW = widthOverride ?? (isOrthoRotation ? intrinsicH : intrinsicW);
3736
+ const canvasH = heightOverride ?? (isOrthoRotation ? intrinsicW : intrinsicH);
3737
+ const cx = canvasW / 2;
3738
+ const cy = canvasH / 2;
3739
+ const rectX = cx - pillW / 2;
3740
+ const rectY = cy - pillH / 2;
3741
+ const group = (background ? buildBackgroundRect({
3742
+ background,
3743
+ x: rectX,
3744
+ y: rectY,
3745
+ width: pillW,
3746
+ height: pillH
3747
+ }) : "") + `<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>`;
3748
+ const wrapped = rotate ? `<g transform="rotate(${rotate} ${cx} ${cy})">${group}</g>` : group;
3749
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${canvasW}" height="${canvasH}">` + wrapped + `</svg>`;
3750
+ return {
3751
+ src: `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,
3752
+ width: canvasW,
3753
+ height: canvasH
3754
+ };
3755
+ };
3756
+ var StyledText = ({
3757
+ children,
3758
+ text,
3759
+ alt,
3760
+ variant,
3761
+ format,
3762
+ orientation,
3763
+ color,
3764
+ background,
3765
+ fontFamily,
3766
+ fontSize,
3767
+ paddingX,
3768
+ paddingY,
3769
+ width,
3770
+ height
3771
+ }) => {
3772
+ const resolvedText = text ?? (typeof children === "string" ? children : "");
3773
+ if (canUseNativeTag({
3774
+ background,
3775
+ orientation,
3776
+ color,
3777
+ fontFamily,
3778
+ fontSize,
3779
+ width,
3780
+ height,
3781
+ paddingX,
3782
+ paddingY,
3783
+ format
3784
+ })) {
3785
+ const nativeVariant = NATIVE_TAG_VARIANT_ALIASES[background == null ? void 0 : background.variant] ?? (background == null ? void 0 : background.variant) ?? "default";
3786
+ return React3.createElement(Tag2, { variant: nativeVariant }, resolvedText);
3787
+ }
3788
+ const { src, width: w, height: h } = makeStyledTextDataUri(resolvedText, {
3789
+ variant,
3790
+ format,
3791
+ orientation,
3792
+ color,
3793
+ background,
3794
+ fontFamily,
3795
+ fontSize,
3796
+ paddingX,
3797
+ paddingY,
3798
+ width,
3799
+ height
3800
+ });
3801
+ return React3.createElement(Image, {
3802
+ src,
3803
+ width: w,
3804
+ height: h,
3805
+ alt: alt ?? String(resolvedText)
3806
+ });
3807
+ };
3808
+
3809
+ // packages/kanban/src/Kanban.jsx
3810
+ import {
3811
+ Alert as Alert2,
3812
+ AutoGrid as AutoGrid2,
3813
+ Box as Box3,
3814
+ Button as Button3,
3815
+ Checkbox as Checkbox3,
3816
+ DateInput as DateInput3,
3817
+ DescriptionList,
3818
+ DescriptionListItem,
3819
+ Divider as Divider2,
3820
+ Dropdown,
3821
+ EmptyState as EmptyState2,
3822
+ Flex as Flex3,
3823
+ Icon as Icon3,
3824
+ Image as Image2,
3825
+ Inline as Inline2,
3826
+ Link as Link3,
3827
+ LoadingSpinner as LoadingSpinner2,
3828
+ Modal,
3829
+ ModalBody,
3830
+ MultiSelect as MultiSelect3,
3831
+ SearchInput as SearchInput2,
3832
+ Select as Select3,
3833
+ Statistics,
3834
+ StatisticsItem,
3835
+ StatisticsTrend,
3836
+ Tag as Tag3,
3837
+ Text as Text3,
3838
+ Tile as Tile2
3839
+ } from "@hubspot/ui-extensions";
3840
+ var DEFAULT_DENSITY = "compact";
3841
+ var DEFAULT_MAX_CARDS = 10;
3842
+ var DEFAULT_MAX_EXPANDED = 50;
3843
+ var DEFAULT_COLUMN_WIDTH = 350;
3844
+ var MIN_COLUMN_WIDTH = 350;
3845
+ var DEFAULT_FILTER_INLINE_LIMIT = 4;
3846
+ var DEFAULT_SEARCH_DEBOUNCE = 250;
3847
+ var DEFAULT_TITLE_TRUNCATE = 60;
3848
+ var applyTruncate = (value, truncate, fallback) => {
3849
+ if (truncate === false) return value;
3850
+ if (typeof value !== "string") return value;
3851
+ const limit = typeof truncate === "number" ? truncate : truncate === true ? fallback || DEFAULT_TITLE_TRUNCATE : fallback || DEFAULT_TITLE_TRUNCATE;
3852
+ if (value.length <= limit) return value;
3853
+ return value.slice(0, limit).trimEnd() + "\u2026";
3854
+ };
3855
+ var DEFAULT_LABELS = {
3856
+ search: "Search cards...",
3857
+ // Only the total is surfaced — callers asked for a single headline number
3858
+ // rather than a "loaded / total" fraction. Fall back to the bare label when
3859
+ // no total is known.
3860
+ showMore: (_shown, total) => total ? `Show more (${total})` : "Show more",
3861
+ showLess: "Show less",
3862
+ loadMore: (_loaded, total) => total ? `Load more (${total})` : "Load more",
3863
+ loadingMore: "Loading...",
3864
+ retryLoadMore: "Retry",
3865
+ emptyColumn: "\u2014",
3866
+ emptyTitle: "No cards",
3867
+ emptyMessage: "Nothing matches the current filters.",
3868
+ loading: "Loading board...",
3869
+ errorTitle: "Something went wrong.",
3870
+ errorMessage: "An error occurred while loading data.",
3871
+ cardCount: (n) => String(n),
3872
+ moveTo: "Move",
3873
+ clearAll: "Clear all",
3874
+ selectAll: (count, label) => `Select all ${count} ${label}`,
3875
+ deselectAll: "Deselect all",
3876
+ selected: (count, label) => `${count}\xA0${label}\xA0selected`,
3877
+ filtersButton: "Filters",
3878
+ dateFrom: "From",
3879
+ dateTo: "To",
3880
+ sortButton: "Sort",
3881
+ sortAscending: "Ascending",
3882
+ sortDescending: "Descending",
3883
+ metricsButton: "Metrics"
3884
+ };
3885
+ var makeRotatedTagDataUri = (label) => makeStyledTextDataUri(label, {
3886
+ variant: "microcopy",
3887
+ format: { fontWeight: "demibold" },
3888
+ orientation: "vertical-down",
3889
+ background: { preset: "tag" }
3890
+ });
3891
+ var makeRotatedLabelDataUri = (label) => makeStyledTextDataUri(label, {
3892
+ variant: "bodytext",
3893
+ format: { fontWeight: "demibold" },
3894
+ orientation: "vertical-down"
3895
+ });
3896
+ var getEmptyFilterValue2 = (filter) => {
3897
+ const type = filter.type || "select";
3898
+ if (type === "multiselect") return [];
3899
+ if (type === "dateRange") return { from: null, to: null };
3900
+ return "";
3901
+ };
3902
+ var isFilterActive2 = (filter, value) => {
3903
+ const type = filter.type || "select";
3904
+ if (type === "multiselect") return Array.isArray(value) && value.length > 0;
3905
+ if (type === "dateRange") return value && (value.from || value.to);
3906
+ return !!value;
3907
+ };
3908
+ var formatDateChip2 = (dateObj) => {
3909
+ if (!dateObj) return "";
3910
+ const { year, month, date } = dateObj;
3911
+ return new Intl.DateTimeFormat("en-US", {
3912
+ month: "short",
3913
+ day: "numeric",
3914
+ year: "numeric"
3915
+ }).format(new Date(year, month, date));
3916
+ };
3917
+ var dateToTimestamp2 = (dateObj) => {
3918
+ if (!dateObj) return null;
3919
+ return new Date(dateObj.year, dateObj.month, dateObj.date).getTime();
3920
+ };
3921
+ var toStableKey2 = (value) => {
3922
+ try {
3923
+ return JSON.stringify(value);
3924
+ } catch (_error) {
3925
+ return String(value);
3926
+ }
3927
+ };
3928
+ var canStageReceiveRow = (stage, row, canMove) => {
3929
+ if (!stage) return false;
3930
+ if (typeof canMove === "function" && !canMove(row, stage.value)) return false;
3931
+ if (typeof stage.canEnter === "function" && !stage.canEnter(row)) return false;
3932
+ return true;
3933
+ };
3934
+ var isFieldDirectionSortOption = (option) => !!(option && option.field && (option.direction === "asc" || option.direction === "desc"));
3935
+ var resolveDividers = (cardDividers, density) => {
3936
+ if (cardDividers === true) {
3937
+ return { afterTitle: true, afterSubtitle: true, afterBody: true, afterFooter: true };
3938
+ }
3939
+ if (cardDividers === false) {
3940
+ return { afterTitle: false, afterSubtitle: false, afterBody: false, afterFooter: false };
3941
+ }
3942
+ if (cardDividers && typeof cardDividers === "object") {
3943
+ return {
3944
+ afterTitle: cardDividers.afterTitle ?? false,
3945
+ afterSubtitle: cardDividers.afterSubtitle ?? false,
3946
+ afterBody: cardDividers.afterBody ?? false,
3947
+ afterFooter: cardDividers.afterFooter ?? false
3948
+ };
3949
+ }
3950
+ if (density === "comfortable") {
3951
+ return { afterTitle: true, afterSubtitle: true, afterBody: true, afterFooter: true };
3952
+ }
3953
+ return { afterTitle: false, afterSubtitle: false, afterBody: true, afterFooter: false };
3954
+ };
3955
+ var partitionFields = (cardFields) => {
3956
+ const buckets = { title: null, subtitle: null, meta: [], body: [], footer: [] };
3957
+ for (const field of cardFields || []) {
3958
+ const placement = field.placement || "body";
3959
+ if (placement === "title" && !buckets.title) buckets.title = field;
3960
+ else if (placement === "subtitle" && !buckets.subtitle) buckets.subtitle = field;
3961
+ else if (placement === "meta") buckets.meta.push(field);
3962
+ else if (placement === "footer") buckets.footer.push(field);
3963
+ else buckets.body.push(field);
3964
+ }
3965
+ return buckets;
3966
+ };
3967
+ var resolveFieldValue = (field, row) => {
3968
+ if (!field) return void 0;
3969
+ if (field.field && row && Object.prototype.hasOwnProperty.call(row, field.field)) {
3970
+ return row[field.field];
3971
+ }
3972
+ return void 0;
3973
+ };
3974
+ var resolveHref = (href, row) => {
3975
+ if (!href) return null;
3976
+ if (typeof href === "function") return href(row);
3977
+ return href;
3978
+ };
3979
+ var KanbanCard = ({
3980
+ row,
3981
+ rowId,
3982
+ stage,
3983
+ stages,
3984
+ fields,
3985
+ density,
3986
+ dividers,
3987
+ bodyAs,
3988
+ maxBodyLines,
3989
+ stageControl,
3990
+ stageControlPlacement,
3991
+ canMove,
3992
+ onStageChangeRequest,
3993
+ isChanging,
3994
+ selectable,
3995
+ selected,
3996
+ onToggleSelect,
3997
+ labels
3998
+ }) => {
3999
+ const titleHref = fields.title ? resolveHref(fields.title.href, row) : null;
4000
+ const rawTitleValue = fields.title ? fields.title.render ? fields.title.render(resolveFieldValue(fields.title, row), row) : resolveFieldValue(fields.title, row) : null;
4001
+ const titleValue = fields.title && typeof rawTitleValue === "string" ? applyTruncate(rawTitleValue, fields.title.truncate, DEFAULT_TITLE_TRUNCATE) : rawTitleValue;
4002
+ const titleNode = titleHref ? /* @__PURE__ */ React4.createElement(Link3, { href: titleHref }, titleValue) : /* @__PURE__ */ React4.createElement(Text3, { format: { fontWeight: "demibold" } }, titleValue);
4003
+ const metaNodes = fields.meta.filter((f) => !f.visible || f.visible(row)).map((f) => {
4004
+ const val = resolveFieldValue(f, row);
4005
+ return /* @__PURE__ */ React4.createElement(Text3, { key: f.field || f.label, variant: "microcopy" }, f.render ? f.render(val, row) : val);
4006
+ });
4007
+ const showSubtitle = density === "comfortable" && fields.subtitle;
4008
+ const subtitleNode = showSubtitle ? fields.subtitle.render ? fields.subtitle.render(resolveFieldValue(fields.subtitle, row), row) : resolveFieldValue(fields.subtitle, row) : null;
4009
+ const bodyFields = fields.body.filter((f) => !f.visible || f.visible(row)).slice(0, maxBodyLines);
4010
+ const footerFields = fields.footer.filter((f) => !f.visible || f.visible(row));
4011
+ const footerAlerts = footerFields.slice(0, -1);
4012
+ const footerActionsField = footerFields.length > 0 ? footerFields[footerFields.length - 1] : null;
4013
+ const renderFooterField = (f, idx) => {
4014
+ const val = resolveFieldValue(f, row);
4015
+ const rendered = f.render ? f.render(val, row) : val;
4016
+ const key = f.key || f.field || f.label || `footer-${idx}`;
4017
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key }, rendered);
4018
+ };
4019
+ const stageControlNode = stageControl === "none" ? null : /* @__PURE__ */ React4.createElement(
4020
+ StageControl,
4021
+ {
4022
+ row,
4023
+ rowId,
4024
+ currentStage: stage,
4025
+ stages,
4026
+ canMove,
4027
+ isChanging,
4028
+ mode: stageControl,
4029
+ onStageChangeRequest,
4030
+ labels
4031
+ }
4032
+ );
4033
+ const titleRow = /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", justify: "between", align: "center", gap: "sm" }, /* @__PURE__ */ React4.createElement(Box3, { flex: 1 }, titleNode), selectable ? /* @__PURE__ */ React4.createElement(
4034
+ Checkbox3,
4035
+ {
4036
+ name: `kanban-select-${rowId}`,
4037
+ checked: selected,
4038
+ onChange: () => onToggleSelect(rowId)
4039
+ }
4040
+ ) : null);
4041
+ const metaRow = metaNodes.length === 0 ? null : /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", justify: "end", align: "center", gap: "xs" }, metaNodes);
4042
+ const bodyRow = bodyFields.length === 0 ? null : bodyAs === "descriptionList" ? /* @__PURE__ */ React4.createElement(DescriptionList, { direction: "row" }, bodyFields.map((f, idx) => {
4043
+ const val = resolveFieldValue(f, row);
4044
+ const rendered = f.render ? f.render(val, row) : val ?? "\u2014";
4045
+ const key = f.key || f.field || f.label || `body-${idx}`;
4046
+ return /* @__PURE__ */ React4.createElement(DescriptionListItem, { key, label: f.label || "" }, rendered);
4047
+ })) : /* @__PURE__ */ React4.createElement(Flex3, { direction: "column", gap: "flush" }, bodyFields.map((f, idx) => {
4048
+ const val = resolveFieldValue(f, row);
4049
+ const rendered = f.render ? f.render(val, row) : val ?? "\u2014";
4050
+ const key = f.key || f.field || f.label || `body-${idx}`;
4051
+ return /* @__PURE__ */ React4.createElement(Text3, { key, variant: "microcopy" }, f.label ? /* @__PURE__ */ React4.createElement(Text3, { inline: true, variant: "microcopy" }, `${f.label}: `) : null, rendered);
4052
+ }));
4053
+ const footerAlertsNode = footerAlerts.length === 0 ? null : /* @__PURE__ */ React4.createElement(Flex3, { direction: "column", gap: "xs" }, footerAlerts.map((f, idx) => renderFooterField(f, idx)));
4054
+ const footerActionsNode = footerActionsField ? renderFooterField(footerActionsField, footerFields.length - 1) : null;
4055
+ const inlineStageControl = stageControlPlacement === "inline" ? stageControlNode : null;
4056
+ const separateRowStageControl = stageControlPlacement === "separateRow" ? stageControlNode : null;
4057
+ const footerMainRow = inlineStageControl || footerActionsNode ? /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", justify: "between", align: "start", gap: "sm" }, inlineStageControl ? /* @__PURE__ */ React4.createElement(Box3, { alignSelf: "center" }, inlineStageControl) : /* @__PURE__ */ React4.createElement(Box3, null), footerActionsNode ? /* @__PURE__ */ React4.createElement(Box3, { flex: 1 }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", justify: "end", align: "start" }, footerActionsNode)) : null) : null;
4058
+ const footerRow = !footerAlertsNode && !footerMainRow ? null : /* @__PURE__ */ React4.createElement(Flex3, { direction: "column", gap: "xs" }, footerAlertsNode, footerMainRow);
4059
+ return /* @__PURE__ */ React4.createElement(Tile2, { compact: density === "compact" }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "column", gap: density === "compact" ? "xs" : "sm" }, titleRow, dividers.afterTitle && (metaRow || bodyRow || footerRow || separateRowStageControl) ? /* @__PURE__ */ React4.createElement(Divider2, null) : null, subtitleNode ? /* @__PURE__ */ React4.createElement(Text3, { variant: "microcopy" }, subtitleNode) : null, dividers.afterSubtitle && subtitleNode && (metaRow || bodyRow || footerRow || separateRowStageControl) ? /* @__PURE__ */ React4.createElement(Divider2, null) : null, metaRow, bodyRow, dividers.afterBody && bodyRow && (footerRow || separateRowStageControl) ? /* @__PURE__ */ React4.createElement(Divider2, null) : null, footerRow, dividers.afterFooter && footerRow && separateRowStageControl ? /* @__PURE__ */ React4.createElement(Divider2, null) : null, separateRowStageControl));
4060
+ };
4061
+ var StageControl = ({
4062
+ row,
4063
+ rowId,
4064
+ currentStage,
4065
+ stages,
4066
+ canMove,
4067
+ isChanging,
4068
+ mode,
4069
+ onStageChangeRequest,
4070
+ labels
4071
+ }) => {
4072
+ if (isChanging) {
4073
+ return /* @__PURE__ */ React4.createElement(LoadingSpinner2, { size: "xs" });
4074
+ }
4075
+ const availableStages = (stages || []).filter(
4076
+ (stage) => stage.value === currentStage.value || canStageReceiveRow(stage, row, canMove)
4077
+ );
4078
+ if (mode === "menu") {
4079
+ const targetStages = availableStages.filter((stage) => stage.value !== currentStage.value);
4080
+ if (targetStages.length === 0) {
4081
+ return /* @__PURE__ */ React4.createElement(Button3, { variant: "transparent", size: "extra-small", disabled: true }, labels.moveTo);
4082
+ }
4083
+ return /* @__PURE__ */ React4.createElement(
4084
+ Dropdown,
4085
+ {
4086
+ variant: "transparent",
4087
+ buttonText: labels.moveTo,
4088
+ buttonSize: "xs"
4089
+ },
4090
+ targetStages.map((stage) => /* @__PURE__ */ React4.createElement(
4091
+ Dropdown.ButtonItem,
4092
+ {
4093
+ key: stage.value,
4094
+ onClick: () => onStageChangeRequest(row, stage.value, currentStage.value)
4095
+ },
4096
+ stage.shortLabel || stage.label
4097
+ ))
4098
+ );
4099
+ }
4100
+ return /* @__PURE__ */ React4.createElement(
4101
+ Select3,
4102
+ {
4103
+ name: `stage-${rowId}`,
4104
+ label: "",
4105
+ value: currentStage.value,
4106
+ onChange: (val) => {
4107
+ if (val !== currentStage.value) onStageChangeRequest(row, val, currentStage.value);
4108
+ },
4109
+ options: availableStages.map((stage) => ({
4110
+ label: stage.shortLabel || stage.label,
4111
+ value: stage.value
4112
+ }))
4113
+ }
4114
+ );
4115
+ };
4116
+ var KanbanColumn = ({
4117
+ stage,
4118
+ rows,
4119
+ bucketCount,
4120
+ totalCount,
4121
+ hasMore,
4122
+ loading,
4123
+ error,
4124
+ onLoadMore,
4125
+ expanded,
4126
+ onToggleExpanded,
4127
+ collapsed,
4128
+ onToggleCollapsed,
4129
+ columnFooter,
4130
+ countDisplay,
4131
+ labels,
4132
+ children
4133
+ }) => {
4134
+ const countLabel = labels.cardCount(totalCount != null ? totalCount : bucketCount);
4135
+ const countNode = countDisplay === "text" ? /* @__PURE__ */ React4.createElement(Text3, { format: { fontWeight: "demibold" } }, countLabel) : countDisplay === "none" ? null : /* @__PURE__ */ React4.createElement(Tag3, { variant: "subtle" }, countLabel);
4136
+ if (collapsed) {
4137
+ const rotated = makeRotatedLabelDataUri(stage.label);
4138
+ const rotatedCount = countDisplay === "none" ? null : makeRotatedTagDataUri(countLabel);
4139
+ const stageIdentifier = stage.icon ? /* @__PURE__ */ React4.createElement(Icon3, { name: stage.icon, size: "sm", screenReaderText: stage.label }) : /* @__PURE__ */ React4.createElement(
4140
+ Image2,
4141
+ {
4142
+ src: rotated.src,
4143
+ width: rotated.width,
4144
+ height: rotated.height,
4145
+ alt: stage.label
4146
+ }
4147
+ );
4148
+ return /* @__PURE__ */ React4.createElement(Tile2, { compact: true }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "column", gap: "xs", align: "center" }, /* @__PURE__ */ React4.createElement(
4149
+ Button3,
4150
+ {
4151
+ variant: "transparent",
4152
+ size: "sm",
4153
+ onClick: onToggleCollapsed,
4154
+ tooltip: `Expand ${stage.label}`
4155
+ },
4156
+ /* @__PURE__ */ React4.createElement(Icon3, { name: "right", size: "sm", screenReaderText: `Expand ${stage.label}` })
4157
+ ), stageIdentifier, rotatedCount ? /* @__PURE__ */ React4.createElement(
4158
+ Image2,
4159
+ {
4160
+ src: rotatedCount.src,
4161
+ width: rotatedCount.width,
4162
+ height: rotatedCount.height,
4163
+ alt: `${bucketCount} items`
4164
+ }
4165
+ ) : null));
4166
+ }
4167
+ const footerContent = stage.footer ? stage.footer(rows) : columnFooter ? columnFooter(rows, stage) : null;
4168
+ return /* @__PURE__ */ React4.createElement(Tile2, { compact: true }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "column", gap: "xs" }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", align: "center", justify: "between", gap: "xs" }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React4.createElement(Text3, { format: { fontWeight: "demibold" } }, stage.shortLabel || stage.label), countNode, loading ? /* @__PURE__ */ React4.createElement(LoadingSpinner2, { size: "xs" }) : null), /* @__PURE__ */ React4.createElement(Button3, { variant: "transparent", size: "sm", onClick: onToggleCollapsed, tooltip: "Collapse" }, /* @__PURE__ */ React4.createElement(Icon3, { name: "left", size: "sm", screenReaderText: `Collapse ${stage.label}` }))), footerContent ? /* @__PURE__ */ React4.createElement(Text3, { variant: "microcopy" }, footerContent) : null, /* @__PURE__ */ React4.createElement(Divider2, null), children, error ? /* @__PURE__ */ React4.createElement(Alert2, { variant: "danger", title: labels.errorTitle }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", gap: "xs", align: "center" }, /* @__PURE__ */ React4.createElement(Text3, { variant: "microcopy" }, error), onLoadMore ? /* @__PURE__ */ React4.createElement(Button3, { variant: "transparent", size: "xs", onClick: () => onLoadMore(stage.value) }, labels.retryLoadMore) : null)) : null, !error && hasMore && onLoadMore && !loading && bucketCount > 0 ? /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", justify: "center" }, /* @__PURE__ */ React4.createElement(Link3, { onClick: () => onLoadMore(stage.value) }, labels.loadMore(bucketCount, totalCount))) : null, !error && loading && hasMore ? /* @__PURE__ */ React4.createElement(LoadingSpinner2, { size: "sm", layout: "centered", label: labels.loadingMore }) : null, !error && !hasMore && bucketCount > rows.length ? /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", justify: "center" }, /* @__PURE__ */ React4.createElement(Link3, { onClick: onToggleExpanded }, expanded ? labels.showLess : labels.showMore(rows.length, bucketCount))) : null, rows.length === 0 && bucketCount === 0 && !loading ? /* @__PURE__ */ React4.createElement(Text3, { variant: "microcopy", format: { italic: true } }, labels.emptyColumn) : null));
4169
+ };
4170
+ var SortModalBody = ({ sortOptions, sortValue, onSortChange, labels }) => {
4171
+ const hasFieldDirection = Array.isArray(sortOptions) && sortOptions.length > 0 && sortOptions.every(isFieldDirectionSortOption);
4172
+ if (!hasFieldDirection) {
4173
+ return /* @__PURE__ */ React4.createElement(Flex3, { direction: "column", gap: "xs" }, sortOptions.map((opt) => /* @__PURE__ */ React4.createElement(
4174
+ Button3,
4175
+ {
4176
+ key: opt.value,
4177
+ variant: sortValue === opt.value ? "primary" : "secondary",
4178
+ onClick: () => onSortChange(opt.value)
4179
+ },
4180
+ opt.label
4181
+ )));
4182
+ }
4183
+ const currentOption = sortOptions.find((o) => o.value === sortValue) || sortOptions[0];
4184
+ const currentField = currentOption.field;
4185
+ const currentDirection = currentOption.direction;
4186
+ const uniqueFields = [];
4187
+ const seen = /* @__PURE__ */ new Set();
4188
+ for (const opt of sortOptions) {
4189
+ if (!seen.has(opt.field)) {
4190
+ seen.add(opt.field);
4191
+ uniqueFields.push({ value: opt.field, label: opt.fieldLabel || opt.field });
4192
+ }
4193
+ }
4194
+ const fieldOpts = sortOptions.filter((o) => o.field === currentField);
4195
+ const ascOption = fieldOpts.find((o) => o.direction === "asc");
4196
+ const descOption = fieldOpts.find((o) => o.direction === "desc");
4197
+ const handleFieldChange = (newField) => {
4198
+ const next = sortOptions.find((o) => o.field === newField && o.direction === currentDirection) || sortOptions.find((o) => o.field === newField && o.direction === "desc") || sortOptions.find((o) => o.field === newField);
4199
+ if (next) onSortChange(next.value);
4200
+ };
4201
+ return /* @__PURE__ */ React4.createElement(Inline2, { align: "center", gap: "small" }, /* @__PURE__ */ React4.createElement(
4202
+ Select3,
4203
+ {
4204
+ name: "kanban-sort-field",
4205
+ label: "",
4206
+ value: currentField,
4207
+ onChange: handleFieldChange,
4208
+ options: uniqueFields
4209
+ }
4210
+ ), /* @__PURE__ */ React4.createElement(Inline2, { align: "center", gap: "flush" }, descOption ? /* @__PURE__ */ React4.createElement(
4211
+ Button3,
4212
+ {
4213
+ variant: currentDirection === "desc" ? "primary" : "secondary",
4214
+ onClick: () => onSortChange(descOption.value)
4215
+ },
4216
+ labels.sortDescending
4217
+ ) : null, ascOption ? /* @__PURE__ */ React4.createElement(
4218
+ Button3,
4219
+ {
4220
+ variant: currentDirection === "asc" ? "primary" : "secondary",
4221
+ onClick: () => onSortChange(ascOption.value)
4222
+ },
4223
+ labels.sortAscending
4224
+ ) : null));
4225
+ };
4226
+ var renderFilterControl = ({ filter, value, onChange, labels }) => {
4227
+ const type = filter.type || "select";
4228
+ if (type === "multiselect") {
4229
+ return /* @__PURE__ */ React4.createElement(
4230
+ MultiSelect3,
4231
+ {
4232
+ key: filter.name,
4233
+ name: `kanban-filter-${filter.name}`,
4234
+ label: "",
4235
+ placeholder: filter.placeholder || "All",
4236
+ value: value || [],
4237
+ onChange: (val) => onChange(filter.name, val),
4238
+ options: filter.options
4239
+ }
4240
+ );
4241
+ }
4242
+ if (type === "dateRange") {
4243
+ const rangeVal = value || { from: null, to: null };
4244
+ return /* @__PURE__ */ React4.createElement(Flex3, { key: filter.name, direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React4.createElement(
4245
+ DateInput3,
4246
+ {
4247
+ size: "sm",
4248
+ name: `kanban-filter-${filter.name}-from`,
4249
+ label: "",
4250
+ placeholder: labels.dateFrom,
4251
+ format: "medium",
4252
+ value: rangeVal.from,
4253
+ onChange: (val) => onChange(filter.name, { ...rangeVal, from: val })
4254
+ }
4255
+ ), /* @__PURE__ */ React4.createElement(Icon3, { name: "dataSync", size: "sm" }), /* @__PURE__ */ React4.createElement(
4256
+ DateInput3,
4257
+ {
4258
+ size: "sm",
4259
+ name: `kanban-filter-${filter.name}-to`,
4260
+ label: "",
4261
+ placeholder: labels.dateTo,
4262
+ format: "medium",
4263
+ value: rangeVal.to,
4264
+ onChange: (val) => onChange(filter.name, { ...rangeVal, to: val })
4265
+ }
4266
+ ));
4267
+ }
4268
+ return /* @__PURE__ */ React4.createElement(
4269
+ Select3,
4270
+ {
4271
+ key: filter.name,
4272
+ name: `kanban-filter-${filter.name}`,
4273
+ variant: "transparent",
4274
+ placeholder: filter.placeholder || "All",
4275
+ value,
4276
+ onChange: (val) => onChange(filter.name, val),
4277
+ options: [
4278
+ { label: filter.placeholder || "All", value: "" },
4279
+ ...filter.options
4280
+ ]
4281
+ }
4282
+ );
4283
+ };
4284
+ var renderMetricsPanel = (metrics) => {
4285
+ if (!metrics) return null;
4286
+ if (!Array.isArray(metrics)) return metrics;
4287
+ if (metrics.length === 0) return null;
4288
+ return /* @__PURE__ */ React4.createElement(Statistics, null, metrics.map((m, i) => /* @__PURE__ */ React4.createElement(
4289
+ StatisticsItem,
4290
+ {
4291
+ key: m.id || m.label || i,
4292
+ label: m.label,
4293
+ number: m.number != null ? String(m.number) : ""
4294
+ },
4295
+ m.trend ? /* @__PURE__ */ React4.createElement(
4296
+ StatisticsTrend,
4297
+ {
4298
+ direction: m.trend.direction || "increase",
4299
+ value: m.trend.value,
4300
+ color: m.trend.color
4301
+ }
4302
+ ) : null
4303
+ )));
4304
+ };
4305
+ var KanbanToolbar = ({
4306
+ showSearch,
4307
+ searchValue,
4308
+ searchPlaceholder,
4309
+ onSearchChange,
4310
+ filters,
4311
+ filterValues,
4312
+ onFilterChange,
4313
+ filterInlineLimit,
4314
+ showFilterBadges,
4315
+ showClearFiltersButton,
4316
+ activeChips,
4317
+ onFilterRemove,
4318
+ sortOptions,
4319
+ sortValue,
4320
+ onSortChange,
4321
+ metrics,
4322
+ showMetrics,
4323
+ onToggleMetrics,
4324
+ labels
4325
+ }) => {
4326
+ const [showMoreFilters, setShowMoreFilters] = useState3(false);
4327
+ const inlineFilters = (filters || []).slice(0, filterInlineLimit);
4328
+ const overflowFilters = (filters || []).slice(filterInlineLimit);
4329
+ return /* @__PURE__ */ React4.createElement(Flex3, { direction: "column", gap: "xs" }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", gap: "sm" }, /* @__PURE__ */ React4.createElement(Box3, { flex: 3 }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "column", gap: "sm" }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch ? /* @__PURE__ */ React4.createElement(
4330
+ SearchInput2,
4331
+ {
4332
+ name: "kanban-search",
4333
+ placeholder: searchPlaceholder,
4334
+ value: searchValue,
4335
+ onChange: onSearchChange
4336
+ }
4337
+ ) : null, inlineFilters.map(
4338
+ (filter) => renderFilterControl({
4339
+ filter,
4340
+ value: filterValues[filter.name],
4341
+ onChange: onFilterChange,
4342
+ labels
4343
+ })
4344
+ ), overflowFilters.length > 0 ? /* @__PURE__ */ React4.createElement(
4345
+ Button3,
4346
+ {
4347
+ variant: "transparent",
4348
+ size: "small",
4349
+ onClick: () => setShowMoreFilters((prev) => !prev)
4350
+ },
4351
+ /* @__PURE__ */ React4.createElement(Icon3, { name: "filter", size: "sm" }),
4352
+ " ",
4353
+ labels.filtersButton
4354
+ ) : null), showMoreFilters && overflowFilters.length > 0 ? /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, overflowFilters.map(
4355
+ (filter) => renderFilterControl({
4356
+ filter,
4357
+ value: filterValues[filter.name],
4358
+ onChange: onFilterChange,
4359
+ labels
4360
+ })
4361
+ )) : null, activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) ? /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showFilterBadges ? activeChips.map((chip) => /* @__PURE__ */ React4.createElement(
4362
+ Tag3,
4363
+ {
4364
+ key: chip.key,
4365
+ variant: "default",
4366
+ onDelete: () => onFilterRemove(chip.key)
4367
+ },
4368
+ chip.label
4369
+ )) : null, showClearFiltersButton ? /* @__PURE__ */ React4.createElement(
4370
+ Button3,
4371
+ {
4372
+ variant: "transparent",
4373
+ size: "extra-small",
4374
+ onClick: () => onFilterRemove("all")
4375
+ },
4376
+ labels.clearAll
4377
+ ) : null) : null)), (sortOptions == null ? void 0 : sortOptions.length) > 0 || metrics ? /* @__PURE__ */ React4.createElement(Box3, { flex: 1, alignSelf: "start" }, /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", align: "center", gap: "sm", justify: "end" }, sortOptions && sortOptions.length > 0 ? /* @__PURE__ */ React4.createElement(
4378
+ Button3,
4379
+ {
4380
+ variant: "secondary",
4381
+ size: "small",
4382
+ overlay: /* @__PURE__ */ React4.createElement(Modal, { id: "kanban-sort-modal", title: labels.sortButton }, /* @__PURE__ */ React4.createElement(ModalBody, null, /* @__PURE__ */ React4.createElement(
4383
+ SortModalBody,
4384
+ {
4385
+ sortOptions,
4386
+ sortValue,
4387
+ onSortChange,
4388
+ labels
4389
+ }
4390
+ )))
4391
+ },
4392
+ /* @__PURE__ */ React4.createElement(Icon3, { name: "sortAmtDesc", size: "sm" }),
4393
+ " ",
4394
+ labels.sortButton
4395
+ ) : null, metrics ? /* @__PURE__ */ React4.createElement(Button3, { variant: "secondary", size: "small", onClick: onToggleMetrics }, /* @__PURE__ */ React4.createElement(Icon3, { name: "reports", size: "sm" }), " ", labels.metricsButton) : null)) : null), showMetrics && metrics ? renderMetricsPanel(metrics) : null);
4396
+ };
4397
+ var DefaultSelectionBar = ({
4398
+ selectedIds,
4399
+ selectedCount,
4400
+ displayCount,
4401
+ countLabel,
4402
+ allSelected,
4403
+ onSelectAll,
4404
+ onDeselectAll,
4405
+ selectionActions,
4406
+ labels
4407
+ }) => {
4408
+ const pluralForCount = (n) => countLabel(n);
4409
+ return /* @__PURE__ */ React4.createElement(Tile2, { compact: true }, /* @__PURE__ */ React4.createElement(Inline2, { align: "center", justify: "between", gap: "small" }, /* @__PURE__ */ React4.createElement(Inline2, { align: "center", gap: "small" }, /* @__PURE__ */ React4.createElement(Text3, { inline: true, format: { fontWeight: "demibold" } }, typeof labels.selected === "function" ? labels.selected(selectedCount, pluralForCount(selectedCount)) : `${selectedCount} selected`), !allSelected ? /* @__PURE__ */ React4.createElement(Button3, { variant: "transparent", size: "extra-small", onClick: onSelectAll }, typeof labels.selectAll === "function" ? labels.selectAll(displayCount, pluralForCount(displayCount)) : labels.selectAll) : null, /* @__PURE__ */ React4.createElement(Button3, { variant: "transparent", size: "extra-small", onClick: onDeselectAll }, labels.deselectAll)), (selectionActions || []).length > 0 ? /* @__PURE__ */ React4.createElement(Inline2, { align: "center", gap: "extra-small" }, selectionActions.map((action, i) => /* @__PURE__ */ React4.createElement(
4410
+ Button3,
4411
+ {
4412
+ key: action.key || action.label || i,
4413
+ variant: action.variant || "transparent",
4414
+ size: "extra-small",
4415
+ onClick: () => action.onClick([...selectedIds])
4416
+ },
4417
+ action.icon ? /* @__PURE__ */ React4.createElement(Icon3, { name: action.icon, size: "sm" }) : null,
4418
+ " ",
4419
+ action.label
4420
+ ))) : null));
4421
+ };
4422
+ var Kanban = ({
4423
+ // --- Data ---
4424
+ data = [],
4425
+ stages = [],
4426
+ groupBy = "status",
4427
+ rowIdField = "id",
4428
+ // --- Card rendering ---
4429
+ renderCard,
4430
+ cardFields,
4431
+ cardDensity = DEFAULT_DENSITY,
4432
+ cardDividers,
4433
+ cardBodyAs = "descriptionList",
4434
+ maxBodyLines,
4435
+ maxCardsPerColumn = DEFAULT_MAX_CARDS,
4436
+ maxCardsExpanded = DEFAULT_MAX_EXPANDED,
4437
+ expandedStages,
4438
+ onExpandedStagesChange,
4439
+ // --- Per-stage pagination ---
4440
+ stageMeta,
4441
+ onLoadMore,
4442
+ // --- Selection ---
4443
+ selectable = false,
4444
+ selectedIds,
4445
+ onSelectionChange,
4446
+ selectionActions,
4447
+ recordLabel,
4448
+ selectionResetKey,
4449
+ resetSelectionOnQueryChange = true,
4450
+ showSelectionBar = true,
4451
+ renderSelectionBar,
4452
+ // --- Stage transitions ---
4453
+ stageControl,
4454
+ stageControlPlacement,
4455
+ onStageChange,
4456
+ isStageChanging,
4457
+ canMove,
4458
+ // --- Toolbar ---
4459
+ showSearch = true,
4460
+ searchFields,
4461
+ searchPlaceholder,
4462
+ searchDebounce = DEFAULT_SEARCH_DEBOUNCE,
4463
+ fuzzySearch = false,
4464
+ fuzzyOptions,
4465
+ filters,
4466
+ filterInlineLimit = DEFAULT_FILTER_INLINE_LIMIT,
4467
+ showFilterBadges = true,
4468
+ showClearFiltersButton = true,
4469
+ sortOptions,
4470
+ defaultSort,
4471
+ sort,
4472
+ onSortChange,
4473
+ // --- Column level ---
4474
+ columnFooter,
4475
+ columnWidth = DEFAULT_COLUMN_WIDTH,
4476
+ countDisplay = "tag",
4477
+ collapsedStages,
4478
+ onCollapsedStagesChange,
4479
+ // --- Metrics panel ---
4480
+ metrics,
4481
+ // Array of stat items or a ReactNode for full override
4482
+ showMetrics: controlledShowMetrics,
4483
+ onMetricsToggle,
4484
+ // --- State (controlled) ---
4485
+ searchValue,
4486
+ onSearchChange,
4487
+ filterValues,
4488
+ onFilterChange,
4489
+ onParamsChange,
4490
+ loading = false,
4491
+ error,
4492
+ // --- Labels ---
4493
+ labels: labelsProp,
4494
+ renderEmptyState,
4495
+ renderLoadingState,
4496
+ renderErrorState
4497
+ }) => {
4498
+ var _a;
4499
+ const labels = useMemo3(() => ({ ...DEFAULT_LABELS, ...labelsProp || {} }), [labelsProp]);
4500
+ const [internalSearch, setInternalSearch] = useState3(searchValue != null ? searchValue : "");
4501
+ const [internalFilters, setInternalFilters] = useState3(() => {
4502
+ const init = {};
4503
+ (filters || []).forEach((f) => {
4504
+ init[f.name] = getEmptyFilterValue2(f);
4505
+ });
4506
+ return init;
4507
+ });
4508
+ const [internalSort, setInternalSort] = useState3(defaultSort || (((_a = sortOptions == null ? void 0 : sortOptions[0]) == null ? void 0 : _a.value) ?? ""));
4509
+ const [internalCollapsed, setInternalCollapsed] = useState3([]);
4510
+ const [internalExpanded, setInternalExpanded] = useState3([]);
4511
+ const [internalSelection, setInternalSelection] = useState3([]);
4512
+ const [internalShowMetrics, setInternalShowMetrics] = useState3(false);
4513
+ const [transitionPrompts, setTransitionPrompts] = useState3({});
4514
+ const selectionResetRef = useRef3("");
4515
+ const resolvedShowMetrics = controlledShowMetrics != null ? controlledShowMetrics : internalShowMetrics;
4516
+ const toggleMetrics = useCallback3(() => {
4517
+ const next = !resolvedShowMetrics;
4518
+ if (onMetricsToggle) onMetricsToggle(next);
4519
+ if (controlledShowMetrics == null) setInternalShowMetrics(next);
4520
+ }, [resolvedShowMetrics, onMetricsToggle, controlledShowMetrics]);
4521
+ const effectiveColumnWidth = Math.max(MIN_COLUMN_WIDTH, columnWidth || DEFAULT_COLUMN_WIDTH);
4522
+ const resolvedSearch = searchValue != null ? searchValue : internalSearch;
4523
+ const resolvedFilters = filterValues != null ? filterValues : internalFilters;
4524
+ const resolvedSort = sort != null ? sort : internalSort;
4525
+ const resolvedCollapsed = collapsedStages != null ? collapsedStages : internalCollapsed;
4526
+ const resolvedExpanded = expandedStages != null ? expandedStages : internalExpanded;
4527
+ const resolvedSelection = selectedIds != null ? selectedIds : internalSelection;
4528
+ const searchEnabled = showSearch && Array.isArray(searchFields) && searchFields.length > 0;
4529
+ const stagesByValue = useMemo3(() => {
4530
+ const map = {};
4531
+ for (const stage of stages || []) {
4532
+ map[stage.value] = stage;
4533
+ }
4534
+ return map;
4535
+ }, [stages]);
4536
+ const fireParamsChange = useCallback3((overrides = {}) => {
4537
+ if (!onParamsChange) return;
4538
+ onParamsChange({
4539
+ search: overrides.search != null ? overrides.search : resolvedSearch,
4540
+ filters: overrides.filters != null ? overrides.filters : resolvedFilters,
4541
+ sort: overrides.sort != null ? overrides.sort : resolvedSort || null,
4542
+ collapsedStages: overrides.collapsedStages != null ? overrides.collapsedStages : resolvedCollapsed
4543
+ });
4544
+ }, [onParamsChange, resolvedCollapsed, resolvedFilters, resolvedSearch, resolvedSort]);
4545
+ const debounceRef = useRef3(null);
4546
+ useEffect3(() => () => {
4547
+ if (debounceRef.current) clearTimeout(debounceRef.current);
4548
+ }, []);
4549
+ const handleSearch = useCallback3(
4550
+ (val) => {
4551
+ if (searchValue == null) setInternalSearch(val);
4552
+ const dispatch = () => {
4553
+ if (onSearchChange) onSearchChange(val);
4554
+ fireParamsChange({ search: val });
4555
+ };
4556
+ if (searchDebounce > 0) {
4557
+ if (debounceRef.current) clearTimeout(debounceRef.current);
4558
+ debounceRef.current = setTimeout(dispatch, searchDebounce);
4559
+ } else {
4560
+ dispatch();
4561
+ }
4562
+ },
4563
+ [fireParamsChange, onSearchChange, searchValue, searchDebounce]
4564
+ );
4565
+ const handleFilter = useCallback3(
4566
+ (name, val) => {
4567
+ const next = { ...resolvedFilters, [name]: val };
4568
+ if (filterValues == null) setInternalFilters(next);
4569
+ if (onFilterChange) onFilterChange(next);
4570
+ fireParamsChange({ filters: next });
4571
+ },
4572
+ [fireParamsChange, onFilterChange, filterValues, resolvedFilters]
4573
+ );
4574
+ const handleFilterRemove = useCallback3(
4575
+ (key) => {
4576
+ if (key === "all") {
4577
+ const cleared = {};
4578
+ (filters || []).forEach((f) => {
4579
+ cleared[f.name] = getEmptyFilterValue2(f);
4580
+ });
4581
+ if (filterValues == null) setInternalFilters(cleared);
4582
+ if (onFilterChange) onFilterChange(cleared);
4583
+ fireParamsChange({ filters: cleared });
4584
+ return;
4585
+ }
4586
+ const filter = (filters || []).find((f) => f.name === key);
4587
+ const emptyVal = filter ? getEmptyFilterValue2(filter) : "";
4588
+ const next = { ...resolvedFilters, [key]: emptyVal };
4589
+ if (filterValues == null) setInternalFilters(next);
4590
+ if (onFilterChange) onFilterChange(next);
4591
+ fireParamsChange({ filters: next });
4592
+ },
4593
+ [filters, filterValues, fireParamsChange, onFilterChange, resolvedFilters]
4594
+ );
4595
+ const handleSort = useCallback3(
4596
+ (val) => {
4597
+ if (onSortChange) onSortChange(val);
4598
+ if (sort == null) setInternalSort(val);
4599
+ fireParamsChange({ sort: val });
4600
+ },
4601
+ [fireParamsChange, onSortChange, sort]
4602
+ );
4603
+ const handleCollapsed = useCallback3(
4604
+ (stageValue) => {
4605
+ const next = resolvedCollapsed.includes(stageValue) ? resolvedCollapsed.filter((v) => v !== stageValue) : [...resolvedCollapsed, stageValue];
4606
+ if (onCollapsedStagesChange) onCollapsedStagesChange(next);
4607
+ if (collapsedStages == null) setInternalCollapsed(next);
4608
+ fireParamsChange({ collapsedStages: next });
4609
+ },
4610
+ [fireParamsChange, resolvedCollapsed, collapsedStages, onCollapsedStagesChange]
4611
+ );
4612
+ const handleExpanded = useCallback3(
4613
+ (stageValue) => {
4614
+ const next = resolvedExpanded.includes(stageValue) ? resolvedExpanded.filter((v) => v !== stageValue) : [...resolvedExpanded, stageValue];
4615
+ if (onExpandedStagesChange) onExpandedStagesChange(next);
4616
+ if (expandedStages == null) setInternalExpanded(next);
4617
+ },
4618
+ [resolvedExpanded, expandedStages, onExpandedStagesChange]
4619
+ );
4620
+ const handleToggleSelect = useCallback3(
4621
+ (rowId) => {
4622
+ const next = resolvedSelection.includes(rowId) ? resolvedSelection.filter((id) => id !== rowId) : [...resolvedSelection, rowId];
4623
+ if (onSelectionChange) onSelectionChange(next);
4624
+ if (selectedIds == null) setInternalSelection(next);
4625
+ },
4626
+ [resolvedSelection, selectedIds, onSelectionChange]
4627
+ );
4628
+ const clearTransitionPrompt = useCallback3((rowId) => {
4629
+ setTransitionPrompts((prev) => {
4630
+ if (!Object.prototype.hasOwnProperty.call(prev, rowId)) return prev;
4631
+ const next = { ...prev };
4632
+ delete next[rowId];
4633
+ return next;
4634
+ });
4635
+ }, []);
4636
+ const commitStageChange = useCallback3(
4637
+ (row, newStage, oldStage, result) => {
4638
+ clearTransitionPrompt(row[rowIdField]);
4639
+ if (onStageChange) onStageChange(row, newStage, oldStage, result);
4640
+ },
4641
+ [clearTransitionPrompt, onStageChange, rowIdField]
4642
+ );
4643
+ const selectionQueryKey = useMemo3(() => {
4644
+ if (!resetSelectionOnQueryChange) return "";
4645
+ return toStableKey2({
4646
+ search: resolvedSearch,
4647
+ filters: resolvedFilters,
4648
+ sort: resolvedSort || null
4649
+ });
4650
+ }, [resetSelectionOnQueryChange, resolvedFilters, resolvedSearch, resolvedSort]);
4651
+ const combinedSelectionResetKey = useMemo3(
4652
+ () => `${selectionQueryKey}::${selectionResetKey == null ? "" : toStableKey2(selectionResetKey)}`,
4653
+ [selectionQueryKey, selectionResetKey]
4654
+ );
4655
+ useEffect3(() => {
4656
+ if (!selectable || selectedIds != null) {
4657
+ selectionResetRef.current = combinedSelectionResetKey;
4658
+ return;
4659
+ }
4660
+ if (selectionResetRef.current && selectionResetRef.current !== combinedSelectionResetKey) {
4661
+ setInternalSelection([]);
4662
+ }
4663
+ selectionResetRef.current = combinedSelectionResetKey;
4664
+ }, [combinedSelectionResetKey, selectable, selectedIds]);
4665
+ const getStageFor = useCallback3(
4666
+ (row) => {
4667
+ if (typeof groupBy === "function") return groupBy(row);
4668
+ return row[groupBy];
4669
+ },
4670
+ [groupBy]
4671
+ );
4672
+ const filteredData = useMemo3(() => {
4673
+ let result = data;
4674
+ for (const filter of filters || []) {
4675
+ const val = resolvedFilters[filter.name];
4676
+ if (!isFilterActive2(filter, val)) continue;
4677
+ const type = filter.type || "select";
4678
+ if (filter.filterFn) {
4679
+ result = result.filter((row) => filter.filterFn(row, val));
4680
+ } else if (type === "multiselect") {
4681
+ result = result.filter((row) => val.includes(row[filter.name]));
4682
+ } else if (type === "dateRange") {
4683
+ const fromTs = dateToTimestamp2(val.from);
4684
+ const toTs = val.to ? dateToTimestamp2(val.to) + 864e5 - 1 : null;
4685
+ result = result.filter((row) => {
4686
+ const rowTs = new Date(row[filter.name]).getTime();
4687
+ if (Number.isNaN(rowTs)) return false;
4688
+ if (fromTs && rowTs < fromTs) return false;
4689
+ if (toTs && rowTs > toTs) return false;
4690
+ return true;
4691
+ });
4692
+ } else {
4693
+ result = result.filter((row) => row[filter.name] === val);
4694
+ }
4695
+ }
4696
+ const searchLower = (resolvedSearch || "").toLowerCase().trim();
4697
+ if (searchEnabled && searchLower) {
4698
+ if (fuzzySearch) {
4699
+ const fuse = new Fuse2(result, {
4700
+ keys: searchFields,
4701
+ threshold: 0.4,
4702
+ distance: 100,
4703
+ ignoreLocation: true,
4704
+ ...fuzzyOptions
4705
+ });
4706
+ result = fuse.search(searchLower).map((r) => r.item);
4707
+ } else {
4708
+ result = result.filter(
4709
+ (row) => searchFields.some(
4710
+ (f) => String(row[f] || "").toLowerCase().includes(searchLower)
4711
+ )
4712
+ );
4713
+ }
4714
+ }
4715
+ return result;
4716
+ }, [data, resolvedSearch, resolvedFilters, filters, searchEnabled, searchFields, fuzzySearch, fuzzyOptions]);
4717
+ const buckets = useMemo3(() => {
4718
+ const map = {};
4719
+ for (const stage of stages) map[stage.value] = [];
4720
+ for (const row of filteredData) {
4721
+ const key = getStageFor(row);
4722
+ if (map[key]) {
4723
+ map[key].push(row);
4724
+ } else if (stages.length > 0) {
4725
+ if (!map.__unknown) map.__unknown = [];
4726
+ map.__unknown.push(row);
4727
+ }
4728
+ }
4729
+ return map;
4730
+ }, [filteredData, stages, getStageFor]);
4731
+ const sortComparator = useMemo3(() => {
4732
+ if (!sortOptions || !resolvedSort) return null;
4733
+ const opt = sortOptions.find((s) => s.value === resolvedSort);
4734
+ return (opt == null ? void 0 : opt.comparator) || null;
4735
+ }, [sortOptions, resolvedSort]);
4736
+ const sortedBuckets = useMemo3(() => {
4737
+ if (!sortComparator) return buckets;
4738
+ const out = {};
4739
+ for (const key of Object.keys(buckets)) {
4740
+ out[key] = [...buckets[key]].sort(sortComparator);
4741
+ }
4742
+ return out;
4743
+ }, [buckets, sortComparator]);
4744
+ const activeChips = useMemo3(() => {
4745
+ const chips = [];
4746
+ for (const filter of filters || []) {
4747
+ const val = resolvedFilters[filter.name];
4748
+ if (!isFilterActive2(filter, val)) continue;
4749
+ const type = filter.type || "select";
4750
+ const prefix = filter.chipLabel || filter.placeholder || filter.name;
4751
+ if (type === "multiselect") {
4752
+ const labelList = val.map((v) => {
4753
+ var _a2;
4754
+ return ((_a2 = filter.options.find((o) => o.value === v)) == null ? void 0 : _a2.label) || v;
4755
+ }).join(", ");
4756
+ chips.push({ key: filter.name, label: `${prefix}: ${labelList}` });
4757
+ } else if (type === "dateRange") {
4758
+ const parts = [];
4759
+ if (val.from) parts.push(`from ${formatDateChip2(val.from)}`);
4760
+ if (val.to) parts.push(`to ${formatDateChip2(val.to)}`);
4761
+ chips.push({ key: filter.name, label: `${prefix}: ${parts.join(" ")}` });
4762
+ } else {
4763
+ const opt = filter.options.find((o) => o.value === val);
4764
+ chips.push({ key: filter.name, label: `${prefix}: ${(opt == null ? void 0 : opt.label) || val}` });
4765
+ }
4766
+ }
4767
+ return chips;
4768
+ }, [filters, resolvedFilters]);
4769
+ const partitioned = useMemo3(() => partitionFields(cardFields || []), [cardFields]);
4770
+ const dividers = useMemo3(() => resolveDividers(cardDividers, cardDensity), [cardDividers, cardDensity]);
4771
+ const resolvedMaxBody = maxBodyLines || (cardDensity === "comfortable" ? 5 : 3);
4772
+ const resolvedStageControl = stageControl || (cardDensity === "comfortable" ? "select" : "menu");
4773
+ const resolvedStageControlPlacement = stageControlPlacement || (resolvedStageControl === "menu" ? "inline" : "separateRow");
4774
+ const handleStageChangeRequest = useCallback3(
4775
+ (row, newStage, oldStage) => {
4776
+ var _a2;
4777
+ if (!newStage || newStage === oldStage) return;
4778
+ const targetStage = stagesByValue[newStage];
4779
+ if (!targetStage || !canStageReceiveRow(targetStage, row, canMove)) return;
4780
+ const rowId = row[rowIdField];
4781
+ if ((_a2 = targetStage.onEnterRequired) == null ? void 0 : _a2.render) {
4782
+ setTransitionPrompts((prev) => ({
4783
+ ...prev,
4784
+ [rowId]: {
4785
+ row,
4786
+ fromStage: oldStage,
4787
+ toStage: newStage
4788
+ }
4789
+ }));
4790
+ return;
4791
+ }
4792
+ commitStageChange(row, newStage, oldStage);
4793
+ },
4794
+ [canMove, commitStageChange, rowIdField, stagesByValue]
4795
+ );
4796
+ const renderCardNode = useCallback3(
4797
+ (row, stage) => {
4798
+ var _a2;
4799
+ const rowId = row[rowIdField];
4800
+ const activePrompt = transitionPrompts[rowId];
4801
+ const promptStage = activePrompt ? stagesByValue[activePrompt.toStage] : null;
4802
+ if ((_a2 = promptStage == null ? void 0 : promptStage.onEnterRequired) == null ? void 0 : _a2.render) {
4803
+ return /* @__PURE__ */ React4.createElement(Tile2, { key: rowId, compact: cardDensity === "compact" }, promptStage.onEnterRequired.render({
4804
+ row: activePrompt.row,
4805
+ fromStage: activePrompt.fromStage,
4806
+ toStage: activePrompt.toStage,
4807
+ onConfirm: (result) => commitStageChange(activePrompt.row, activePrompt.toStage, activePrompt.fromStage, result),
4808
+ onCancel: () => clearTransitionPrompt(rowId)
4809
+ }));
4810
+ }
4811
+ if (renderCard) {
4812
+ return renderCard(row, {
4813
+ stage,
4814
+ isChanging: isStageChanging ? isStageChanging(row) : false,
4815
+ density: cardDensity,
4816
+ onStageChange: (newStage) => handleStageChangeRequest(row, newStage, stage.value)
4817
+ });
4818
+ }
4819
+ return /* @__PURE__ */ React4.createElement(
4820
+ KanbanCard,
4821
+ {
4822
+ key: rowId,
4823
+ row,
4824
+ rowId,
4825
+ stage,
4826
+ stages,
4827
+ fields: partitioned,
4828
+ density: cardDensity,
4829
+ dividers,
4830
+ bodyAs: cardBodyAs,
4831
+ maxBodyLines: resolvedMaxBody,
4832
+ stageControl: resolvedStageControl,
4833
+ stageControlPlacement: resolvedStageControlPlacement,
4834
+ canMove,
4835
+ onStageChangeRequest: handleStageChangeRequest,
4836
+ isChanging: isStageChanging ? isStageChanging(row) : false,
4837
+ selectable,
4838
+ selected: resolvedSelection.includes(rowId),
4839
+ onToggleSelect: handleToggleSelect,
4840
+ labels
4841
+ }
4842
+ );
4843
+ },
4844
+ [
4845
+ clearTransitionPrompt,
4846
+ commitStageChange,
4847
+ renderCard,
4848
+ rowIdField,
4849
+ partitioned,
4850
+ cardDensity,
4851
+ dividers,
4852
+ resolvedMaxBody,
4853
+ resolvedStageControl,
4854
+ resolvedStageControlPlacement,
4855
+ canMove,
4856
+ isStageChanging,
4857
+ selectable,
4858
+ resolvedSelection,
4859
+ handleStageChangeRequest,
4860
+ handleToggleSelect,
4861
+ labels,
4862
+ stages,
4863
+ stagesByValue,
4864
+ transitionPrompts
4865
+ ]
4866
+ );
4867
+ const totalMatching = filteredData.length;
4868
+ const selectedCount = resolvedSelection.length;
4869
+ const singular = ((recordLabel == null ? void 0 : recordLabel.singular) || "card").toLowerCase();
4870
+ const plural = ((recordLabel == null ? void 0 : recordLabel.plural) || "cards").toLowerCase();
4871
+ const countLabel = (n) => n === 1 ? singular : plural;
4872
+ const resolvedSearchPlaceholder = searchPlaceholder ?? ((recordLabel == null ? void 0 : recordLabel.plural) ? `Search ${plural}...` : labels.search);
4873
+ const selectionBarProps = {
4874
+ selectedIds: resolvedSelection,
4875
+ selectedCount,
4876
+ displayCount: totalMatching,
4877
+ countLabel,
4878
+ allSelected: selectedCount >= totalMatching && totalMatching > 0,
4879
+ onSelectAll: () => {
4880
+ const allIds = filteredData.map((r) => r[rowIdField]);
4881
+ if (onSelectionChange) onSelectionChange(allIds);
4882
+ if (selectedIds == null) setInternalSelection(allIds);
4883
+ },
4884
+ onDeselectAll: () => {
4885
+ if (onSelectionChange) onSelectionChange([]);
4886
+ if (selectedIds == null) setInternalSelection([]);
4887
+ },
4888
+ selectionActions: selectionActions || [],
4889
+ labels
4890
+ };
4891
+ const mainContent = error ? renderErrorState ? renderErrorState({
4892
+ error,
4893
+ title: labels.errorTitle,
4894
+ message: typeof error === "string" ? error : labels.errorMessage
4895
+ }) : /* @__PURE__ */ React4.createElement(Alert2, { variant: "danger", title: labels.errorTitle }, typeof error === "string" ? error : labels.errorMessage) : loading && data.length === 0 ? renderLoadingState ? renderLoadingState({ label: labels.loading }) : /* @__PURE__ */ React4.createElement(LoadingSpinner2, { size: "md", layout: "centered", label: labels.loading }) : filteredData.length === 0 ? renderEmptyState ? renderEmptyState({
4896
+ title: labels.emptyTitle,
4897
+ message: labels.emptyMessage
4898
+ }) : /* @__PURE__ */ React4.createElement(Tile2, null, /* @__PURE__ */ React4.createElement(EmptyState2, { title: labels.emptyTitle, layout: "vertical", reverseOrder: true }, /* @__PURE__ */ React4.createElement(Text3, null, labels.emptyMessage))) : /* @__PURE__ */ React4.createElement(Flex3, { direction: "row", gap: "sm", wrap: "nowrap" }, stages.map((stage) => {
4899
+ const stageRows = sortedBuckets[stage.value] || [];
4900
+ const meta = stageMeta == null ? void 0 : stageMeta[stage.value];
4901
+ const isExpanded = resolvedExpanded.includes(stage.value);
4902
+ const clamp = isExpanded ? maxCardsExpanded : maxCardsPerColumn;
4903
+ const visibleRows = stageRows.slice(0, clamp);
4904
+ const isCollapsed = resolvedCollapsed.includes(stage.value);
4905
+ return /* @__PURE__ */ React4.createElement(AutoGrid2, { key: stage.value, columnWidth: isCollapsed ? 72 : effectiveColumnWidth }, /* @__PURE__ */ React4.createElement(
4906
+ KanbanColumn,
4907
+ {
4908
+ stage,
4909
+ rows: visibleRows,
4910
+ bucketCount: stageRows.length,
4911
+ totalCount: meta == null ? void 0 : meta.totalCount,
4912
+ hasMore: meta == null ? void 0 : meta.hasMore,
4913
+ loading: meta == null ? void 0 : meta.loading,
4914
+ error: meta == null ? void 0 : meta.error,
4915
+ onLoadMore,
4916
+ expanded: isExpanded,
4917
+ onToggleExpanded: () => handleExpanded(stage.value),
4918
+ collapsed: isCollapsed,
4919
+ onToggleCollapsed: () => handleCollapsed(stage.value),
4920
+ columnFooter,
4921
+ countDisplay,
4922
+ labels
4923
+ },
4924
+ visibleRows.map((row) => renderCardNode(row, stage))
4925
+ ));
4926
+ }));
4927
+ return /* @__PURE__ */ React4.createElement(Flex3, { direction: "column", gap: "sm" }, /* @__PURE__ */ React4.createElement(
4928
+ KanbanToolbar,
4929
+ {
4930
+ showSearch: searchEnabled,
4931
+ searchValue: resolvedSearch,
4932
+ searchPlaceholder: resolvedSearchPlaceholder,
4933
+ onSearchChange: handleSearch,
4934
+ filters,
4935
+ filterValues: resolvedFilters,
4936
+ onFilterChange: handleFilter,
4937
+ filterInlineLimit,
4938
+ showFilterBadges,
4939
+ showClearFiltersButton,
4940
+ activeChips,
4941
+ onFilterRemove: handleFilterRemove,
4942
+ sortOptions,
4943
+ sortValue: resolvedSort,
4944
+ onSortChange: handleSort,
4945
+ metrics,
4946
+ showMetrics: resolvedShowMetrics,
4947
+ onToggleMetrics: toggleMetrics,
4948
+ labels
4949
+ }
4950
+ ), showSelectionBar && selectable && selectedCount > 0 ? renderSelectionBar ? renderSelectionBar(selectionBarProps) : /* @__PURE__ */ React4.createElement(DefaultSelectionBar, { ...selectionBarProps }) : null, mainContent);
4951
+ };
4952
+
4953
+ // packages/kanban/src/KanbanCardActions.jsx
4954
+ import React5 from "react";
4955
+ import {
4956
+ Button as Button4,
4957
+ Dropdown as Dropdown2,
4958
+ Flex as Flex4,
4959
+ Icon as Icon4,
4960
+ Text as Text4
4961
+ } from "@hubspot/ui-extensions";
4962
+ var renderButton = (action, display, size) => {
4963
+ const { key, label, icon, tooltip, variant = "transparent", disabled, onClick, href } = action;
4964
+ const buttonProps = {
4965
+ key: key || label,
4966
+ variant,
4967
+ size,
4968
+ disabled,
4969
+ tooltip: tooltip || label
4970
+ };
4971
+ if (href) buttonProps.href = typeof href === "string" ? href : href;
4972
+ if (onClick) buttonProps.onClick = onClick;
4973
+ if (display === "icon") {
4974
+ return /* @__PURE__ */ React5.createElement(Button4, { ...buttonProps }, icon ? /* @__PURE__ */ React5.createElement(Icon4, { name: icon, size: "sm", screenReaderText: label }) : label);
4975
+ }
4976
+ if (display === "label") {
4977
+ return /* @__PURE__ */ React5.createElement(Button4, { ...buttonProps }, label);
4978
+ }
4979
+ return /* @__PURE__ */ React5.createElement(Button4, { ...buttonProps }, /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", align: "center", gap: "xs" }, icon ? /* @__PURE__ */ React5.createElement(Icon4, { name: icon, size: "sm", screenReaderText: label }) : null, /* @__PURE__ */ React5.createElement(Text4, { variant: "microcopy" }, label)));
4980
+ };
4981
+ var KanbanCardActions = ({
4982
+ actions = [],
4983
+ display = "icon",
4984
+ // Default to xs — the Button's own padding at sm visibly pushes icons away
4985
+ // from Tile edges. Icon glyph inside stays pinned to size="sm" in renderButton
4986
+ // so the icon itself doesn't shrink, only the hit-target/button padding does.
4987
+ size = "xs",
4988
+ align = "end",
4989
+ gap = "flush",
4990
+ separator = "none",
4991
+ overflowAfter,
4992
+ overflowLabel = "More"
4993
+ }) => {
4994
+ const visible = actions.filter((a) => a.visible !== false);
4995
+ if (visible.length === 0) return null;
4996
+ const cutoff = typeof overflowAfter === "number" ? Math.max(0, overflowAfter) : visible.length;
4997
+ const primary = visible.slice(0, cutoff);
4998
+ const overflow = visible.slice(cutoff);
4999
+ const renderedPrimary = primary.map((action, idx) => {
5000
+ const button = renderButton(action, display, size);
5001
+ if (separator === "pipe" && idx > 0) {
5002
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, { key: `${action.key || action.label}-sep` }, /* @__PURE__ */ React5.createElement(Text4, { variant: "microcopy" }, "|"), button);
5003
+ }
5004
+ return button;
5005
+ });
5006
+ const renderedOverflow = overflow.length > 0 ? /* @__PURE__ */ React5.createElement(Dropdown2, { variant: "transparent", buttonText: overflowLabel, buttonSize: size, key: "overflow" }, overflow.map((action) => /* @__PURE__ */ React5.createElement(
5007
+ Dropdown2.ButtonItem,
5008
+ {
5009
+ key: action.key || action.label,
5010
+ onClick: action.onClick
5011
+ },
5012
+ action.label
5013
+ ))) : null;
5014
+ const justify = align === "end" ? "end" : align === "between" ? "between" : "start";
5015
+ return /* @__PURE__ */ React5.createElement(Flex4, { direction: "row", align: "end", justify, gap }, renderedPrimary, renderedOverflow);
5016
+ };
5017
+
5018
+ // src/common-components/AutoTag.js
5019
+ import React6 from "react";
5020
+ import { Tag as Tag4 } from "@hubspot/ui-extensions";
3515
5021
 
3516
5022
  // src/utils/tagVariants.js
3517
5023
  var DEFAULT_VARIANT = "default";
@@ -3667,15 +5173,15 @@ var AutoTag = ({
3667
5173
  overrides,
3668
5174
  fallback
3669
5175
  });
3670
- return React3.createElement(
3671
- Tag2,
5176
+ return React6.createElement(
5177
+ Tag4,
3672
5178
  { variant: resolvedVariant, ...props },
3673
5179
  displayValue
3674
5180
  );
3675
5181
  };
3676
5182
 
3677
5183
  // src/common-components/AutoStatusTag.js
3678
- import React4 from "react";
5184
+ import React7 from "react";
3679
5185
  import { StatusTag } from "@hubspot/ui-extensions";
3680
5186
  var AutoStatusTag = ({
3681
5187
  value,
@@ -3692,27 +5198,260 @@ var AutoStatusTag = ({
3692
5198
  overrides,
3693
5199
  fallback
3694
5200
  });
3695
- return React4.createElement(
5201
+ return React7.createElement(
3696
5202
  StatusTag,
3697
5203
  { variant: resolvedVariant, ...props },
3698
5204
  displayValue
3699
5205
  );
3700
5206
  };
3701
5207
 
5208
+ // src/common-components/AvatarStack.js
5209
+ import React8 from "react";
5210
+ import { Image as Image3 } from "@hubspot/ui-extensions";
5211
+ var DEFAULT_COLORS = [
5212
+ "#0091ae",
5213
+ "#8B0000",
5214
+ "#ff5c35",
5215
+ "#00bda5",
5216
+ "#fdcc00",
5217
+ "#516f90",
5218
+ "#003366",
5219
+ "#8e7cc3"
5220
+ ];
5221
+ var SIZE_TOKENS = {
5222
+ xs: 16,
5223
+ "extra-small": 16,
5224
+ sm: 20,
5225
+ "small": 20,
5226
+ md: 24,
5227
+ "med": 24,
5228
+ "medium": 24,
5229
+ lg: 32,
5230
+ "large": 32,
5231
+ xl: 40,
5232
+ "extra-large": 40
5233
+ };
5234
+ var resolveSize = (size) => {
5235
+ if (typeof size === "number") return size;
5236
+ if (typeof size === "string" && SIZE_TOKENS[size] != null) return SIZE_TOKENS[size];
5237
+ return 24;
5238
+ };
5239
+ var isImageUri = (s) => typeof s === "string" && /^(https?:|data:image\/)/i.test(s);
5240
+ var escapeXmlAttr = (s) => String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5241
+ var pickColor = (key, palette, index) => {
5242
+ if (!key) return palette[index % palette.length];
5243
+ const code = String(key).charCodeAt(0) || 0;
5244
+ return palette[(code + index) % palette.length];
5245
+ };
5246
+ var normalizeEntry = (entry) => {
5247
+ if (entry == null) return null;
5248
+ if (typeof entry === "string") {
5249
+ if (entry.length === 0) return null;
5250
+ if (isImageUri(entry)) return { src: entry };
5251
+ return { letter: entry.slice(0, 2).toUpperCase() };
5252
+ }
5253
+ if (typeof entry === "object") {
5254
+ if (entry.src) return { src: entry.src, letter: entry.letter };
5255
+ if (entry.letter) return { letter: String(entry.letter).slice(0, 2).toUpperCase(), color: entry.color };
5256
+ }
5257
+ return null;
5258
+ };
5259
+ var makeAvatarStackDataUri = (rawEntries, opts = {}) => {
5260
+ const {
5261
+ size: sizeProp = "medium",
5262
+ step: stepProp,
5263
+ overlap: overlapProp,
5264
+ maxVisible = 4,
5265
+ colors = DEFAULT_COLORS,
5266
+ overflowBg = HS_NEUTRAL_CHIP,
5267
+ overflowColor = HS_TEXT_COLOR,
5268
+ fontFamily = HS_FONT_FAMILY
5269
+ } = opts;
5270
+ const size = resolveSize(sizeProp);
5271
+ let step;
5272
+ if (stepProp != null) {
5273
+ step = stepProp;
5274
+ } else if (overlapProp != null) {
5275
+ const clampedOverlap = Math.max(0, Math.min(size - 1, overlapProp));
5276
+ step = size - clampedOverlap;
5277
+ } else {
5278
+ step = Math.round(size * 0.65);
5279
+ }
5280
+ const entries = (rawEntries || []).map(normalizeEntry).filter(Boolean);
5281
+ if (entries.length === 0) return null;
5282
+ const visible = entries.slice(0, maxVisible);
5283
+ const overflowCount = entries.length - visible.length;
5284
+ const slots = overflowCount > 0 ? [...entries.slice(0, maxVisible - 1), { overflow: overflowCount }] : visible;
5285
+ const count = slots.length;
5286
+ const r = size / 2;
5287
+ const haloR = r + 1;
5288
+ const width = size + (count - 1) * step;
5289
+ const height = size;
5290
+ const defs = `<defs><clipPath id="hsuixAvatarClip"><circle cx="${r}" cy="${r}" r="${r}"/></clipPath></defs>`;
5291
+ const fontFamilyAttr = fontFamily.replace(/"/g, "&quot;");
5292
+ const pieces = slots.map((slot, i) => {
5293
+ const cx = r + i * step;
5294
+ const tx = i * step;
5295
+ const halo = i > 0 ? `<circle cx="${cx}" cy="${r}" r="${haloR}" fill="#ffffff" />` : "";
5296
+ if (slot.overflow) {
5297
+ return halo + `<circle cx="${cx}" cy="${r}" r="${r}" fill="${overflowBg}" /><text x="${cx}" y="${r + 1}" text-anchor="middle" dominant-baseline="central" font-family="${fontFamilyAttr}" font-size="${Math.round(size * 0.42)}" font-weight="700" fill="${overflowColor}">+${slot.overflow}</text>`;
5298
+ }
5299
+ if (slot.src) {
5300
+ return halo + `<g transform="translate(${tx}, 0)"><image href="${escapeXmlAttr(slot.src)}" x="0" y="0" width="${size}" height="${size}" preserveAspectRatio="xMidYMid slice" clip-path="url(#hsuixAvatarClip)" /></g>`;
5301
+ }
5302
+ const letter = slot.letter || "?";
5303
+ const bgColor = slot.color || pickColor(letter, colors, i);
5304
+ return halo + `<circle cx="${cx}" cy="${r}" r="${r}" fill="${bgColor}" /><text x="${cx}" y="${r + 1}" text-anchor="middle" dominant-baseline="central" font-family="${fontFamilyAttr}" font-size="${Math.round(size * 0.46)}" font-weight="700" fill="#ffffff">${escapeXmlAttr(letter)}</text>`;
5305
+ });
5306
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${width}" height="${height}">` + defs + pieces.join("") + `</svg>`;
5307
+ return {
5308
+ src: `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,
5309
+ width,
5310
+ height,
5311
+ count
5312
+ };
5313
+ };
5314
+ var AvatarStack = ({
5315
+ items,
5316
+ size,
5317
+ overlap,
5318
+ step,
5319
+ maxVisible,
5320
+ colors,
5321
+ overflowBg,
5322
+ overflowColor,
5323
+ fontFamily,
5324
+ alt
5325
+ }) => {
5326
+ const stack = makeAvatarStackDataUri(items, {
5327
+ size,
5328
+ overlap,
5329
+ step,
5330
+ maxVisible,
5331
+ colors,
5332
+ overflowBg,
5333
+ overflowColor,
5334
+ fontFamily
5335
+ });
5336
+ if (!stack) return null;
5337
+ return React8.createElement(Image3, {
5338
+ src: stack.src,
5339
+ width: stack.width,
5340
+ height: stack.height,
5341
+ alt: alt ?? `${items.length} associated records`
5342
+ });
5343
+ };
5344
+
5345
+ // src/common-components/datePresets.js
5346
+ var HS_DATE_PRESETS = [
5347
+ { label: "Today", value: "today" },
5348
+ { label: "Yesterday", value: "yesterday" },
5349
+ { label: "Tomorrow", value: "tomorrow" },
5350
+ { label: "This week", value: "this_week" },
5351
+ { label: "Last week", value: "last_week" },
5352
+ { label: "Last 7 days", value: "7d" },
5353
+ { label: "Last 30 days", value: "30d" },
5354
+ { label: "Last 90 days", value: "90d" },
5355
+ { label: "This month", value: "this_month" },
5356
+ { label: "Last month", value: "last_month" },
5357
+ { label: "This quarter", value: "this_quarter" },
5358
+ { label: "Last quarter", value: "last_quarter" },
5359
+ { label: "This year", value: "this_year" },
5360
+ { label: "Last year", value: "last_year" }
5361
+ ];
5362
+ var HS_DATE_DIRECTION_LABELS = {
5363
+ asc: "Ascending",
5364
+ desc: "Descending"
5365
+ };
5366
+
3702
5367
  // src/common-components/KeyValueList.js
3703
- import React5 from "react";
3704
- import { DescriptionList, DescriptionListItem, Flex as Flex3 } from "@hubspot/ui-extensions";
5368
+ import React9 from "react";
5369
+ import { DescriptionList as DescriptionList2, DescriptionListItem as DescriptionListItem2, Flex as Flex5 } from "@hubspot/ui-extensions";
5370
+ var KeyValueList = ({ items = [], direction = "row", gap = "sm" }) => {
5371
+ const rows = items.map(
5372
+ (item, index) => React9.createElement(
5373
+ DescriptionListItem2,
5374
+ {
5375
+ key: item.key ?? item.label ?? `kv-${index}`,
5376
+ label: item.label
5377
+ },
5378
+ item.value
5379
+ )
5380
+ );
5381
+ return React9.createElement(
5382
+ Flex5,
5383
+ { direction: "column", gap },
5384
+ React9.createElement(DescriptionList2, { direction }, ...rows)
5385
+ );
5386
+ };
3705
5387
 
3706
5388
  // src/common-components/SectionHeader.js
3707
- import React6 from "react";
3708
- import { Flex as Flex4, Heading, Text as Text3 } from "@hubspot/ui-extensions";
5389
+ import React10 from "react";
5390
+ import { Flex as Flex6, Heading, Text as Text5 } from "@hubspot/ui-extensions";
5391
+ var SectionHeader = ({
5392
+ title,
5393
+ description,
5394
+ actions,
5395
+ children,
5396
+ gap = "xs",
5397
+ titleAs = "h2"
5398
+ }) => {
5399
+ const body = [];
5400
+ if (title != null) {
5401
+ body.push(React10.createElement(Heading, { key: "title", as: titleAs }, title));
5402
+ }
5403
+ if (description != null) {
5404
+ body.push(
5405
+ React10.createElement(
5406
+ Text5,
5407
+ { key: "description", variant: "microcopy" },
5408
+ description
5409
+ )
5410
+ );
5411
+ }
5412
+ if (children != null) {
5413
+ body.push(children);
5414
+ }
5415
+ const content = React10.createElement(Flex6, { direction: "column", gap }, ...body);
5416
+ if (actions == null) return content;
5417
+ return React10.createElement(
5418
+ Flex6,
5419
+ { direction: "row", justify: "between", align: "start", gap: "sm" },
5420
+ content,
5421
+ actions
5422
+ );
5423
+ };
3709
5424
  export {
3710
5425
  AutoStatusTag,
3711
5426
  AutoTag,
5427
+ AvatarStack,
5428
+ DEFAULT_SVG_FONT_WEIGHT,
3712
5429
  DataTable,
3713
5430
  FormBuilder,
5431
+ HS_DATE_DIRECTION_LABELS,
5432
+ HS_DATE_PRESETS,
5433
+ HS_FONT_FAMILY,
5434
+ HS_MUTED_TEXT,
5435
+ HS_NEUTRAL_CHIP,
5436
+ HS_SUBTLE_BG,
5437
+ HS_TAG_BORDER_RADIUS,
5438
+ HS_TAG_BORDER_WIDTH,
5439
+ HS_TAG_FONT_SIZE,
5440
+ HS_TAG_LINE_HEIGHT,
5441
+ HS_TAG_PADDING_X,
5442
+ HS_TAG_PADDING_Y,
5443
+ HS_TAG_SUBTLE_BORDER,
5444
+ HS_TAG_TEXT_COLOR,
5445
+ HS_TEXT_COLOR,
5446
+ Kanban,
5447
+ KanbanCardActions,
5448
+ KeyValueList,
5449
+ SectionHeader,
5450
+ StyledText,
3714
5451
  createStatusTagSortComparator,
3715
5452
  getAutoStatusTagVariant,
3716
5453
  getAutoTagVariant,
5454
+ makeAvatarStackDataUri,
5455
+ makeStyledTextDataUri,
3717
5456
  useFormPrefill
3718
5457
  };