@inf-monkeys-tech/monkeys-design 1.0.38 → 1.0.39

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.
Files changed (58) hide show
  1. package/dist/components/agent-workbench/index.js +9 -0
  2. package/dist/components/agent-workbench/index.js.map +1 -1
  3. package/dist/components/agent-workbench/index.mjs +9 -0
  4. package/dist/components/agent-workbench/index.mjs.map +1 -1
  5. package/dist/components/base/index.js +9 -0
  6. package/dist/components/base/index.js.map +1 -1
  7. package/dist/components/base/index.mjs +9 -0
  8. package/dist/components/base/index.mjs.map +1 -1
  9. package/dist/components/data-explorer/index.js +9 -0
  10. package/dist/components/data-explorer/index.js.map +1 -1
  11. package/dist/components/data-explorer/index.mjs +9 -0
  12. package/dist/components/data-explorer/index.mjs.map +1 -1
  13. package/dist/components/layout/index.js +9 -0
  14. package/dist/components/layout/index.js.map +1 -1
  15. package/dist/components/layout/index.mjs +9 -0
  16. package/dist/components/layout/index.mjs.map +1 -1
  17. package/dist/components/runtime/index.js +9 -0
  18. package/dist/components/runtime/index.js.map +1 -1
  19. package/dist/components/runtime/index.mjs +9 -0
  20. package/dist/components/runtime/index.mjs.map +1 -1
  21. package/dist/components/toast/index.d.mts +1 -1
  22. package/dist/components/toast/index.d.ts +1 -1
  23. package/dist/components/toast/index.js +9 -0
  24. package/dist/components/toast/index.js.map +1 -1
  25. package/dist/components/toast/index.mjs +9 -0
  26. package/dist/components/toast/index.mjs.map +1 -1
  27. package/dist/components/toolbar/index.js +9 -0
  28. package/dist/components/toolbar/index.js.map +1 -1
  29. package/dist/components/toolbar/index.mjs +9 -0
  30. package/dist/components/toolbar/index.mjs.map +1 -1
  31. package/dist/components/workbench/index.d.mts +218 -6
  32. package/dist/components/workbench/index.d.ts +218 -6
  33. package/dist/components/workbench/index.js +1088 -97
  34. package/dist/components/workbench/index.js.map +1 -1
  35. package/dist/components/workbench/index.mjs +1089 -99
  36. package/dist/components/workbench/index.mjs.map +1 -1
  37. package/dist/index.d.mts +2 -2
  38. package/dist/index.d.ts +2 -2
  39. package/dist/index.js +838 -100
  40. package/dist/index.js.map +1 -1
  41. package/dist/index.mjs +838 -101
  42. package/dist/index.mjs.map +1 -1
  43. package/dist/locale/index.d.mts +20 -2
  44. package/dist/locale/index.d.ts +20 -2
  45. package/dist/locale/index.js +22 -0
  46. package/dist/locale/index.js.map +1 -1
  47. package/dist/locale/index.mjs +22 -0
  48. package/dist/locale/index.mjs.map +1 -1
  49. package/dist/provider/index.d.mts +2 -2
  50. package/dist/provider/index.d.ts +2 -2
  51. package/dist/provider/index.js +22 -0
  52. package/dist/provider/index.js.map +1 -1
  53. package/dist/provider/index.mjs +22 -0
  54. package/dist/provider/index.mjs.map +1 -1
  55. package/dist/styles.css +1 -1
  56. package/dist/{types-b8d18b1454c2.d.mts → types-453d18eab188.d.mts} +10 -0
  57. package/dist/{types-b8d18b1454c2.d.ts → types-453d18eab188.d.ts} +10 -0
  58. package/package.json +1 -1
@@ -491,6 +491,15 @@ var enUS = {
491
491
  }
492
492
  },
493
493
  workbench: {
494
+ collection: {
495
+ label: "Collection",
496
+ loading: "Loading items",
497
+ empty: "No items",
498
+ loadError: "Unable to load items",
499
+ refreshError: "Unable to refresh items",
500
+ retry: "Retry",
501
+ loadingMore: "Loading more items"
502
+ },
494
503
  interactiveTable: {
495
504
  columnActions: "Column actions",
496
505
  freezeColumn: "Freeze to left",
@@ -2223,6 +2232,16 @@ function getBaseButtonToneClassName(theme, tone = "default", active = false) {
2223
2232
  active && theme.slots.buttonActive
2224
2233
  );
2225
2234
  }
2235
+ function getBaseNoticeToneClassName(theme, tone = "default") {
2236
+ return cn(
2237
+ tone === "default" && theme.slots.noticeDefault,
2238
+ tone === "primary" && theme.slots.noticePrimary,
2239
+ tone === "success" && theme.slots.noticeSuccess,
2240
+ tone === "warning" && theme.slots.noticeWarning,
2241
+ tone === "danger" && theme.slots.noticeDanger,
2242
+ tone === "muted" && theme.slots.noticeMuted
2243
+ );
2244
+ }
2226
2245
  function getBaseMenuItemToneClassName(theme, tone = "default") {
2227
2246
  return cn(
2228
2247
  tone === "default" && theme.slots.menuItemDefault,
@@ -3456,6 +3475,98 @@ function BaseDescriptionList({
3456
3475
  }
3457
3476
  );
3458
3477
  }
3478
+ function DefaultEmptyIcon() {
3479
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3480
+ "svg",
3481
+ {
3482
+ "aria-hidden": "true",
3483
+ viewBox: "0 0 24 24",
3484
+ className: "h-6 w-6",
3485
+ fill: "none",
3486
+ stroke: "currentColor",
3487
+ strokeLinecap: "round",
3488
+ strokeLinejoin: "round",
3489
+ strokeWidth: "1.8",
3490
+ children: [
3491
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "16", height: "12", x: "4", y: "6", rx: "2" }),
3492
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 10h8" }),
3493
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 14h5" })
3494
+ ]
3495
+ }
3496
+ );
3497
+ }
3498
+ var BaseEmptyState = react.forwardRef(
3499
+ function BaseEmptyState2({
3500
+ icon = /* @__PURE__ */ jsxRuntime.jsx(DefaultEmptyIcon, {}),
3501
+ title,
3502
+ description,
3503
+ actions,
3504
+ appearance,
3505
+ className,
3506
+ classNames,
3507
+ style,
3508
+ ...stateProps
3509
+ }, ref) {
3510
+ const resolvedAppearance = useMonkeysBaseAppearance(appearance);
3511
+ const componentAttributes = useMonkeysComponentAttributes();
3512
+ const theme = resolveBaseAppearance(resolvedAppearance);
3513
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3514
+ "div",
3515
+ {
3516
+ ...componentAttributes,
3517
+ ...stateProps,
3518
+ ref,
3519
+ "data-monkeys-component": "empty-state",
3520
+ "data-slot": "root",
3521
+ "data-state": "empty",
3522
+ className: cn(theme.slots.stateRoot, classNames?.root, className),
3523
+ style: {
3524
+ ...theme.styles.stateRoot,
3525
+ ...style
3526
+ },
3527
+ children: [
3528
+ hasRenderableNode(icon) ? /* @__PURE__ */ jsxRuntime.jsx(
3529
+ "div",
3530
+ {
3531
+ "data-slot": "icon",
3532
+ "data-state": "empty",
3533
+ className: cn(theme.slots.stateIcon, classNames?.icon),
3534
+ style: theme.styles.stateIcon,
3535
+ children: icon
3536
+ }
3537
+ ) : null,
3538
+ hasRenderableNode(title) ? /* @__PURE__ */ jsxRuntime.jsx(
3539
+ "div",
3540
+ {
3541
+ "data-slot": "title",
3542
+ "data-state": "empty",
3543
+ className: cn(theme.slots.stateTitle, classNames?.title),
3544
+ children: title
3545
+ }
3546
+ ) : null,
3547
+ hasRenderableNode(description) ? /* @__PURE__ */ jsxRuntime.jsx(
3548
+ "div",
3549
+ {
3550
+ "data-slot": "description",
3551
+ "data-state": "empty",
3552
+ className: cn(theme.slots.stateDescription, classNames?.description),
3553
+ children: description
3554
+ }
3555
+ ) : null,
3556
+ hasRenderableNode(actions) ? /* @__PURE__ */ jsxRuntime.jsx(
3557
+ "div",
3558
+ {
3559
+ "data-slot": "actions",
3560
+ "data-state": "empty",
3561
+ className: cn(theme.slots.stateActions, classNames?.actions),
3562
+ children: actions
3563
+ }
3564
+ ) : null
3565
+ ]
3566
+ }
3567
+ );
3568
+ }
3569
+ );
3459
3570
  function mergeAriaIds(...values) {
3460
3571
  const ids = values.flatMap(
3461
3572
  (value) => value?.split(/\s+/).filter(Boolean) ?? []
@@ -3814,6 +3925,170 @@ var BaseInput = react.forwardRef(
3814
3925
  );
3815
3926
  }
3816
3927
  );
