@inf-monkeys-tech/monkeys-design 1.0.56 → 1.0.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,21 +4,21 @@ var debounce = require('lodash/debounce.js');
4
4
  var runtime = require('@inf-monkeys-tech/monkeys/runtime');
5
5
  var React4 = require('react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
- var DropdownMenu2 = require('@radix-ui/react-dropdown-menu');
7
+ var PopoverPrimitive = require('@radix-ui/react-popover');
8
+ var lucideReact = require('lucide-react');
8
9
  var tailwindMerge = require('tailwind-merge');
10
+ var DropdownMenu2 = require('@radix-ui/react-dropdown-menu');
9
11
  var ContextMenu = require('@radix-ui/react-context-menu');
10
12
  var reactDom = require('react-dom');
11
13
  var Dialog = require('@radix-ui/react-dialog');
12
14
  var Tooltip2 = require('@radix-ui/react-tooltip');
13
15
  var AccordionPrimitive = require('@radix-ui/react-accordion');
14
16
  var CheckboxPrimitive = require('@radix-ui/react-checkbox');
15
- var PopoverPrimitive = require('@radix-ui/react-popover');
16
17
  var ScrollAreaPrimitive = require('@radix-ui/react-scroll-area');
17
18
  var reactSlot = require('@radix-ui/react-slot');
18
19
  var SwitchPrimitive = require('@radix-ui/react-switch');
19
20
  var TabsPrimitive = require('@radix-ui/react-tabs');
20
21
  var classVarianceAuthority = require('class-variance-authority');
21
- var lucideReact = require('lucide-react');
22
22
  var SelectPrimitive = require('@radix-ui/react-select');
23
23
  var core = require('@dnd-kit/core');
24
24
  var sonner = require('sonner');
@@ -45,13 +45,13 @@ function _interopNamespace(e) {
45
45
 
46
46
  var debounce__default = /*#__PURE__*/_interopDefault(debounce);
47
47
  var React4__namespace = /*#__PURE__*/_interopNamespace(React4);
48
+ var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitive);
48
49
  var DropdownMenu2__namespace = /*#__PURE__*/_interopNamespace(DropdownMenu2);
49
50
  var ContextMenu__namespace = /*#__PURE__*/_interopNamespace(ContextMenu);
50
51
  var Dialog__namespace = /*#__PURE__*/_interopNamespace(Dialog);
51
52
  var Tooltip2__namespace = /*#__PURE__*/_interopNamespace(Tooltip2);
52
53
  var AccordionPrimitive__namespace = /*#__PURE__*/_interopNamespace(AccordionPrimitive);
53
54
  var CheckboxPrimitive__namespace = /*#__PURE__*/_interopNamespace(CheckboxPrimitive);
54
- var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitive);
55
55
  var ScrollAreaPrimitive__namespace = /*#__PURE__*/_interopNamespace(ScrollAreaPrimitive);
56
56
  var SwitchPrimitive__namespace = /*#__PURE__*/_interopNamespace(SwitchPrimitive);
57
57
  var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
@@ -4082,6 +4082,352 @@ function hasNonEmptyRenderableNode(node) {
4082
4082
  if (typeof node === "string") return node.trim().length > 0;
4083
4083
  return hasRenderableNode(node);
4084
4084
  }
