hs-uix 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -0
- package/common-components.d.ts +127 -0
- package/dist/common-components.js +491 -16
- package/dist/common-components.mjs +465 -10
- package/dist/datatable.js +3 -3
- package/dist/datatable.mjs +5 -4
- package/dist/form.js +18 -1
- package/dist/form.mjs +18 -1
- package/dist/index.js +1745 -17
- package/dist/index.mjs +1760 -20
- package/dist/kanban.js +1410 -0
- package/dist/kanban.mjs +1408 -0
- package/dist/utils.js +60 -0
- package/dist/utils.mjs +58 -0
- package/form.d.ts +1 -0
- package/index.d.ts +65 -0
- package/kanban.d.ts +1 -0
- package/package.json +9 -1
package/dist/index.mjs
CHANGED
|
@@ -30,7 +30,8 @@ import {
|
|
|
30
30
|
Text,
|
|
31
31
|
TextArea,
|
|
32
32
|
TimeInput,
|
|
33
|
-
Toggle
|
|
33
|
+
Toggle,
|
|
34
|
+
Tooltip
|
|
34
35
|
} from "@hubspot/ui-extensions";
|
|
35
36
|
var formatDateChip = (dateObj) => {
|
|
36
37
|
if (!dateObj) return "";
|
|
@@ -942,7 +943,7 @@ var DataTable = ({
|
|
|
942
943
|
}
|
|
943
944
|
return isEmpty ? "--" : content;
|
|
944
945
|
};
|
|
945
|
-
const
|
|
946
|
+
const renderFilterControl2 = (filter) => {
|
|
946
947
|
const type = filter.type || "select";
|
|
947
948
|
if (type === "multiselect") {
|
|
948
949
|
return /* @__PURE__ */ React.createElement(
|
|
@@ -999,7 +1000,7 @@ var DataTable = ({
|
|
|
999
1000
|
}
|
|
1000
1001
|
);
|
|
1001
1002
|
};
|
|
1002
|
-
return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "
|
|
1003
|
+
return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ React.createElement(
|
|
1003
1004
|
SearchInput,
|
|
1004
1005
|
{
|
|
1005
1006
|
name: "datatable-search",
|
|
@@ -1007,7 +1008,7 @@ var DataTable = ({
|
|
|
1007
1008
|
value: internalSearchTerm,
|
|
1008
1009
|
onChange: handleSearchChange
|
|
1009
1010
|
}
|
|
1010
|
-
), filters.slice(0, filterInlineLimit).map(
|
|
1011
|
+
), filters.slice(0, filterInlineLimit).map(renderFilterControl2), filters.length > filterInlineLimit && /* @__PURE__ */ React.createElement(
|
|
1011
1012
|
Button,
|
|
1012
1013
|
{
|
|
1013
1014
|
variant: "transparent",
|
|
@@ -1017,7 +1018,7 @@ var DataTable = ({
|
|
|
1017
1018
|
/* @__PURE__ */ React.createElement(Icon, { name: "filter", size: "sm" }),
|
|
1018
1019
|
" ",
|
|
1019
1020
|
resolvedFiltersButtonLabel
|
|
1020
|
-
)), showMoreFilters && filters.length > filterInlineLimit && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "
|
|
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(
|
|
1021
1022
|
Button,
|
|
1022
1023
|
{
|
|
1023
1024
|
variant: "transparent",
|
|
@@ -1081,7 +1082,7 @@ var DataTable = ({
|
|
|
1081
1082
|
sortDirection: col.sortable ? sortState[col.field] || "none" : "never",
|
|
1082
1083
|
onSortChange: col.sortable ? () => handleSortChange(col.field) : void 0
|
|
1083
1084
|
},
|
|
1084
|
-
col.label
|
|
1085
|
+
col.description ? /* @__PURE__ */ React.createElement(React.Fragment, null, col.label, "\xA0", /* @__PURE__ */ React.createElement(Link, { inline: true, variant: "dark", overlay: /* @__PURE__ */ React.createElement(Tooltip, null, col.description) }, /* @__PURE__ */ React.createElement(Icon, { name: "info", screenReaderText: typeof col.description === "string" ? col.description : void 0 }))) : col.label
|
|
1085
1086
|
);
|
|
1086
1087
|
}), showRowActionsColumn && /* @__PURE__ */ React.createElement(TableHeader, { width: "min" }))),
|
|
1087
1088
|
/* @__PURE__ */ React.createElement(TableBody, null, displayRows.map(
|
|
@@ -1159,7 +1160,7 @@ import {
|
|
|
1159
1160
|
Button as Button2,
|
|
1160
1161
|
LoadingButton,
|
|
1161
1162
|
Alert,
|
|
1162
|
-
Tooltip,
|
|
1163
|
+
Tooltip as Tooltip2,
|
|
1163
1164
|
StepIndicator,
|
|
1164
1165
|
Input as Input2,
|
|
1165
1166
|
TextArea as TextArea2,
|
|
@@ -3154,6 +3155,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3154
3155
|
}
|
|
3155
3156
|
};
|
|
3156
3157
|
const getFieldColSpan = (field) => {
|
|
3158
|
+
if (field.colSpan === "full") return columns;
|
|
3157
3159
|
if (field.colSpan != null) return Math.min(field.colSpan, columns);
|
|
3158
3160
|
if (field.width === "full" && columns > 1) return columns;
|
|
3159
3161
|
if (columns > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return columns;
|
|
@@ -3169,7 +3171,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3169
3171
|
const groupLabel = getDependsOnLabel(firstWithLabel) || dependentPropertiesLabel;
|
|
3170
3172
|
const rawMessage = getDependsOnMessage(firstWithMessage);
|
|
3171
3173
|
const tooltipMessage = typeof rawMessage === "function" ? rawMessage(parentField.label) : rawMessage || "";
|
|
3172
|
-
return /* @__PURE__ */ React2.createElement(Tile, { key: `dep-${parentField.name}`, compact: true }, /* @__PURE__ */ React2.createElement(Flex2, { direction: "column", gap }, /* @__PURE__ */ React2.createElement(Flex2, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React2.createElement(Text2, { format: { fontWeight: "demibold" } }, groupLabel, " ", tooltipMessage && /* @__PURE__ */ React2.createElement(Link2, { inline: true, variant: "dark", overlay: /* @__PURE__ */ React2.createElement(
|
|
3174
|
+
return /* @__PURE__ */ React2.createElement(Tile, { key: `dep-${parentField.name}`, compact: true }, /* @__PURE__ */ React2.createElement(Flex2, { direction: "column", gap }, /* @__PURE__ */ React2.createElement(Flex2, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React2.createElement(Text2, { format: { fontWeight: "demibold" } }, groupLabel, " ", tooltipMessage && /* @__PURE__ */ React2.createElement(Link2, { inline: true, variant: "dark", overlay: /* @__PURE__ */ React2.createElement(Tooltip2, null, tooltipMessage) }, /* @__PURE__ */ React2.createElement(Icon2, { name: "info" })))), renderFieldSubset(dependents)));
|
|
3173
3175
|
};
|
|
3174
3176
|
const renderGridLayout = (fieldSubset, effectiveCols) => {
|
|
3175
3177
|
const cols = effectiveCols || columns;
|
|
@@ -3179,6 +3181,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3179
3181
|
let currentRowSpan = 0;
|
|
3180
3182
|
const gridColumnWidth = 200;
|
|
3181
3183
|
const colSpan = (field) => {
|
|
3184
|
+
if (field.colSpan === "full") return cols;
|
|
3182
3185
|
if (field.colSpan != null) return Math.min(field.colSpan, cols);
|
|
3183
3186
|
if (field.width === "full" && cols > 1) return cols;
|
|
3184
3187
|
if (cols > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return cols;
|
|
@@ -3312,8 +3315,16 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3312
3315
|
}
|
|
3313
3316
|
batch = [];
|
|
3314
3317
|
};
|
|
3318
|
+
const isFullSpan = (f) => f.colSpan === "full" || typeof f.colSpan === "number" && f.colSpan > 1;
|
|
3315
3319
|
for (const field of fieldList) {
|
|
3316
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
|
+
}
|
|
3317
3328
|
batch.push(field);
|
|
3318
3329
|
const dependents = getDependents(field);
|
|
3319
3330
|
if (dependents.length > 0) {
|
|
@@ -3325,6 +3336,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3325
3336
|
return elements;
|
|
3326
3337
|
};
|
|
3327
3338
|
const wrapWithGroups = (fieldList, renderFn) => {
|
|
3339
|
+
const formatGroupLabel = (groupName) => String(groupName || "").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().replace(/\b\w/g, (char) => char.toUpperCase());
|
|
3328
3340
|
const hasGroups = fieldList.some((f) => f.group);
|
|
3329
3341
|
if (!hasGroups) return renderFn(fieldList);
|
|
3330
3342
|
const chunks = [];
|
|
@@ -3346,6 +3358,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3346
3358
|
for (let i = 0; i < chunks.length; i++) {
|
|
3347
3359
|
const chunk = chunks[i];
|
|
3348
3360
|
const opts = chunk.group && groups && groups[chunk.group] || {};
|
|
3361
|
+
const resolvedGroupLabel = opts.label || formatGroupLabel(chunk.group);
|
|
3349
3362
|
const showDivider = opts.showDivider !== false;
|
|
3350
3363
|
const showLabel = opts.showLabel !== false;
|
|
3351
3364
|
if (i > 0 && showDivider) {
|
|
@@ -3359,8 +3372,13 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3359
3372
|
);
|
|
3360
3373
|
} else {
|
|
3361
3374
|
elements.push(
|
|
3362
|
-
/* @__PURE__ */ React2.createElement(Text2, { key: `group-label-${i}`, format: { fontWeight: "demibold" } },
|
|
3375
|
+
/* @__PURE__ */ React2.createElement(Text2, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, resolvedGroupLabel)
|
|
3363
3376
|
);
|
|
3377
|
+
if (opts.description) {
|
|
3378
|
+
elements.push(
|
|
3379
|
+
/* @__PURE__ */ React2.createElement(Text2, { key: `group-description-${i}`, variant: "microcopy" }, opts.description)
|
|
3380
|
+
);
|
|
3381
|
+
}
|
|
3364
3382
|
}
|
|
3365
3383
|
}
|
|
3366
3384
|
const chunkElements = renderFn(chunk.fields);
|
|
@@ -3405,7 +3423,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3405
3423
|
);
|
|
3406
3424
|
if (sec.info) {
|
|
3407
3425
|
elements.push(
|
|
3408
|
-
/* @__PURE__ */ React2.createElement(Flex2, { key: sec.id, direction: "row", align: "start", justify: "start", gap: "flush" }, /* @__PURE__ */ React2.createElement(Box2, { flex: 1 }, accordion), /* @__PURE__ */ React2.createElement(Link2, { variant: "dark", overlay: /* @__PURE__ */ React2.createElement(
|
|
3426
|
+
/* @__PURE__ */ React2.createElement(Flex2, { key: sec.id, direction: "row", align: "start", justify: "start", gap: "flush" }, /* @__PURE__ */ React2.createElement(Box2, { flex: 1 }, accordion), /* @__PURE__ */ React2.createElement(Link2, { variant: "dark", overlay: /* @__PURE__ */ React2.createElement(Tooltip2, null, sec.info) }, /* @__PURE__ */ React2.createElement(Icon2, { name: "info", size: "sm", screenReaderText: sec.info })))
|
|
3409
3427
|
);
|
|
3410
3428
|
} else {
|
|
3411
3429
|
elements.push(accordion);
|
|
@@ -3508,9 +3526,1498 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
|
|
|
3508
3526
|
);
|
|
3509
3527
|
});
|
|
3510
3528
|
|
|
3511
|
-
// src/
|
|
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
|
|
3512
3534
|
import React3 from "react";
|
|
3513
|
-
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, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
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, """)}" 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";
|
|
3514
5021
|
|
|
3515
5022
|
// src/utils/tagVariants.js
|
|
3516
5023
|
var DEFAULT_VARIANT = "default";
|
|
@@ -3666,15 +5173,15 @@ var AutoTag = ({
|
|
|
3666
5173
|
overrides,
|
|
3667
5174
|
fallback
|
|
3668
5175
|
});
|
|
3669
|
-
return
|
|
3670
|
-
|
|
5176
|
+
return React6.createElement(
|
|
5177
|
+
Tag4,
|
|
3671
5178
|
{ variant: resolvedVariant, ...props },
|
|
3672
5179
|
displayValue
|
|
3673
5180
|
);
|
|
3674
5181
|
};
|
|
3675
5182
|
|
|
3676
5183
|
// src/common-components/AutoStatusTag.js
|
|
3677
|
-
import
|
|
5184
|
+
import React7 from "react";
|
|
3678
5185
|
import { StatusTag } from "@hubspot/ui-extensions";
|
|
3679
5186
|
var AutoStatusTag = ({
|
|
3680
5187
|
value,
|
|
@@ -3691,27 +5198,260 @@ var AutoStatusTag = ({
|
|
|
3691
5198
|
overrides,
|
|
3692
5199
|
fallback
|
|
3693
5200
|
});
|
|
3694
|
-
return
|
|
5201
|
+
return React7.createElement(
|
|
3695
5202
|
StatusTag,
|
|
3696
5203
|
{ variant: resolvedVariant, ...props },
|
|
3697
5204
|
displayValue
|
|
3698
5205
|
);
|
|
3699
5206
|
};
|
|
3700
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, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
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, """);
|
|
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
|
+
|
|
3701
5367
|
// src/common-components/KeyValueList.js
|
|
3702
|
-
import
|
|
3703
|
-
import { DescriptionList, DescriptionListItem, Flex as
|
|
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
|
+
};
|
|
3704
5387
|
|
|
3705
5388
|
// src/common-components/SectionHeader.js
|
|
3706
|
-
import
|
|
3707
|
-
import { Flex as
|
|
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
|
+
};
|
|
3708
5424
|
export {
|
|
3709
5425
|
AutoStatusTag,
|
|
3710
5426
|
AutoTag,
|
|
5427
|
+
AvatarStack,
|
|
5428
|
+
DEFAULT_SVG_FONT_WEIGHT,
|
|
3711
5429
|
DataTable,
|
|
3712
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,
|
|
3713
5451
|
createStatusTagSortComparator,
|
|
3714
5452
|
getAutoStatusTagVariant,
|
|
3715
5453
|
getAutoTagVariant,
|
|
5454
|
+
makeAvatarStackDataUri,
|
|
5455
|
+
makeStyledTextDataUri,
|
|
3716
5456
|
useFormPrefill
|
|
3717
5457
|
};
|