myoperator-mcp 0.2.373 → 0.2.375

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 +1023 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2562,6 +2562,777 @@ const DateDivider = React.forwardRef(
2562
2562
  DateDivider.displayName = "DateDivider";
2563
2563
 
2564
2564
  export { DateDivider };
2565
+ `,
2566
+ "date-range-picker": `import * as React from "react";
2567
+ import { createPortal } from "react-dom";
2568
+ import {
2569
+ autoUpdate,
2570
+ flip,
2571
+ offset,
2572
+ shift,
2573
+ size as floatingSize,
2574
+ useFloating,
2575
+ type Placement,
2576
+ type Strategy,
2577
+ } from "@floating-ui/react-dom";
2578
+ import { cva, type VariantProps } from "class-variance-authority";
2579
+ import { ChevronDown, ChevronLeft, ChevronRight } from "lucide-react";
2580
+
2581
+ import { cn } from "@/lib/utils";
2582
+ import {
2583
+ DropdownMenu,
2584
+ DropdownMenuContent,
2585
+ DropdownMenuItem,
2586
+ DropdownMenuTrigger,
2587
+ } from "./dropdown-menu";
2588
+
2589
+ const DEFAULT_PLACEHOLDER = "Date Range";
2590
+ const POPOVER_MARGIN = 8;
2591
+ const POPOVER_GAP = 4;
2592
+ const MAX_POPOVER_HEIGHT = 420;
2593
+ const POPOVER_SCROLL_HEIGHT_VAR = "--date-range-picker-scroll-height";
2594
+ const CALENDAR_PLACEMENT: Placement = "bottom-start";
2595
+ // Above the calendar popover's own z-index (10050) so the month/year
2596
+ // dropdowns render on top of it rather than behind.
2597
+ const CALENDAR_DROPDOWN_Z_INDEX = "z-[10060]";
2598
+ const YEAR_OPTIONS_SPAN = 10;
2599
+
2600
+ const weekDays = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"];
2601
+ const MONTH_SHORT_NAMES = [
2602
+ "Jan",
2603
+ "Feb",
2604
+ "Mar",
2605
+ "Apr",
2606
+ "May",
2607
+ "Jun",
2608
+ "Jul",
2609
+ "Aug",
2610
+ "Sep",
2611
+ "Oct",
2612
+ "Nov",
2613
+ "Dec",
2614
+ ];
2615
+ const MONTH_LONG_NAMES = [
2616
+ "January",
2617
+ "February",
2618
+ "March",
2619
+ "April",
2620
+ "May",
2621
+ "June",
2622
+ "July",
2623
+ "August",
2624
+ "September",
2625
+ "October",
2626
+ "November",
2627
+ "December",
2628
+ ];
2629
+
2630
+ const dateRangePickerTriggerVariants = cva(
2631
+ "flex h-10 w-full items-center gap-2 rounded-lg border border-solid border-semantic-border-input bg-semantic-bg-primary px-4 py-2.5 text-left text-sm text-semantic-text-primary outline-none transition-colors hover:border-semantic-border-input-focus/50 disabled:cursor-not-allowed disabled:opacity-50",
2632
+ {
2633
+ variants: {
2634
+ state: {
2635
+ default: "",
2636
+ error:
2637
+ "border-semantic-error-primary hover:border-semantic-error-primary",
2638
+ },
2639
+ },
2640
+ defaultVariants: {
2641
+ state: "default",
2642
+ },
2643
+ }
2644
+ );
2645
+
2646
+ export interface DateRangeValue {
2647
+ start?: Date;
2648
+ end?: Date;
2649
+ }
2650
+
2651
+ export interface DateRangePreset {
2652
+ label: string;
2653
+ getRange: () => DateRangeValue;
2654
+ }
2655
+
2656
+ export interface DateRangePickerProps
2657
+ extends
2658
+ Omit<React.HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange">,
2659
+ Pick<VariantProps<typeof dateRangePickerTriggerVariants>, "state"> {
2660
+ value?: DateRangeValue;
2661
+ defaultValue?: DateRangeValue;
2662
+ onValueChange?: (value: DateRangeValue) => void;
2663
+ /** Trigger placeholder text when no range is selected. Defaults to "Date Range". */
2664
+ placeholder?: string;
2665
+ /**
2666
+ * Presets shown in the left column. Defaults to
2667
+ * Today/Yesterday/Last 7 days/Last 30 days/This month/Last month.
2668
+ * Pass an empty array to hide the presets column entirely.
2669
+ */
2670
+ presets?: DateRangePreset[];
2671
+ minDate?: Date;
2672
+ maxDate?: Date;
2673
+ disablePastDates?: boolean;
2674
+ disabled?: boolean;
2675
+ open?: boolean;
2676
+ defaultOpen?: boolean;
2677
+ onOpenChange?: (open: boolean) => void;
2678
+ portalContainer?: HTMLElement | null;
2679
+ /** Custom display formatter for the trigger's filled-state text. Defaults to "D MMM YYYY - D MMM YYYY". */
2680
+ formatRange?: (value: DateRangeValue) => string;
2681
+ /** Additional className merged onto the trigger button (overrides base trigger styling) */
2682
+ triggerClassName?: string;
2683
+ /** Additional className merged onto the trigger's label span \u2014 e.g. \`"hidden sm:inline"\` to collapse to an icon-only button below a breakpoint */
2684
+ triggerLabelClassName?: string;
2685
+ }
2686
+
2687
+ function normalizeValue(value?: DateRangeValue): DateRangeValue {
2688
+ return { start: value?.start, end: value?.end };
2689
+ }
2690
+
2691
+ function isSameDay(a?: Date, b?: Date) {
2692
+ if (!a || !b) return false;
2693
+
2694
+ return (
2695
+ a.getFullYear() === b.getFullYear() &&
2696
+ a.getMonth() === b.getMonth() &&
2697
+ a.getDate() === b.getDate()
2698
+ );
2699
+ }
2700
+
2701
+ function startOfDay(date: Date) {
2702
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
2703
+ }
2704
+
2705
+ function isBeforeDay(date: Date, minDate: Date) {
2706
+ return startOfDay(date).getTime() < startOfDay(minDate).getTime();
2707
+ }
2708
+
2709
+ function isAfterDay(date: Date, maxDate: Date) {
2710
+ return startOfDay(date).getTime() > startOfDay(maxDate).getTime();
2711
+ }
2712
+
2713
+ function startOfMonth(date: Date) {
2714
+ return new Date(date.getFullYear(), date.getMonth(), 1);
2715
+ }
2716
+
2717
+ function addMonths(date: Date, months: number) {
2718
+ return new Date(date.getFullYear(), date.getMonth() + months, 1);
2719
+ }
2720
+
2721
+ function addDays(date: Date, days: number) {
2722
+ const result = new Date(date);
2723
+ result.setDate(result.getDate() + days);
2724
+ return result;
2725
+ }
2726
+
2727
+ function getCalendarDays(month: Date) {
2728
+ const firstDay = startOfMonth(month);
2729
+ const gridStart = new Date(firstDay);
2730
+ gridStart.setDate(firstDay.getDate() - firstDay.getDay());
2731
+
2732
+ return Array.from({ length: 42 }, (_, index) => {
2733
+ const day = new Date(gridStart);
2734
+ day.setDate(gridStart.getDate() + index);
2735
+ return day;
2736
+ });
2737
+ }
2738
+
2739
+ function isPointerInsideElement(
2740
+ event: MouseEvent,
2741
+ element: HTMLElement | null
2742
+ ) {
2743
+ if (!element) return false;
2744
+
2745
+ const target = event.target;
2746
+ if (target instanceof Node && element.contains(target)) return true;
2747
+
2748
+ if (typeof event.composedPath === "function") {
2749
+ return event
2750
+ .composedPath()
2751
+ .some((node) => node instanceof Node && element.contains(node));
2752
+ }
2753
+
2754
+ return false;
2755
+ }
2756
+
2757
+ function formatDateShort(date: Date) {
2758
+ return \`\${date.getDate()} \${MONTH_SHORT_NAMES[date.getMonth()]} \${date.getFullYear()}\`;
2759
+ }
2760
+
2761
+ function defaultFormatRange(value: DateRangeValue) {
2762
+ if (value.start && value.end) {
2763
+ return \`\${formatDateShort(value.start)} - \${formatDateShort(value.end)}\`;
2764
+ }
2765
+ if (value.start) return formatDateShort(value.start);
2766
+ if (value.end) return formatDateShort(value.end);
2767
+
2768
+ return "";
2769
+ }
2770
+
2771
+ /**
2772
+ * Default presets shown in the DateRangePicker's left column. Exported so
2773
+ * consumers can reference, filter, or extend the list.
2774
+ */
2775
+ export const DEFAULT_DATE_RANGE_PRESETS: DateRangePreset[] = [
2776
+ {
2777
+ label: "Today",
2778
+ getRange: () => {
2779
+ const today = startOfDay(new Date());
2780
+ return { start: today, end: today };
2781
+ },
2782
+ },
2783
+ {
2784
+ label: "Yesterday",
2785
+ getRange: () => {
2786
+ const yesterday = startOfDay(addDays(new Date(), -1));
2787
+ return { start: yesterday, end: yesterday };
2788
+ },
2789
+ },
2790
+ {
2791
+ label: "Last 7 days",
2792
+ getRange: () => {
2793
+ const today = startOfDay(new Date());
2794
+ return { start: addDays(today, -6), end: today };
2795
+ },
2796
+ },
2797
+ {
2798
+ label: "Last 30 days",
2799
+ getRange: () => {
2800
+ const today = startOfDay(new Date());
2801
+ return { start: addDays(today, -29), end: today };
2802
+ },
2803
+ },
2804
+ {
2805
+ label: "This month",
2806
+ getRange: () => {
2807
+ const today = startOfDay(new Date());
2808
+ return { start: startOfMonth(today), end: today };
2809
+ },
2810
+ },
2811
+ {
2812
+ label: "Last month",
2813
+ getRange: () => {
2814
+ const start = addMonths(startOfMonth(new Date()), -1);
2815
+ const end = new Date(start.getFullYear(), start.getMonth() + 1, 0);
2816
+ return { start, end };
2817
+ },
2818
+ },
2819
+ ];
2820
+
2821
+ function FigmaCalendarIcon({ className }: { className?: string }) {
2822
+ return (
2823
+ <svg
2824
+ viewBox="0 0 18 18"
2825
+ fill="none"
2826
+ xmlns="http://www.w3.org/2000/svg"
2827
+ className={className}
2828
+ aria-hidden="true"
2829
+ >
2830
+ <path
2831
+ d="M6 1.5V4.5M12 1.5V4.5M2.25 7.5H15.75M3.75 3H14.25C15.0784 3 15.75 3.67157 15.75 4.5V15C15.75 15.8284 15.0784 16.5 14.25 16.5H3.75C2.92157 16.5 2.25 15.8284 2.25 15V4.5C2.25 3.67157 2.92157 3 3.75 3Z"
2832
+ stroke="currentColor"
2833
+ strokeWidth="0.8"
2834
+ strokeLinecap="round"
2835
+ strokeLinejoin="round"
2836
+ />
2837
+ </svg>
2838
+ );
2839
+ }
2840
+
2841
+ const DateRangePicker = React.forwardRef<HTMLDivElement, DateRangePickerProps>(
2842
+ (
2843
+ {
2844
+ className,
2845
+ state,
2846
+ value,
2847
+ defaultValue,
2848
+ onValueChange,
2849
+ placeholder = DEFAULT_PLACEHOLDER,
2850
+ presets = DEFAULT_DATE_RANGE_PRESETS,
2851
+ minDate,
2852
+ maxDate,
2853
+ disablePastDates = false,
2854
+ disabled = false,
2855
+ open: controlledOpen,
2856
+ defaultOpen = false,
2857
+ onOpenChange,
2858
+ portalContainer,
2859
+ formatRange,
2860
+ triggerClassName,
2861
+ triggerLabelClassName,
2862
+ id,
2863
+ ...props
2864
+ },
2865
+ ref
2866
+ ) => {
2867
+ const generatedId = React.useId();
2868
+ const triggerId = id ?? generatedId;
2869
+ const isValueControlled = value !== undefined;
2870
+ const isOpenControlled = controlledOpen !== undefined;
2871
+ const [internalValue, setInternalValue] = React.useState(() =>
2872
+ normalizeValue(defaultValue)
2873
+ );
2874
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
2875
+ const currentValue = normalizeValue(
2876
+ isValueControlled ? value : internalValue
2877
+ );
2878
+ const open = isOpenControlled ? controlledOpen : internalOpen;
2879
+
2880
+ // Draft state \u2014 the calendar operates on a working copy of the range so
2881
+ // that Cancel can discard in-progress selections and Apply commits them.
2882
+ const [draftValue, setDraftValue] = React.useState<DateRangeValue>(
2883
+ () => currentValue
2884
+ );
2885
+ // Tracks whether the next day click should complete the range (true) or
2886
+ // start a new one (false). Drives the "click 1 = start, click 2 = end,
2887
+ // click 3 = restart" cycle.
2888
+ const [pendingEnd, setPendingEnd] = React.useState(false);
2889
+ const [visibleMonth, setVisibleMonth] = React.useState(() =>
2890
+ startOfMonth(currentValue.start ?? currentValue.end ?? new Date())
2891
+ );
2892
+
2893
+ const rootRef = React.useRef<HTMLDivElement | null>(null);
2894
+ const triggerRef = React.useRef<HTMLButtonElement | null>(null);
2895
+ const popoverRef = React.useRef<HTMLDivElement | null>(null);
2896
+ const usesContainerPortal = portalContainer !== undefined;
2897
+ const floatingStrategy: Strategy = usesContainerPortal
2898
+ ? "absolute"
2899
+ : "fixed";
2900
+ const floatingMiddleware = React.useMemo(
2901
+ () => [
2902
+ offset(POPOVER_GAP),
2903
+ flip({ padding: POPOVER_MARGIN }),
2904
+ shift({ padding: POPOVER_MARGIN }),
2905
+ floatingSize({
2906
+ padding: POPOVER_MARGIN,
2907
+ apply({ availableHeight, elements }) {
2908
+ const maxHeight = Math.max(
2909
+ 1,
2910
+ Math.min(MAX_POPOVER_HEIGHT, availableHeight)
2911
+ );
2912
+ elements.floating.style.setProperty(
2913
+ POPOVER_SCROLL_HEIGHT_VAR,
2914
+ \`\${maxHeight}px\`
2915
+ );
2916
+ },
2917
+ }),
2918
+ ],
2919
+ []
2920
+ );
2921
+ const { refs, floatingStyles, isPositioned } =
2922
+ useFloating<HTMLButtonElement>({
2923
+ open,
2924
+ placement: CALENDAR_PLACEMENT,
2925
+ strategy: floatingStrategy,
2926
+ transform: false,
2927
+ middleware: floatingMiddleware,
2928
+ whileElementsMounted: (reference, floating, update) =>
2929
+ autoUpdate(reference, floating, update, { animationFrame: true }),
2930
+ });
2931
+ const calendarDays = React.useMemo(
2932
+ () => getCalendarDays(visibleMonth),
2933
+ [visibleMonth]
2934
+ );
2935
+ const displayValue = (formatRange ?? defaultFormatRange)(currentValue);
2936
+ const effectiveMinDate = React.useMemo(() => {
2937
+ if (!disablePastDates) return minDate;
2938
+
2939
+ const today = startOfDay(new Date());
2940
+ if (!minDate) return today;
2941
+
2942
+ return isBeforeDay(minDate, today) ? today : minDate;
2943
+ }, [disablePastDates, minDate]);
2944
+ const portalMount =
2945
+ typeof document !== "undefined"
2946
+ ? usesContainerPortal
2947
+ ? portalContainer
2948
+ : document.body
2949
+ : null;
2950
+
2951
+ const setOpen = React.useCallback(
2952
+ (nextOpen: boolean) => {
2953
+ if (!isOpenControlled) {
2954
+ setInternalOpen(nextOpen);
2955
+ }
2956
+
2957
+ onOpenChange?.(nextOpen);
2958
+ },
2959
+ [isOpenControlled, onOpenChange]
2960
+ );
2961
+
2962
+ const setTriggerRef = React.useCallback(
2963
+ (node: HTMLButtonElement | null) => {
2964
+ triggerRef.current = node;
2965
+ refs.setReference(node);
2966
+ },
2967
+ [refs]
2968
+ );
2969
+
2970
+ const setPopoverRef = React.useCallback(
2971
+ (node: HTMLDivElement | null) => {
2972
+ popoverRef.current = node;
2973
+ refs.setFloating(node);
2974
+ },
2975
+ [refs]
2976
+ );
2977
+
2978
+ // Reset the draft to the last committed value (and jump the calendar to
2979
+ // that month) each time the popover opens, so a Cancel from a previous
2980
+ // session never leaks into the next one.
2981
+ const currentValueRef = React.useRef(currentValue);
2982
+ React.useEffect(() => {
2983
+ currentValueRef.current = currentValue;
2984
+ });
2985
+
2986
+ React.useEffect(() => {
2987
+ if (!open) return;
2988
+
2989
+ const latestValue = currentValueRef.current;
2990
+ setDraftValue(latestValue);
2991
+ setPendingEnd(false);
2992
+ setVisibleMonth(
2993
+ startOfMonth(latestValue.start ?? latestValue.end ?? new Date())
2994
+ );
2995
+ }, [open]);
2996
+
2997
+ React.useEffect(() => {
2998
+ if (!open) return;
2999
+
3000
+ const handlePointerDown = (event: MouseEvent) => {
3001
+ if (
3002
+ !isPointerInsideElement(event, rootRef.current) &&
3003
+ !isPointerInsideElement(event, popoverRef.current)
3004
+ ) {
3005
+ setOpen(false);
3006
+ }
3007
+ };
3008
+
3009
+ const handleKeyDown = (event: KeyboardEvent) => {
3010
+ if (event.key === "Escape") {
3011
+ setOpen(false);
3012
+ }
3013
+ };
3014
+
3015
+ document.addEventListener("mousedown", handlePointerDown);
3016
+ document.addEventListener("keydown", handleKeyDown);
3017
+
3018
+ return () => {
3019
+ document.removeEventListener("mousedown", handlePointerDown);
3020
+ document.removeEventListener("keydown", handleKeyDown);
3021
+ };
3022
+ }, [open, setOpen]);
3023
+
3024
+ const commitValue = React.useCallback(
3025
+ (nextValue: DateRangeValue) => {
3026
+ if (!isValueControlled) {
3027
+ setInternalValue(nextValue);
3028
+ }
3029
+
3030
+ onValueChange?.(nextValue);
3031
+ },
3032
+ [isValueControlled, onValueChange]
3033
+ );
3034
+
3035
+ const handlePresetClick = (preset: DateRangePreset) => {
3036
+ const range = preset.getRange();
3037
+ commitValue(range);
3038
+ setOpen(false);
3039
+ };
3040
+
3041
+ const handleDayClick = (day: Date) => {
3042
+ if (!pendingEnd) {
3043
+ setDraftValue({ start: day, end: day });
3044
+ setPendingEnd(true);
3045
+ return;
3046
+ }
3047
+
3048
+ // Second click completes the range \u2014 commit and close immediately,
3049
+ // the same way a preset does, since there's no Apply step anymore.
3050
+ const start = draftValue.start ?? day;
3051
+ const nextValue = isBeforeDay(day, start)
3052
+ ? { start: day, end: start }
3053
+ : { start, end: day };
3054
+
3055
+ setDraftValue(nextValue);
3056
+ setPendingEnd(false);
3057
+ commitValue(nextValue);
3058
+ setOpen(false);
3059
+ };
3060
+
3061
+ const popover =
3062
+ open &&
3063
+ !disabled &&
3064
+ portalMount &&
3065
+ createPortal(
3066
+ <div
3067
+ ref={setPopoverRef}
3068
+ role="dialog"
3069
+ aria-modal="false"
3070
+ aria-labelledby={\`\${triggerId}-calendar-heading\`}
3071
+ className={cn(
3072
+ "flex flex-col rounded-lg border border-solid border-semantic-border-layout bg-semantic-bg-primary shadow-lg overflow-y-auto overflow-x-hidden overscroll-contain pointer-events-auto",
3073
+ "[scrollbar-gutter:stable] [scrollbar-width:thin] [scrollbar-color:var(--semantic-border-secondary)_transparent]",
3074
+ "[&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-semantic-border-secondary"
3075
+ )}
3076
+ style={{
3077
+ ...floatingStyles,
3078
+ maxWidth: \`calc(100vw - \${POPOVER_MARGIN * 2}px)\`,
3079
+ maxHeight: \`var(\${POPOVER_SCROLL_HEIGHT_VAR}, min(\${MAX_POPOVER_HEIGHT}px, calc(100dvh - \${
3080
+ POPOVER_MARGIN * 2
3081
+ }px)))\`,
3082
+ zIndex: 10050,
3083
+ visibility: isPositioned ? undefined : "hidden",
3084
+ }}
3085
+ onPointerDown={(event) => event.stopPropagation()}
3086
+ onMouseDown={(event) => event.stopPropagation()}
3087
+ onWheel={(event) => event.stopPropagation()}
3088
+ onTouchMove={(event) => event.stopPropagation()}
3089
+ >
3090
+ <div className="flex flex-row">
3091
+ {presets.length > 0 && (
3092
+ <div className="flex w-36 shrink-0 flex-col gap-0.5 border-r border-solid border-semantic-border-layout p-3">
3093
+ {presets.map((preset) => (
3094
+ <button
3095
+ key={preset.label}
3096
+ type="button"
3097
+ className="w-full rounded px-2 py-1.5 text-left text-sm text-semantic-text-primary transition-colors hover:bg-semantic-bg-hover"
3098
+ onClick={() => handlePresetClick(preset)}
3099
+ >
3100
+ {preset.label}
3101
+ </button>
3102
+ ))}
3103
+ </div>
3104
+ )}
3105
+
3106
+ <div className="min-w-[272px] flex-1 p-3 touch-pan-y">
3107
+ <div className="mb-3 flex items-center justify-between gap-2">
3108
+ <button
3109
+ type="button"
3110
+ aria-label="Previous month"
3111
+ className="p-1 rounded hover:bg-semantic-bg-hover text-semantic-text-secondary transition-colors"
3112
+ onClick={() =>
3113
+ setVisibleMonth((month) => addMonths(month, -1))
3114
+ }
3115
+ >
3116
+ <ChevronLeft className="size-4" aria-hidden="true" />
3117
+ </button>
3118
+
3119
+ <div
3120
+ id={\`\${triggerId}-calendar-heading\`}
3121
+ className="flex items-center gap-2"
3122
+ >
3123
+ <DropdownMenu>
3124
+ <DropdownMenuTrigger asChild>
3125
+ <button
3126
+ type="button"
3127
+ className="rounded border border-solid border-semantic-border-layout px-3 py-1.5 text-sm font-semibold text-semantic-text-primary transition-colors hover:bg-semantic-bg-hover"
3128
+ >
3129
+ {MONTH_LONG_NAMES[visibleMonth.getMonth()]}
3130
+ </button>
3131
+ </DropdownMenuTrigger>
3132
+ <DropdownMenuContent
3133
+ align="start"
3134
+ className={cn(CALENDAR_DROPDOWN_Z_INDEX, "max-h-[240px] overflow-y-auto")}
3135
+ >
3136
+ {MONTH_LONG_NAMES.map((label, monthIndex) => (
3137
+ <DropdownMenuItem
3138
+ key={label}
3139
+ onSelect={() =>
3140
+ setVisibleMonth(
3141
+ startOfMonth(
3142
+ new Date(visibleMonth.getFullYear(), monthIndex, 1)
3143
+ )
3144
+ )
3145
+ }
3146
+ >
3147
+ {label}
3148
+ </DropdownMenuItem>
3149
+ ))}
3150
+ </DropdownMenuContent>
3151
+ </DropdownMenu>
3152
+
3153
+ <DropdownMenu>
3154
+ <DropdownMenuTrigger asChild>
3155
+ <button
3156
+ type="button"
3157
+ className="flex items-center gap-1 rounded border border-solid border-semantic-border-layout px-3 py-1.5 text-sm font-semibold text-semantic-text-primary transition-colors hover:bg-semantic-bg-hover"
3158
+ >
3159
+ {visibleMonth.getFullYear()}
3160
+ <ChevronDown
3161
+ className="size-3.5 text-semantic-text-muted"
3162
+ aria-hidden="true"
3163
+ />
3164
+ </button>
3165
+ </DropdownMenuTrigger>
3166
+ <DropdownMenuContent
3167
+ align="start"
3168
+ className={cn(CALENDAR_DROPDOWN_Z_INDEX, "max-h-[240px] overflow-y-auto")}
3169
+ >
3170
+ {Array.from(
3171
+ { length: YEAR_OPTIONS_SPAN * 2 + 1 },
3172
+ (_, index) => visibleMonth.getFullYear() - YEAR_OPTIONS_SPAN + index
3173
+ ).map((year) => (
3174
+ <DropdownMenuItem
3175
+ key={year}
3176
+ onSelect={() =>
3177
+ setVisibleMonth(
3178
+ startOfMonth(new Date(year, visibleMonth.getMonth(), 1))
3179
+ )
3180
+ }
3181
+ >
3182
+ {year}
3183
+ </DropdownMenuItem>
3184
+ ))}
3185
+ </DropdownMenuContent>
3186
+ </DropdownMenu>
3187
+ </div>
3188
+
3189
+ <button
3190
+ type="button"
3191
+ aria-label="Next month"
3192
+ className="p-1 rounded hover:bg-semantic-bg-hover text-semantic-text-secondary transition-colors"
3193
+ onClick={() =>
3194
+ setVisibleMonth((month) => addMonths(month, 1))
3195
+ }
3196
+ >
3197
+ <ChevronRight className="size-4" aria-hidden="true" />
3198
+ </button>
3199
+ </div>
3200
+
3201
+ <div className="grid grid-cols-7">
3202
+ {weekDays.map((day) => (
3203
+ <div
3204
+ key={day}
3205
+ className="flex h-8 items-center justify-center text-xs font-medium text-semantic-text-muted"
3206
+ >
3207
+ {day}
3208
+ </div>
3209
+ ))}
3210
+ {calendarDays.map((day) => {
3211
+ const isCurrentMonth =
3212
+ day.getMonth() === visibleMonth.getMonth();
3213
+ const isToday = isSameDay(day, new Date());
3214
+ const { start, end } = draftValue;
3215
+ const hasRange = !!start && !!end && !isSameDay(start, end);
3216
+ const isRangeStart = isSameDay(day, start);
3217
+ const isRangeEnd = isSameDay(day, end);
3218
+ const isBetween =
3219
+ hasRange &&
3220
+ !!start &&
3221
+ !!end &&
3222
+ isAfterDay(day, start) &&
3223
+ isBeforeDay(day, end);
3224
+ const isInBand =
3225
+ hasRange && (isRangeStart || isRangeEnd || isBetween);
3226
+ const isSelectedEdge = isRangeStart || isRangeEnd;
3227
+ const isDisabled =
3228
+ (effectiveMinDate &&
3229
+ isBeforeDay(day, effectiveMinDate)) ||
3230
+ (maxDate && isAfterDay(day, maxDate));
3231
+ const dayLabel = day.toLocaleDateString("en-US", {
3232
+ month: "long",
3233
+ day: "numeric",
3234
+ year: "numeric",
3235
+ });
3236
+
3237
+ return (
3238
+ <div
3239
+ key={day.toISOString()}
3240
+ className={cn(
3241
+ "relative flex h-8 items-center justify-center",
3242
+ isInBand && "bg-semantic-info-surface",
3243
+ isRangeStart && hasRange && "rounded-l-full",
3244
+ isRangeEnd && hasRange && "rounded-r-full"
3245
+ )}
3246
+ >
3247
+ <button
3248
+ type="button"
3249
+ aria-label={dayLabel}
3250
+ aria-pressed={isSelectedEdge}
3251
+ aria-current={isToday ? "date" : undefined}
3252
+ disabled={!!isDisabled}
3253
+ className={cn(
3254
+ "relative flex size-8 items-center justify-center rounded-full text-xs transition-colors",
3255
+ isSelectedEdge
3256
+ ? "bg-semantic-primary text-semantic-text-inverted font-semibold"
3257
+ : isCurrentMonth
3258
+ ? "text-semantic-text-primary hover:bg-semantic-bg-hover"
3259
+ : "text-semantic-text-muted hover:bg-semantic-bg-hover",
3260
+ isDisabled &&
3261
+ "opacity-40 cursor-not-allowed pointer-events-none"
3262
+ )}
3263
+ onClick={() => {
3264
+ if (isDisabled) return;
3265
+
3266
+ handleDayClick(day);
3267
+ }}
3268
+ >
3269
+ {day.getDate()}
3270
+ {isToday && !isSelectedEdge && (
3271
+ <span className="absolute bottom-0.5 left-1/2 -translate-x-1/2 size-1 rounded-full bg-semantic-primary" />
3272
+ )}
3273
+ </button>
3274
+ </div>
3275
+ );
3276
+ })}
3277
+ </div>
3278
+ </div>
3279
+ </div>
3280
+ </div>,
3281
+ portalMount
3282
+ );
3283
+
3284
+ return (
3285
+ <div
3286
+ ref={(node) => {
3287
+ rootRef.current = node;
3288
+
3289
+ if (typeof ref === "function") {
3290
+ ref(node);
3291
+ } else if (ref) {
3292
+ (ref as React.MutableRefObject<HTMLDivElement | null>).current =
3293
+ node;
3294
+ }
3295
+ }}
3296
+ className={cn("relative inline-block w-full max-w-full", className)}
3297
+ {...props}
3298
+ >
3299
+ <button
3300
+ ref={setTriggerRef}
3301
+ id={triggerId}
3302
+ type="button"
3303
+ disabled={disabled}
3304
+ aria-haspopup="dialog"
3305
+ aria-expanded={open}
3306
+ className={cn(
3307
+ dateRangePickerTriggerVariants({ state }),
3308
+ open &&
3309
+ state !== "error" &&
3310
+ "border-semantic-border-input-focus/50 shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
3311
+ !displayValue && "text-semantic-text-placeholder",
3312
+ triggerClassName
3313
+ )}
3314
+ onClick={() => setOpen(!open)}
3315
+ >
3316
+ <FigmaCalendarIcon className="size-[18px] shrink-0 text-semantic-text-secondary" />
3317
+ <span
3318
+ className={cn(
3319
+ "min-w-0 flex-1 truncate",
3320
+ !displayValue && "font-normal",
3321
+ triggerLabelClassName
3322
+ )}
3323
+ >
3324
+ {displayValue || placeholder}
3325
+ </span>
3326
+ </button>
3327
+
3328
+ {popover}
3329
+ </div>
3330
+ );
3331
+ }
3332
+ );
3333
+ DateRangePicker.displayName = "DateRangePicker";
3334
+
3335
+ export { DateRangePicker, dateRangePickerTriggerVariants };
2565
3336
  `,
2566
3337
  "date-time-picker": `import * as React from "react";
2567
3338
  import { createPortal } from "react-dom";
@@ -5029,7 +5800,7 @@ const DialogContent = React.forwardRef(({ className, children, size, hideCloseBu
5029
5800
  {children}
5030
5801
  {/* Accessibility: Add hidden description if none provided */}
5031
5802
  {!hasDescription && (
5032
- <DialogPrimitive.Description className="sr-only">
5803
+ <DialogPrimitive.Description className="sr-only m-0">
5033
5804
  Dialog content
5034
5805
  </DialogPrimitive.Description>
5035
5806
  )}
@@ -5088,7 +5859,7 @@ DialogTitle.displayName = DialogPrimitive.Title.displayName;
5088
5859
  const DialogDescription = React.forwardRef(({ className, ...props }: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>, ref: React.Ref<React.ElementRef<typeof DialogPrimitive.Description>>) => (
5089
5860
  <DialogPrimitive.Description
5090
5861
  ref={ref}
5091
- className={cn("text-sm text-muted-foreground", className)}
5862
+ className={cn("m-0 text-sm text-muted-foreground", className)}
5092
5863
  {...props}
5093
5864
  />
5094
5865
  ));
@@ -5816,11 +6587,11 @@ const Input = React.forwardRef(
5816
6587
  input.value.slice(selectionEnd);
5817
6588
 
5818
6589
  if (nextValue.includes(" ")) {
6590
+ // Blocking the insertion is enough \u2014 nothing moved, so the caret is
6591
+ // already where it belongs. Re-setting it (especially from a later
6592
+ // animation frame, with a selection offset captured before this
6593
+ // keystroke) drags the caret backwards and reorders what follows.
5819
6594
  e.preventDefault();
5820
- input.setSelectionRange(selectionStart, selectionStart);
5821
- window.requestAnimationFrame(() => {
5822
- input.setSelectionRange(selectionStart, selectionStart);
5823
- });
5824
6595
  }
5825
6596
  }}
5826
6597
  onChange={(e) => {
@@ -5841,9 +6612,19 @@ const Input = React.forwardRef(
5841
6612
  );
5842
6613
 
5843
6614
  input.value = collapsedValue;
6615
+ // Restore the caret synchronously: assigning \`value\` above parks it
6616
+ // at 0, and the next keystroke can land before any animation frame
6617
+ // runs, which reorders what the user typed. The deferred pass is
6618
+ // kept for browsers that reset the caret again after React
6619
+ // re-renders, but it must no-op once the value has moved on \u2014 a
6620
+ // frame that fires mid-typing would otherwise drag the caret back
6621
+ // to a position captured several keystrokes ago.
6622
+ input.setSelectionRange(nextCursor, nextCursor);
5844
6623
  window.requestAnimationFrame(() => {
6624
+ if (input.value !== collapsedValue) return;
5845
6625
  input.setSelectionRange(nextCursor, nextCursor);
5846
6626
  });
6627
+
5847
6628
  }
5848
6629
  onChange?.(e);
5849
6630
  }}
@@ -5992,6 +6773,13 @@ const MENU_MIN_WIDTH = 180;
5992
6773
  /** Floor for the flip/shrink height so the list is never collapsed to nothing. */
5993
6774
  const MENU_MIN_HEIGHT = 120;
5994
6775
 
6776
+ /**
6777
+ * Distance from the bottom of the option list, in px, at which \`onScrollEnd\`
6778
+ * fires. Roughly one option row, so the next page starts loading just before
6779
+ * the user actually hits the end.
6780
+ */
6781
+ const SCROLL_END_THRESHOLD_PX = 48;
6782
+
5995
6783
  /**
5996
6784
  * MultiSelect trigger variants matching TextField styling
5997
6785
  */
@@ -6025,6 +6813,27 @@ export interface MultiSelectProps extends VariantProps<
6025
6813
  disabled?: boolean;
6026
6814
  /** Loading state with spinner */
6027
6815
  loading?: boolean;
6816
+ /**
6817
+ * Called once when the option list is scrolled to within
6818
+ * \`SCROLL_END_THRESHOLD_PX\` of its bottom. Use it to fetch the next page of
6819
+ * server-side options. Fires at most once per scroll gesture \u2014 it re-arms
6820
+ * only after the user scrolls back away from the bottom, so a single
6821
+ * trackpad flick (which keeps emitting scroll events while it decelerates)
6822
+ * cannot fan out into several requests.
6823
+ */
6824
+ onScrollEnd?: () => void;
6825
+ /**
6826
+ * Whether the server has more pages. When false, \`onScrollEnd\` is never
6827
+ * called.
6828
+ * @default false
6829
+ */
6830
+ hasMore?: boolean;
6831
+ /**
6832
+ * Whether a page fetch is in flight. Renders a loading row at the bottom of
6833
+ * the list and suppresses further \`onScrollEnd\` calls.
6834
+ * @default false
6835
+ */
6836
+ loadingMore?: boolean;
6028
6837
  /** Placeholder text when no value selected */
6029
6838
  placeholder?: string;
6030
6839
  /** Currently selected values (controlled) */
@@ -6044,6 +6853,18 @@ export interface MultiSelectProps extends VariantProps<
6044
6853
  searchable?: boolean;
6045
6854
  /** Search placeholder text */
6046
6855
  searchPlaceholder?: string;
6856
+ /**
6857
+ * When set, the trigger shows a single compact summary (e.g. "3 lines
6858
+ * selected") instead of one chip per selection; hovering it reveals the full
6859
+ * list of selected labels in a tooltip. Receives the selected count.
6860
+ */
6861
+ summaryLabel?: (count: number) => string;
6862
+ /**
6863
+ * When set, pins a select-all row at the top of the list with this label
6864
+ * (e.g. "All lines"). Toggles every non-disabled option; respects
6865
+ * \`maxSelections\`. Omit to hide the row entirely.
6866
+ */
6867
+ selectAllLabel?: string;
6047
6868
  /** Maximum selections allowed */
6048
6869
  maxSelections?: number;
6049
6870
  /**
@@ -6114,6 +6935,9 @@ const MultiSelect = React.forwardRef(
6114
6935
  error,
6115
6936
  disabled,
6116
6937
  loading,
6938
+ onScrollEnd,
6939
+ hasMore = false,
6940
+ loadingMore = false,
6117
6941
  placeholder = "Select options",
6118
6942
  value,
6119
6943
  defaultValue = [],
@@ -6121,6 +6945,8 @@ const MultiSelect = React.forwardRef(
6121
6945
  options,
6122
6946
  searchable,
6123
6947
  searchPlaceholder = "Search...",
6948
+ selectAllLabel,
6949
+ summaryLabel,
6124
6950
  maxSelections,
6125
6951
  showSelectionFooter = true,
6126
6952
  wrapperClassName,
@@ -6261,6 +7087,61 @@ const MultiSelect = React.forwardRef(
6261
7087
  };
6262
7088
  }, [isOpen, portalTarget, refs.floating]);
6263
7089
 
7090
+ /** The scrollable option list; also read by the no-scrollbar fallback. */
7091
+ const listRef = React.useRef<HTMLDivElement | null>(null);
7092
+ /**
7093
+ * True once \`onScrollEnd\` has fired for the current visit to the bottom.
7094
+ * Cleared only when the user scrolls back out of the threshold zone, so
7095
+ * trackpad inertia \u2014 which keeps firing \`scroll\` for hundreds of ms after
7096
+ * the finger lifts \u2014 cannot re-trigger the callback.
7097
+ */
7098
+ const isLatchedRef = React.useRef(false);
7099
+
7100
+ /**
7101
+ * Deliberately a plain function, not a \`useCallback\` \u2014 it must read the
7102
+ * current \`hasMore\` / \`loadingMore\` on every scroll event, and a memoised
7103
+ * handler would need a props ref (writing refs during render is banned).
7104
+ */
7105
+ const maybeLoadMore = () => {
7106
+ const node = listRef.current;
7107
+ if (!node) return;
7108
+
7109
+ const isNearBottom =
7110
+ node.scrollHeight - node.scrollTop - node.clientHeight <
7111
+ SCROLL_END_THRESHOLD_PX;
7112
+
7113
+ if (!isNearBottom) {
7114
+ // Only an explicit scroll away from the boundary re-arms the latch.
7115
+ isLatchedRef.current = false;
7116
+ return;
7117
+ }
7118
+ if (isLatchedRef.current || !hasMore || loadingMore || !onScrollEnd)
7119
+ return;
7120
+
7121
+ isLatchedRef.current = true;
7122
+ onScrollEnd();
7123
+ };
7124
+
7125
+ // A closed menu or a new search starts from a clean latch.
7126
+ React.useEffect(() => {
7127
+ isLatchedRef.current = false;
7128
+ }, [isOpen, searchQuery]);
7129
+
7130
+ /**
7131
+ * A page can arrive without making the list taller than its max-height
7132
+ * (few results, or a short viewport). No further \`scroll\` event would ever
7133
+ * fire, so pagination would stall silently \u2014 re-check whenever the rendered
7134
+ * options or the fetch state change.
7135
+ */
7136
+ React.useEffect(() => {
7137
+ if (!isOpen || !hasMore || loadingMore) return;
7138
+ const node = listRef.current;
7139
+ if (!node || node.scrollHeight > node.clientHeight) return;
7140
+ if (isLatchedRef.current) return;
7141
+ isLatchedRef.current = true;
7142
+ onScrollEnd?.();
7143
+ }, [isOpen, portalTarget, hasMore, loadingMore, options, onScrollEnd]);
7144
+
6264
7145
  const flatOptions = React.useMemo(
6265
7146
  () => flattenMultiSelectOptions(options),
6266
7147
  [options]
@@ -6270,6 +7151,28 @@ const MultiSelect = React.forwardRef(
6270
7151
  const isControlled = value !== undefined;
6271
7152
  const selectedValues = isControlled ? value : internalValue;
6272
7153
 
7154
+ // Select-all: values eligible for select-all exclude disabled options
7155
+ const selectableValues = React.useMemo(
7156
+ () => flatOptions.filter((o) => !o.disabled).map((o) => o.value),
7157
+ [flatOptions]
7158
+ );
7159
+ const allSelected =
7160
+ selectableValues.length > 0 &&
7161
+ selectableValues.every((v) => selectedValues.includes(v));
7162
+ const someSelected = selectedValues.length > 0 && !allSelected;
7163
+
7164
+ const toggleSelectAll = () => {
7165
+ const newValues = allSelected
7166
+ ? []
7167
+ : maxSelections
7168
+ ? selectableValues.slice(0, maxSelections)
7169
+ : selectableValues;
7170
+ if (!isControlled) {
7171
+ setInternalValue(newValues);
7172
+ }
7173
+ onValueChange?.(newValues);
7174
+ };
7175
+
6273
7176
  // Derive state from props
6274
7177
  const derivedState = error ? "error" : (state ?? "default");
6275
7178
 
@@ -6490,6 +7393,23 @@ const MultiSelect = React.forwardRef(
6490
7393
  <span className="text-base text-semantic-text-placeholder">
6491
7394
  {placeholder}
6492
7395
  </span>
7396
+ ) : summaryLabel ? (
7397
+ <TooltipProvider delayDuration={200}>
7398
+ <Tooltip>
7399
+ <TooltipTrigger asChild>
7400
+ <span className="min-w-0 truncate text-sm text-semantic-text-primary">
7401
+ {summaryLabel(selectedValues.length)}
7402
+ </span>
7403
+ </TooltipTrigger>
7404
+ <TooltipContent>
7405
+ <div className="flex flex-col gap-0.5">
7406
+ {selectedLabels.map((label, index) => (
7407
+ <span key={selectedValues[index]}>{label}</span>
7408
+ ))}
7409
+ </div>
7410
+ </TooltipContent>
7411
+ </Tooltip>
7412
+ </TooltipProvider>
6493
7413
  ) : (
6494
7414
  selectedLabels.map((label, index) => (
6495
7415
  <span
@@ -6623,15 +7543,43 @@ const MultiSelect = React.forwardRef(
6623
7543
  </div>
6624
7544
  )}
6625
7545
 
7546
+ {/* Select all */}
7547
+ {selectAllLabel && (
7548
+ <div
7549
+ role="option"
7550
+ aria-selected={allSelected}
7551
+ tabIndex={0}
7552
+ onClick={toggleSelectAll}
7553
+ onKeyDown={(e) => {
7554
+ if (e.key === "Enter" || e.key === " ") {
7555
+ e.preventDefault();
7556
+ toggleSelectAll();
7557
+ }
7558
+ }}
7559
+ className="flex w-full cursor-pointer select-none items-center gap-2 border-b border-solid border-semantic-border-layout px-3 py-2 text-sm text-semantic-text-primary outline-none hover:bg-semantic-bg-ui"
7560
+ >
7561
+ <Checkbox
7562
+ checked={allSelected ? true : someSelected ? "indeterminate" : false}
7563
+ size="sm"
7564
+ className="pointer-events-none shrink-0"
7565
+ aria-hidden
7566
+ tabIndex={-1}
7567
+ />
7568
+ <span className="min-w-0 flex-1 truncate text-left">{selectAllLabel}</span>
7569
+ </div>
7570
+ )}
7571
+
6626
7572
  {/* Options */}
6627
7573
  <div
7574
+ ref={listRef}
7575
+ onScroll={maybeLoadMore}
6628
7576
  className="overflow-auto overscroll-contain p-1"
6629
7577
  style={{
6630
7578
  maxHeight:
6631
7579
  "min(15rem, var(--multi-select-available-height, 15rem))",
6632
7580
  }}
6633
7581
  >
6634
- {filteredOptions.length === 0 ? (
7582
+ {filteredOptions.length === 0 && !loadingMore ? (
6635
7583
  <div className="py-6 text-center text-sm text-semantic-text-muted">
6636
7584
  No results found
6637
7585
  </div>
@@ -6793,6 +7741,17 @@ const MultiSelect = React.forwardRef(
6793
7741
  return withDisabledTooltip(simpleRow);
6794
7742
  })
6795
7743
  )}
7744
+
7745
+ {loadingMore ? (
7746
+ <div
7747
+ role="status"
7748
+ aria-live="polite"
7749
+ className="flex items-center justify-center gap-2 py-3 text-sm text-semantic-text-muted"
7750
+ >
7751
+ <Loader2 className="size-4 animate-spin" />
7752
+ <span>Loading more...</span>
7753
+ </div>
7754
+ ) : null}
6796
7755
  </div>
6797
7756
 
6798
7757
  {/* Footer with count */}
@@ -8550,11 +9509,32 @@ const SearchFilter = React.forwardRef<HTMLDivElement, SearchFilterProps>(
8550
9509
  [disabled]
8551
9510
  );
8552
9511
 
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
+
8553
9528
  const focusSearchInput = React.useCallback(() => {
8554
- window.requestAnimationFrame(() => {
9529
+ cancelPendingFocus();
9530
+ focusFrameRef.current = window.requestAnimationFrame(() => {
9531
+ focusFrameRef.current = null;
8555
9532
  searchInputRef.current?.focus();
8556
9533
  });
8557
- }, []);
9534
+ }, [cancelPendingFocus]);
9535
+
9536
+ // Never let a queued frame fire into an unmounted component.
9537
+ React.useEffect(() => cancelPendingFocus, [cancelPendingFocus]);
8558
9538
 
8559
9539
  const setRootRef = React.useCallback(
8560
9540
  (node: HTMLDivElement | null) => {
@@ -8633,9 +9613,12 @@ const SearchFilter = React.forwardRef<HTMLDivElement, SearchFilterProps>(
8633
9613
  onValueChange?.(option.value);
8634
9614
  onOptionSelect?.(option);
8635
9615
  onSearchChange?.(option.label);
9616
+ // Drop any queued refocus first, or it reopens what we are closing.
9617
+ cancelPendingFocus();
8636
9618
  setOpen(false);
8637
9619
  },
8638
9620
  [
9621
+ cancelPendingFocus,
8639
9622
  disabled,
8640
9623
  onSearchChange,
8641
9624
  onOptionSelect,
@@ -10816,7 +11799,15 @@ const TextField = React.forwardRef(
10816
11799
  );
10817
11800
 
10818
11801
  input.value = collapsedValue;
11802
+ // Restore the caret synchronously: assigning \`value\` above parks it at
11803
+ // 0, and the next keystroke can land before any animation frame runs,
11804
+ // which reorders what the user typed. The deferred pass is kept for
11805
+ // browsers that reset the caret again after React re-renders, but it
11806
+ // must no-op once the value has moved on \u2014 a frame that fires mid-typing
11807
+ // would otherwise drag the caret back to a stale position.
11808
+ input.setSelectionRange(nextCursor, nextCursor);
10819
11809
  window.requestAnimationFrame(() => {
11810
+ if (input.value !== collapsedValue) return;
10820
11811
  input.setSelectionRange(nextCursor, nextCursor);
10821
11812
  });
10822
11813
  }
@@ -10900,11 +11891,11 @@ const TextField = React.forwardRef(
10900
11891
  input.value.slice(selectionEnd);
10901
11892
 
10902
11893
  if (nextValue.includes(" ")) {
11894
+ // Blocking the insertion is enough \u2014 nothing moved, so the caret is
11895
+ // already where it belongs. Re-setting it from a later animation
11896
+ // frame, with an offset captured before this keystroke, drags the
11897
+ // caret backwards and reorders what follows.
10903
11898
  e.preventDefault();
10904
- input.setSelectionRange(selectionStart, selectionStart);
10905
- window.requestAnimationFrame(() => {
10906
- input.setSelectionRange(selectionStart, selectionStart);
10907
- });
10908
11899
  }
10909
11900
  }}
10910
11901
  onChange={handleChange}
@@ -12768,6 +13759,25 @@ var componentMetadata = {
12768
13759
  }
12769
13760
  ]
12770
13761
  },
13762
+ "date-range-picker": {
13763
+ "name": "DateRangePicker",
13764
+ "description": "A date range picker component.",
13765
+ "dependencies": [
13766
+ "class-variance-authority",
13767
+ "clsx",
13768
+ "tailwind-merge",
13769
+ "lucide-react"
13770
+ ],
13771
+ "props": [],
13772
+ "variants": [],
13773
+ "examples": [
13774
+ {
13775
+ "title": "Basic DateRangePicker",
13776
+ "code": "<DateRangePicker>Content</DateRangePicker>",
13777
+ "description": "Simple date range picker usage"
13778
+ }
13779
+ ]
13780
+ },
12771
13781
  "date-time-picker": {
12772
13782
  "name": "DateTimePicker",
12773
13783
  "description": "A date time picker component.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-mcp",
3
- "version": "0.2.373",
3
+ "version": "0.2.375",
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",