myoperator-mcp 0.2.371 → 0.2.373
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 +151 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5987,6 +5987,11 @@ export function flattenMultiSelectOptions(
|
|
|
5987
5987
|
return (input as MultiSelectOption[]).map(normalizeMultiSelectOption);
|
|
5988
5988
|
}
|
|
5989
5989
|
|
|
5990
|
+
/** Menu never renders narrower than this, even off a tiny trigger. */
|
|
5991
|
+
const MENU_MIN_WIDTH = 180;
|
|
5992
|
+
/** Floor for the flip/shrink height so the list is never collapsed to nothing. */
|
|
5993
|
+
const MENU_MIN_HEIGHT = 120;
|
|
5994
|
+
|
|
5990
5995
|
/**
|
|
5991
5996
|
* MultiSelect trigger variants matching TextField styling
|
|
5992
5997
|
*/
|
|
@@ -6068,6 +6073,19 @@ export interface MultiSelectProps extends VariantProps<
|
|
|
6068
6073
|
showClearAll?: boolean;
|
|
6069
6074
|
/** Vertical rule before the chevron (Figma-style trigger) */
|
|
6070
6075
|
showSeparatorBeforeChevron?: boolean;
|
|
6076
|
+
/**
|
|
6077
|
+
* Element the dropdown is portaled into. Defaults to \`document.body\` so the
|
|
6078
|
+
* menu escapes \`overflow\` and \`transform\` ancestors (a Radix DialogContent is
|
|
6079
|
+
* both, and a transformed ancestor would clip a \`position: fixed\` menu).
|
|
6080
|
+
*/
|
|
6081
|
+
menuContainer?: HTMLElement | null;
|
|
6082
|
+
/**
|
|
6083
|
+
* Truncate long labels in the dropdown list to a single line with an ellipsis
|
|
6084
|
+
* instead of wrapping them. Selected chips in the trigger always truncate \u2014
|
|
6085
|
+
* this only controls the option rows.
|
|
6086
|
+
* @default false for \`simple\`, true for \`detailed\` (single-line row design)
|
|
6087
|
+
*/
|
|
6088
|
+
truncateOptionText?: boolean;
|
|
6071
6089
|
}
|
|
6072
6090
|
|
|
6073
6091
|
/**
|
|
@@ -6114,6 +6132,8 @@ const MultiSelect = React.forwardRef(
|
|
|
6114
6132
|
optionVariant = "simple",
|
|
6115
6133
|
separateSelectedWithDivider = false,
|
|
6116
6134
|
showClearAll = true,
|
|
6135
|
+
menuContainer,
|
|
6136
|
+
truncateOptionText,
|
|
6117
6137
|
showSeparatorBeforeChevron = false,
|
|
6118
6138
|
closeOnEscape = false,
|
|
6119
6139
|
}: MultiSelectProps,
|
|
@@ -6127,9 +6147,25 @@ const MultiSelect = React.forwardRef(
|
|
|
6127
6147
|
// Search query
|
|
6128
6148
|
const [searchQuery, setSearchQuery] = React.useState("");
|
|
6129
6149
|
|
|
6150
|
+
// \`detailed\` rows are a single-line design, so they truncate unless the
|
|
6151
|
+
// caller opts out; \`simple\` rows wrap the full label unless asked not to.
|
|
6152
|
+
const truncateOptions =
|
|
6153
|
+
truncateOptionText ?? optionVariant === "detailed";
|
|
6154
|
+
|
|
6130
6155
|
// Container ref for click outside detection
|
|
6131
6156
|
const containerRef = React.useRef<HTMLDivElement | null>(null);
|
|
6132
6157
|
|
|
6158
|
+
/** Where the dropdown gets portaled; body unless the caller overrides. */
|
|
6159
|
+
const [portalTarget, setPortalTarget] = React.useState<HTMLElement | null>(
|
|
6160
|
+
null
|
|
6161
|
+
);
|
|
6162
|
+
|
|
6163
|
+
React.useEffect(() => {
|
|
6164
|
+
if (!isOpen || typeof document === "undefined") return;
|
|
6165
|
+
setPortalTarget(menuContainer ?? document.body);
|
|
6166
|
+
}, [isOpen, menuContainer]);
|
|
6167
|
+
|
|
6168
|
+
|
|
6133
6169
|
const { refs, floatingStyles, isPositioned } = useFloating({
|
|
6134
6170
|
open: isOpen,
|
|
6135
6171
|
placement: "bottom-start",
|
|
@@ -6140,8 +6176,19 @@ const MultiSelect = React.forwardRef(
|
|
|
6140
6176
|
shift({ padding: 8 }),
|
|
6141
6177
|
size({
|
|
6142
6178
|
padding: 8,
|
|
6143
|
-
apply({ rects, elements }) {
|
|
6144
|
-
|
|
6179
|
+
apply({ rects, elements, availableHeight, availableWidth }) {
|
|
6180
|
+
// Match the trigger, but never render an unreadably narrow menu on a
|
|
6181
|
+
// small trigger, and never spill past the viewport on a small screen.
|
|
6182
|
+
const width = Math.min(
|
|
6183
|
+
Math.max(rects.reference.width, MENU_MIN_WIDTH),
|
|
6184
|
+
availableWidth
|
|
6185
|
+
);
|
|
6186
|
+
elements.floating.style.width = \`\${width}px\`;
|
|
6187
|
+
// Let the option list shrink instead of overflowing a short viewport.
|
|
6188
|
+
elements.floating.style.setProperty(
|
|
6189
|
+
"--multi-select-available-height",
|
|
6190
|
+
\`\${Math.max(availableHeight, MENU_MIN_HEIGHT)}px\`
|
|
6191
|
+
);
|
|
6145
6192
|
},
|
|
6146
6193
|
}),
|
|
6147
6194
|
],
|
|
@@ -6163,6 +6210,57 @@ const MultiSelect = React.forwardRef(
|
|
|
6163
6210
|
[refs]
|
|
6164
6211
|
);
|
|
6165
6212
|
|
|
6213
|
+
/**
|
|
6214
|
+
* Radix Dialog / Drawer wrap their content in \`react-remove-scroll\`, which
|
|
6215
|
+
* listens for \`wheel\` / \`touchmove\` on \`document\` (bubble phase) and calls
|
|
6216
|
+
* \`preventDefault()\` for anything outside the locked subtree \u2014 our
|
|
6217
|
+
* body-portaled menu counts as outside, so its list would not scroll.
|
|
6218
|
+
* Stopping propagation at the menu keeps that document listener from ever
|
|
6219
|
+
* seeing the event, leaving the browser's native scrolling intact.
|
|
6220
|
+
* Clicks are handled separately: the dialog sets \`pointer-events: none\` on
|
|
6221
|
+
* \`<body>\`, which the menu overrides with \`pointer-events: auto\`.
|
|
6222
|
+
*/
|
|
6223
|
+
React.useEffect(() => {
|
|
6224
|
+
const node = refs.floating.current;
|
|
6225
|
+
if (!isOpen || !node) return;
|
|
6226
|
+
|
|
6227
|
+
const stop = (event: Event) => event.stopPropagation();
|
|
6228
|
+
node.addEventListener("wheel", stop, { passive: false });
|
|
6229
|
+
node.addEventListener("touchmove", stop, { passive: false });
|
|
6230
|
+
|
|
6231
|
+
/**
|
|
6232
|
+
* The dialog also traps focus. Radix \`FocusScope\` registers TWO bubble
|
|
6233
|
+
* listeners on \`document\` and either one is enough to make the search
|
|
6234
|
+
* input untypable:
|
|
6235
|
+
* - \`focusin\` \u2014 target outside the dialog, so focus is pulled back.
|
|
6236
|
+
* - \`focusout\` \u2014 fired on the element focus is LEAVING (inside the
|
|
6237
|
+
* dialog) with \`relatedTarget\` = our input; since that is outside, it
|
|
6238
|
+
* restores the previously focused element.
|
|
6239
|
+
* The \`focusout\` one never travels through the menu, so a listener on the
|
|
6240
|
+
* menu node cannot see it. Intercepting in the CAPTURE phase on
|
|
6241
|
+
* \`document\` does: capture at \`document\` runs before the bubble listeners
|
|
6242
|
+
* on the same node, so stopping propagation there means FocusScope never
|
|
6243
|
+
* runs \u2014 but only for focus events that involve the menu. Every other
|
|
6244
|
+
* focus event in the app is untouched.
|
|
6245
|
+
*/
|
|
6246
|
+
const stopMenuFocusEvent = (event: Event) => {
|
|
6247
|
+
const { target, relatedTarget } = event as FocusEvent;
|
|
6248
|
+
const touchesMenu =
|
|
6249
|
+
(target instanceof Node && node.contains(target)) ||
|
|
6250
|
+
(relatedTarget instanceof Node && node.contains(relatedTarget));
|
|
6251
|
+
if (touchesMenu) event.stopPropagation();
|
|
6252
|
+
};
|
|
6253
|
+
document.addEventListener("focusin", stopMenuFocusEvent, true);
|
|
6254
|
+
document.addEventListener("focusout", stopMenuFocusEvent, true);
|
|
6255
|
+
|
|
6256
|
+
return () => {
|
|
6257
|
+
node.removeEventListener("wheel", stop);
|
|
6258
|
+
node.removeEventListener("touchmove", stop);
|
|
6259
|
+
document.removeEventListener("focusin", stopMenuFocusEvent, true);
|
|
6260
|
+
document.removeEventListener("focusout", stopMenuFocusEvent, true);
|
|
6261
|
+
};
|
|
6262
|
+
}, [isOpen, portalTarget, refs.floating]);
|
|
6263
|
+
|
|
6166
6264
|
const flatOptions = React.useMemo(
|
|
6167
6265
|
() => flattenMultiSelectOptions(options),
|
|
6168
6266
|
[options]
|
|
@@ -6344,14 +6442,14 @@ const MultiSelect = React.forwardRef(
|
|
|
6344
6442
|
return (
|
|
6345
6443
|
<div
|
|
6346
6444
|
ref={containerRef}
|
|
6347
|
-
className={cn("flex flex-col gap-1", wrapperClassName)}
|
|
6445
|
+
className={cn("flex min-w-0 flex-col gap-1", wrapperClassName)}
|
|
6348
6446
|
>
|
|
6349
6447
|
{/* Label */}
|
|
6350
6448
|
{label && (
|
|
6351
6449
|
<label
|
|
6352
6450
|
htmlFor={selectId}
|
|
6353
6451
|
className={cn(
|
|
6354
|
-
"text-sm font-semibold text-semantic-text-secondary",
|
|
6452
|
+
"break-words text-sm font-semibold text-semantic-text-secondary",
|
|
6355
6453
|
labelClassName
|
|
6356
6454
|
)}
|
|
6357
6455
|
>
|
|
@@ -6387,7 +6485,7 @@ const MultiSelect = React.forwardRef(
|
|
|
6387
6485
|
triggerClassName
|
|
6388
6486
|
)}
|
|
6389
6487
|
>
|
|
6390
|
-
<div className="flex-1 flex flex-wrap gap-1">
|
|
6488
|
+
<div className="min-w-0 flex-1 flex flex-wrap gap-1">
|
|
6391
6489
|
{selectedValues.length === 0 ? (
|
|
6392
6490
|
<span className="text-base text-semantic-text-placeholder">
|
|
6393
6491
|
{placeholder}
|
|
@@ -6396,9 +6494,14 @@ const MultiSelect = React.forwardRef(
|
|
|
6396
6494
|
selectedLabels.map((label, index) => (
|
|
6397
6495
|
<span
|
|
6398
6496
|
key={selectedValues[index]}
|
|
6399
|
-
className="inline-flex items-center gap-1 bg-semantic-bg-ui text-semantic-text-primary text-sm px-2 py-0.5 rounded"
|
|
6497
|
+
className="inline-flex min-w-0 max-w-full items-center gap-1 bg-semantic-bg-ui text-semantic-text-primary text-sm px-2 py-0.5 rounded"
|
|
6400
6498
|
>
|
|
6401
|
-
|
|
6499
|
+
<span
|
|
6500
|
+
className="min-w-0 truncate"
|
|
6501
|
+
title={typeof label === "string" ? label : undefined}
|
|
6502
|
+
>
|
|
6503
|
+
{label}
|
|
6504
|
+
</span>
|
|
6402
6505
|
<span
|
|
6403
6506
|
role="button"
|
|
6404
6507
|
tabIndex={0}
|
|
@@ -6412,7 +6515,7 @@ const MultiSelect = React.forwardRef(
|
|
|
6412
6515
|
);
|
|
6413
6516
|
}
|
|
6414
6517
|
}}
|
|
6415
|
-
className="cursor-pointer hover:text-semantic-error-primary focus:outline-none"
|
|
6518
|
+
className="shrink-0 cursor-pointer hover:text-semantic-error-primary focus:outline-none"
|
|
6416
6519
|
aria-label={\`Remove \${label}\`}
|
|
6417
6520
|
>
|
|
6418
6521
|
<X className="size-3" />
|
|
@@ -6471,14 +6574,14 @@ const MultiSelect = React.forwardRef(
|
|
|
6471
6574
|
className="size-3.5 shrink-0 text-semantic-error-primary"
|
|
6472
6575
|
aria-hidden
|
|
6473
6576
|
/>
|
|
6474
|
-
<span className="text-sm text-semantic-error-primary">
|
|
6577
|
+
<span className="min-w-0 break-words text-sm text-semantic-error-primary">
|
|
6475
6578
|
{error}
|
|
6476
6579
|
</span>
|
|
6477
6580
|
</div>
|
|
6478
6581
|
) : helperText ? (
|
|
6479
6582
|
<span
|
|
6480
6583
|
id={helperId}
|
|
6481
|
-
className="text-sm text-semantic-text-muted"
|
|
6584
|
+
className="min-w-0 break-words text-sm text-semantic-text-muted"
|
|
6482
6585
|
>
|
|
6483
6586
|
{helperText}
|
|
6484
6587
|
</span>
|
|
@@ -6487,7 +6590,7 @@ const MultiSelect = React.forwardRef(
|
|
|
6487
6590
|
)}
|
|
6488
6591
|
|
|
6489
6592
|
{isOpen &&
|
|
6490
|
-
|
|
6593
|
+
portalTarget &&
|
|
6491
6594
|
createPortal(
|
|
6492
6595
|
<TooltipProvider delayDuration={200}>
|
|
6493
6596
|
<div
|
|
@@ -6499,6 +6602,9 @@ const MultiSelect = React.forwardRef(
|
|
|
6499
6602
|
style={{
|
|
6500
6603
|
...floatingStyles,
|
|
6501
6604
|
zIndex: 10050,
|
|
6605
|
+
// Radix Dialog sets \`pointer-events: none\` on <body> while
|
|
6606
|
+
// open; without this the menu swallows nothing and clicks die.
|
|
6607
|
+
pointerEvents: "auto",
|
|
6502
6608
|
visibility: isPositioned ? undefined : "hidden",
|
|
6503
6609
|
}}
|
|
6504
6610
|
onMouseDown={(e) => e.stopPropagation()}
|
|
@@ -6518,7 +6624,13 @@ const MultiSelect = React.forwardRef(
|
|
|
6518
6624
|
)}
|
|
6519
6625
|
|
|
6520
6626
|
{/* Options */}
|
|
6521
|
-
<div
|
|
6627
|
+
<div
|
|
6628
|
+
className="overflow-auto overscroll-contain p-1"
|
|
6629
|
+
style={{
|
|
6630
|
+
maxHeight:
|
|
6631
|
+
"min(15rem, var(--multi-select-available-height, 15rem))",
|
|
6632
|
+
}}
|
|
6633
|
+
>
|
|
6522
6634
|
{filteredOptions.length === 0 ? (
|
|
6523
6635
|
<div className="py-6 text-center text-sm text-semantic-text-muted">
|
|
6524
6636
|
No results found
|
|
@@ -6583,7 +6695,19 @@ const MultiSelect = React.forwardRef(
|
|
|
6583
6695
|
<Check className="size-4 text-semantic-primary" />
|
|
6584
6696
|
)}
|
|
6585
6697
|
</span>
|
|
6586
|
-
<span
|
|
6698
|
+
<span
|
|
6699
|
+
title={
|
|
6700
|
+
truncateOptions && typeof option.label === "string"
|
|
6701
|
+
? option.label
|
|
6702
|
+
: undefined
|
|
6703
|
+
}
|
|
6704
|
+
className={cn(
|
|
6705
|
+
"min-w-0 flex-1 text-left",
|
|
6706
|
+
truncateOptions
|
|
6707
|
+
? "truncate"
|
|
6708
|
+
: "whitespace-normal break-words"
|
|
6709
|
+
)}
|
|
6710
|
+
>
|
|
6587
6711
|
{option.label}
|
|
6588
6712
|
</span>
|
|
6589
6713
|
</button>
|
|
@@ -6615,7 +6739,19 @@ const MultiSelect = React.forwardRef(
|
|
|
6615
6739
|
aria-hidden
|
|
6616
6740
|
tabIndex={-1}
|
|
6617
6741
|
/>
|
|
6618
|
-
<span
|
|
6742
|
+
<span
|
|
6743
|
+
title={
|
|
6744
|
+
typeof option.label === "string"
|
|
6745
|
+
? option.label
|
|
6746
|
+
: undefined
|
|
6747
|
+
}
|
|
6748
|
+
className={cn(
|
|
6749
|
+
"min-w-0 flex-1 text-left",
|
|
6750
|
+
truncateOptions
|
|
6751
|
+
? "truncate"
|
|
6752
|
+
: "whitespace-normal break-words"
|
|
6753
|
+
)}
|
|
6754
|
+
>
|
|
6619
6755
|
{option.label}
|
|
6620
6756
|
</span>
|
|
6621
6757
|
{secondaryLine ? (
|
|
@@ -6667,7 +6803,7 @@ const MultiSelect = React.forwardRef(
|
|
|
6667
6803
|
) : null}
|
|
6668
6804
|
</div>
|
|
6669
6805
|
</TooltipProvider>,
|
|
6670
|
-
|
|
6806
|
+
portalTarget
|
|
6671
6807
|
)}
|
|
6672
6808
|
</div>
|
|
6673
6809
|
|
package/package.json
CHANGED