myoperator-mcp 0.2.305 → 0.2.307

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 +772 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2503,6 +2503,681 @@ const DateDivider = React.forwardRef(
2503
2503
  DateDivider.displayName = "DateDivider";
2504
2504
 
2505
2505
  export { DateDivider };
2506
+ `,
2507
+ "date-time-picker": `import * as React from "react";
2508
+ import { createPortal } from "react-dom";
2509
+ import {
2510
+ autoUpdate,
2511
+ flip,
2512
+ offset,
2513
+ shift,
2514
+ size as floatingSize,
2515
+ useFloating,
2516
+ type Placement,
2517
+ type Strategy,
2518
+ } from "@floating-ui/react-dom";
2519
+ import { cva, type VariantProps } from "class-variance-authority";
2520
+ import {
2521
+ ChevronLeft,
2522
+ ChevronRight,
2523
+ Clock2,
2524
+ X,
2525
+ } from "lucide-react";
2526
+
2527
+ import { cn } from "@/lib/utils";
2528
+
2529
+ const DEFAULT_START_TIME = "10:30:00";
2530
+ const DEFAULT_END_TIME = "12:30:00";
2531
+ const DEFAULT_PLACEHOLDER = "--/--/-- -- : --";
2532
+ const POPOVER_WIDTH = 336;
2533
+ const POPOVER_MARGIN = 8;
2534
+ const POPOVER_GAP = 4;
2535
+ const MAX_POPOVER_HEIGHT = 420;
2536
+ const POPOVER_SCROLL_HEIGHT_VAR = "--date-time-picker-scroll-height";
2537
+ const POPOVER_WIDTH_VAR = "--date-time-picker-popover-width";
2538
+ const CALENDAR_PLACEMENT: Placement = "bottom-start";
2539
+
2540
+ const weekDays = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
2541
+ const monthFormatter = new Intl.DateTimeFormat("en-US", {
2542
+ month: "long",
2543
+ year: "numeric",
2544
+ });
2545
+
2546
+ const dateTimePickerVariants = cva("relative inline-block w-full max-w-full", {
2547
+ variants: {
2548
+ size: {
2549
+ sm: "sm:w-[280px]",
2550
+ default: "sm:w-[336px]",
2551
+ lg: "sm:w-[360px]",
2552
+ },
2553
+ },
2554
+ defaultVariants: {
2555
+ size: "default",
2556
+ },
2557
+ });
2558
+
2559
+ const dateTimePickerTriggerVariants = cva(
2560
+ "flex w-full items-center justify-between border border-solid border-semantic-border-input bg-semantic-bg-primary text-left text-semantic-text-primary outline-none transition-colors hover:border-semantic-border-input-focus/50 disabled:cursor-not-allowed disabled:opacity-50",
2561
+ {
2562
+ variants: {
2563
+ size: {
2564
+ sm: "h-9 gap-2 rounded-lg px-3 py-2 text-sm",
2565
+ default: "h-10 gap-2 rounded-lg px-4 py-2.5 text-sm",
2566
+ lg: "h-10 gap-2 rounded-lg px-4 py-2.5 text-sm",
2567
+ },
2568
+ state: {
2569
+ default: "",
2570
+ error:
2571
+ "border-semantic-error-primary hover:border-semantic-error-primary",
2572
+ },
2573
+ },
2574
+ defaultVariants: {
2575
+ size: "default",
2576
+ state: "default",
2577
+ },
2578
+ }
2579
+ );
2580
+
2581
+ export interface DateTimePickerValue {
2582
+ date?: Date;
2583
+ startTime: string;
2584
+ endTime: string;
2585
+ }
2586
+
2587
+ export interface DateTimePickerProps
2588
+ extends
2589
+ Omit<React.HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange">,
2590
+ VariantProps<typeof dateTimePickerVariants>,
2591
+ Pick<VariantProps<typeof dateTimePickerTriggerVariants>, "state"> {
2592
+ value?: DateTimePickerValue;
2593
+ defaultValue?: DateTimePickerValue;
2594
+ onValueChange?: (value: DateTimePickerValue) => void;
2595
+ placeholder?: string;
2596
+ disabled?: boolean;
2597
+ readOnly?: boolean;
2598
+ name?: string;
2599
+ showEndTime?: boolean;
2600
+ showClear?: boolean;
2601
+ closeOnSelect?: boolean;
2602
+ startTimeLabel?: string;
2603
+ endTimeLabel?: string;
2604
+ minDate?: Date;
2605
+ maxDate?: Date;
2606
+ disablePastDates?: boolean;
2607
+ open?: boolean;
2608
+ defaultOpen?: boolean;
2609
+ onOpenChange?: (open: boolean) => void;
2610
+ portalContainer?: HTMLElement | null;
2611
+ }
2612
+
2613
+ function normalizeValue(
2614
+ value?: Partial<DateTimePickerValue>
2615
+ ): DateTimePickerValue {
2616
+ return {
2617
+ date: value?.date,
2618
+ startTime: value?.startTime ?? DEFAULT_START_TIME,
2619
+ endTime: value?.endTime ?? DEFAULT_END_TIME,
2620
+ };
2621
+ }
2622
+
2623
+ function isSameDay(a?: Date, b?: Date) {
2624
+ if (!a || !b) return false;
2625
+
2626
+ return (
2627
+ a.getFullYear() === b.getFullYear() &&
2628
+ a.getMonth() === b.getMonth() &&
2629
+ a.getDate() === b.getDate()
2630
+ );
2631
+ }
2632
+
2633
+ function startOfDay(date: Date) {
2634
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
2635
+ }
2636
+
2637
+ function isBeforeDay(date: Date, minDate: Date) {
2638
+ return startOfDay(date).getTime() < startOfDay(minDate).getTime();
2639
+ }
2640
+
2641
+ function isAfterDay(date: Date, maxDate: Date) {
2642
+ return startOfDay(date).getTime() > startOfDay(maxDate).getTime();
2643
+ }
2644
+
2645
+ function startOfMonth(date: Date) {
2646
+ return new Date(date.getFullYear(), date.getMonth(), 1);
2647
+ }
2648
+
2649
+ function addMonths(date: Date, months: number) {
2650
+ return new Date(date.getFullYear(), date.getMonth() + months, 1);
2651
+ }
2652
+
2653
+ function getCalendarDays(month: Date) {
2654
+ const firstDay = startOfMonth(month);
2655
+ const gridStart = new Date(firstDay);
2656
+ gridStart.setDate(firstDay.getDate() - firstDay.getDay());
2657
+
2658
+ return Array.from({ length: 42 }, (_, index) => {
2659
+ const day = new Date(gridStart);
2660
+ day.setDate(gridStart.getDate() + index);
2661
+ return day;
2662
+ });
2663
+ }
2664
+
2665
+ function isPointerInsideElement(
2666
+ event: MouseEvent,
2667
+ element: HTMLElement | null
2668
+ ) {
2669
+ if (!element) return false;
2670
+
2671
+ const target = event.target;
2672
+ if (target instanceof Node && element.contains(target)) return true;
2673
+
2674
+ if (typeof event.composedPath === "function") {
2675
+ return event
2676
+ .composedPath()
2677
+ .some((node) => node instanceof Node && element.contains(node));
2678
+ }
2679
+
2680
+ return false;
2681
+ }
2682
+
2683
+ function formatTimeForDisplay(time: string) {
2684
+ const [hour = "0", minute = "0"] = time.split(":");
2685
+ const hourNumber = Number(hour);
2686
+ const suffix = hourNumber >= 12 ? "PM" : "AM";
2687
+ const hour12 = hourNumber % 12 || 12;
2688
+
2689
+ return \`\${hour12.toString().padStart(2, "0")}:\${minute.padStart(2, "0")} \${suffix}\`;
2690
+ }
2691
+
2692
+ function formatDateForDisplay(date?: Date, time?: string) {
2693
+ if (!date) return "";
2694
+
2695
+ const month = (date.getMonth() + 1).toString().padStart(2, "0");
2696
+ const day = date.getDate().toString().padStart(2, "0");
2697
+ const year = date.getFullYear();
2698
+ const formattedTime = formatTimeForDisplay(time ?? DEFAULT_START_TIME);
2699
+
2700
+ return \`\${month}/\${day}/\${year} \${formattedTime}\`;
2701
+ }
2702
+
2703
+ function formatHiddenValue(value: DateTimePickerValue) {
2704
+ if (!value.date) return "";
2705
+
2706
+ const month = (value.date.getMonth() + 1).toString().padStart(2, "0");
2707
+ const day = value.date.getDate().toString().padStart(2, "0");
2708
+ const year = value.date.getFullYear();
2709
+
2710
+ return \`\${year}-\${month}-\${day}T\${value.startTime}\`;
2711
+ }
2712
+
2713
+ function FigmaCalendarIcon({ className }: { className?: string }) {
2714
+ return (
2715
+ <svg
2716
+ viewBox="0 0 18 18"
2717
+ fill="none"
2718
+ xmlns="http://www.w3.org/2000/svg"
2719
+ className={className}
2720
+ aria-hidden="true"
2721
+ >
2722
+ <path
2723
+ d="M6 1.5V4.5M12 1.5V4.5M2.25 6.375H15.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"
2724
+ stroke="currentColor"
2725
+ strokeWidth="1.5"
2726
+ strokeLinecap="round"
2727
+ strokeLinejoin="round"
2728
+ />
2729
+ </svg>
2730
+ );
2731
+ }
2732
+
2733
+ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
2734
+ (
2735
+ {
2736
+ className,
2737
+ size,
2738
+ state,
2739
+ value,
2740
+ defaultValue,
2741
+ onValueChange,
2742
+ placeholder = DEFAULT_PLACEHOLDER,
2743
+ disabled = false,
2744
+ readOnly = false,
2745
+ name,
2746
+ showEndTime = true,
2747
+ showClear = true,
2748
+ closeOnSelect = false,
2749
+ startTimeLabel = "Start Time",
2750
+ endTimeLabel = "End Time",
2751
+ minDate,
2752
+ maxDate,
2753
+ disablePastDates = false,
2754
+ open: controlledOpen,
2755
+ defaultOpen = false,
2756
+ onOpenChange,
2757
+ portalContainer,
2758
+ id,
2759
+ ...props
2760
+ },
2761
+ ref
2762
+ ) => {
2763
+ const generatedId = React.useId();
2764
+ const triggerId = id ?? generatedId;
2765
+ const isValueControlled = value !== undefined;
2766
+ const isOpenControlled = controlledOpen !== undefined;
2767
+ const [internalValue, setInternalValue] = React.useState(() =>
2768
+ normalizeValue(defaultValue)
2769
+ );
2770
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
2771
+ const currentValue = normalizeValue(
2772
+ isValueControlled ? value : internalValue
2773
+ );
2774
+ const open = isOpenControlled ? controlledOpen : internalOpen;
2775
+ const [visibleMonth, setVisibleMonth] = React.useState(() =>
2776
+ startOfMonth(currentValue.date ?? new Date())
2777
+ );
2778
+ const rootRef = React.useRef<HTMLDivElement | null>(null);
2779
+ const triggerRef = React.useRef<HTMLButtonElement | null>(null);
2780
+ const popoverRef = React.useRef<HTMLDivElement | null>(null);
2781
+ const usesContainerPortal = portalContainer !== undefined;
2782
+ const floatingStrategy: Strategy = usesContainerPortal
2783
+ ? "absolute"
2784
+ : "fixed";
2785
+ const floatingMiddleware = React.useMemo(
2786
+ () => [
2787
+ offset(POPOVER_GAP),
2788
+ flip({ padding: POPOVER_MARGIN }),
2789
+ shift({ padding: POPOVER_MARGIN }),
2790
+ floatingSize({
2791
+ padding: POPOVER_MARGIN,
2792
+ apply({ availableHeight, elements, rects }) {
2793
+ const maxHeight = Math.max(
2794
+ 1,
2795
+ Math.min(MAX_POPOVER_HEIGHT, availableHeight)
2796
+ );
2797
+ elements.floating.style.setProperty(
2798
+ POPOVER_SCROLL_HEIGHT_VAR,
2799
+ \`\${maxHeight}px\`
2800
+ );
2801
+ elements.floating.style.setProperty(
2802
+ POPOVER_WIDTH_VAR,
2803
+ \`\${rects.reference.width}px\`
2804
+ );
2805
+ },
2806
+ }),
2807
+ ],
2808
+ []
2809
+ );
2810
+ const { refs, floatingStyles, isPositioned } =
2811
+ useFloating<HTMLButtonElement>({
2812
+ open,
2813
+ placement: CALENDAR_PLACEMENT,
2814
+ strategy: floatingStrategy,
2815
+ transform: false,
2816
+ middleware: floatingMiddleware,
2817
+ whileElementsMounted: (reference, floating, update) =>
2818
+ autoUpdate(reference, floating, update, { animationFrame: true }),
2819
+ });
2820
+ const calendarDays = React.useMemo(
2821
+ () => getCalendarDays(visibleMonth),
2822
+ [visibleMonth]
2823
+ );
2824
+ const displayValue = formatDateForDisplay(
2825
+ currentValue.date,
2826
+ currentValue.startTime
2827
+ );
2828
+ const effectiveMinDate = React.useMemo(() => {
2829
+ if (!disablePastDates) return minDate;
2830
+
2831
+ const today = startOfDay(new Date());
2832
+ if (!minDate) return today;
2833
+
2834
+ return isBeforeDay(minDate, today) ? today : minDate;
2835
+ }, [disablePastDates, minDate]);
2836
+ const portalMount =
2837
+ typeof document !== "undefined"
2838
+ ? usesContainerPortal
2839
+ ? portalContainer
2840
+ : document.body
2841
+ : null;
2842
+
2843
+ const setOpen = React.useCallback(
2844
+ (nextOpen: boolean) => {
2845
+ if (!isOpenControlled) {
2846
+ setInternalOpen(nextOpen);
2847
+ }
2848
+
2849
+ onOpenChange?.(nextOpen);
2850
+ },
2851
+ [isOpenControlled, onOpenChange]
2852
+ );
2853
+
2854
+ const setTriggerRef = React.useCallback(
2855
+ (node: HTMLButtonElement | null) => {
2856
+ triggerRef.current = node;
2857
+ refs.setReference(node);
2858
+ },
2859
+ [refs]
2860
+ );
2861
+
2862
+ const setPopoverRef = React.useCallback(
2863
+ (node: HTMLDivElement | null) => {
2864
+ popoverRef.current = node;
2865
+ refs.setFloating(node);
2866
+ },
2867
+ [refs]
2868
+ );
2869
+
2870
+ React.useEffect(() => {
2871
+ if (currentValue.date) {
2872
+ setVisibleMonth(startOfMonth(currentValue.date));
2873
+ }
2874
+ }, [currentValue.date]);
2875
+
2876
+ React.useEffect(() => {
2877
+ if (!open) return;
2878
+
2879
+ const handlePointerDown = (event: MouseEvent) => {
2880
+ if (
2881
+ !isPointerInsideElement(event, rootRef.current) &&
2882
+ !isPointerInsideElement(event, popoverRef.current)
2883
+ ) {
2884
+ setOpen(false);
2885
+ }
2886
+ };
2887
+
2888
+ const handleKeyDown = (event: KeyboardEvent) => {
2889
+ if (event.key === "Escape") {
2890
+ setOpen(false);
2891
+ }
2892
+ };
2893
+
2894
+ document.addEventListener("mousedown", handlePointerDown);
2895
+ document.addEventListener("keydown", handleKeyDown);
2896
+
2897
+ return () => {
2898
+ document.removeEventListener("mousedown", handlePointerDown);
2899
+ document.removeEventListener("keydown", handleKeyDown);
2900
+ };
2901
+ }, [open, setOpen]);
2902
+
2903
+ const updateValue = React.useCallback(
2904
+ (nextValue: DateTimePickerValue) => {
2905
+ if (!isValueControlled) {
2906
+ setInternalValue(nextValue);
2907
+ }
2908
+
2909
+ onValueChange?.(nextValue);
2910
+ },
2911
+ [isValueControlled, onValueChange]
2912
+ );
2913
+
2914
+ const clearValue = (event: React.MouseEvent<HTMLElement>) => {
2915
+ event.stopPropagation();
2916
+ if (readOnly) return;
2917
+
2918
+ updateValue({
2919
+ date: undefined,
2920
+ startTime: currentValue.startTime,
2921
+ endTime: currentValue.endTime,
2922
+ });
2923
+ };
2924
+
2925
+ const popover =
2926
+ open &&
2927
+ !disabled &&
2928
+ !readOnly &&
2929
+ portalMount &&
2930
+ createPortal(
2931
+ <div
2932
+ ref={setPopoverRef}
2933
+ role="dialog"
2934
+ aria-modal="false"
2935
+ aria-labelledby={\`\${triggerId}-calendar-heading\`}
2936
+ className={cn(
2937
+ "rounded-lg border border-solid border-semantic-border-layout bg-semantic-bg-primary shadow-lg flex flex-col min-h-0 overflow-y-auto overflow-x-hidden overscroll-contain pointer-events-auto",
2938
+ "[scrollbar-gutter:stable] [scrollbar-width:thin] [scrollbar-color:var(--semantic-border-secondary)_transparent]",
2939
+ "[&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-semantic-border-secondary"
2940
+ )}
2941
+ style={{
2942
+ ...floatingStyles,
2943
+ width: \`var(\${POPOVER_WIDTH_VAR}, \${POPOVER_WIDTH}px)\`,
2944
+ maxHeight: \`var(\${POPOVER_SCROLL_HEIGHT_VAR}, min(\${MAX_POPOVER_HEIGHT}px, calc(100dvh - \${
2945
+ POPOVER_MARGIN * 2
2946
+ }px)))\`,
2947
+ zIndex: 10050,
2948
+ visibility: isPositioned ? undefined : "hidden",
2949
+ }}
2950
+ onPointerDown={(event) => event.stopPropagation()}
2951
+ onMouseDown={(event) => event.stopPropagation()}
2952
+ onWheel={(event) => event.stopPropagation()}
2953
+ onTouchMove={(event) => event.stopPropagation()}
2954
+ >
2955
+ <div className="p-3 touch-pan-y">
2956
+ <div className="mb-3 flex items-center justify-between">
2957
+ <button
2958
+ type="button"
2959
+ aria-label="Previous month"
2960
+ className="p-1 rounded hover:bg-semantic-bg-hover text-semantic-text-secondary transition-colors"
2961
+ onClick={() => setVisibleMonth((month) => addMonths(month, -1))}
2962
+ >
2963
+ <ChevronLeft className="size-4" aria-hidden="true" />
2964
+ </button>
2965
+ <div
2966
+ id={\`\${triggerId}-calendar-heading\`}
2967
+ className="text-sm font-semibold text-semantic-text-primary"
2968
+ >
2969
+ {monthFormatter.format(visibleMonth)}
2970
+ </div>
2971
+ <button
2972
+ type="button"
2973
+ aria-label="Next month"
2974
+ className="p-1 rounded hover:bg-semantic-bg-hover text-semantic-text-secondary transition-colors"
2975
+ onClick={() => setVisibleMonth((month) => addMonths(month, 1))}
2976
+ >
2977
+ <ChevronRight className="size-4" aria-hidden="true" />
2978
+ </button>
2979
+ </div>
2980
+
2981
+ <div className="grid grid-cols-7">
2982
+ {weekDays.map((day) => (
2983
+ <div
2984
+ key={day}
2985
+ className="flex size-8 items-center justify-center text-xs font-medium text-semantic-text-muted"
2986
+ >
2987
+ {day}
2988
+ </div>
2989
+ ))}
2990
+ {calendarDays.map((day) => {
2991
+ const isCurrentMonth =
2992
+ day.getMonth() === visibleMonth.getMonth();
2993
+ const isSelected = isSameDay(day, currentValue.date);
2994
+ const isToday = isSameDay(day, new Date());
2995
+ const isDisabled =
2996
+ (effectiveMinDate && isBeforeDay(day, effectiveMinDate)) ||
2997
+ (maxDate && isAfterDay(day, maxDate));
2998
+ const dayLabel = day.toLocaleDateString("en-US", {
2999
+ month: "long",
3000
+ day: "numeric",
3001
+ year: "numeric",
3002
+ });
3003
+
3004
+ return (
3005
+ <button
3006
+ key={day.toISOString()}
3007
+ type="button"
3008
+ aria-label={dayLabel}
3009
+ aria-pressed={isSelected}
3010
+ aria-current={isToday ? "date" : undefined}
3011
+ disabled={!!isDisabled}
3012
+ className={cn(
3013
+ "relative flex items-center justify-center size-8 mx-auto rounded-full text-xs transition-colors",
3014
+ isSelected
3015
+ ? "bg-semantic-primary text-semantic-text-inverted font-semibold"
3016
+ : isCurrentMonth
3017
+ ? "text-semantic-text-primary hover:bg-semantic-bg-hover"
3018
+ : "text-semantic-text-muted hover:bg-semantic-bg-hover",
3019
+ isDisabled &&
3020
+ "opacity-40 cursor-not-allowed pointer-events-none"
3021
+ )}
3022
+ onClick={() => {
3023
+ if (isDisabled) return;
3024
+
3025
+ updateValue({ ...currentValue, date: day });
3026
+ if (closeOnSelect) {
3027
+ setOpen(false);
3028
+ }
3029
+ }}
3030
+ >
3031
+ {day.getDate()}
3032
+ {isToday && !isSelected && (
3033
+ <span className="absolute bottom-0.5 left-1/2 -translate-x-1/2 size-1 rounded-full bg-semantic-primary" />
3034
+ )}
3035
+ </button>
3036
+ );
3037
+ })}
3038
+ </div>
3039
+ </div>
3040
+
3041
+ <div className="space-y-3 border-t border-solid border-semantic-border-layout bg-semantic-bg-primary p-3">
3042
+ <div className="flex flex-col gap-1.5">
3043
+ <label
3044
+ htmlFor={\`\${triggerId}-start-time\`}
3045
+ className="block text-sm font-semibold text-semantic-text-primary"
3046
+ >
3047
+ {startTimeLabel}
3048
+ </label>
3049
+ <div className="relative">
3050
+ <Clock2
3051
+ className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-semantic-text-muted"
3052
+ aria-hidden="true"
3053
+ />
3054
+ <input
3055
+ id={\`\${triggerId}-start-time\`}
3056
+ type="time"
3057
+ step="1"
3058
+ value={currentValue.startTime}
3059
+ className="h-8 w-full rounded-md border border-solid border-semantic-border-input bg-semantic-bg-primary pl-9 pr-3 text-sm text-semantic-text-primary outline-none transition-colors hover:border-semantic-border-input-focus/50 focus:border-semantic-border-input-focus/50 focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]"
3060
+ onChange={(event) =>
3061
+ updateValue({
3062
+ ...currentValue,
3063
+ startTime: event.target.value,
3064
+ })
3065
+ }
3066
+ />
3067
+ </div>
3068
+ </div>
3069
+
3070
+ {showEndTime && (
3071
+ <div className="flex flex-col gap-1.5">
3072
+ <label
3073
+ htmlFor={\`\${triggerId}-end-time\`}
3074
+ className="block text-sm font-semibold text-semantic-text-primary"
3075
+ >
3076
+ {endTimeLabel}
3077
+ </label>
3078
+ <div className="relative">
3079
+ <Clock2
3080
+ className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-semantic-text-muted"
3081
+ aria-hidden="true"
3082
+ />
3083
+ <input
3084
+ id={\`\${triggerId}-end-time\`}
3085
+ type="time"
3086
+ step="1"
3087
+ value={currentValue.endTime}
3088
+ className="h-8 w-full rounded-md border border-solid border-semantic-border-input bg-semantic-bg-primary pl-9 pr-3 text-sm text-semantic-text-primary outline-none transition-colors hover:border-semantic-border-input-focus/50 focus:border-semantic-border-input-focus/50 focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]"
3089
+ onChange={(event) =>
3090
+ updateValue({
3091
+ ...currentValue,
3092
+ endTime: event.target.value,
3093
+ })
3094
+ }
3095
+ />
3096
+ </div>
3097
+ </div>
3098
+ )}
3099
+ </div>
3100
+ </div>,
3101
+ portalMount
3102
+ );
3103
+
3104
+ return (
3105
+ <div
3106
+ ref={(node) => {
3107
+ rootRef.current = node;
3108
+
3109
+ if (typeof ref === "function") {
3110
+ ref(node);
3111
+ } else if (ref) {
3112
+ (ref as React.MutableRefObject<HTMLDivElement | null>).current =
3113
+ node;
3114
+ }
3115
+ }}
3116
+ className={cn(dateTimePickerVariants({ size, className }))}
3117
+ {...props}
3118
+ >
3119
+ {name && (
3120
+ <input
3121
+ type="hidden"
3122
+ name={name}
3123
+ value={formatHiddenValue(currentValue)}
3124
+ />
3125
+ )}
3126
+ <button
3127
+ ref={setTriggerRef}
3128
+ id={triggerId}
3129
+ type="button"
3130
+ disabled={disabled || readOnly}
3131
+ aria-haspopup="dialog"
3132
+ aria-expanded={open}
3133
+ className={cn(
3134
+ dateTimePickerTriggerVariants({ size, state }),
3135
+ open &&
3136
+ state !== "error" &&
3137
+ "border-semantic-border-input-focus/50 shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
3138
+ !displayValue && "text-semantic-text-placeholder"
3139
+ )}
3140
+ onClick={() => setOpen(!open)}
3141
+ >
3142
+ <span
3143
+ className={cn(
3144
+ "min-w-0 flex-1 truncate",
3145
+ !displayValue && "font-normal"
3146
+ )}
3147
+ >
3148
+ {displayValue || placeholder}
3149
+ </span>
3150
+ {showClear && displayValue && !disabled && !readOnly && (
3151
+ <span
3152
+ aria-hidden="true"
3153
+ className="inline-flex size-5 items-center justify-center rounded text-semantic-text-muted hover:bg-semantic-bg-hover hover:text-semantic-text-primary"
3154
+ onClick={clearValue}
3155
+ >
3156
+ <X className="size-4" aria-hidden="true" />
3157
+ </span>
3158
+ )}
3159
+ <FigmaCalendarIcon
3160
+ className={cn(
3161
+ "shrink-0 text-semantic-text-muted",
3162
+ size === "sm" ? "size-4" : "size-[18px]"
3163
+ )}
3164
+ />
3165
+ </button>
3166
+
3167
+ {popover}
3168
+ </div>
3169
+ );
3170
+ }
3171
+ );
3172
+ DateTimePicker.displayName = "DateTimePicker";
3173
+
3174
+ export {
3175
+ DateTimePicker,
3176
+ dateTimePickerTriggerVariants,
3177
+ dateTimePickerVariants,
3178
+ formatDateForDisplay,
3179
+ formatTimeForDisplay,
3180
+ };
2506
3181
  `,
2507
3182
  "delete-confirmation-modal": `import * as React from "react";
2508
3183
 
@@ -4122,7 +4797,7 @@ const MultiSelect = React.forwardRef(
4122
4797
  const secondaryLine = option.secondaryText ?? option.caption;
4123
4798
 
4124
4799
  const rowClass = cn(
4125
- "relative flex w-full cursor-pointer select-none items-center rounded-sm text-left text-semantic-text-primary outline-none",
4800
+ "relative flex w-full min-w-0 cursor-pointer select-none items-center rounded-sm text-left text-semantic-text-primary outline-none",
4126
4801
  optionVariant === "detailed"
4127
4802
  ? "gap-2 px-2 py-2 text-sm"
4128
4803
  : "py-2 pl-4 pr-8 text-base",
@@ -4148,7 +4823,9 @@ const MultiSelect = React.forwardRef(
4148
4823
  <Check className="size-4 text-semantic-brand" />
4149
4824
  )}
4150
4825
  </span>
4151
- {option.label}
4826
+ <span className="min-w-0 flex-1 whitespace-normal break-words text-left">
4827
+ {option.label}
4828
+ </span>
4152
4829
  </button>
4153
4830
  );
4154
4831
 
@@ -5172,6 +5849,7 @@ import { cn } from "@/lib/utils";
5172
5849
  * \`\`\`tsx
5173
5850
  * <PhoneInput placeholder="Enter phone number" />
5174
5851
  * <PhoneInput countryFlag="\u{1F1FA}\u{1F1F8}" countryCode="+1" />
5852
+ * <PhoneInput phoneMaxNumber={10} />
5175
5853
  * <PhoneInput onCountryClick={() => openCountryPicker()} />
5176
5854
  * \`\`\`
5177
5855
  */
@@ -5187,6 +5865,8 @@ export interface PhoneInputProps
5187
5865
  onCountryClick?: () => void;
5188
5866
  /** Additional className for the outer wrapper */
5189
5867
  wrapperClassName?: string;
5868
+ /** Maximum number of digits allowed in the phone number */
5869
+ phoneMaxNumber?: number;
5190
5870
  }
5191
5871
 
5192
5872
  const PhoneInput = React.forwardRef(
@@ -5199,10 +5879,62 @@ const PhoneInput = React.forwardRef(
5199
5879
  onCountryClick,
5200
5880
  wrapperClassName,
5201
5881
  disabled,
5882
+ inputMode = "numeric",
5883
+ pattern = "[0-9]*",
5884
+ onBeforeInput,
5885
+ onChange,
5886
+ onKeyDown,
5887
+ maxLength,
5888
+ phoneMaxNumber,
5202
5889
  ...props
5203
5890
  }: PhoneInputProps,
5204
5891
  ref: React.Ref<HTMLInputElement>
5205
5892
  ) => {
5893
+ const effectiveMaxLength = phoneMaxNumber ?? maxLength;
5894
+
5895
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
5896
+ onKeyDown?.(event);
5897
+ if (
5898
+ event.defaultPrevented ||
5899
+ event.ctrlKey ||
5900
+ event.metaKey ||
5901
+ event.altKey ||
5902
+ event.key.length !== 1
5903
+ ) {
5904
+ return;
5905
+ }
5906
+
5907
+ if (/\\D/.test(event.key)) {
5908
+ event.preventDefault();
5909
+ }
5910
+ };
5911
+
5912
+ const handleBeforeInput = (
5913
+ event: React.InputEvent<HTMLInputElement>
5914
+ ) => {
5915
+ onBeforeInput?.(event);
5916
+ if (event.defaultPrevented) return;
5917
+
5918
+ const inputEvent = event.nativeEvent as InputEvent;
5919
+ if (inputEvent.data && /\\D/.test(inputEvent.data)) {
5920
+ event.preventDefault();
5921
+ }
5922
+ };
5923
+
5924
+ const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
5925
+ const digitsOnlyValue = event.currentTarget.value.replace(/\\D/g, "");
5926
+ const sanitizedValue =
5927
+ effectiveMaxLength != null
5928
+ ? digitsOnlyValue.slice(0, effectiveMaxLength)
5929
+ : digitsOnlyValue;
5930
+
5931
+ if (event.currentTarget.value !== sanitizedValue) {
5932
+ event.currentTarget.value = sanitizedValue;
5933
+ }
5934
+
5935
+ onChange?.(event);
5936
+ };
5937
+
5206
5938
  return (
5207
5939
  <div
5208
5940
  className={cn(
@@ -5232,10 +5964,16 @@ const PhoneInput = React.forwardRef(
5232
5964
  type="tel"
5233
5965
  ref={ref}
5234
5966
  disabled={disabled}
5967
+ inputMode={inputMode}
5968
+ pattern={pattern}
5969
+ maxLength={effectiveMaxLength}
5235
5970
  className={cn(
5236
5971
  "flex-1 h-10 px-3 text-sm text-semantic-text-primary placeholder:text-semantic-text-muted outline-none bg-transparent disabled:cursor-not-allowed",
5237
5972
  className
5238
5973
  )}
5974
+ onBeforeInput={handleBeforeInput}
5975
+ onChange={handleChange}
5976
+ onKeyDown={handleKeyDown}
5239
5977
  {...props}
5240
5978
  />
5241
5979
  </div>
@@ -5997,7 +6735,7 @@ import { cn } from "@/lib/utils";
5997
6735
  * SelectTrigger variants matching TextField styling
5998
6736
  */
5999
6737
  const selectTriggerVariants = cva(
6000
- "flex h-[42px] w-full items-center justify-between rounded bg-semantic-bg-primary px-4 py-2 text-left text-base text-semantic-text-primary outline-none transition-all disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-[var(--color-neutral-50)] [&>span]:line-clamp-1",
6738
+ "flex h-[42px] w-full items-center justify-between gap-2 rounded bg-semantic-bg-primary px-4 py-2 text-left text-base text-semantic-text-primary outline-none transition-all disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-[var(--color-neutral-50)] [&>span]:min-w-0 [&>span]:flex-1 [&>span]:truncate",
6001
6739
  {
6002
6740
  variants: {
6003
6741
  state: {
@@ -6039,7 +6777,7 @@ const SelectTrigger = React.forwardRef(({ className, state, children, ...props }
6039
6777
  >
6040
6778
  {children}
6041
6779
  <SelectPrimitive.Icon asChild>
6042
- <ChevronDown className="size-4 text-semantic-text-muted opacity-70" />
6780
+ <ChevronDown className="size-4 shrink-0 text-semantic-text-muted opacity-70" />
6043
6781
  </SelectPrimitive.Icon>
6044
6782
  </SelectPrimitive.Trigger>
6045
6783
  ));
@@ -6266,7 +7004,9 @@ const SelectItem = React.forwardRef(({ className, children, ...props }: React.Co
6266
7004
  <Check className="size-4 text-semantic-brand" />
6267
7005
  </SelectPrimitive.ItemIndicator>
6268
7006
  </span>
6269
- <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
7007
+ <SelectPrimitive.ItemText asChild>
7008
+ <span className="min-w-0 flex-1 truncate">{children}</span>
7009
+ </SelectPrimitive.ItemText>
6270
7010
  </SelectPrimitive.Item>
6271
7011
  ));
6272
7012
  SelectItem.displayName = SelectPrimitive.Item.displayName;
@@ -7249,8 +7989,10 @@ const textFieldContainerVariants = cva(
7249
7989
  state: {
7250
7990
  default:
7251
7991
  "border border-solid border-semantic-border-input focus-within:border-semantic-border-input-focus focus-within:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
7992
+ empty:
7993
+ "border border-solid border-semantic-border-input focus-within:border-semantic-border-input-focus focus-within:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
7252
7994
  error:
7253
- "border border-solid border-semantic-error-primary/40 focus-within:border-semantic-error-primary focus-within:shadow-[0_0_0_1px_rgba(240,68,56,0.1)]",
7995
+ "border border-solid border-semantic-error-primary focus-within:border-semantic-error-primary focus-within:shadow-[0_0_0_1px_rgba(240,68,56,0.12)]",
7254
7996
  },
7255
7997
  disabled: {
7256
7998
  true: "cursor-not-allowed opacity-50 bg-[var(--color-neutral-50)]",
@@ -7274,8 +8016,10 @@ const textFieldInputVariants = cva(
7274
8016
  state: {
7275
8017
  default:
7276
8018
  "border border-solid border-semantic-border-input focus:outline-none focus:border-semantic-border-input-focus focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
8019
+ empty:
8020
+ "border border-solid border-semantic-border-input focus:outline-none focus:border-semantic-border-input-focus focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
7277
8021
  error:
7278
- "border border-solid border-semantic-error-primary/40 focus:outline-none focus:border-semantic-error-primary focus:shadow-[0_0_0_1px_rgba(240,68,56,0.1)]",
8022
+ "border border-solid border-semantic-error-primary focus:outline-none focus:border-semantic-error-primary focus:shadow-[0_0_0_1px_rgba(240,68,56,0.12)]",
7279
8023
  },
7280
8024
  size: {
7281
8025
  default: "h-[42px] px-4 py-2 text-base file:text-base",
@@ -7305,6 +8049,8 @@ export interface TextFieldProps
7305
8049
  VariantProps<typeof textFieldInputVariants> {
7306
8050
  /** Size of the text field \u2014 \`default\` (42px) or \`sm\` (36px, compact) */
7307
8051
  size?: "default" | "sm";
8052
+ /** Visual state of the text field */
8053
+ state?: "default" | "empty" | "error";
7308
8054
  /** Label text displayed above the input */
7309
8055
  label?: string;
7310
8056
  /** Shows red asterisk next to label when true */
@@ -9255,6 +10001,25 @@ var componentMetadata = {
9255
10001
  }
9256
10002
  ]
9257
10003
  },
10004
+ "date-time-picker": {
10005
+ "name": "DateTimePicker",
10006
+ "description": "A date time picker component.",
10007
+ "dependencies": [
10008
+ "class-variance-authority",
10009
+ "clsx",
10010
+ "tailwind-merge",
10011
+ "lucide-react"
10012
+ ],
10013
+ "props": [],
10014
+ "variants": [],
10015
+ "examples": [
10016
+ {
10017
+ "title": "Basic DateTimePicker",
10018
+ "code": "<DateTimePicker>Content</DateTimePicker>",
10019
+ "description": "Simple date time picker usage"
10020
+ }
10021
+ ]
10022
+ },
9258
10023
  "delete-confirmation-modal": {
9259
10024
  "name": "DeleteConfirmationModal",
9260
10025
  "description": "A delete confirmation modal component.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-mcp",
3
- "version": "0.2.305",
3
+ "version": "0.2.307",
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",