3928
+ function DefaultLoadingIcon({ className }) {
3929
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3930
+ "svg",
3931
+ {
3932
+ "aria-hidden": "true",
3933
+ viewBox: "0 0 24 24",
3934
+ className,
3935
+ fill: "none",
3936
+ children: [
3937
+ /* @__PURE__ */ jsxRuntime.jsx(
3938
+ "circle",
3939
+ {
3940
+ cx: "12",
3941
+ cy: "12",
3942
+ r: "9",
3943
+ className: "opacity-25",
3944
+ stroke: "currentColor",
3945
+ strokeWidth: "3"
3946
+ }
3947
+ ),
3948
+ /* @__PURE__ */ jsxRuntime.jsx(
3949
+ "path",
3950
+ {
3951
+ className: "opacity-75",
3952
+ fill: "currentColor",
3953
+ d: "M12 3a9 9 0 0 1 9 9h-3a6 6 0 0 0-6-6V3Z"
3954
+ }
3955
+ )
3956
+ ]
3957
+ }
3958
+ );
3959
+ }
3960
+ var BaseLoadingState = react.forwardRef(function BaseLoadingState2({
3961
+ icon,
3962
+ title,
3963
+ description,
3964
+ actions,
3965
+ appearance,
3966
+ className,
3967
+ classNames,
3968
+ style,
3969
+ role = "status",
3970
+ "aria-live": ariaLive = "polite",
3971
+ "aria-busy": ariaBusy = true,
3972
+ "aria-label": nativeAriaLabel,
3973
+ "aria-labelledby": nativeAriaLabelledBy,
3974
+ ...stateProps
3975
+ }, ref) {
3976
+ const resolvedAppearance = useMonkeysBaseAppearance(appearance);
3977
+ const componentAttributes = useMonkeysComponentAttributes();
3978
+ const loadingMessages = useMonkeysLocaleMessages().loading;
3979
+ const theme = resolveBaseAppearance(resolvedAppearance);
3980
+ const resolvedIcon = icon === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(DefaultLoadingIcon, { className: theme.slots.loadingSpinner }) : icon;
3981
+ const resolvedAriaLabel = nativeAriaLabel ?? (nativeAriaLabelledBy === void 0 ? loadingMessages.spinner : void 0);
3982
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3983
+ "div",
3984
+ {
3985
+ ...componentAttributes,
3986
+ ...stateProps,
3987
+ ref,
3988
+ role,
3989
+ "aria-live": ariaLive,
3990
+ "aria-busy": ariaBusy,
3991
+ "aria-label": resolvedAriaLabel,
3992
+ "aria-labelledby": nativeAriaLabelledBy,
3993
+ "data-monkeys-component": "loading-state",
3994
+ "data-slot": "root",
3995
+ "data-state": "loading",
3996
+ className: cn(theme.slots.stateRoot, classNames?.root, className),
3997
+ style: {
3998
+ ...theme.styles.stateRoot,
3999
+ ...style
4000
+ },
4001
+ children: [
4002
+ hasRenderableNode(resolvedIcon) ? /* @__PURE__ */ jsxRuntime.jsx(
4003
+ "div",
4004
+ {
4005
+ "data-slot": "icon",
4006
+ "data-state": "loading",
4007
+ className: cn(theme.slots.stateIcon, classNames?.icon),
4008
+ style: theme.styles.stateIcon,
4009
+ children: resolvedIcon
4010
+ }
4011
+ ) : null,
4012
+ hasRenderableNode(title) ? /* @__PURE__ */ jsxRuntime.jsx(
4013
+ "div",
4014
+ {
4015
+ "data-slot": "title",
4016
+ "data-state": "loading",
4017
+ className: cn(theme.slots.stateTitle, classNames?.title),
4018
+ children: title
4019
+ }
4020
+ ) : null,
4021
+ hasRenderableNode(description) ? /* @__PURE__ */ jsxRuntime.jsx(
4022
+ "div",
4023
+ {
4024
+ "data-slot": "description",
4025
+ "data-state": "loading",
4026
+ className: cn(theme.slots.stateDescription, classNames?.description),
4027
+ children: description
4028
+ }
4029
+ ) : null,
4030
+ hasRenderableNode(actions) ? /* @__PURE__ */ jsxRuntime.jsx(
4031
+ "div",
4032
+ {
4033
+ "data-slot": "actions",
4034
+ "data-state": "loading",
4035
+ className: cn(theme.slots.stateActions, classNames?.actions),
4036
+ children: actions
4037
+ }
4038
+ ) : null
4039
+ ]
4040
+ }
4041
+ );
4042
+ });
4043
+ function BaseNotice({
4044
+ tone,
4045
+ icon,
4046
+ title,
4047
+ description,
4048
+ actions,
4049
+ children,
4050
+ appearance,
4051
+ className,
4052
+ classNames,
4053
+ style,
4054
+ ...noticeProps
4055
+ }) {
4056
+ const theme = resolveBaseAppearance(appearance);
4057
+ const resolvedDescription = children ?? description;
4058
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4059
+ "div",
4060
+ {
4061
+ ...noticeProps,
4062
+ className: cn(
4063
+ theme.slots.noticeBase,
4064
+ getBaseNoticeToneClassName(theme, tone),
4065
+ classNames?.root,
4066
+ className
4067
+ ),
4068
+ style: {
4069
+ ...theme.styles.notice,
4070
+ ...style
4071
+ },
4072
+ children: [
4073
+ hasRenderableNode(icon) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(theme.slots.noticeIcon, classNames?.icon), children: icon }) : null,
4074
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
4075
+ hasRenderableNode(title) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(theme.slots.noticeTitle, classNames?.title), children: title }) : null,
4076
+ hasRenderableNode(resolvedDescription) ? /* @__PURE__ */ jsxRuntime.jsx(
4077
+ "div",
4078
+ {
4079
+ className: cn(
4080
+ theme.slots.noticeDescription,
4081
+ classNames?.description
4082
+ ),
4083
+ children: resolvedDescription
4084
+ }
4085
+ ) : null,
4086
+ hasRenderableNode(actions) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(theme.slots.noticeActions, classNames?.actions), children: actions }) : null
4087
+ ] })
4088
+ ]
4089
+ }
4090
+ );
4091
+ }
3817
4092
  var typeaheadResetDelay = 500;
