myoperator-mcp 0.2.375 → 0.2.376
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 +95 -48
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6780,6 +6780,14 @@ const MENU_MIN_HEIGHT = 120;
|
|
|
6780
6780
|
*/
|
|
6781
6781
|
const SCROLL_END_THRESHOLD_PX = 48;
|
|
6782
6782
|
|
|
6783
|
+
/**
|
|
6784
|
+
* How long to wait after a page lands before re-measuring the list. React has
|
|
6785
|
+
* committed the new rows by the time the effect runs, but the browser has not
|
|
6786
|
+
* necessarily laid them out, so \`scrollHeight\` can still read the pre-growth
|
|
6787
|
+
* value. One frame is usually enough; 50ms is a cheap margin.
|
|
6788
|
+
*/
|
|
6789
|
+
const POST_FETCH_MEASURE_DELAY_MS = 50;
|
|
6790
|
+
|
|
6783
6791
|
/**
|
|
6784
6792
|
* MultiSelect trigger variants matching TextField styling
|
|
6785
6793
|
*/
|
|
@@ -7091,11 +7099,32 @@ const MultiSelect = React.forwardRef(
|
|
|
7091
7099
|
const listRef = React.useRef<HTMLDivElement | null>(null);
|
|
7092
7100
|
/**
|
|
7093
7101
|
* True once \`onScrollEnd\` has fired for the current visit to the bottom.
|
|
7094
|
-
* Cleared
|
|
7095
|
-
*
|
|
7096
|
-
* the finger lifts \u2014
|
|
7102
|
+
* Cleared when the user scrolls back out of the threshold zone, or when a
|
|
7103
|
+
* landed page pushes the bottom back out of reach, so trackpad inertia \u2014
|
|
7104
|
+
* which keeps firing \`scroll\` for hundreds of ms after the finger lifts \u2014
|
|
7105
|
+
* cannot re-trigger the callback.
|
|
7097
7106
|
*/
|
|
7098
7107
|
const isLatchedRef = React.useRef(false);
|
|
7108
|
+
/** Previous \`loadingMore\`, so the re-measure knows a page just landed. */
|
|
7109
|
+
const wasLoadingMoreRef = React.useRef(false);
|
|
7110
|
+
/** Pending \`requestAnimationFrame\` id for the coalesced scroll handler. */
|
|
7111
|
+
const scrollFrameRef = React.useRef<number | null>(null);
|
|
7112
|
+
/**
|
|
7113
|
+
* \`onScrollEnd\` read from a timer rather than from the closure, so an
|
|
7114
|
+
* inline arrow from the consumer cannot restart the post-fetch timer on
|
|
7115
|
+
* every render.
|
|
7116
|
+
*/
|
|
7117
|
+
const onScrollEndRef = React.useRef(onScrollEnd);
|
|
7118
|
+
React.useEffect(() => {
|
|
7119
|
+
onScrollEndRef.current = onScrollEnd;
|
|
7120
|
+
}, [onScrollEnd]);
|
|
7121
|
+
|
|
7122
|
+
/** Distance in px from the current scroll position to the list bottom. */
|
|
7123
|
+
const distanceToBottom = () => {
|
|
7124
|
+
const node = listRef.current;
|
|
7125
|
+
if (!node) return null;
|
|
7126
|
+
return node.scrollHeight - node.scrollTop - node.clientHeight;
|
|
7127
|
+
};
|
|
7099
7128
|
|
|
7100
7129
|
/**
|
|
7101
7130
|
* Deliberately a plain function, not a \`useCallback\` \u2014 it must read the
|
|
@@ -7103,14 +7132,10 @@ const MultiSelect = React.forwardRef(
|
|
|
7103
7132
|
* handler would need a props ref (writing refs during render is banned).
|
|
7104
7133
|
*/
|
|
7105
7134
|
const maybeLoadMore = () => {
|
|
7106
|
-
const
|
|
7107
|
-
if (
|
|
7135
|
+
const distance = distanceToBottom();
|
|
7136
|
+
if (distance === null) return;
|
|
7108
7137
|
|
|
7109
|
-
|
|
7110
|
-
node.scrollHeight - node.scrollTop - node.clientHeight <
|
|
7111
|
-
SCROLL_END_THRESHOLD_PX;
|
|
7112
|
-
|
|
7113
|
-
if (!isNearBottom) {
|
|
7138
|
+
if (distance >= SCROLL_END_THRESHOLD_PX) {
|
|
7114
7139
|
// Only an explicit scroll away from the boundary re-arms the latch.
|
|
7115
7140
|
isLatchedRef.current = false;
|
|
7116
7141
|
return;
|
|
@@ -7122,25 +7147,71 @@ const MultiSelect = React.forwardRef(
|
|
|
7122
7147
|
onScrollEnd();
|
|
7123
7148
|
};
|
|
7124
7149
|
|
|
7150
|
+
/**
|
|
7151
|
+
* \`scroll\` fires far more often than the browser paints, so a fast flick
|
|
7152
|
+
* delivers a burst of events whose geometry is mid-flight. Coalescing to
|
|
7153
|
+
* one \`requestAnimationFrame\` per burst measures once per frame, after
|
|
7154
|
+
* layout has settled, and still sees the final resting position.
|
|
7155
|
+
*/
|
|
7156
|
+
const handleListScroll = () => {
|
|
7157
|
+
if (scrollFrameRef.current !== null) return;
|
|
7158
|
+
scrollFrameRef.current = requestAnimationFrame(() => {
|
|
7159
|
+
scrollFrameRef.current = null;
|
|
7160
|
+
maybeLoadMore();
|
|
7161
|
+
});
|
|
7162
|
+
};
|
|
7163
|
+
|
|
7125
7164
|
// A closed menu or a new search starts from a clean latch.
|
|
7126
7165
|
React.useEffect(() => {
|
|
7127
7166
|
isLatchedRef.current = false;
|
|
7128
7167
|
}, [isOpen, searchQuery]);
|
|
7129
7168
|
|
|
7169
|
+
React.useEffect(
|
|
7170
|
+
() => () => {
|
|
7171
|
+
if (scrollFrameRef.current !== null) {
|
|
7172
|
+
cancelAnimationFrame(scrollFrameRef.current);
|
|
7173
|
+
}
|
|
7174
|
+
},
|
|
7175
|
+
[]
|
|
7176
|
+
);
|
|
7177
|
+
|
|
7130
7178
|
/**
|
|
7131
|
-
*
|
|
7132
|
-
*
|
|
7133
|
-
*
|
|
7134
|
-
*
|
|
7179
|
+
* Re-measure once a page has landed. Content growth emits no \`scroll\`
|
|
7180
|
+
* event, so without this the latch set on the way down would never clear
|
|
7181
|
+
* and pagination would stall permanently after a fast flick to the bottom.
|
|
7182
|
+
* It also covers the case where the page does not make the list taller
|
|
7183
|
+
* than its max-height (few results, or a short viewport), where no further
|
|
7184
|
+
* \`scroll\` event would ever fire either.
|
|
7185
|
+
*
|
|
7186
|
+
* The latch is cleared only once the bottom is genuinely out of reach \u2014
|
|
7187
|
+
* while the user is still pinned at distance ~0, it stays armed and the
|
|
7188
|
+
* next page is chained explicitly, so inertia cannot fan out into
|
|
7189
|
+
* duplicate requests.
|
|
7135
7190
|
*/
|
|
7136
7191
|
React.useEffect(() => {
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
if (!
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7192
|
+
const pageJustLanded = wasLoadingMoreRef.current && !loadingMore;
|
|
7193
|
+
wasLoadingMoreRef.current = loadingMore;
|
|
7194
|
+
if (!isOpen || loadingMore) return;
|
|
7195
|
+
|
|
7196
|
+
const timeoutId = window.setTimeout(() => {
|
|
7197
|
+
const distance = distanceToBottom();
|
|
7198
|
+
if (distance === null) return;
|
|
7199
|
+
|
|
7200
|
+
if (distance >= SCROLL_END_THRESHOLD_PX) {
|
|
7201
|
+
isLatchedRef.current = false;
|
|
7202
|
+
return;
|
|
7203
|
+
}
|
|
7204
|
+
if (!hasMore || !onScrollEndRef.current) return;
|
|
7205
|
+
// Still latched and nothing landed: the scroll handler's request is
|
|
7206
|
+
// in flight (or the consumer never set \`loadingMore\`) \u2014 don't refire.
|
|
7207
|
+
if (isLatchedRef.current && !pageJustLanded) return;
|
|
7208
|
+
|
|
7209
|
+
isLatchedRef.current = true;
|
|
7210
|
+
onScrollEndRef.current();
|
|
7211
|
+
}, POST_FETCH_MEASURE_DELAY_MS);
|
|
7212
|
+
|
|
7213
|
+
return () => window.clearTimeout(timeoutId);
|
|
7214
|
+
}, [isOpen, portalTarget, hasMore, loadingMore, options.length]);
|
|
7144
7215
|
|
|
7145
7216
|
const flatOptions = React.useMemo(
|
|
7146
7217
|
() => flattenMultiSelectOptions(options),
|
|
@@ -7572,7 +7643,7 @@ const MultiSelect = React.forwardRef(
|
|
|
7572
7643
|
{/* Options */}
|
|
7573
7644
|
<div
|
|
7574
7645
|
ref={listRef}
|
|
7575
|
-
onScroll={
|
|
7646
|
+
onScroll={handleListScroll}
|
|
7576
7647
|
className="overflow-auto overscroll-contain p-1"
|
|
7577
7648
|
style={{
|
|
7578
7649
|
maxHeight:
|
|
@@ -9509,32 +9580,11 @@ const SearchFilter = React.forwardRef<HTMLDivElement, SearchFilterProps>(
|
|
|
9509
9580
|
[disabled]
|
|
9510
9581
|
);
|
|
9511
9582
|
|
|
9512
|
-
/**
|
|
9513
|
-
* The pending focus frame has to be cancellable. Opening the menu schedules
|
|
9514
|
-
* a frame that refocuses the input, and the input's \`onFocus\` reopens the
|
|
9515
|
-
* menu. If that frame lands AFTER an option has been picked, it reopens the
|
|
9516
|
-
* dropdown that \`selectOption\` just closed \u2014 so selecting an option looks
|
|
9517
|
-
* like it does nothing. Whether the frame lands before or after the click is
|
|
9518
|
-
* pure timing, which is why the bug comes and goes.
|
|
9519
|
-
*/
|
|
9520
|
-
const focusFrameRef = React.useRef<number | null>(null);
|
|
9521
|
-
|
|
9522
|
-
const cancelPendingFocus = React.useCallback(() => {
|
|
9523
|
-
if (focusFrameRef.current === null) return;
|
|
9524
|
-
window.cancelAnimationFrame(focusFrameRef.current);
|
|
9525
|
-
focusFrameRef.current = null;
|
|
9526
|
-
}, []);
|
|
9527
|
-
|
|
9528
9583
|
const focusSearchInput = React.useCallback(() => {
|
|
9529
|
-
|
|
9530
|
-
focusFrameRef.current = window.requestAnimationFrame(() => {
|
|
9531
|
-
focusFrameRef.current = null;
|
|
9584
|
+
window.requestAnimationFrame(() => {
|
|
9532
9585
|
searchInputRef.current?.focus();
|
|
9533
9586
|
});
|
|
9534
|
-
}, [
|
|
9535
|
-
|
|
9536
|
-
// Never let a queued frame fire into an unmounted component.
|
|
9537
|
-
React.useEffect(() => cancelPendingFocus, [cancelPendingFocus]);
|
|
9587
|
+
}, []);
|
|
9538
9588
|
|
|
9539
9589
|
const setRootRef = React.useCallback(
|
|
9540
9590
|
(node: HTMLDivElement | null) => {
|
|
@@ -9613,12 +9663,9 @@ const SearchFilter = React.forwardRef<HTMLDivElement, SearchFilterProps>(
|
|
|
9613
9663
|
onValueChange?.(option.value);
|
|
9614
9664
|
onOptionSelect?.(option);
|
|
9615
9665
|
onSearchChange?.(option.label);
|
|
9616
|
-
// Drop any queued refocus first, or it reopens what we are closing.
|
|
9617
|
-
cancelPendingFocus();
|
|
9618
9666
|
setOpen(false);
|
|
9619
9667
|
},
|
|
9620
9668
|
[
|
|
9621
|
-
cancelPendingFocus,
|
|
9622
9669
|
disabled,
|
|
9623
9670
|
onSearchChange,
|
|
9624
9671
|
onOptionSelect,
|
package/package.json
CHANGED