myoperator-mcp 0.2.294 → 0.2.296
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 +111 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5593,6 +5593,22 @@ export interface SelectFieldProps {
|
|
|
5593
5593
|
searchable?: boolean;
|
|
5594
5594
|
/** Search placeholder text */
|
|
5595
5595
|
searchPlaceholder?: string;
|
|
5596
|
+
/**
|
|
5597
|
+
* Controlled search value. When provided, internal search state is
|
|
5598
|
+
* ignored and the consumer owns the value \u2014 typically used to drive
|
|
5599
|
+
* server-side filtering against a paginated API. The client-side
|
|
5600
|
+
* \`option.label\` filter is also skipped, since the consumer is expected
|
|
5601
|
+
* to have already filtered the options upstream.
|
|
5602
|
+
*
|
|
5603
|
+
* Pair with \`onSearchChange\`. Leave both undefined for the default
|
|
5604
|
+
* uncontrolled, client-side filtering behavior.
|
|
5605
|
+
*/
|
|
5606
|
+
searchValue?: string;
|
|
5607
|
+
/**
|
|
5608
|
+
* Fires on every keystroke in the search input. Also fires with \`""\`
|
|
5609
|
+
* when the dropdown closes (so consumers can reset their query state).
|
|
5610
|
+
*/
|
|
5611
|
+
onSearchChange?: (value: string) => void;
|
|
5596
5612
|
/** Additional class for wrapper */
|
|
5597
5613
|
wrapperClassName?: string;
|
|
5598
5614
|
/** Additional class for trigger */
|
|
@@ -5691,6 +5707,8 @@ const SelectField = React.forwardRef(
|
|
|
5691
5707
|
options,
|
|
5692
5708
|
searchable,
|
|
5693
5709
|
searchPlaceholder = "Search...",
|
|
5710
|
+
searchValue,
|
|
5711
|
+
onSearchChange,
|
|
5694
5712
|
wrapperClassName,
|
|
5695
5713
|
triggerClassName,
|
|
5696
5714
|
labelClassName,
|
|
@@ -5702,8 +5720,11 @@ const SelectField = React.forwardRef(
|
|
|
5702
5720
|
}: SelectFieldProps,
|
|
5703
5721
|
ref: React.Ref<HTMLButtonElement>
|
|
5704
5722
|
) => {
|
|
5705
|
-
// Internal state for
|
|
5723
|
+
// Internal state for uncontrolled mode. When \`searchValue\` is provided,
|
|
5724
|
+
// the consumer owns the value and this state is unused.
|
|
5706
5725
|
const [searchQuery, setSearchQuery] = React.useState("");
|
|
5726
|
+
const isSearchControlled = searchValue !== undefined;
|
|
5727
|
+
const effectiveSearchQuery = isSearchControlled ? searchValue : searchQuery;
|
|
5707
5728
|
|
|
5708
5729
|
// Combined value change handler that also fires onSelect with full option object.
|
|
5709
5730
|
// When interceptValue returns false, onValueChange is skipped (only onSelect fires).
|
|
@@ -5756,11 +5777,16 @@ const SelectField = React.forwardRef(
|
|
|
5756
5777
|
const ungrouped: SelectOption[] = [];
|
|
5757
5778
|
|
|
5758
5779
|
options.forEach((option) => {
|
|
5759
|
-
//
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5780
|
+
// Client-side filter only in uncontrolled mode. In controlled mode
|
|
5781
|
+
// the consumer has already filtered server-side; re-filtering here
|
|
5782
|
+
// would mask items their API returned.
|
|
5783
|
+
if (
|
|
5784
|
+
searchable &&
|
|
5785
|
+
!isSearchControlled &&
|
|
5786
|
+
searchQuery &&
|
|
5787
|
+
!option.label.toLowerCase().includes(searchQuery.toLowerCase())
|
|
5788
|
+
) {
|
|
5789
|
+
return;
|
|
5764
5790
|
}
|
|
5765
5791
|
|
|
5766
5792
|
if (option.group) {
|
|
@@ -5774,7 +5800,7 @@ const SelectField = React.forwardRef(
|
|
|
5774
5800
|
});
|
|
5775
5801
|
|
|
5776
5802
|
return { groups, ungrouped };
|
|
5777
|
-
}, [options, searchable, searchQuery]);
|
|
5803
|
+
}, [options, searchable, isSearchControlled, searchQuery]);
|
|
5778
5804
|
|
|
5779
5805
|
const hasGroups = Object.keys(groupedOptions.groups).length > 0;
|
|
5780
5806
|
|
|
@@ -5789,13 +5815,23 @@ const SelectField = React.forwardRef(
|
|
|
5789
5815
|
|
|
5790
5816
|
// Handle search input change
|
|
5791
5817
|
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
5792
|
-
|
|
5818
|
+
const next = e.target.value;
|
|
5819
|
+
if (!isSearchControlled) {
|
|
5820
|
+
setSearchQuery(next);
|
|
5821
|
+
}
|
|
5822
|
+
onSearchChange?.(next);
|
|
5793
5823
|
};
|
|
5794
5824
|
|
|
5795
|
-
// Reset search when dropdown closes
|
|
5825
|
+
// Reset search when dropdown closes. In controlled mode we notify the
|
|
5826
|
+
// consumer so they can reset their state; in uncontrolled mode we clear
|
|
5827
|
+
// our own state directly.
|
|
5796
5828
|
const handleOpenChange = (open: boolean) => {
|
|
5797
5829
|
if (!open) {
|
|
5798
|
-
|
|
5830
|
+
if (isSearchControlled) {
|
|
5831
|
+
onSearchChange?.("");
|
|
5832
|
+
} else {
|
|
5833
|
+
setSearchQuery("");
|
|
5834
|
+
}
|
|
5799
5835
|
}
|
|
5800
5836
|
};
|
|
5801
5837
|
|
|
@@ -5849,7 +5885,7 @@ const SelectField = React.forwardRef(
|
|
|
5849
5885
|
<input
|
|
5850
5886
|
type="text"
|
|
5851
5887
|
placeholder={searchPlaceholder}
|
|
5852
|
-
value={
|
|
5888
|
+
value={effectiveSearchQuery}
|
|
5853
5889
|
onChange={handleSearchChange}
|
|
5854
5890
|
className="w-full h-8 text-sm bg-transparent placeholder:text-semantic-text-muted focus:outline-none"
|
|
5855
5891
|
// Prevent closing dropdown when clicking input
|
|
@@ -5893,7 +5929,7 @@ const SelectField = React.forwardRef(
|
|
|
5893
5929
|
|
|
5894
5930
|
{/* No results message */}
|
|
5895
5931
|
{searchable &&
|
|
5896
|
-
|
|
5932
|
+
effectiveSearchQuery &&
|
|
5897
5933
|
groupedOptions.ungrouped.length === 0 &&
|
|
5898
5934
|
Object.keys(groupedOptions.groups).length === 0 && (
|
|
5899
5935
|
<div className="py-6 text-center text-sm text-semantic-text-muted">
|
|
@@ -6085,12 +6121,23 @@ export type SelectContentProps = React.ComponentPropsWithoutRef<
|
|
|
6085
6121
|
typeof SelectPrimitive.Content
|
|
6086
6122
|
> & {
|
|
6087
6123
|
/**
|
|
6088
|
-
* Fires on the scrollable list viewport when
|
|
6089
|
-
*
|
|
6124
|
+
* Fires on the scrollable list viewport when the user reaches the bottom.
|
|
6125
|
+
* React 18 has no synthetic event for \`scrollend\`, so the listener is
|
|
6126
|
+
* attached imperatively to the viewport DOM node. On browsers that
|
|
6127
|
+
* support the native \`scrollend\` event (Chrome/Edge 114+, Firefox 109+,
|
|
6128
|
+
* Safari 17.4+) we use it directly; on older Safari we fall back to a
|
|
6129
|
+
* debounced \`scroll\` listener with a 24px bottom threshold.
|
|
6130
|
+
*
|
|
6131
|
+
* The handler receives a native Event typed as React.UIEvent for
|
|
6132
|
+
* back-compat \u2014 \`event.currentTarget\` is the viewport div, so consumers
|
|
6133
|
+
* can still read scrollTop/scrollHeight/clientHeight from it.
|
|
6090
6134
|
*/
|
|
6091
6135
|
onViewportScrollEnd?: (event: React.UIEvent<HTMLDivElement>) => void;
|
|
6092
6136
|
};
|
|
6093
6137
|
|
|
6138
|
+
const BOTTOM_THRESHOLD_PX = 24;
|
|
6139
|
+
const SCROLL_DEBOUNCE_MS = 150;
|
|
6140
|
+
|
|
6094
6141
|
const SelectContent = React.forwardRef(
|
|
6095
6142
|
(
|
|
6096
6143
|
{
|
|
@@ -6104,6 +6151,55 @@ const SelectContent = React.forwardRef(
|
|
|
6104
6151
|
) => {
|
|
6105
6152
|
useUnlockBodyScroll();
|
|
6106
6153
|
|
|
6154
|
+
// Use a state-backed ref so the effect re-runs when the viewport mounts.
|
|
6155
|
+
// The viewport lives inside Radix's Portal and only attaches when the
|
|
6156
|
+
// Select opens \u2014 a plain useRef wouldn't trigger the effect.
|
|
6157
|
+
const [viewport, setViewport] = React.useState<HTMLDivElement | null>(null);
|
|
6158
|
+
|
|
6159
|
+
React.useEffect(() => {
|
|
6160
|
+
if (!viewport || !onViewportScrollEnd) return;
|
|
6161
|
+
|
|
6162
|
+
|
|
6163
|
+
const isAtBottom = () => {
|
|
6164
|
+
const { scrollTop, scrollHeight, clientHeight } = viewport;
|
|
6165
|
+
return (
|
|
6166
|
+
scrollTop + clientHeight >= scrollHeight - BOTTOM_THRESHOLD_PX
|
|
6167
|
+
);
|
|
6168
|
+
};
|
|
6169
|
+
|
|
6170
|
+
const supportsScrollEnd =
|
|
6171
|
+
typeof window !== "undefined" && "onscrollend" in window;
|
|
6172
|
+
|
|
6173
|
+
if (supportsScrollEnd) {
|
|
6174
|
+
const handler = (event: Event) => {
|
|
6175
|
+
if (isAtBottom()) {
|
|
6176
|
+
onViewportScrollEnd(
|
|
6177
|
+
event as unknown as React.UIEvent<HTMLDivElement>
|
|
6178
|
+
);
|
|
6179
|
+
}
|
|
6180
|
+
};
|
|
6181
|
+
viewport.addEventListener("scrollend", handler);
|
|
6182
|
+
return () => viewport.removeEventListener("scrollend", handler);
|
|
6183
|
+
}
|
|
6184
|
+
|
|
6185
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
6186
|
+
const handler = (event: Event) => {
|
|
6187
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
6188
|
+
timeoutId = setTimeout(() => {
|
|
6189
|
+
if (isAtBottom()) {
|
|
6190
|
+
onViewportScrollEnd(
|
|
6191
|
+
event as unknown as React.UIEvent<HTMLDivElement>
|
|
6192
|
+
);
|
|
6193
|
+
}
|
|
6194
|
+
}, SCROLL_DEBOUNCE_MS);
|
|
6195
|
+
};
|
|
6196
|
+
viewport.addEventListener("scroll", handler, { passive: true });
|
|
6197
|
+
return () => {
|
|
6198
|
+
viewport.removeEventListener("scroll", handler);
|
|
6199
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
6200
|
+
};
|
|
6201
|
+
}, [viewport, onViewportScrollEnd]);
|
|
6202
|
+
|
|
6107
6203
|
return (
|
|
6108
6204
|
<SelectPrimitive.Portal>
|
|
6109
6205
|
<SelectPrimitive.Content
|
|
@@ -6124,17 +6220,13 @@ const SelectContent = React.forwardRef(
|
|
|
6124
6220
|
>
|
|
6125
6221
|
<SelectScrollUpButton />
|
|
6126
6222
|
<SelectPrimitive.Viewport
|
|
6223
|
+
ref={setViewport}
|
|
6127
6224
|
data-select-viewport=""
|
|
6128
6225
|
className={cn(
|
|
6129
6226
|
"p-1",
|
|
6130
6227
|
position === "popper" &&
|
|
6131
6228
|
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
6132
6229
|
)}
|
|
6133
|
-
{...((onViewportScrollEnd
|
|
6134
|
-
? { onScrollEnd: onViewportScrollEnd }
|
|
6135
|
-
: {}) as React.ComponentPropsWithoutRef<
|
|
6136
|
-
typeof SelectPrimitive.Viewport
|
|
6137
|
-
>)}
|
|
6138
6230
|
>
|
|
6139
6231
|
{children}
|
|
6140
6232
|
</SelectPrimitive.Viewport>
|
package/package.json
CHANGED