3818
4093
  function validateSelectProps({
3819
4094
  options,
@@ -7967,6 +8242,7 @@ var DEFAULT_MASONRY_GAP_PX = 12;
7967
8242
  var DEFAULT_MASONRY_ROW_HEIGHT_PX = 8;
7968
8243
  var DEFAULT_MASONRY_COLUMN_WIDTH_PX = 280;
7969
8244
  var DEFAULT_MASONRY_ITEM_HEIGHT_PX = 240;
8245
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
7970
8246
  function clamp(value, min, max) {
7971
8247
  return Math.min(Math.max(value, min), max);
7972
8248
  }
@@ -8071,9 +8347,12 @@ function WorkbenchMasonryLayout({
8071
8347
  getItemKey = (item, index) => item?.id ?? item?.key ?? index,
8072
8348
  getItemSize,
8073
8349
  columnWidth = DEFAULT_MASONRY_COLUMN_WIDTH_PX,
8350
+ columnCount: requestedColumnCount,
8074
8351
  gap = DEFAULT_MASONRY_GAP_PX,
8075
8352
  rowHeight = DEFAULT_MASONRY_ROW_HEIGHT_PX,
8076
8353
  placementStrategy = "shelf",
8354
+ containerProps,
8355
+ getItemProps,
8077
8356
  className
8078
8357
  }) {
8079
8358
  const containerRef = react.useRef(null);
@@ -8104,8 +8383,9 @@ function WorkbenchMasonryLayout({
8104
8383
  }, []);
8105
8384
  react.useEffect(() => {
8106
8385
  setMeasuredHeights((prev) => {
8386
+ const currentItemKeys = new Set(itemKeys);
8107
8387
  const next = Object.fromEntries(
8108
- Object.entries(prev).filter(([key]) => itemKeys.includes(key))
8388
+ Object.entries(prev).filter(([key]) => currentItemKeys.has(key))
8109
8389
  );
8110
8390
  return areMeasurementMapsEqual(prev, next) ? prev : next;
8111
8391
  });
@@ -8116,7 +8396,8 @@ function WorkbenchMasonryLayout({
8116
8396
  containerRef.current
8117
8397
  );
8118
8398
  const isDensePlacement = placementStrategy === "dense";
8119
- const columnCount = getMasonryColumnCount({
8399
+ const normalizedRequestedColumnCount = Number.isFinite(requestedColumnCount) && Number(requestedColumnCount) > 0 ? clamp(Math.round(Number(requestedColumnCount)), 1, 24) : void 0;
8400
+ const columnCount = normalizedRequestedColumnCount || getMasonryColumnCount({
8120
8401
  containerWidth,
8121
8402
  itemCount: items.length,
8122
8403
  targetColumnWidth: resolvedTargetColumnWidth,
@@ -8162,7 +8443,7 @@ function WorkbenchMasonryLayout({
8162
8443
  resolvedColumnWidth,
8163
8444
  rowHeight
8164
8445
  ]);
8165
- react.useLayoutEffect(() => {
8446
+ useIsomorphicLayoutEffect(() => {
8166
8447
  const nextMeasurements = {};
8167
8448
  itemNodesRef.current.forEach((node, key) => {
8168
8449
  const nextHeight = Math.ceil(node.getBoundingClientRect().height);
@@ -8224,10 +8505,21 @@ function WorkbenchMasonryLayout({
8224
8505
  const positionedItems = layoutItems.map(({ key, item, columnSpan, rowSpan, size }) => {
8225
8506
  const resolvedSpan = clamp(columnSpan, 1, columnCount);
8226
8507
  let startColumn = nextColumnIndex;
8227
- if (startColumn + resolvedSpan > columnCount) {
8228
- startColumn = 0;
8508
+ if (placementStrategy === "shortest") {
8509
+ let shortestBottom = Number.POSITIVE_INFINITY;
8510
+ for (let candidate = 0; candidate <= columnCount - resolvedSpan; candidate += 1) {
8511
+ const candidateBottom = Math.max(...columnBottoms.slice(candidate, candidate + resolvedSpan));
8512
+ if (candidateBottom < shortestBottom) {
8513
+ shortestBottom = candidateBottom;
8514
+ startColumn = candidate;
8515
+ }
8516
+ }
8517
+ } else {
8518
+ if (startColumn + resolvedSpan > columnCount) {
8519
+ startColumn = 0;
8520
+ }
8521
+ startColumn = Math.min(startColumn, columnCount - resolvedSpan);
8229
8522
  }
8230
- startColumn = Math.min(startColumn, columnCount - resolvedSpan);
8231
8523
  const itemWidth = resolvedColumnWidth * resolvedSpan + gap * Math.max(resolvedSpan - 1, 0);
8232
8524
  const itemHeight = getResolvedItemHeight({
8233
8525
  size,
@@ -8240,9 +8532,11 @@ function WorkbenchMasonryLayout({
8240
8532
  columnBottoms[columnIndex] = bottom + gap;
8241
8533
  }
8242
8534
  maxBottom = Math.max(maxBottom, bottom);
8243
- nextColumnIndex = startColumn + resolvedSpan;
8244
- if (nextColumnIndex >= columnCount) {
8245
- nextColumnIndex = 0;
8535
+ if (placementStrategy !== "shortest") {
8536
+ nextColumnIndex = startColumn + resolvedSpan;
8537
+ if (nextColumnIndex >= columnCount) {
8538
+ nextColumnIndex = 0;
8539
+ }
8246
8540
  }
8247
8541
  return {
8248
8542
  key,
@@ -8265,33 +8559,38 @@ function WorkbenchMasonryLayout({
8265
8559
  "div",
8266
8560
  {
8267
8561
  ref: containerRef,
8268
- className: cn("relative w-full", className),
8269
- style: { height: `${orderedLayout.containerHeight}px` },
8562
+ ...containerProps,
8563
+ className: cn("relative w-full", className, containerProps?.className),
8564
+ style: { ...containerProps?.style, height: `${orderedLayout.containerHeight}px` },
8270
8565
  children: orderedLayout.positionedItems.map(({ key, item, columnSpan, rowSpan, size, itemWidth, left, top }) => {
8271
8566
  const unresolvedSingleColumnWidth = containerWidth <= 0 && columnCount === 1;
8272
8567
  const itemWidthValue = unresolvedSingleColumnWidth ? "100%" : `${itemWidth}px`;
8568
+ const layoutContext = {
8569
+ columnCount,
8570
+ columnSpan,
8571
+ columnWidth: resolvedColumnWidth,
8572
+ containerWidth,
8573
+ gap,
8574
+ itemWidth,
8575
+ placementStrategy,
8576
+ rowSpan,
8577
+ rowHeight,
8578
+ size
8579
+ };
8580
+ const itemProps = getItemProps?.(item, layoutContext);
8273
8581
  return /* @__PURE__ */ jsxRuntime.jsx(
8274
8582
  "div",
8275
8583
  {
8276
- className: "absolute min-w-0",
8584
+ ...itemProps,
8585
+ className: cn("absolute min-w-0", itemProps?.className),
8277
8586
  style: {
8587
+ ...itemProps?.style,
8278
8588
  left: unresolvedSingleColumnWidth ? 0 : `${left}px`,
8279
8589
  top: `${top}px`,
8280
8590
  width: itemWidthValue,
8281
8591
  maxWidth: itemWidthValue
8282
8592
  },
8283
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: (node) => registerItemNode(key, node), className: "min-w-0", children: renderItem(item, {
8284
- columnCount,
8285
- columnSpan,
8286
- columnWidth: resolvedColumnWidth,
8287
- containerWidth,
8288
- gap,
8289
- itemWidth,
8290
- placementStrategy,
8291
- rowSpan,
8292
- rowHeight,
8293
- size
8294
- }) })
8593
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: (node) => registerItemNode(key, node), role: "presentation", className: "min-w-0", children: renderItem(item, layoutContext) })
8295
8594
  },
8296
8595
  key
8297
8596
  );
@@ -8303,35 +8602,717 @@ function WorkbenchMasonryLayout({
8303
8602
  "div",
8304
8603
  {
8305
8604
  ref: containerRef,
8306
- className: cn("grid w-full items-start", className),
8605
+ ...containerProps,
8606
+ className: cn("grid w-full items-start", className, containerProps?.className),
8307
8607
  style: {
8608
+ ...containerProps?.style,
8308
8609
  gap: `${gap}px`,
8309
8610
  gridAutoFlow: "row dense",
8310
8611
  gridAutoRows: `${rowHeight}px`,
8311
8612
  gridTemplateColumns: `repeat(${columnCount}, minmax(0, 1fr))`
8312
8613
  },
8313
- children: layoutItems.map(({ key, item, columnSpan, rowSpan, size }) => /* @__PURE__ */ jsxRuntime.jsx(
8314
- "div",
8614
+ children: layoutItems.map(({ key, item, columnSpan, rowSpan, size }) => {
8615
+ const layoutContext = {
8616
+ columnCount,
8617
+ columnSpan,
8618
+ columnWidth: resolvedColumnWidth,
8619
+ containerWidth,
8620
+ gap,
8621
+ placementStrategy,
8622
+ rowSpan,
8623
+ rowHeight,
8624
+ size
8625
+ };
8626
+ const itemProps = getItemProps?.(item, layoutContext);
8627
+ return /* @__PURE__ */ jsxRuntime.jsx(
8628
+ "div",
8629
+ {
8630
+ ...itemProps,
8631
+ className: cn("min-w-0", itemProps?.className),
8632
+ style: {
8633
+ ...itemProps?.style,
8634
+ gridColumn: `span ${columnSpan}`,
8635
+ gridRow: `span ${rowSpan}`
8636
+ },
8637
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: (node) => registerItemNode(key, node), role: "presentation", className: "min-w-0", children: renderItem(item, layoutContext) })
8638
+ },
8639
+ key
8640
+ );
8641
+ })
8642
+ }
8643
+ );
8644
+ }
8645
+ var DEFAULT_GAP_PX = 12;
8646
+ var DEFAULT_ROW_HEIGHT_PX = 8;
8647
+ var DEFAULT_CARD_WIDTH_PX = 280;
8648
+ var BREAKPOINT_TABLET_PX = 640;
8649
+ var BREAKPOINT_DESKTOP_PX = 1024;
8650
+ var MAX_COLUMN_COUNT = 24;
8651
+ function clamp2(value, min, max) {
8652
+ return Math.min(Math.max(value, min), max);
8653
+ }
8654
+ function resolveRenderableSlot(slot, context) {
8655
+ return typeof slot === "function" ? slot(context) : slot;
8656
+ }
8657
+ function resolveLengthToPixels2(value, fallback, node) {
8658
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
8659
+ if (typeof value !== "string") return fallback;
8660
+ const normalized = value.trim().toLowerCase();
8661
+ if (!normalized) return fallback;
8662
+ if (/^\d+(\.\d+)?(px)?$/.test(normalized)) {
8663
+ const parsed = Number.parseFloat(normalized);
8664
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
8665
+ }
8666
+ if (normalized.endsWith("rem")) {
8667
+ const parsed = Number.parseFloat(normalized);
8668
+ const rootFontSize = typeof window === "undefined" ? 16 : Number.parseFloat(window.getComputedStyle(node || document.documentElement).fontSize) || 16;
8669
+ return Number.isFinite(parsed) && parsed > 0 ? parsed * rootFontSize : fallback;
8670
+ }
8671
+ return fallback;
8672
+ }
8673
+ function normalizeColumns(value) {
8674
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return void 0;
8675
+ return clamp2(Math.round(value), 1, MAX_COLUMN_COUNT);
8676
+ }
8677
+ function resolveResponsiveColumns(columns, containerWidth) {
8678
+ if (columns === "auto" || columns === void 0) return void 0;
8679
+ if (typeof columns === "number") return normalizeColumns(columns);
8680
+ const mobile = normalizeColumns(columns.mobile);
8681
+ const tablet = normalizeColumns(columns.tablet);
8682
+ const desktop = normalizeColumns(columns.desktop);
8683
+ if (containerWidth >= BREAKPOINT_DESKTOP_PX) return desktop ?? tablet ?? mobile;
8684
+ if (containerWidth >= BREAKPOINT_TABLET_PX) return tablet ?? mobile ?? desktop;
8685
+ return mobile ?? tablet ?? desktop;
8686
+ }
8687
+ function resolveColumnCount({
8688
+ containerWidth,
8689
+ itemCount,
8690
+ gap,
8691
+ columns,
8692
+ minCardWidth,
8693
+ idealCardWidth,
8694
+ maxCardWidth,
8695
+ node
8696
+ }) {
8697
+ const fixedColumns = resolveResponsiveColumns(columns, containerWidth);
8698
+ if (fixedColumns) return fixedColumns;
8699
+ if (!containerWidth) return 1;
8700
+ const idealWidth = resolveLengthToPixels2(idealCardWidth, DEFAULT_CARD_WIDTH_PX, node);
8701
+ const minWidth = resolveLengthToPixels2(minCardWidth, idealWidth * 0.78, node);
8702
+ const maxWidth = Math.max(
8703
+ minWidth,
8704
+ resolveLengthToPixels2(maxCardWidth, idealWidth * 1.35, node)
8705
+ );
8706
+ const safeItemCount = Math.max(itemCount, 1);
8707
+ const maxCandidate = Math.max(
8708
+ 1,
8709
+ Math.min(
8710
+ safeItemCount,
8711
+ Math.ceil((containerWidth + gap) / (Math.max(minWidth * 0.75, 1) + gap))
8712
+ )
8713
+ );
8714
+ let bestCount = 1;
8715
+ let bestScore = Number.POSITIVE_INFINITY;
8716
+ for (let candidate = 1; candidate <= maxCandidate; candidate += 1) {
8717
+ const width = (containerWidth - gap * Math.max(candidate - 1, 0)) / candidate;
8718
+ if (!Number.isFinite(width) || width <= 0) continue;
8719
+ const score = Math.abs(width - idealWidth) + Math.max(0, minWidth - width) * 3 + Math.max(0, width - maxWidth) * 2;
8720
+ if (score < bestScore) {
8721
+ bestScore = score;
8722
+ bestCount = candidate;
8723
+ }
8724
+ }
8725
+ return bestCount;
8726
+ }
8727
+ function normalizeGap(value) {
8728
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : DEFAULT_GAP_PX;
8729
+ }
8730
+ function normalizeRowHeight(value) {
8731
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : DEFAULT_ROW_HEIGHT_PX;
8732
+ }
8733
+ function normalizeThreshold(value) {
8734
+ return typeof value === "number" && Number.isFinite(value) ? clamp2(value, 0, 1) : 0;
8735
+ }
8736
+ function normalizeItemSize(size, aspectRatio, columnWidth) {
8737
+ const normalizedAspectRatio = Number.isFinite(aspectRatio) && Number(aspectRatio) > 0 ? Number(aspectRatio) : void 0;
8738
+ const normalizedHeight = size?.height ?? (normalizedAspectRatio ? Math.max(1, columnWidth / normalizedAspectRatio) : void 0);
8739
+ return {
8740
+ ...size,
8741
+ height: normalizedHeight
8742
+ };
8743
+ }
8744
+ function getDefaultItemKey(item, index) {
8745
+ if (item && typeof item === "object") {
8746
+ const candidate = item;
8747
+ if (typeof candidate.id === "string" || typeof candidate.id === "number") return candidate.id;
8748
+ if (typeof candidate.key === "string" || typeof candidate.key === "number") return candidate.key;
8749
+ }
8750
+ return index;
8751
+ }
8752
+ function getScrollStorageKey(key) {
8753
+ return `@inf-monkeys-tech/monkeys-design:workbench-collection:${key}`;
8754
+ }
8755
+ function getParentScrollOwner(node) {
8756
+ let current = node?.parentElement ?? null;
8757
+ while (current) {
8758
+ if (typeof window === "undefined") return current;
8759
+ const overflowY = window.getComputedStyle(current).overflowY;
8760
+ if (overflowY === "auto" || overflowY === "scroll") return current;
8761
+ current = current.parentElement;
8762
+ }
8763
+ return null;
8764
+ }
8765
+ function setsEqual(left, right) {
8766
+ if (left.size !== right.size) return false;
8767
+ for (const value of left) {
8768
+ if (!right.has(value)) return false;
8769
+ }
8770
+ return true;
8771
+ }
8772
+ function isInteractiveEventTarget(target, currentTarget) {
8773
+ if (typeof Element === "undefined" || !(target instanceof Element) || target === currentTarget) return false;
8774
+ const control = target.closest(
8775
+ 'a, button, input, select, textarea, summary, [contenteditable="true"], [data-collection-action]'
8776
+ );
8777
+ return Boolean(control && currentTarget.contains(control));
8778
+ }
8779
+ function warnOnDuplicateKeys(keys) {
8780
+ if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return;
8781
+ const seen = /* @__PURE__ */ new Set();
8782
+ const duplicates = /* @__PURE__ */ new Set();
8783
+ keys.forEach((key) => {
8784
+ const stringKey = String(key);
8785
+ if (seen.has(stringKey)) duplicates.add(stringKey);
8786
+ seen.add(stringKey);
8787
+ });
8788
+ if (duplicates.size) {
8789
+ console.warn(`WorkbenchCollection received duplicate item keys: ${Array.from(duplicates).join(", ")}`);
8790
+ }
8791
+ }
8792
+ function WorkbenchCollection({
8793
+ items,
8794
+ config,
8795
+ renderItem,
8796
+ slots,
8797
+ callbacks,
8798
+ className,
8799
+ classNames,
8800
+ style,
8801
+ ariaLabel
8802
+ }) {
8803
+ const componentAttributes = useMonkeysComponentAttributes();
8804
+ const localeMessages = useMonkeysLocaleMessages().workbench.collection;
8805
+ const componentId = react.useId().replace(/:/g, "");
8806
+ const rootRef = react.useRef(null);
8807
+ const itemsRef = react.useRef(null);
8808
+ const loadMoreRef = react.useRef(null);
8809
+ const lastSelectedIndexRef = react.useRef(null);
8810
+ const loadMoreLockedRef = react.useRef(false);
8811
+ const layoutReadyRef = react.useRef(false);
8812
+ const [containerWidth, setContainerWidth] = react.useState(0);
8813
+ const [itemsNode, setItemsNode] = react.useState(null);
8814
+ const [internalSelectedKeys, setInternalSelectedKeys] = react.useState(
8815
+ () => new Set(config.selection?.defaultSelectedKeys ?? [])
8816
+ );
8817
+ const callbacksRef = react.useRef(callbacks);
8818
+ callbacksRef.current = callbacks;
8819
+ const registerItemsNode = react.useCallback((node) => {
8820
+ itemsRef.current = node;
8821
+ setItemsNode(node);
8822
+ }, []);
8823
+ const mode = config.layout.mode;
8824
+ if (mode !== "grid" && mode !== "masonry" && mode !== "dense" && mode !== "rail") {
8825
+ throw new TypeError(`WorkbenchCollection layout.mode "${String(mode)}" is not supported.`);
8826
+ }
8827
+ const gap = normalizeGap(config.layout.gap);
8828
+ const rowHeight = normalizeRowHeight(config.layout.rowHeight);
8829
+ const getItemKey = config.getItemKey ?? getDefaultItemKey;
8830
+ const normalizedItems = react.useMemo(() => items.map((item, index) => {
8831
+ const key = getItemKey(item, index);
8832
+ return { item, index, key, stringKey: String(key) };
8833
+ }), [getItemKey, items]);
8834
+ const keys = react.useMemo(
8835
+ () => normalizedItems.map(({ key }) => key),
8836
+ [normalizedItems]
8837
+ );
8838
+ const keysSignature = normalizedItems.map(({ stringKey }) => stringKey).join("");
8839
+ react.useEffect(() => warnOnDuplicateKeys(keys), [keys, keysSignature]);
8840
+ const selectionMode = config.selection?.mode ?? "none";
8841
+ const controlledSelection = config.selection?.selectedKeys !== void 0;
8842
+ const selectedKeys = react.useMemo(
8843
+ () => selectionMode === "none" ? /* @__PURE__ */ new Set() : controlledSelection ? new Set(config.selection?.selectedKeys ?? []) : internalSelectedKeys,
8844
+ [config.selection?.selectedKeys, controlledSelection, internalSelectedKeys, selectionMode]
8845
+ );
8846
+ react.useEffect(() => {
8847
+ if (controlledSelection) return;
8848
+ const availableKeys = new Set(keys);
8849
+ setInternalSelectedKeys((previous) => {
8850
+ const next = new Set(Array.from(previous).filter((key) => availableKeys.has(key)));
8851
+ return setsEqual(previous, next) ? previous : next;
8852
+ });
8853
+ }, [controlledSelection, keys, keysSignature]);
8854
+ react.useEffect(() => {
8855
+ const node = itemsNode;
8856
+ if (!node) return void 0;
8857
+ const updateWidth = () => {
8858
+ const nextWidth = Math.round(node.getBoundingClientRect().width);
8859
+ setContainerWidth((previous) => previous === nextWidth ? previous : nextWidth);
8860
+ };
8861
+ updateWidth();
8862
+ if (typeof ResizeObserver === "function") {
8863
+ const observer = new ResizeObserver(updateWidth);
8864
+ observer.observe(node);
8865
+ return () => observer.disconnect();
8866
+ }
8867
+ if (typeof window === "undefined") return void 0;
8868
+ window.addEventListener("resize", updateWidth);
8869
+ return () => window.removeEventListener("resize", updateWidth);
8870
+ }, [itemsNode]);
8871
+ const columnCount = resolveColumnCount({
8872
+ containerWidth,
8873
+ itemCount: normalizedItems.length,
8874
+ gap,
8875
+ columns: config.layout.columns,
8876
+ minCardWidth: config.layout.minCardWidth,
8877
+ idealCardWidth: config.layout.idealCardWidth ?? config.layout.columnWidth,
8878
+ maxCardWidth: config.layout.maxCardWidth,
8879
+ node: itemsRef.current
8880
+ });
8881
+ const columnWidth = containerWidth > 0 ? Math.max(0, (containerWidth - gap * Math.max(columnCount - 1, 0)) / columnCount) : resolveLengthToPixels2(
8882
+ config.layout.cardWidth ?? config.layout.columnWidth ?? config.layout.idealCardWidth,
8883
+ DEFAULT_CARD_WIDTH_PX,
8884
+ itemsRef.current
8885
+ );
8886
+ const placementStrategy = config.layout.placementStrategy ?? (mode === "dense" ? "dense" : mode === "masonry" ? "shortest" : "shelf");
8887
+ const labels = react.useMemo(() => ({
8888
+ collection: config.labels?.collection ?? localeMessages.label,
8889
+ loading: config.labels?.loading ?? localeMessages.loading,
8890
+ empty: config.labels?.empty ?? localeMessages.empty,
8891
+ loadError: config.labels?.loadError ?? localeMessages.loadError,
8892
+ refreshError: config.labels?.refreshError ?? localeMessages.refreshError,
8893
+ retry: config.labels?.retry ?? localeMessages.retry,
8894
+ loadingMore: config.labels?.loadingMore ?? localeMessages.loadingMore
8895
+ }), [config.labels, localeMessages]);
8896
+ const virtualizationError = react.useMemo(() => {
8897
+ const virtualization = config.virtualization;
8898
+ if (virtualization?.enabled !== true || virtualization.fallback !== "error") return null;
8899
+ if (!virtualization.adapter) {
8900
+ return new Error("WorkbenchCollection virtualization requires an adapter.");
8901
+ }
8902
+ if (virtualization.adapter.supports?.(mode) === false) {
8903
+ return new Error(`WorkbenchCollection virtualization adapter does not support mode "${mode}".`);
8904
+ }
8905
+ return null;
8906
+ }, [config.virtualization, mode]);
8907
+ const layoutSnapshot = react.useMemo(() => ({
8908
+ mode,
8909
+ columnCount,
8910
+ columnWidth,
8911
+ containerWidth,
8912
+ gap,
8913
+ itemCount: normalizedItems.length
8914
+ }), [columnCount, columnWidth, containerWidth, gap, mode, normalizedItems.length]);
8915
+ react.useEffect(() => {
8916
+ callbacksRef.current?.onLayoutChange?.(layoutSnapshot);
8917
+ if (containerWidth > 0 && !layoutReadyRef.current) {
8918
+ layoutReadyRef.current = true;
8919
+ callbacksRef.current?.onLayoutReady?.(layoutSnapshot);
8920
+ }
8921
+ }, [layoutSnapshot, containerWidth]);
8922
+ react.useEffect(() => {
8923
+ layoutReadyRef.current = false;
8924
+ }, [mode]);
8925
+ const resolveScrollOwner = react.useCallback(() => {
8926
+ const requestedOwner = config.layout.scrollOwner;
8927
+ if (requestedOwner && requestedOwner !== "self" && requestedOwner !== "parent") return requestedOwner;
8928
+ if (requestedOwner === "parent") return getParentScrollOwner(rootRef.current);
8929
+ return rootRef.current;
8930
+ }, [config.layout.scrollOwner]);
8931
+ react.useEffect(() => {
8932
+ const restorationKey = config.scrollRestorationKey?.trim();
8933
+ if (!restorationKey || typeof window === "undefined") return void 0;
8934
+ const owner = resolveScrollOwner();
8935
+ if (!owner) return void 0;
8936
+ const storageKey = getScrollStorageKey(restorationKey);
8937
+ try {
8938
+ const stored = Number.parseFloat(window.sessionStorage.getItem(storageKey) ?? "");
8939
+ if (Number.isFinite(stored) && stored >= 0) owner.scrollTop = stored;
8940
+ } catch {
8941
+ }
8942
+ const saveScrollPosition = () => {
8943
+ try {
8944
+ window.sessionStorage.setItem(storageKey, String(owner.scrollTop));
8945
+ } catch {
8946
+ }
8947
+ };
8948
+ owner.addEventListener("scroll", saveScrollPosition, { passive: true });
8949
+ return () => {
8950
+ saveScrollPosition();
8951
+ owner.removeEventListener("scroll", saveScrollPosition);
8952
+ };
8953
+ }, [config.scrollRestorationKey, resolveScrollOwner]);
8954
+ const state = config.state;
8955
+ const loading = state?.loading === true;
8956
+ const loadingMore = state?.loadingMore === true;
8957
+ const hasMore = state?.hasMore === true;
8958
+ const canLoadMore = typeof callbacks?.onLoadMore === "function";
8959
+ const effectiveError = state?.error ?? virtualizationError;
8960
+ react.useEffect(() => {
8961
+ if (!loadingMore || !hasMore) loadMoreLockedRef.current = false;
8962
+ }, [hasMore, loadingMore, normalizedItems.length, keysSignature]);
8963
+ react.useEffect(() => {
8964
+ const sentinel = loadMoreRef.current;
8965
+ if (!sentinel || !canLoadMore || !hasMore || loadingMore) return void 0;
8966
+ if (typeof IntersectionObserver !== "function") return void 0;
8967
+ const owner = resolveScrollOwner();
8968
+ const observer = new IntersectionObserver((entries) => {
8969
+ if (!entries.some((entry) => entry.isIntersecting) || loadMoreLockedRef.current) return;
8970
+ loadMoreLockedRef.current = true;
8971
+ try {
8972
+ const result = callbacksRef.current?.onLoadMore?.();
8973
+ if (result && typeof result.then === "function") {
8974
+ void result.catch(() => {
8975
+ loadMoreLockedRef.current = false;
8976
+ });
8977
+ }
8978
+ } catch (error) {
8979
+ loadMoreLockedRef.current = false;
8980
+ throw error;
8981
+ }
8982
+ }, {
8983
+ root: owner === rootRef.current ? owner : owner || null,
8984
+ rootMargin: config.loading?.rootMargin ?? "240px",
8985
+ threshold: normalizeThreshold(config.loading?.threshold)
8986
+ });
8987
+ observer.observe(sentinel);
8988
+ return () => observer.disconnect();
8989
+ }, [canLoadMore, config.loading?.rootMargin, config.loading?.threshold, hasMore, keysSignature, loadingMore, normalizedItems.length, resolveScrollOwner]);
8990
+ const groups = react.useMemo(() => {
8991
+ if (!config.grouping) {
8992
+ return [{ key: "__all__", stringKey: "__all__", items: normalizedItems }];
8993
+ }
8994
+ const grouped = /* @__PURE__ */ new Map();
8995
+ normalizedItems.forEach((entry) => {
8996
+ const groupKey = config.grouping?.getGroupKey(entry.item, entry.index) ?? "__all__";
8997
+ const stringKey = String(groupKey);
8998
+ const existing = grouped.get(stringKey);
8999
+ if (existing) {
9000
+ existing.items.push(entry);
9001
+ return;
9002
+ }
9003
+ grouped.set(stringKey, {
9004
+ key: groupKey,
9005
+ stringKey,
9006
+ label: config.grouping?.getGroupLabel?.(groupKey, entry.item),
9007
+ items: [entry]
9008
+ });
9009
+ });
9010
+ return Array.from(grouped.values());
9011
+ }, [config.grouping, normalizedItems]);
9012
+ const setSelection = react.useCallback((entry, behavior = config.selection?.behavior ?? (selectionMode === "multiple" ? "toggle" : "replace")) => {
9013
+ if (selectionMode === "none" || config.selection?.isItemDisabled?.(entry.item, entry.index)) return;
9014
+ const current = new Set(selectedKeys);
9015
+ const next = /* @__PURE__ */ new Set();
9016
+ if (selectionMode === "single") {
9017
+ if (!(behavior === "toggle" && current.has(entry.key))) next.add(entry.key);
9018
+ } else if (behavior === "range" && lastSelectedIndexRef.current !== null) {
9019
+ current.forEach((key) => next.add(key));
9020
+ const start = Math.min(lastSelectedIndexRef.current, entry.index);
9021
+ const end = Math.max(lastSelectedIndexRef.current, entry.index);
9022
+ normalizedItems.slice(start, end + 1).forEach((candidate) => {
9023
+ if (!config.selection?.isItemDisabled?.(candidate.item, candidate.index)) next.add(candidate.key);
9024
+ });
9025
+ } else if (behavior === "replace") {
9026
+ next.add(entry.key);
9027
+ } else {
9028
+ current.forEach((key) => next.add(key));
9029
+ if (next.has(entry.key)) next.delete(entry.key);
9030
+ else next.add(entry.key);
9031
+ }
9032
+ lastSelectedIndexRef.current = entry.index;
9033
+ if (!controlledSelection) setInternalSelectedKeys(next);
9034
+ callbacks?.onSelectionChange?.(next, { item: entry.item, key: entry.key, behavior });
9035
+ }, [callbacks, config.selection, controlledSelection, normalizedItems, selectedKeys, selectionMode]);
9036
+ const createItemContext = react.useCallback((entry, layout) => {
9037
+ const disabled = config.selection?.isItemDisabled?.(entry.item, entry.index) ?? false;
9038
+ const context = {
9039
+ item: entry.item,
9040
+ index: entry.index,
9041
+ key: entry.key,
9042
+ status: config.getItemStatus?.(entry.item, entry.index) ?? "idle",
9043
+ disabled,
9044
+ selected: selectedKeys.has(entry.key),
9045
+ selectionMode,
9046
+ layout,
9047
+ select: (behavior) => setSelection(entry, behavior),
9048
+ preview: () => callbacks?.onItemPreview?.(entry.item, context),
9049
+ detail: () => callbacks?.onItemDetail?.(entry.item, context),
9050
+ renderSlot: (name) => {
9051
+ return slots?.item?.[name]?.(entry.item, context);
9052
+ },
9053
+ notifyMediaLoad: () => callbacks?.onMediaLoad?.(entry.item, context),
9054
+ notifyMediaError: (error) => callbacks?.onMediaError?.(entry.item, context, error)
9055
+ };
9056
+ return context;
9057
+ }, [callbacks, config.getItemStatus, config.selection, selectedKeys, selectionMode, setSelection, slots?.item]);
9058
+ const getItemInteractionProps = react.useCallback((entry, context) => {
9059
+ const handleKeyboard = (event) => {
9060
+ if (event.target !== event.currentTarget || event.key !== "Enter" && event.key !== " ") return;
9061
+ event.preventDefault();
9062
+ if (selectionMode !== "none") context.select(event.shiftKey ? "range" : void 0);
9063
+ else callbacks?.onItemClick?.(entry.item, context, event);
9064
+ };
9065
+ return {
9066
+ id: `${componentId}-item-${entry.index}`,
9067
+ "data-monkeys-collection-item": "",
9068
+ "data-item-key": entry.stringKey,
9069
+ "data-selected": context.selected || void 0,
9070
+ "data-disabled": context.disabled || void 0,
9071
+ "data-status": context.status,
9072
+ role: selectionMode === "none" ? "listitem" : "option",
9073
+ "aria-selected": selectionMode === "none" ? void 0 : context.selected,
9074
+ "aria-disabled": context.disabled || void 0,
9075
+ tabIndex: callbacks?.onItemClick || selectionMode !== "none" ? 0 : void 0,
9076
+ draggable: callbacks?.onItemDragStart ? true : void 0,
9077
+ className: cn(
9078
+ "min-w-0 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
9079
+ config.appearance?.hover === "lift" && "transition-transform hover:-translate-y-0.5 motion-reduce:transform-none",
9080
+ config.appearance?.selection === "ring" && context.selected && "ring-2 ring-primary",
9081
+ config.appearance?.selection === "outline" && context.selected && "outline outline-2 outline-primary",
9082
+ classNames?.item
9083
+ ),
9084
+ onClick: (event) => {
9085
+ if (isInteractiveEventTarget(event.target, event.currentTarget)) return;
9086
+ if (selectionMode !== "none") context.select(event.shiftKey ? "range" : void 0);
9087
+ callbacks?.onItemClick?.(entry.item, context, event);
9088
+ },
9089
+ onDoubleClick: (event) => {
9090
+ if (isInteractiveEventTarget(event.target, event.currentTarget)) return;
9091
+ callbacks?.onItemDoubleClick?.(entry.item, context, event);
9092
+ },
9093
+ onKeyDown: handleKeyboard,
9094
+ onDragStart: (event) => callbacks?.onItemDragStart?.(entry.item, context, event),
9095
+ onDragOver: callbacks?.onItemDrop ? (event) => event.preventDefault() : void 0,
9096
+ onDrop: (event) => callbacks?.onItemDrop?.(entry.item, context, event)
9097
+ };
9098
+ }, [callbacks, classNames?.item, componentId, config.appearance, selectionMode]);
9099
+ const renderEntry = react.useCallback((entry, layout, includeShell = true) => {
9100
+ const context = createItemContext(entry, layout);
9101
+ if (!includeShell) return renderItem(entry.item, context);
9102
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ...getItemInteractionProps(entry, context), children: renderItem(entry.item, context) });
9103
+ }, [createItemContext, getItemInteractionProps, renderItem]);
9104
+ const baseLayoutContext = react.useMemo(() => ({
9105
+ mode,
9106
+ columnCount,
9107
+ columnWidth,
9108
+ containerWidth,
9109
+ gap,
9110
+ placementStrategy,
9111
+ rowHeight
9112
+ }), [columnCount, columnWidth, containerWidth, gap, mode, placementStrategy, rowHeight]);
9113
+ const renderGroupItems = (group) => {
9114
+ const virtualization = config.virtualization;
9115
+ const adapter = virtualization?.adapter;
9116
+ const virtualizationRequested = virtualization?.enabled === true;
9117
+ if (virtualizationRequested && (!adapter || adapter.supports?.(mode) === false)) {
9118
+ if (virtualization?.fallback === "error") {
9119
+ return null;
9120
+ }
9121
+ }
9122
+ if (virtualizationRequested && adapter && adapter.supports?.(mode) !== false) {
9123
+ return adapter.render({
9124
+ items: group.items.map(({ item }) => item),
9125
+ keys: group.items.map(({ key }) => key),
9126
+ layout: config.layout,
9127
+ scrollOwner: resolveScrollOwner(),
9128
+ renderItem: (item, localIndex, layout) => {
9129
+ const entry = group.items[localIndex];
9130
+ return entry ? renderEntry(entry, layout) : null;
9131
+ }
9132
+ });
9133
+ }
9134
+ if (mode === "masonry" || mode === "dense") {
9135
+ return /* @__PURE__ */ jsxRuntime.jsx(
9136
+ WorkbenchMasonryLayout,
8315
9137
  {
8316
- className: "min-w-0",
8317
- style: {
8318
- gridColumn: `span ${columnSpan}`,
8319
- gridRow: `span ${rowSpan}`
9138
+ items: group.items,
9139
+ columnCount,
9140
+ columnWidth: config.layout.cardWidth ?? config.layout.columnWidth ?? config.layout.idealCardWidth,
9141
+ gap,
9142
+ rowHeight,
9143
+ placementStrategy: mode === "dense" ? "dense" : placementStrategy,
9144
+ className: classNames?.layout,
9145
+ containerProps: {
9146
+ role: selectionMode === "none" ? "list" : "listbox",
9147
+ "aria-label": ariaLabel ?? labels.collection,
9148
+ "aria-multiselectable": selectionMode === "multiple" || void 0
8320
9149
  },
8321
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: (node) => registerItemNode(key, node), className: "h-full", children: renderItem(item, {
8322
- columnCount,
8323
- columnSpan,
8324
- columnWidth: resolvedColumnWidth,
8325
- containerWidth,
8326
- gap,
8327
- placementStrategy,
8328
- rowSpan,
8329
- rowHeight,
8330
- size
8331
- }) })
9150
+ getItemProps: (entry, masonryLayout) => {
9151
+ const context = createItemContext(entry, { ...masonryLayout, mode });
9152
+ return getItemInteractionProps(entry, context);
9153
+ },
9154
+ getItemKey: (entry) => entry.key,
9155
+ getItemSize: (entry, masonryLayout) => {
9156
+ const layout = {
9157
+ ...masonryLayout,
9158
+ mode
9159
+ };
9160
+ const customSize = config.layout.getItemSize?.(entry.item, layout);
9161
+ const aspectRatio = config.layout.getItemAspectRatio?.(entry.item, entry.index);
9162
+ return normalizeItemSize(customSize, aspectRatio, masonryLayout.columnWidth);
9163
+ },
9164
+ renderItem: (entry, masonryLayout) => renderEntry(entry, {
9165
+ ...masonryLayout,
9166
+ mode
9167
+ }, false)
9168
+ }
9169
+ );
9170
+ }
9171
+ if (mode === "rail") {
9172
+ const cardWidth = config.layout.cardWidth ?? config.layout.idealCardWidth ?? DEFAULT_CARD_WIDTH_PX;
9173
+ return /* @__PURE__ */ jsxRuntime.jsx(
9174
+ "div",
9175
+ {
9176
+ role: selectionMode === "none" ? "list" : "listbox",
9177
+ "aria-label": ariaLabel ?? labels.collection,
9178
+ "aria-multiselectable": selectionMode === "multiple" || void 0,
9179
+ className: cn("grid min-w-0 grid-flow-col items-start overflow-x-auto", classNames?.layout),
9180
+ style: { gap, gridAutoColumns: typeof cardWidth === "number" ? `${cardWidth}px` : cardWidth },
9181
+ children: group.items.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderEntry(entry, baseLayoutContext) }, entry.stringKey))
9182
+ }
9183
+ );
9184
+ }
9185
+ return /* @__PURE__ */ jsxRuntime.jsx(
9186
+ "div",
9187
+ {
9188
+ role: selectionMode === "none" ? "list" : "listbox",
9189
+ "aria-label": ariaLabel ?? labels.collection,
9190
+ "aria-multiselectable": selectionMode === "multiple" || void 0,
9191
+ className: cn("grid min-w-0 items-start", classNames?.layout),
9192
+ style: {
9193
+ gap,
9194
+ gridTemplateColumns: `repeat(${columnCount}, minmax(0, 1fr))`
8332
9195
  },
8333
- key
8334
- ))
9196
+ children: group.items.map((entry) => {
9197
+ const customSize = config.layout.getItemSize?.(entry.item, baseLayoutContext);
9198
+ const span = clamp2(Math.round(Number(customSize?.columnSpan) || 1), 1, columnCount);
9199
+ const aspectRatio = config.layout.getItemAspectRatio?.(entry.item, entry.index);
9200
+ return /* @__PURE__ */ jsxRuntime.jsx(
9201
+ "div",
9202
+ {
9203
+ ...getItemInteractionProps(
9204
+ entry,
9205
+ createItemContext(entry, { ...baseLayoutContext, columnSpan: span, size: customSize })
9206
+ ),
9207
+ style: {
9208
+ gridColumn: span > 1 ? `span ${span}` : void 0,
9209
+ aspectRatio: Number.isFinite(aspectRatio) && Number(aspectRatio) > 0 ? String(aspectRatio) : void 0
9210
+ },
9211
+ children: renderEntry(entry, { ...baseLayoutContext, columnSpan: span, size: customSize }, false)
9212
+ },
9213
+ entry.stringKey
9214
+ );
9215
+ })
9216
+ }
9217
+ );
9218
+ };
9219
+ const slotContext = {
9220
+ items,
9221
+ error: effectiveError,
9222
+ loading,
9223
+ loadingMore,
9224
+ selectedKeys,
9225
+ retry: () => callbacks?.onRetry?.()
9226
+ };
9227
+ const header = resolveRenderableSlot(slots?.header, slotContext);
9228
+ const toolbar = resolveRenderableSlot(slots?.toolbar, slotContext);
9229
+ const selectionToolbar = selectedKeys.size > 0 ? resolveRenderableSlot(slots?.selectionToolbar, slotContext) : null;
9230
+ const beforeItems = resolveRenderableSlot(slots?.beforeItems, slotContext);
9231
+ const footer = resolveRenderableSlot(slots?.footer, slotContext);
9232
+ const showInitialLoading = loading && items.length === 0;
9233
+ const showInitialError = Boolean(effectiveError) && items.length === 0 && !showInitialLoading;
9234
+ const showEmpty = !showInitialLoading && !showInitialError && (state?.empty === true || items.length === 0);
9235
+ const loadingSlot = resolveRenderableSlot(slots?.loading, slotContext);
9236
+ const errorSlot = resolveRenderableSlot(slots?.error, slotContext);
9237
+ const emptySlot = resolveRenderableSlot(slots?.empty, slotContext);
9238
+ const loadMoreSlot = resolveRenderableSlot(slots?.loadMore, slotContext);
9239
+ const selfScrollable = config.layout.scrollOwner === "self" || config.layout.scrollOwner === void 0 && (config.layout.fillHeight || config.layout.maxHeight);
9240
+ const rootStyle = {
9241
+ ...style,
9242
+ padding: config.layout.padding ?? style?.padding,
9243
+ height: config.layout.fillHeight ? "100%" : style?.height,
9244
+ maxHeight: config.layout.maxHeight ?? style?.maxHeight,
9245
+ overflow: selfScrollable ? "auto" : style?.overflow
9246
+ };
9247
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9248
+ "section",
9249
+ {
9250
+ ref: rootRef,
9251
+ ...componentAttributes,
9252
+ "data-monkeys-component": "workbench-collection",
9253
+ "data-layout-mode": mode,
9254
+ "data-density": config.appearance?.density,
9255
+ "data-radius": config.appearance?.radius,
9256
+ "data-media-fit": config.appearance?.mediaFit,
9257
+ "aria-label": ariaLabel ?? labels.collection,
9258
+ "aria-busy": loading || loadingMore || void 0,
9259
+ className: cn("min-h-0 min-w-0", config.layout.fillHeight && "h-full", classNames?.root, className),
9260
+ style: rootStyle,
9261
+ children: [
9262
+ hasRenderableNode(header) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames?.header, children: header }) : null,
9263
+ hasRenderableNode(toolbar) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames?.toolbar, children: toolbar }) : null,
9264
+ hasRenderableNode(selectionToolbar) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames?.selectionToolbar, children: selectionToolbar }) : null,
9265
+ hasRenderableNode(beforeItems) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames?.beforeItems, children: beforeItems }) : null,
9266
+ showInitialLoading ? hasRenderableNode(loadingSlot) ? loadingSlot : /* @__PURE__ */ jsxRuntime.jsx(BaseLoadingState, { title: labels.loading }) : showInitialError ? hasRenderableNode(errorSlot) ? errorSlot : /* @__PURE__ */ jsxRuntime.jsx(
9267
+ BaseNotice,
9268
+ {
9269
+ tone: "danger",
9270
+ role: "alert",
9271
+ "aria-live": "assertive",
9272
+ title: labels.loadError,
9273
+ actions: callbacks?.onRetry ? /* @__PURE__ */ jsxRuntime.jsx(BaseButton, { size: "sm", label: labels.retry, onClick: callbacks.onRetry }) : void 0
9274
+ }
9275
+ ) : showEmpty ? hasRenderableNode(emptySlot) ? emptySlot : /* @__PURE__ */ jsxRuntime.jsx(BaseEmptyState, { title: labels.empty }) : /* @__PURE__ */ jsxRuntime.jsx(
9276
+ "div",
9277
+ {
9278
+ ref: registerItemsNode,
9279
+ className: cn("min-w-0", classNames?.items),
9280
+ children: groups.map((group, groupIndex) => {
9281
+ const groupContext = {
9282
+ key: group.key,
9283
+ label: group.label,
9284
+ items: group.items.map(({ item }) => item),
9285
+ groupIndex
9286
+ };
9287
+ const heading = config.grouping ? slots?.groupHeading?.(groupContext, slotContext) ?? group.label : null;
9288
+ const headingId = `${componentId}-group-${groupIndex}`;
9289
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9290
+ "div",
9291
+ {
9292
+ "data-collection-group": group.stringKey,
9293
+ className: classNames?.group,
9294
+ children: [
9295
+ hasRenderableNode(heading) ? /* @__PURE__ */ jsxRuntime.jsx("div", { id: headingId, role: "heading", "aria-level": 2, className: classNames?.groupHeading, children: heading }) : null,
9296
+ renderGroupItems(group)
9297
+ ]
9298
+ },
9299
+ group.stringKey
9300
+ );
9301
+ })
9302
+ }
9303
+ ),
9304
+ items.length > 0 && effectiveError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames?.status, children: hasRenderableNode(errorSlot) ? errorSlot : /* @__PURE__ */ jsxRuntime.jsx(
9305
+ BaseNotice,
9306
+ {
9307
+ tone: "danger",
9308
+ role: "alert",
9309
+ title: labels.refreshError,
9310
+ actions: callbacks?.onRetry ? /* @__PURE__ */ jsxRuntime.jsx(BaseButton, { size: "sm", label: labels.retry, onClick: callbacks.onRetry }) : void 0
9311
+ }
9312
+ ) }) : null,
9313
+ items.length > 0 && (hasMore || loadingMore) ? /* @__PURE__ */ jsxRuntime.jsx("div", { ref: loadMoreRef, className: classNames?.loadMore, "data-collection-load-more": "", children: hasRenderableNode(loadMoreSlot) ? loadMoreSlot : loadingMore ? /* @__PURE__ */ jsxRuntime.jsx(BaseLoadingState, { title: labels.loadingMore }) : null }) : null,
9314
+ hasRenderableNode(footer) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames?.footer, children: footer }) : null
9315
+ ]
8335
9316
  }
8336
9317
  );
8337
9318
  }
@@ -8772,45 +9753,54 @@ function WorkbenchGalleryView({
8772
9753
  );
8773
9754
  if (isMasonry) {
8774
9755
  return /* @__PURE__ */ jsxRuntime.jsx(
8775
- WorkbenchMasonryLayout,
9756
+ WorkbenchCollection,
8776
9757
  {
8777
9758
  items: rows,
8778
- className,
8779
- gap: GALLERY_GRID_GAP_PX,
8780
- columnWidth: resolvedMasonryColumnWidth,
8781
- placementStrategy: masonryPlacementStrategy,
8782
- getItemKey: (row, rowIndex) => row?.id || row?.linkKey || rowIndex,
8783
- getItemSize: (row, layout) => {
8784
- const defaultSize = getDefaultGalleryMasonryItemSize({
8785
- row,
8786
- columns,
8787
- galleryFieldKey,
8788
- galleryDensity,
8789
- galleryBentoColumns,
8790
- galleryDetailVisibility,
8791
- galleryDetailLimit,
8792
- getFieldValue: resolvedGetFieldValue,
8793
- isMediaColumn: resolvedIsMediaColumn,
8794
- isTitleCandidate: resolvedIsTitleCandidate,
8795
- isSummaryCandidate: resolvedIsSummaryCandidate,
8796
- getDisplayText: resolvedGetDisplayText,
8797
- isImageField: resolvedIsImageField,
8798
- isAudioField: resolvedIsAudioField,
8799
- cardVariant,
8800
- renderPreview,
8801
- renderLeadingVisual,
8802
- hiddenDetailFieldKeys,
8803
- layout,
8804
- resolveRecordVisual
8805
- });
8806
- if (typeof getMasonryItemSize !== "function") return defaultSize;
8807
- const customSize = getMasonryItemSize(row, { ...layout, defaultSize }) || {};
8808
- return {
8809
- ...defaultSize,
8810
- ...customSize
8811
- };
9759
+ classNames: { layout: className },
9760
+ config: {
9761
+ getItemKey: (row, rowIndex) => row?.id || row?.linkKey || rowIndex,
9762
+ layout: {
9763
+ mode: masonryPlacementStrategy === "dense" ? "dense" : "masonry",
9764
+ gap: GALLERY_GRID_GAP_PX,
9765
+ idealCardWidth: resolvedMasonryColumnWidth,
9766
+ placementStrategy: masonryPlacementStrategy,
9767
+ getItemSize: (row, layout) => {
9768
+ const defaultSize = getDefaultGalleryMasonryItemSize({
9769
+ row,
9770
+ columns,
9771
+ galleryFieldKey,
9772
+ galleryDensity,
9773
+ galleryBentoColumns,
9774
+ galleryDetailVisibility,
9775
+ galleryDetailLimit,
9776
+ getFieldValue: resolvedGetFieldValue,
9777
+ isMediaColumn: resolvedIsMediaColumn,
9778
+ isTitleCandidate: resolvedIsTitleCandidate,
9779
+ isSummaryCandidate: resolvedIsSummaryCandidate,
9780
+ getDisplayText: resolvedGetDisplayText,
9781
+ isImageField: resolvedIsImageField,
9782
+ isAudioField: resolvedIsAudioField,
9783
+ cardVariant,
9784
+ renderPreview,
9785
+ renderLeadingVisual,
9786
+ hiddenDetailFieldKeys,
9787
+ layout,
9788
+ resolveRecordVisual
9789
+ });
9790
+ if (typeof getMasonryItemSize !== "function") return defaultSize;
9791
+ const customSize = getMasonryItemSize(row, {
9792
+ ...layout,
9793
+ placementStrategy: layout.placementStrategy === "dense" ? "dense" : "shelf",
9794
+ defaultSize
9795
+ }) || {};
9796
+ return {
9797
+ ...defaultSize,
9798
+ ...customSize
9799
+ };
9800
+ }
9801
+ }
8812
9802
  },
8813
- renderItem: (row) => renderGalleryCard(row, rows.indexOf(row))
9803
+ renderItem: (row, context) => renderGalleryCard(row, context.index)
8814
9804
  }
8815
9805
  );
8816
9806
  }
@@ -9328,7 +10318,7 @@ var getDomain = (values, min, max) => {
9328
10318
  return lower < upper ? [lower, upper] : [upper, lower];
9329
10319
  };
9330
10320
  var scale = (value, domain, start, end) => start + (value - domain[0]) / (domain[1] - domain[0]) * (end - start);
9331
- var clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
10321
+ var clamp3 = (value, min, max) => Math.min(max, Math.max(min, value));
9332
10322
  var activatePoint = (event, point, onPointSelect) => {
9333
10323
  if (!onPointSelect || event.key !== "Enter" && event.key !== " ") return;
9334
10324
  event.preventDefault();
@@ -9410,9 +10400,9 @@ function WorkbenchScatterPlot({
9410
10400
  xThreshold === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(
9411
10401
  "line",
9412
10402
  {
9413
- x1: clamp2(xThreshold, plotLeft, plotRight),
10403
+ x1: clamp3(xThreshold, plotLeft, plotRight),
9414
10404
  y1: plotTop,
9415
- x2: clamp2(xThreshold, plotLeft, plotRight),
10405
+ x2: clamp3(xThreshold, plotLeft, plotRight),
9416
10406
  y2: plotBottom,
9417
10407
  className: "stroke-muted-foreground/50",
9418
10408
  strokeDasharray: "5 5"
@@ -9422,9 +10412,9 @@ function WorkbenchScatterPlot({
9422
10412
  "line",
9423
10413
  {
9424
10414
  x1: plotLeft,
9425
- y1: clamp2(yThreshold, plotTop, plotBottom),
10415
+ y1: clamp3(yThreshold, plotTop, plotBottom),
9426
10416
  x2: plotRight,
9427
- y2: clamp2(yThreshold, plotTop, plotBottom),
10417
+ y2: clamp3(yThreshold, plotTop, plotBottom),
9428
10418
  className: "stroke-muted-foreground/50",
9429
10419
  strokeDasharray: "5 5"
9430
10420
  }
@@ -9451,17 +10441,17 @@ function WorkbenchScatterPlot({
9451
10441
  }
9452
10442
  ),
9453
10443
  validPoints.map((point) => {
9454
- const x = clamp2(
10444
+ const x = clamp3(
9455
10445
  scale(point.x, xDomain, plotLeft, plotRight),
9456
10446
  plotLeft,
9457
10447
  plotRight
9458
10448
  );
9459
- const y = clamp2(
10449
+ const y = clamp3(
9460
10450
  scale(point.y, yDomain, plotBottom, plotTop),
9461
10451
  plotTop,
9462
10452
  plotBottom
9463
10453
  );
9464
- const radius = point.size === void 0 ? 9 : clamp2(scale(point.size, sizeDomain, 7, 18), 7, 18);
10454
+ const radius = point.size === void 0 ? 9 : clamp3(scale(point.size, sizeDomain, 7, 18), 7, 18);
9465
10455
  const selected = selectedPointId === point.id;
9466
10456
  return /* @__PURE__ */ jsxRuntime.jsxs(
9467
10457
  "g",
@@ -9993,7 +10983,7 @@ var getDomain2 = (values, min, max) => {
9993
10983
  return lower < upper ? [lower, upper] : [upper, lower];
9994
10984
  };
9995
10985
  var scale2 = (value, domain, start, end) => start + (value - domain[0]) / (domain[1] - domain[0]) * (end - start);
9996
- var clamp3 = (value, min, max) => Math.min(max, Math.max(min, value));
10986
+ var clamp4 = (value, min, max) => Math.min(max, Math.max(min, value));
9997
10987
  var toneVariable = {
9998
10988
  primary: "--radar-series-one",
9999
10989
  secondary: "--radar-series-two",
@@ -10056,7 +11046,7 @@ function WorkbenchRadarMatrix({
10056
11046
  const plotRight = width;
10057
11047
  const plotTop = 0;
10058
11048
  const plotBottom = height;
10059
- const xThreshold = clamp3(
11049
+ const xThreshold = clamp4(
10060
11050
  scale2(
10061
11051
  Number.isFinite(thresholds?.x) ? Number(thresholds?.x) : (xDomain[0] + xDomain[1]) / 2,
10062
11052
  xDomain,
@@ -10066,7 +11056,7 @@ function WorkbenchRadarMatrix({
10066
11056
  plotLeft,
10067
11057
  plotRight
10068
11058
  );
10069
- const yThreshold = clamp3(
11059
+ const yThreshold = clamp4(
10070
11060
  scale2(
10071
11061
  Number.isFinite(thresholds?.y) ? Number(thresholds?.y) : (yDomain[0] + yDomain[1]) / 2,
10072
11062
  yDomain,
@@ -10174,13 +11164,13 @@ function WorkbenchRadarMatrix({
10174
11164
  ] }, quadrant.id);
10175
11165
  }),
10176
11166
  validPoints.map((point, index) => {
10177
- const x = clamp3(scale2(point.x, xDomain, 40, width - 40), 40, width - 40);
10178
- const y = clamp3(scale2(point.y, yDomain, height - 48, 54), 54, height - 48);
11167
+ const x = clamp4(scale2(point.x, xDomain, 40, width - 40), 40, width - 40);
11168
+ const y = clamp4(scale2(point.y, yDomain, height - 48, 54), 54, height - 48);
10179
11169
  const tone = point.tone || (index % 2 === 0 ? "primary" : "accent");
10180
11170
  const selected = selectedPointId === point.id;
10181
11171
  const labelled = labelledPointIds.has(point.id);
10182
- const radius = labelled ? clamp3(scale2(point.size ?? 1, sizeDomain, 5, 9), 5, 9) : 3.5;
10183
- const fontSize = labelled ? clamp3(scale2(point.size ?? 1, sizeDomain, 14, 31), 14, 31) : 0;
11172
+ const radius = labelled ? clamp4(scale2(point.size ?? 1, sizeDomain, 5, 9), 5, 9) : 3.5;
11173
+ const fontSize = labelled ? clamp4(scale2(point.size ?? 1, sizeDomain, 14, 31), 14, 31) : 0;
10184
11174
  const pointStyle = {
10185
11175
  fill: toneColor(tone),
10186
11176
  stroke: "hsl(var(--foreground) / 0.74)"
@@ -10282,8 +11272,8 @@ function WorkbenchRadarMatrix({
10282
11272
  validPoints.map((point, index) => /* @__PURE__ */ jsxRuntime.jsx(
10283
11273
  "circle",
10284
11274
  {
10285
- cx: clamp3(scale2(point.x, xDomain, 40, width - 40), 40, width - 40),
10286
- cy: clamp3(scale2(point.y, yDomain, height - 48, 54), 54, height - 48),
11275
+ cx: clamp4(scale2(point.x, xDomain, 40, width - 40), 40, width - 40),
11276
+ cy: clamp4(scale2(point.y, yDomain, height - 48, 54), 54, height - 48),
10287
11277
  r: "8",
10288
11278
  style: { fill: toneColor(point.tone || (index % 2 === 0 ? "primary" : "accent")) }
10289
11279
  },
@@ -10322,6 +11312,7 @@ exports.InteractiveTableEditableTextCell = InteractiveTableEditableTextCell;
10322
11312
  exports.InteractiveTableReadonlyCell = InteractiveTableReadonlyCell;
10323
11313
  exports.InteractiveTableSelectCell = InteractiveTableSelectCell;
10324
11314
  exports.WorkbenchAssetGallery = WorkbenchAssetGallery;
11315
+ exports.WorkbenchCollection = WorkbenchCollection;
10325
11316
  exports.WorkbenchContentPane = WorkbenchContentPane;
10326
11317
  exports.WorkbenchContentToolbar = WorkbenchContentToolbar;
10327
11318
  exports.WorkbenchDetailSidebar = WorkbenchDetailSidebar;