4085
+ var placementMap = {
4086
+ "bottom-start": { side: "bottom", align: "start" },
4087
+ "bottom-end": { side: "bottom", align: "end" },
4088
+ "top-start": { side: "top", align: "start" },
4089
+ "top-end": { side: "top", align: "end" }
4090
+ };
4091
+ function flattenOptions(options, depth = 0) {
4092
+ return options.flatMap((option) => [
4093
+ { ...option, depth },
4094
+ ...option.children ? flattenOptions(option.children, depth + 1) : []
4095
+ ]);
4096
+ }
4097
+ function validateOptions(options) {
4098
+ const values = /* @__PURE__ */ new Set();
4099
+ flattenOptions(options).forEach((option) => {
4100
+ if (!option || typeof option !== "object") {
4101
+ throw new TypeError("UnifiedDropdown options must contain objects.");
4102
+ }
4103
+ if (typeof option.value !== "string" || !option.value.trim()) {
4104
+ throw new TypeError("UnifiedDropdown option value must be a non-empty string.");
4105
+ }
4106
+ if (values.has(option.value)) {
4107
+ throw new TypeError(`UnifiedDropdown option value "${option.value}" must be unique.`);
4108
+ }
4109
+ values.add(option.value);
4110
+ });
4111
+ }
4112
+ function normalizeValues(value) {
4113
+ const values = Array.isArray(value) ? value : value === void 0 ? [] : [value];
4114
+ return [...new Set(values.filter((item) => typeof item === "string"))];
4115
+ }
4116
+ function getOptionText(option) {
4117
+ if (option.textValue) return option.textValue;
4118
+ return typeof option.label === "string" ? option.label : option.value;
4119
+ }
4120
+ function isMultiMode(mode) {
4121
+ return mode === "multi" || mode === "checkbox";
4122
+ }
4123
+ function matchesSearch(option, query) {
4124
+ return getOptionText(option).toLocaleLowerCase().includes(query.toLocaleLowerCase());
4125
+ }
4126
+ function validateValueShape(mode, value, name) {
4127
+ if (value === void 0) return;
4128
+ if (isMultiMode(mode) ? !Array.isArray(value) : typeof value !== "string") {
4129
+ throw new TypeError(
4130
+ `UnifiedDropdown ${name} must be ${isMultiMode(mode) ? "a string array" : "a string"} in ${mode} mode.`
4131
+ );
4132
+ }
4133
+ }
4134
+ var UnifiedDropdown = React4.forwardRef(
4135
+ function UnifiedDropdown2({
4136
+ mode,
4137
+ options = [],
4138
+ groups = [],
4139
+ value,
4140
+ defaultValue,
4141
+ placeholder = "Select\u2026",
4142
+ trigger,
4143
+ triggerLabel,
4144
+ searchPlaceholder = "Search\u2026",
4145
+ emptyLabel = "No options",
4146
+ loadingLabel = "Loading\u2026",
4147
+ clearLabel = "Clear selection",
4148
+ retryLabel = "Retry",
4149
+ loading = false,
4150
+ error,
4151
+ searchable = mode === "searchable",
4152
+ clearable = false,
4153
+ maxSelections,
4154
+ disabled = false,
4155
+ invalid = false,
4156
+ open: controlledOpen,
4157
+ defaultOpen = false,
4158
+ placement = "bottom-start",
4159
+ closeOnSelect = mode !== "multi" && mode !== "checkbox",
4160
+ portal = true,
4161
+ size = "default",
4162
+ width = "trigger",
4163
+ appearance,
4164
+ className,
4165
+ classNames,
4166
+ slots,
4167
+ style,
4168
+ onOpenChange,
4169
+ onValueChange,
4170
+ onSelect,
4171
+ onAction,
4172
+ onClear,
4173
+ onRetry,
4174
+ ...rootProps
4175
+ }, ref) {
4176
+ if (!["single", "multi", "searchable", "radio", "checkbox", "tree", "action"].includes(mode)) {
4177
+ throw new TypeError(`UnifiedDropdown mode "${String(mode)}" is not supported.`);
4178
+ }
4179
+ const groupedOptions = React4.useMemo(
4180
+ () => groups.reduce((result, group) => {
4181
+ const groupName = typeof group.label === "string" ? group.label : group.value;
4182
+ result.push(...group.options.map((option) => ({ ...option, group: option.group ?? groupName })));
4183
+ return result;
4184
+ }, [...options]),
4185
+ [groups, options]
4186
+ );
4187
+ validateOptions(groupedOptions);
4188
+ if (maxSelections !== void 0 && (!Number.isFinite(maxSelections) || maxSelections < 0)) {
4189
+ throw new TypeError("UnifiedDropdown maxSelections must be a non-negative finite number.");
4190
+ }
4191
+ if (mode === "action" && (value !== void 0 || defaultValue !== void 0)) {
4192
+ throw new TypeError("UnifiedDropdown action mode does not accept a value.");
4193
+ }
4194
+ if (mode !== "action") {
4195
+ validateValueShape(mode, value, "value");
4196
+ validateValueShape(mode, defaultValue, "defaultValue");
4197
+ }
4198
+ const id = React4.useId();
4199
+ const triggerId = `${id}-trigger`;
4200
+ const listId = `${id}-listbox`;
4201
+ const searchId = `${id}-search`;
4202
+ const triggerRef = React4.useRef(null);
4203
+ const itemRefs = React4.useRef([]);
4204
+ const [uncontrolledOpen, setUncontrolledOpen] = React4.useState(defaultOpen);
4205
+ const [uncontrolledValue, setUncontrolledValue] = React4.useState(
4206
+ defaultValue ?? (isMultiMode(mode) ? [] : "")
4207
+ );
4208
+ const [query, setQuery] = React4.useState("");
4209
+ const [activeIndex, setActiveIndex] = React4.useState(0);
4210
+ const isControlled = controlledOpen !== void 0;
4211
+ const open = isControlled ? controlledOpen : uncontrolledOpen;
4212
+ const selectedValues = normalizeValues(value ?? uncontrolledValue);
4213
+ const flattened = React4.useMemo(() => flattenOptions(groupedOptions), [groupedOptions]);
4214
+ const visibleOptions = React4.useMemo(
4215
+ () => searchable ? flattened.filter((option) => matchesSearch(option, query)) : flattened,
4216
+ [flattened, query, searchable]
4217
+ );
4218
+ const selectedSet = React4.useMemo(() => new Set(selectedValues), [selectedValues]);
4219
+ const selectedOptions = flattened.filter((option) => selectedSet.has(option.value));
4220
+ const selectedLabel = selectedOptions.map(getOptionText).join(", ");
4221
+ const resolvedTriggerLabel = triggerLabel ?? (selectedLabel || placeholder);
4222
+ const isPlaceholder = selectedOptions.length === 0 && !triggerLabel;
4223
+ const { side, align } = placementMap[placement];
4224
+ const isMenu = mode === "action";
4225
+ const usesCheckedMenu = mode === "radio" || mode === "checkbox";
4226
+ const contentRole = isMenu || usesCheckedMenu ? "menu" : "listbox";
4227
+ React4.useEffect(() => {
4228
+ if (open) setActiveIndex(0);
4229
+ }, [open, query]);
4230
+ const setOpen = (nextOpen) => {
4231
+ if (disabled) return;
4232
+ if (!isControlled) setUncontrolledOpen(nextOpen);
4233
+ onOpenChange?.(nextOpen);
4234
+ };
4235
+ const commitValue = (nextValue) => {
4236
+ if (value === void 0) setUncontrolledValue(nextValue);
4237
+ onValueChange?.(nextValue);
4238
+ };
4239
+ const selectOption = (option) => {
4240
+ if (disabled || option.disabled) return;
4241
+ onSelect?.(option);
4242
+ if (mode === "action") {
4243
+ if (closeOnSelect) setOpen(false);
4244
+ if (onAction) queueMicrotask(() => onAction(option));
4245
+ return;
4246
+ }
4247
+ if (isMultiMode(mode)) {
4248
+ const nextValues = selectedSet.has(option.value) ? selectedValues.filter((item) => item !== option.value) : maxSelections !== void 0 && selectedValues.length >= maxSelections ? selectedValues : [...selectedValues, option.value];
4249
+ commitValue(nextValues);
4250
+ } else {
4251
+ commitValue(option.value);
4252
+ }
4253
+ if (closeOnSelect && !isMultiMode(mode)) setOpen(false);
4254
+ };
4255
+ const handleTriggerKeyDown = (event) => {
4256
+ if (disabled) return;
4257
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
4258
+ event.preventDefault();
4259
+ if (!open) setOpen(true);
4260
+ const direction = event.key === "ArrowDown" ? 1 : -1;
4261
+ setActiveIndex((index) => Math.max(0, Math.min(visibleOptions.length - 1, index + direction)));
4262
+ }
4263
+ };
4264
+ const handleContentKeyDown = (event) => {
4265
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
4266
+ event.preventDefault();
4267
+ const direction = event.key === "ArrowDown" ? 1 : -1;
4268
+ setActiveIndex((index) => {
4269
+ const next = index + direction;
4270
+ return Math.max(0, Math.min(visibleOptions.length - 1, next));
4271
+ });
4272
+ }
4273
+ if (event.key === "Enter" && visibleOptions[activeIndex]) {
4274
+ event.preventDefault();
4275
+ selectOption(visibleOptions[activeIndex]);
4276
+ }
4277
+ };
4278
+ React4.useEffect(() => {
4279
+ if (!open) return;
4280
+ itemRefs.current[activeIndex]?.scrollIntoView({ block: "nearest" });
4281
+ }, [activeIndex, open]);
4282
+ const clearSelection = () => {
4283
+ commitValue(isMultiMode(mode) ? [] : "");
4284
+ onClear?.();
4285
+ };
4286
+ return /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Root, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(
4287
+ "div",
4288
+ {
4289
+ ...rootProps,
4290
+ ref,
4291
+ className: cn("relative inline-flex min-w-0", className),
4292
+ style,
4293
+ "data-monkeys-component": "unified-dropdown",
4294
+ "data-mode": mode,
4295
+ "data-state": open ? "open" : "closed",
4296
+ "data-invalid": invalid || void 0,
4297
+ "data-disabled": disabled || void 0,
4298
+ "data-appearance-preset": appearance?.preset,
4299
+ "data-appearance-variant": appearance?.variant,
4300
+ "data-appearance-density": appearance?.density,
4301
+ "data-appearance-radius": appearance?.radius,
4302
+ children: [
4303
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
4304
+ "button",
4305
+ {
4306
+ ref: triggerRef,
4307
+ id: triggerId,
4308
+ type: "button",
4309
+ disabled,
4310
+ "aria-expanded": open,
4311
+ "aria-haspopup": isMenu || usesCheckedMenu ? "menu" : "listbox",
4312
+ "aria-controls": open ? listId : void 0,
4313
+ "aria-invalid": invalid || void 0,
4314
+ onKeyDown: handleTriggerKeyDown,
4315
+ className: cn(
4316
+ "flex min-w-0 items-center justify-between gap-2 rounded-md border border-input bg-control-surface px-3 text-left text-sm text-foreground shadow-sm outline-none transition-colors",
4317
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-control-surface",
4318
+ "disabled:cursor-not-allowed disabled:opacity-50",
4319
+ size === "sm" ? "h-8 py-1 text-xs" : "h-10 py-2",
4320
+ invalid && "border-destructive focus-visible:ring-destructive",
4321
+ width === "responsive" && "w-full sm:w-auto",
4322
+ classNames?.trigger
4323
+ ),
4324
+ children: [
4325
+ trigger ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 shrink-0", children: trigger }) : null,
4326
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("min-w-0 flex-1 truncate", isPlaceholder && "text-muted-foreground"), children: resolvedTriggerLabel }),
4327
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { "aria-hidden": "true", className: "h-4 w-4 shrink-0 opacity-60" })
4328
+ ]
4329
+ }
4330
+ ) }),
4331
+ (() => {
4332
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(
4333
+ PopoverPrimitive__namespace.Content,
4334
+ {
4335
+ id: listId,
4336
+ side,
4337
+ align,
4338
+ sideOffset: 6,
4339
+ collisionPadding: 8,
4340
+ avoidCollisions: true,
4341
+ onKeyDown: handleContentKeyDown,
4342
+ onOpenAutoFocus: (event) => {
4343
+ event.preventDefault();
4344
+ const target = searchable ? document.getElementById(searchId) : itemRefs.current[0];
4345
+ target?.focus();
4346
+ },
4347
+ tabIndex: -1,
4348
+ className: cn(
4349
+ "z-50 max-h-[min(24rem,calc(100vh-1rem))] max-w-[calc(100vw-1rem)] overflow-y-auto rounded-md border bg-menu-surface p-1 text-popover-foreground shadow-md outline-none",
4350
+ width === "trigger" && "w-[var(--radix-popover-trigger-width)] min-w-[12rem]",
4351
+ width === "content" && "min-w-[12rem] max-w-[min(32rem,calc(100vw-1rem))]",
4352
+ width === "responsive" && "w-[min(var(--radix-popover-trigger-width),calc(100vw-1rem))] min-w-[12rem]",
4353
+ classNames?.content
4354
+ ),
4355
+ children: [
4356
+ searchable ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative mb-1", children: [
4357
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { "aria-hidden": "true", className: "pointer-events-none absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
4358
+ /* @__PURE__ */ jsxRuntime.jsx(
4359
+ "input",
4360
+ {
4361
+ id: searchId,
4362
+ value: query,
4363
+ onChange: (event) => setQuery(event.target.value),
4364
+ placeholder: searchPlaceholder,
4365
+ "aria-label": searchPlaceholder,
4366
+ className: cn("h-9 w-full rounded-sm border-0 bg-transparent pl-8 pr-2 text-sm outline-none ring-0 placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring", classNames?.search)
4367
+ }
4368
+ )
4369
+ ] }) : null,
4370
+ clearable && selectedOptions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "mb-1 w-full rounded-sm px-2 py-1 text-left text-xs text-muted-foreground hover:bg-accent", onClick: clearSelection, children: clearLabel }) : null,
4371
+ loading && visibleOptions.length === 0 ? slots?.loading ? slots.loading() : /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", className: cn("px-2 py-6 text-center text-sm text-muted-foreground", classNames?.empty), children: loadingLabel }) : error && visibleOptions.length === 0 ? slots?.error ? slots.error() : /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", className: cn("px-2 py-6 text-center text-sm text-destructive-text", classNames?.empty), children: [
4372
+ error,
4373
+ onRetry ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "mt-2 block w-full text-center text-foreground underline", onClick: onRetry, children: retryLabel }) : null
4374
+ ] }) : visibleOptions.length === 0 ? slots?.empty ? slots.empty() : /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", className: cn("px-2 py-6 text-center text-sm text-muted-foreground", classNames?.empty), children: emptyLabel }) : /* @__PURE__ */ jsxRuntime.jsx("div", { role: mode === "tree" ? "tree" : contentRole, "aria-multiselectable": mode === "multi" || void 0, children: visibleOptions.map((option, index) => {
4375
+ const selected = selectedSet.has(option.value);
4376
+ const indicator = mode === "radio" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Circle, { "aria-hidden": "true", className: cn("h-3.5 w-3.5", selected && "fill-current") }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { "aria-hidden": "true", className: "h-4 w-4" });
4377
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4378
+ option.group && (index === 0 || visibleOptions[index - 1]?.group !== option.group) ? /* @__PURE__ */ jsxRuntime.jsx("div", { role: "presentation", className: cn("px-2 pb-1 pt-2 text-xs font-medium text-muted-foreground", classNames?.groupLabel), children: option.group }) : null,
4379
+ /* @__PURE__ */ jsxRuntime.jsxs(
4380
+ "button",
4381
+ {
4382
+ ref: (node) => {
4383
+ itemRefs.current[index] = node;
4384
+ },
4385
+ type: "button",
4386
+ role: mode === "tree" ? "treeitem" : mode === "radio" ? "menuitemradio" : mode === "checkbox" ? "menuitemcheckbox" : isMenu ? "menuitem" : "option",
4387
+ "aria-level": mode === "tree" ? option.depth + 1 : void 0,
4388
+ "aria-selected": !isMenu && !usesCheckedMenu ? selected : void 0,
4389
+ "aria-checked": usesCheckedMenu ? selected : void 0,
4390
+ disabled: option.disabled,
4391
+ "data-highlighted": index === activeIndex || void 0,
4392
+ "data-state": selected ? "checked" : "unchecked",
4393
+ onMouseEnter: () => setActiveIndex(index),
4394
+ onClick: () => selectOption(option),
4395
+ className: cn(
4396
+ "relative flex min-h-9 w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm outline-none transition-colors",
4397
+ "hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground",
4398
+ "disabled:pointer-events-none disabled:opacity-50",
4399
+ index === activeIndex && "bg-accent text-accent-foreground",
4400
+ selected && "font-medium",
4401
+ option.danger && "text-destructive-text",
4402
+ mode !== "action" && "pl-8",
4403
+ classNames?.item
4404
+ ),
4405
+ style: mode === "tree" ? { paddingInlineStart: `${8 + option.depth * 16}px` } : void 0,
4406
+ children: [
4407
+ mode !== "action" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-2 flex h-4 w-4 items-center justify-center", children: selected ? indicator : null }) : null,
4408
+ slots?.option ? slots.option({ option, selected, highlighted: index === activeIndex }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4409
+ option.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: option.icon }) : null,
4410
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("min-w-0 flex-1 truncate", classNames?.itemLabel), children: option.label }),
4411
+ option.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("max-w-[45%] truncate text-xs text-muted-foreground", classNames?.itemDescription), children: option.description }) : null
4412
+ ] })
4413
+ ]
4414
+ }
4415
+ )
4416
+ ] }, option.value);
4417
+ }) }),
4418
+ error && visibleOptions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", className: "px-2 py-1 text-xs text-destructive-text", children: error }) : null,
4419
+ loading && visibleOptions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", className: "px-2 py-1 text-xs text-muted-foreground", children: loadingLabel }) : null,
4420
+ slots?.footer ? slots.footer() : null
4421
+ ]
4422
+ }
4423
+ );
4424
+ return portal ? /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Portal, { children: content }) : content;
4425
+ })()
4426
+ ]
4427
+ }
4428
+ ) });
4429
+ }
4430
+ );
4085
4431
 
