myoperator-mcp 0.2.290 → 0.2.291

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.
Files changed (2) hide show
  1. package/dist/index.js +88 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5465,6 +5465,29 @@ export interface SelectFieldProps {
5465
5465
  id?: string;
5466
5466
  /** Name attribute for form submission */
5467
5467
  name?: string;
5468
+ /**
5469
+ * Fires when the user scrolls to the bottom of the open dropdown.
5470
+ * Use this to load the next page from the server. The callback is
5471
+ * forwarded to SelectContent's \`onViewportScrollEnd\` (debounced by
5472
+ * the native \`scrollend\` event), so it won't fire while a scroll is
5473
+ * still in progress.
5474
+ *
5475
+ * No virtualization is applied \u2014 all loaded items render to the DOM.
5476
+ * For >2k items consumers may notice some lag; virtualization is a
5477
+ * separate ticket if it becomes a real-world problem.
5478
+ */
5479
+ onScrollEnd?: () => void;
5480
+ /**
5481
+ * When true, renders a small "Loading more\u2026" row at the bottom of
5482
+ * the options list. Set this to true while your API call is in flight
5483
+ * so the user knows more items are on the way.
5484
+ */
5485
+ loadingMore?: boolean;
5486
+ /**
5487
+ * When false, prevents \`onScrollEnd\` from firing further and renders
5488
+ * an "End of list" footer row. Default true (keep firing).
5489
+ */
5490
+ hasMore?: boolean;
5468
5491
  }
5469
5492
 
5470
5493
  /**
@@ -5483,6 +5506,34 @@ export interface SelectFieldProps {
5483
5506
  * required
5484
5507
  * />
5485
5508
  * \`\`\`
5509
+ *
5510
+ * @example Lazy-load on scroll
5511
+ * \`\`\`tsx
5512
+ * const [items, setItems] = useState<SelectOption[]>([]);
5513
+ * const [loadingMore, setLoadingMore] = useState(false);
5514
+ * const [hasMore, setHasMore] = useState(true);
5515
+ * const page = useRef(0);
5516
+ *
5517
+ * const loadNext = async () => {
5518
+ * if (loadingMore || !hasMore) return;
5519
+ * setLoadingMore(true);
5520
+ * const { results, isLast } = await api.fetchTemplates(page.current);
5521
+ * setItems(prev => [...prev, ...results]);
5522
+ * setHasMore(!isLast);
5523
+ * page.current += 1;
5524
+ * setLoadingMore(false);
5525
+ * };
5526
+ *
5527
+ * useEffect(() => { loadNext(); }, []);
5528
+ *
5529
+ * <SelectField
5530
+ * label="Template"
5531
+ * options={items}
5532
+ * onScrollEnd={loadNext}
5533
+ * loadingMore={loadingMore}
5534
+ * hasMore={hasMore}
5535
+ * />
5536
+ * \`\`\`
5486
5537
  */
5487
5538
  const SelectField = React.forwardRef(
5488
5539
  (
@@ -5507,6 +5558,9 @@ const SelectField = React.forwardRef(
5507
5558
  labelClassName,
5508
5559
  id,
5509
5560
  name,
5561
+ onScrollEnd,
5562
+ loadingMore,
5563
+ hasMore,
5510
5564
  }: SelectFieldProps,
5511
5565
  ref: React.Ref<HTMLButtonElement>
5512
5566
  ) => {
@@ -5586,6 +5640,15 @@ const SelectField = React.forwardRef(
5586
5640
 
5587
5641
  const hasGroups = Object.keys(groupedOptions.groups).length > 0;
5588
5642
 
5643
+ // Count rendered options for the "End of list" footer (only show when at least one is visible).
5644
+ const totalRendered =
5645
+ groupedOptions.ungrouped.length +
5646
+ Object.values(groupedOptions.groups).reduce(
5647
+ (sum, items) => sum + items.length,
5648
+ 0
5649
+ );
5650
+ const showEndOfList = hasMore === false && totalRendered > 0 && !loadingMore;
5651
+
5589
5652
  // Handle search input change
5590
5653
  const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
5591
5654
  setSearchQuery(e.target.value);
@@ -5638,7 +5701,9 @@ const SelectField = React.forwardRef(
5638
5701
  <Loader2 className="absolute right-8 size-4 animate-spin text-semantic-text-muted" />
5639
5702
  )}
5640
5703
  </SelectTrigger>
5641
- <SelectContent>
5704
+ <SelectContent
5705
+ onViewportScrollEnd={hasMore !== false ? onScrollEnd : undefined}
5706
+ >
5642
5707
  {/* Search input */}
5643
5708
  {searchable && (
5644
5709
  <div className="flex items-center gap-2 px-3 pb-1.5 border-b border-solid border-semantic-border-layout">
@@ -5697,6 +5762,28 @@ const SelectField = React.forwardRef(
5697
5762
  No results found
5698
5763
  </div>
5699
5764
  )}
5765
+
5766
+ {/* Loading-more row (lazy-load) */}
5767
+ {loadingMore && (
5768
+ <div
5769
+ role="status"
5770
+ aria-live="polite"
5771
+ className="flex items-center justify-center gap-2 py-2 text-sm text-semantic-text-muted"
5772
+ >
5773
+ <Loader2 className="size-3.5 animate-spin" aria-hidden="true" />
5774
+ <span>Loading more\u2026</span>
5775
+ </div>
5776
+ )}
5777
+
5778
+ {/* End-of-list footer (lazy-load) */}
5779
+ {showEndOfList && (
5780
+ <div
5781
+ role="status"
5782
+ className="py-2 text-center text-xs text-semantic-text-muted"
5783
+ >
5784
+ End of list
5785
+ </div>
5786
+ )}
5700
5787
  </SelectContent>
5701
5788
  </Select>
5702
5789
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-mcp",
3
- "version": "0.2.290",
3
+ "version": "0.2.291",
4
4
  "description": "MCP server for myOperator UI components - enables AI assistants to access component metadata, examples, and design tokens",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",