@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.
- package/dist/components/agent-workbench/index.js +9 -0
- package/dist/components/agent-workbench/index.js.map +1 -1
- package/dist/components/agent-workbench/index.mjs +9 -0
- package/dist/components/agent-workbench/index.mjs.map +1 -1
- package/dist/components/base/index.js +9 -0
- package/dist/components/base/index.js.map +1 -1
- package/dist/components/base/index.mjs +9 -0
- package/dist/components/base/index.mjs.map +1 -1
- package/dist/components/data-explorer/index.js +9 -0
- package/dist/components/data-explorer/index.js.map +1 -1
- package/dist/components/data-explorer/index.mjs +9 -0
- package/dist/components/data-explorer/index.mjs.map +1 -1
- package/dist/components/layout/index.js +9 -0
- package/dist/components/layout/index.js.map +1 -1
- package/dist/components/layout/index.mjs +9 -0
- package/dist/components/layout/index.mjs.map +1 -1
- package/dist/components/runtime/index.js +9 -0
- package/dist/components/runtime/index.js.map +1 -1
- package/dist/components/runtime/index.mjs +9 -0
- package/dist/components/runtime/index.mjs.map +1 -1
- package/dist/components/toast/index.d.mts +1 -1
- package/dist/components/toast/index.d.ts +1 -1
- package/dist/components/toast/index.js +9 -0
- package/dist/components/toast/index.js.map +1 -1
- package/dist/components/toast/index.mjs +9 -0
- package/dist/components/toast/index.mjs.map +1 -1
- package/dist/components/toolbar/index.js +9 -0
- package/dist/components/toolbar/index.js.map +1 -1
- package/dist/components/toolbar/index.mjs +9 -0
- package/dist/components/toolbar/index.mjs.map +1 -1
- package/dist/components/workbench/index.d.mts +218 -6
- package/dist/components/workbench/index.d.ts +218 -6
- package/dist/components/workbench/index.js +1088 -97
- package/dist/components/workbench/index.js.map +1 -1
- package/dist/components/workbench/index.mjs +1089 -99
- package/dist/components/workbench/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +838 -100
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +838 -101
- package/dist/index.mjs.map +1 -1
- package/dist/locale/index.d.mts +20 -2
- package/dist/locale/index.d.ts +20 -2
- package/dist/locale/index.js +22 -0
- package/dist/locale/index.js.map +1 -1
- package/dist/locale/index.mjs +22 -0
- package/dist/locale/index.mjs.map +1 -1
- package/dist/provider/index.d.mts +2 -2
- package/dist/provider/index.d.ts +2 -2
- package/dist/provider/index.js +22 -0
- package/dist/provider/index.js.map +1 -1
- package/dist/provider/index.mjs +22 -0
- package/dist/provider/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/{types-b8d18b1454c2.d.mts → types-453d18eab188.d.mts} +10 -0
- package/dist/{types-b8d18b1454c2.d.ts → types-453d18eab188.d.ts} +10 -0
- 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]) =>
|
|
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
|
|
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
|
-
|
|
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 (
|
|
8228
|
-
|
|
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
|
-
|
|
8244
|
-
|
|
8245
|
-
nextColumnIndex
|
|
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
|
-
|
|
8269
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 }) =>
|
|
8314
|
-
|
|
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
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
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
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9756
|
+
WorkbenchCollection,
|
|
8776
9757
|
{
|
|
8777
9758
|
items: rows,
|
|
8778
|
-
className,
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
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,
|
|
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
|
|
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:
|
|
10403
|
+
x1: clamp3(xThreshold, plotLeft, plotRight),
|
|
9414
10404
|
y1: plotTop,
|
|
9415
|
-
x2:
|
|
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:
|
|
10415
|
+
y1: clamp3(yThreshold, plotTop, plotBottom),
|
|
9426
10416
|
x2: plotRight,
|
|
9427
|
-
y2:
|
|
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 =
|
|
10444
|
+
const x = clamp3(
|
|
9455
10445
|
scale(point.x, xDomain, plotLeft, plotRight),
|
|
9456
10446
|
plotLeft,
|
|
9457
10447
|
plotRight
|
|
9458
10448
|
);
|
|
9459
|
-
const y =
|
|
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 :
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
10178
|
-
const y =
|
|
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 ?
|
|
10183
|
-
const fontSize = labelled ?
|
|
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:
|
|
10286
|
-
cy:
|
|
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;
|