4086
4432
  // src/components/base/theme.ts
4087
4433
  var emptyStyles = {};
@@ -10372,7 +10718,7 @@ function validateMultiSelectProps({
10372
10718
  });
10373
10719
  }
10374
10720
  }
10375
- function normalizeValues(values) {
10721
+ function normalizeValues2(values) {
10376
10722
  const nextValues = [];
10377
10723
  const seen = /* @__PURE__ */ new Set();
10378
10724
  values?.forEach((value) => {
@@ -10404,7 +10750,7 @@ function getSafeTagCount(maxTagCount, total) {
10404
10750
  if (maxTagCount === void 0 || !Number.isFinite(maxTagCount)) return total;
10405
10751
  return Math.max(0, Math.min(total, Math.floor(maxTagCount)));
10406
10752
  }
10407
- function getOptionText(option) {
10753
+ function getOptionText2(option) {
10408
10754
  if (option.textValue !== void 0) return option.textValue;
10409
10755
  if (typeof option.label === "string") return option.label;
10410
10756
  return option.value;
@@ -10470,7 +10816,7 @@ var BaseMultiSelect = React4.forwardRef(
10470
10816
  const forceOpen = openForPreview;
10471
10817
  const [uncontrolledOpen, setUncontrolledOpen] = React4.useState(false);
10472
10818
  const open = forceOpen || uncontrolledOpen;
10473
- const initialDefaultValueRef = React4.useRef(normalizeValues(defaultValue));
10819
+ const initialDefaultValueRef = React4.useRef(normalizeValues2(defaultValue));
10474
10820
  const [uncontrolledValue, setUncontrolledValue] = React4.useState(() => [
10475
10821
  ...initialDefaultValueRef.current
10476
10822
  ]);
@@ -10479,7 +10825,7 @@ var BaseMultiSelect = React4.forwardRef(
10479
10825
  );
10480
10826
  const isControlled = value !== void 0;
10481
10827
  const selectedValues = React4.useMemo(
10482
- () => normalizeValues(isControlled ? value : uncontrolledValue),
10828
+ () => normalizeValues2(isControlled ? value : uncontrolledValue),
10483
10829
  [isControlled, uncontrolledValue, value]
10484
10830
  );
10485
10831
  const selectedValueSet = React4.useMemo(
@@ -10523,7 +10869,7 @@ var BaseMultiSelect = React4.forwardRef(
10523
10869
  }
10524
10870
  };
10525
10871
  const commitValues = (nextValues) => {
10526
- const normalizedValues = normalizeValues(nextValues);
10872
+ const normalizedValues = normalizeValues2(nextValues);
10527
10873
  if (!isControlled) setUncontrolledValue(normalizedValues);
10528
10874
  onValueChange?.(normalizedValues);
10529
10875
  };
@@ -10567,7 +10913,7 @@ var BaseMultiSelect = React4.forwardRef(
10567
10913
  for (let offset = 1; offset <= optionList.length; offset += 1) {
10568
10914
  const index = (resolvedActiveIndex + offset + optionList.length) % optionList.length;
10569
10915
  const option = optionList[index];
10570
- if (!option?.disabled && getOptionText(option).trim().toLocaleLowerCase().startsWith(query)) {
10916
+ if (!option?.disabled && getOptionText2(option).trim().toLocaleLowerCase().startsWith(query)) {
10571
10917
  event.preventDefault();
10572
10918
  moveActive(index);
10573
10919
  return;
@@ -12387,7 +12733,7 @@ function getBoundaryEnabledIndex2(options, boundary) {
12387
12733
  }
12388
12734
  return -1;
12389
12735
  }
12390
- function getOptionText2(option) {
12736
+ function getOptionText3(option) {
12391
12737
  if (option.textValue !== void 0) return option.textValue;
12392
12738
  if (typeof option.label === "string") return option.label;
12393
12739
  return option.value;
@@ -12566,7 +12912,7 @@ var BaseSelect = React4.forwardRef(
12566
12912
  for (let offset = 1; offset <= optionList.length; offset += 1) {
12567
12913
  const index = (startIndex + offset + optionList.length) % optionList.length;
12568
12914
  const option = optionList[index];
12569
- if (!option?.disabled && getOptionText2(option).trim().toLocaleLowerCase().startsWith(query)) {
12915
+ if (!option?.disabled && getOptionText3(option).trim().toLocaleLowerCase().startsWith(query)) {
12570
12916
  event.preventDefault();
12571
12917
  moveActive(index);
12572
12918
  return;
@@ -12872,7 +13218,7 @@ var BaseSelect = React4.forwardRef(
12872
13218
  {
12873
13219
  value: option.value,
12874
13220
  disabled: option.disabled,
12875
- children: getOptionText2(option)
13221
+ children: getOptionText3(option)
12876
13222
  },
12877
13223
  option.value
12878
13224
  ))
@@ -25396,6 +25742,22 @@ function WorkbenchAssetGallery({
25396
25742
  }
25397
25743
  ) });
25398
25744
  }
25745
+
25746
+ // src/components/workbench/workbenchVisualizationTone.ts
25747
+ var toneVariable = {
25748
+ primary: "--radar-series-one",
25749
+ secondary: "--radar-series-two",
25750
+ accent: "--radar-series-three",
25751
+ muted: "--radar-series-four"
25752
+ };
25753
+ var toneFallback = {
25754
+ primary: "var(--primary)",
25755
+ secondary: "var(--secondary-foreground)",
25756
+ accent: "var(--accent-foreground)",
25757
+ muted: "var(--muted-foreground)"
25758
+ };
25759
+ var workbenchVisualizationToneColor = (tone) => `hsl(var(${toneVariable[tone]}, ${toneFallback[tone]}))`;
25760
+ var workbenchVisualizationToneColorWithAlpha = (tone, alpha) => `hsl(var(${toneVariable[tone]}, ${toneFallback[tone]}) / ${alpha})`;
25399
25761
  var radarWorkspaceStyle = {
25400
25762
  backgroundColor: "hsl(var(--radar-canvas, var(--background)))",
25401
25763
  color: "hsl(var(--foreground))"
@@ -25560,24 +25922,166 @@ function WorkbenchRadarInspectorSection({
25560
25922
  }
25561
25923
  );
25562
25924
  }
25925
+ function assertValidRadarFilterRowProps(props) {
25926
+ const { disclosure, onSelect } = props;
25927
+ const invalid = !disclosure && !onSelect || disclosure?.mode === "integrated" && (Boolean(onSelect) || props.selected !== void 0) || disclosure?.mode === "separate" && !onSelect;
25928
+ if (invalid) {
25929
+ throw new Error("Invalid WorkbenchRadarFilterRow props.");
25930
+ }
25931
+ }
25932
+ function WorkbenchRadarFilterRow(props) {
25933
+ assertValidRadarFilterRowProps(props);
25934
+ const {
25935
+ label,
25936
+ value,
25937
+ icon,
25938
+ selected,
25939
+ disabled = false,
25940
+ tone,
25941
+ onSelect,
25942
+ disclosure,
25943
+ className
25944
+ } = props;
25945
+ const isSelected = selected ?? false;
25946
+ const selectedClasses = isSelected ? void 0 : "monkeys-workbench-radar-filter-row--idle";
25947
+ const disabledClasses = disabled ? "monkeys-workbench-radar-filter-disabled" : void 0;
25948
+ const resolvedTone = tone ?? "primary";
25949
+ const toneColor = workbenchVisualizationToneColor(resolvedTone);
25950
+ const style = {
25951
+ "--workbench-radar-filter-tone": toneColor,
25952
+ "--tw-ring-color": tone ? toneColor : "hsl(var(--ring))",
25953
+ ...isSelected || tone ? {
25954
+ backgroundColor: workbenchVisualizationToneColorWithAlpha(
25955
+ resolvedTone,
25956
+ isSelected ? 0.14 : 0.08
25957
+ ),
25958
+ borderColor: toneColor
25959
+ } : {}
25960
+ };
25961
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
25962
+ hasRenderableNode(icon) ? /* @__PURE__ */ jsxRuntime.jsx(
25963
+ "span",
25964
+ {
25965
+ className: cn(
25966
+ "flex size-4 shrink-0 items-center justify-center",
25967
+ tone ? "text-[var(--workbench-radar-filter-tone)]" : "text-muted-foreground"
25968
+ ),
25969
+ "aria-hidden": "true",
25970
+ children: icon
25971
+ }
25972
+ ) : null,
25973
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: label }),
25974
+ hasRenderableNode(value) ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto shrink-0 tabular-nums text-muted-foreground", children: value }) : null
25975
+ ] });
25976
+ const disclosureIcon = disclosure ? /* @__PURE__ */ jsxRuntime.jsx(
25977
+ "span",
25978
+ {
25979
+ "aria-hidden": "true",
25980
+ className: cn(
25981
+ "text-lg leading-none transition-transform motion-reduce:transition-none",
25982
+ disclosure.expanded && "rotate-90"
25983
+ ),
25984
+ children: "\u203A"
25985
+ }
25986
+ ) : null;
25987
+ if (disclosure?.mode === "separate") {
25988
+ const disclosureLabel2 = disclosure.expanded ? disclosure.collapseLabel : disclosure.expandLabel;
25989
+ return /* @__PURE__ */ jsxRuntime.jsxs(
25990
+ "div",
25991
+ {
25992
+ "data-monkeys-component": "workbench-radar-filter-row",
25993
+ className: cn(
25994
+ "monkeys-workbench-radar-filter-row monkeys-workbench-radar-filter-row--separate",
25995
+ selectedClasses,
25996
+ disabledClasses,
25997
+ className
25998
+ ),
25999
+ style,
26000
+ children: [
26001
+ /* @__PURE__ */ jsxRuntime.jsx(
26002
+ "button",
26003
+ {
26004
+ type: "button",
26005
+ "aria-pressed": selected,
26006
+ disabled,
26007
+ onClick: onSelect,
26008
+ className: cn(
26009
+ "monkeys-workbench-radar-filter-control monkeys-workbench-radar-filter-row__action",
26010
+ disabledClasses
26011
+ ),
26012
+ children: content
26013
+ }
26014
+ ),
26015
+ /* @__PURE__ */ jsxRuntime.jsx(
26016
+ "button",
26017
+ {
26018
+ type: "button",
26019
+ "aria-expanded": disclosure.expanded,
26020
+ "aria-controls": disclosure.controls,
26021
+ "aria-label": disclosureLabel2,
26022
+ disabled,
26023
+ onClick: disclosure.onToggle,
26024
+ className: cn(
26025
+ "monkeys-workbench-radar-filter-row__disclosure",
26026
+ disabledClasses
26027
+ ),
26028
+ children: disclosureIcon
26029
+ }
26030
+ )
26031
+ ]
26032
+ }
26033
+ );
26034
+ }
26035
+ const integratedDisclosure = disclosure?.mode === "integrated" ? disclosure : void 0;
26036
+ const disclosureLabel = integratedDisclosure ? integratedDisclosure.expanded ? integratedDisclosure.collapseLabel : integratedDisclosure.expandLabel : void 0;
26037
+ return /* @__PURE__ */ jsxRuntime.jsxs(
26038
+ "button",
26039
+ {
26040
+ type: "button",
26041
+ "data-monkeys-component": "workbench-radar-filter-row",
26042
+ "aria-label": disclosureLabel,
26043
+ "aria-pressed": integratedDisclosure ? void 0 : selected,
26044
+ "aria-expanded": integratedDisclosure?.expanded,
26045
+ "aria-controls": integratedDisclosure?.controls,
26046
+ disabled,
26047
+ onClick: integratedDisclosure?.onToggle ?? onSelect,
26048
+ className: cn(
26049
+ "monkeys-workbench-radar-filter-control monkeys-workbench-radar-filter-row",
26050
+ selectedClasses,
26051
+ disabledClasses,
26052
+ className
26053
+ ),
26054
+ style,
26055
+ children: [
26056
+ content,
26057
+ integratedDisclosure ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto shrink-0 text-muted-foreground", children: disclosureIcon }) : null
26058
+ ]
26059
+ }
26060
+ );
26061
+ }
25563
26062
  function WorkbenchRadarFilterChip({
25564
26063
  label,
25565
26064
  value,
25566
26065
  icon,
25567
26066
  selected = false,
26067
+ disabled = false,
25568
26068
  onSelect,
25569
26069
  className
25570
26070
  }) {
25571
- const Comp = onSelect ? "button" : "div";
26071
+ const interactive = Boolean(onSelect) || disabled;
26072
+ const Comp = interactive ? "button" : "div";
25572
26073
  return /* @__PURE__ */ jsxRuntime.jsxs(
25573
26074
  Comp,
25574
26075
  {
25575
- type: onSelect ? "button" : void 0,
25576
- "aria-pressed": onSelect ? selected : void 0,
26076
+ type: interactive ? "button" : void 0,
26077
+ "aria-pressed": onSelect && !disabled ? selected : void 0,
26078
+ disabled: interactive ? disabled : void 0,
25577
26079
  onClick: onSelect,
25578
26080
  className: cn(
25579
- "flex min-h-9 min-w-0 items-center gap-2 rounded-full border px-3 text-xs font-semibold outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
26081
+ "flex min-w-0 items-center border text-xs font-semibold outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
26082
+ "monkeys-workbench-radar-filter-control rounded-full",
25580
26083
  selected ? "border-primary bg-primary/20 text-primary" : "border-border bg-background/40 text-foreground hover:border-primary/60 hover:bg-primary/10",
26084
+ disabled && "cursor-not-allowed opacity-50",
25581
26085
  className
25582
26086
  ),
25583
26087
  children: [
@@ -25598,20 +26102,6 @@ var getDomain2 = (values, min5, max5) => {
25598
26102
  };
25599
26103
  var scale2 = (value, domain, start, end) => start + (value - domain[0]) / (domain[1] - domain[0]) * (end - start);
25600
26104
  var clamp7 = (value, min5, max5) => Math.min(max5, Math.max(min5, value));
25601
- var toneVariable = {
25602
- primary: "--radar-series-one",
25603
- secondary: "--radar-series-two",
25604
- accent: "--radar-series-three",
25605
- muted: "--radar-series-four"
25606
- };
25607
- var toneFallback = {
25608
- primary: "var(--primary)",
25609
- secondary: "var(--secondary-foreground)",
25610
- accent: "var(--accent-foreground)",
25611
- muted: "var(--muted-foreground)"
25612
- };
25613
- var toneColor = (tone) => `hsl(var(${toneVariable[tone]}, ${toneFallback[tone]}))`;
25614
- var toneColorWithAlpha = (tone, alpha) => `hsl(var(${toneVariable[tone]}, ${toneFallback[tone]}) / ${alpha})`;
25615
26105
  var activatePoint2 = (event, point, onPointSelect) => {
25616
26106
  if (!onPointSelect || event.key !== "Enter" && event.key !== " ") return;
25617
26107
  event.preventDefault();
@@ -25786,12 +26276,12 @@ function WorkbenchRadarMatrix({
25786
26276
  const radius = labelled ? clamp7(scale2(point.size ?? 1, sizeDomain, 5, 9), 5, 9) : 3.5;
25787
26277
  const fontSize = labelled ? clamp7(scale2(point.size ?? 1, sizeDomain, 14, 31), 14, 31) : 0;
25788
26278
  const pointStyle = {
25789
- fill: toneColor(tone),
26279
+ fill: workbenchVisualizationToneColor(tone),
25790
26280
  stroke: "hsl(var(--foreground) / 0.74)"
25791
26281
  };
25792
26282
  const labelStyle = {
25793
- fill: toneColor(tone),
25794
- filter: `drop-shadow(0 0 0.65rem ${toneColorWithAlpha(tone, 0.72)})`
26283
+ fill: workbenchVisualizationToneColor(tone),
26284
+ filter: `drop-shadow(0 0 0.65rem ${workbenchVisualizationToneColorWithAlpha(tone, 0.72)})`
25795
26285
  };
25796
26286
  return /* @__PURE__ */ jsxRuntime.jsxs(
25797
26287
  "g",
@@ -25811,7 +26301,7 @@ function WorkbenchRadarMatrix({
25811
26301
  r: radius + 10,
25812
26302
  fill: "none",
25813
26303
  strokeWidth: "2",
25814
- style: { stroke: toneColorWithAlpha(tone, 0.48) }
26304
+ style: { stroke: workbenchVisualizationToneColorWithAlpha(tone, 0.48) }
25815
26305
  }
25816
26306
  ) : null,
25817
26307
  /* @__PURE__ */ jsxRuntime.jsx("circle", { r: radius, strokeWidth: "1.25", style: pointStyle }),
@@ -25889,7 +26379,7 @@ function WorkbenchRadarMatrix({
25889
26379
  cx: clamp7(scale2(point.x, xDomain, 40, width - 40), 40, width - 40),
25890
26380
  cy: clamp7(scale2(point.y, yDomain, height - 48, 54), 54, height - 48),
25891
26381
  r: "8",
25892
- style: { fill: toneColor(point.tone || (index % 2 === 0 ? "primary" : "accent")) }
26382
+ style: { fill: workbenchVisualizationToneColor(point.tone || (index % 2 === 0 ? "primary" : "accent")) }
25893
26383
  },
25894
26384
  point.id
25895
26385
  )),
@@ -25912,7 +26402,7 @@ function WorkbenchRadarMatrix({
25912
26402
  legend.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-4 left-1/2 hidden -translate-x-1/2 items-center gap-4 rounded-full border border-border bg-card/95 px-4 py-2 text-xs font-semibold shadow-xl md:flex", children: legend.map((item) => {
25913
26403
  const tone = item.tone || "muted";
25914
26404
  return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2 text-muted-foreground", children: [
25915
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-2 rounded-full", style: { backgroundColor: toneColor(tone) } }),
26405
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-2 rounded-full", style: { backgroundColor: workbenchVisualizationToneColor(tone) } }),
25916
26406
  item.label
25917
26407
  ] }, item.id);
25918
26408
  }) }) : null
@@ -31394,6 +31884,7 @@ exports.OverlayNodeHost = OverlayNodeHost;
31394
31884
  exports.RenderNodeHost = RenderNodeHost;
31395
31885
  exports.ToastProvider = MonkeysToastProvider;
31396
31886
  exports.Toaster = MonkeysToaster;
31887
+ exports.UnifiedDropdown = UnifiedDropdown;
31397
31888
  exports.UserAccountMenu = UserAccountMenu;
31398
31889
  exports.WorkbenchAssetGallery = WorkbenchAssetGallery;
31399
31890
  exports.WorkbenchCollection = WorkbenchCollection;
@@ -31410,6 +31901,7 @@ exports.WorkbenchLaneView = WorkbenchLaneView;
31410
31901
  exports.WorkbenchMasonryLayout = WorkbenchMasonryLayout;
31411
31902
  exports.WorkbenchMetricGrid = WorkbenchMetricGrid;
31412
31903
  exports.WorkbenchRadarFilterChip = WorkbenchRadarFilterChip;
31904
+ exports.WorkbenchRadarFilterRow = WorkbenchRadarFilterRow;
31413
31905
  exports.WorkbenchRadarInspectorSection = WorkbenchRadarInspectorSection;
31414
31906
  exports.WorkbenchRadarMatrix = WorkbenchRadarMatrix;
31415
31907
  exports.WorkbenchRadarNavigation = WorkbenchRadarNavigation;