myoperator-mcp 0.2.329 → 0.2.330

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 +326 -103
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2523,7 +2523,7 @@ import { cn } from "@/lib/utils";
2523
2523
 
2524
2524
  const DEFAULT_START_TIME = "10:30:00";
2525
2525
  const DEFAULT_END_TIME = "12:30:00";
2526
- const DEFAULT_PLACEHOLDER = "--/--/-- -- : --";
2526
+ const DEFAULT_PLACEHOLDER = "--/--/---- --:-- --";
2527
2527
  const POPOVER_WIDTH = 336;
2528
2528
  const POPOVER_MARGIN = 8;
2529
2529
  const POPOVER_GAP = 4;
@@ -2533,17 +2533,8 @@ const POPOVER_WIDTH_VAR = "--date-time-picker-popover-width";
2533
2533
  const CALENDAR_PLACEMENT: Placement = "bottom-start";
2534
2534
  const YEAR_RANGE_BEFORE = 100;
2535
2535
  const YEAR_RANGE_AFTER = 10;
2536
- const CALENDAR_NATIVE_SELECT_CLASS =
2536
+ const CALENDAR_DROPDOWN_TRIGGER_CLASS =
2537
2537
  "h-9 min-w-[90px] rounded-md border border-solid border-semantic-border-input bg-semantic-bg-primary px-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)]";
2538
- const DATE_TIME_INPUT_SEGMENT_RANGES = {
2539
- day: [0, 2],
2540
- month: [3, 5],
2541
- year: [6, 10],
2542
- hour: [11, 13],
2543
- minute: [14, 16],
2544
- meridiem: [17, 19],
2545
- } as const;
2546
-
2547
2538
  const weekDays = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
2548
2539
  const monthNames = Array.from({ length: 12 }, (_, monthIndex) =>
2549
2540
  new Intl.DateTimeFormat("en-US", { month: "short" }).format(
@@ -2615,6 +2606,7 @@ export interface DateTimePickerProps
2615
2606
  readOnly?: boolean;
2616
2607
  name?: string;
2617
2608
  showEndTime?: boolean;
2609
+ showSeconds?: boolean;
2618
2610
  showClear?: boolean;
2619
2611
  closeOnSelect?: boolean;
2620
2612
  startTimeLabel?: string;
@@ -2631,6 +2623,13 @@ export interface DateTimePickerProps
2631
2623
  type DateTimePickerVariant = NonNullable<
2632
2624
  VariantProps<typeof dateTimePickerVariants>["variant"]
2633
2625
  >;
2626
+ type CalendarDropdownKind = "month" | "year";
2627
+
2628
+ interface CalendarDropdownOption {
2629
+ value: string;
2630
+ label: string;
2631
+ disabled?: boolean;
2632
+ }
2634
2633
 
2635
2634
  function normalizeValue(
2636
2635
  value?: Partial<DateTimePickerValue>
@@ -2746,22 +2745,34 @@ function isPointerInsideElement(
2746
2745
  return false;
2747
2746
  }
2748
2747
 
2749
- function formatTimeForDisplay(time: string) {
2750
- const [hour = "0", minute = "0"] = time.split(":");
2748
+ function timeHasVisibleSeconds(time?: string) {
2749
+ const [, , second = "00"] = (time ?? "").split(":");
2750
+ return /^\\d{1,2}$/.test(second) && Number(second) !== 0;
2751
+ }
2752
+
2753
+ function formatTimeForDisplay(time: string, showSeconds = false) {
2754
+ const [hour = "0", minute = "0", second = "00"] = time.split(":");
2751
2755
  const hourNumber = Number(hour);
2752
2756
  const suffix = hourNumber >= 12 ? "PM" : "AM";
2753
2757
  const hour12 = hourNumber % 12 || 12;
2758
+ const normalizedSecond = second.padStart(2, "0");
2759
+ const formattedTime = showSeconds
2760
+ ? \`\${hour12.toString().padStart(2, "0")}:\${minute.padStart(2, "0")}:\${normalizedSecond}\`
2761
+ : \`\${hour12.toString().padStart(2, "0")}:\${minute.padStart(2, "0")}\`;
2754
2762
 
2755
- return \`\${hour12.toString().padStart(2, "0")}:\${minute.padStart(2, "0")} \${suffix}\`;
2763
+ return \`\${formattedTime} \${suffix}\`;
2756
2764
  }
2757
2765
 
2758
- function formatDateForDisplay(date?: Date, time?: string) {
2766
+ function formatDateForDisplay(date?: Date, time?: string, showSeconds = false) {
2759
2767
  if (!date) return "";
2760
2768
 
2761
2769
  const month = (date.getMonth() + 1).toString().padStart(2, "0");
2762
2770
  const day = date.getDate().toString().padStart(2, "0");
2763
2771
  const year = date.getFullYear();
2764
- const formattedTime = formatTimeForDisplay(time ?? DEFAULT_START_TIME);
2772
+ const formattedTime = formatTimeForDisplay(
2773
+ time ?? DEFAULT_START_TIME,
2774
+ showSeconds
2775
+ );
2765
2776
 
2766
2777
  return \`\${day}/\${month}/\${year} \${formattedTime}\`;
2767
2778
  }
@@ -2780,7 +2791,8 @@ function formatValueForDisplay(
2780
2791
  value: DateTimePickerValue,
2781
2792
  variant: DateTimePickerVariant,
2782
2793
  showEndTime: boolean,
2783
- hasTimeValue: boolean
2794
+ hasTimeValue: boolean,
2795
+ showSeconds: boolean
2784
2796
  ) {
2785
2797
  if (variant === "date-only") {
2786
2798
  return formatDateOnlyForDisplay(value.date);
@@ -2790,16 +2802,23 @@ function formatValueForDisplay(
2790
2802
  if (!hasTimeValue) return "";
2791
2803
 
2792
2804
  return showEndTime
2793
- ? \`\${formatTimeForDisplay(value.startTime)} - \${formatTimeForDisplay(value.endTime)}\`
2794
- : formatTimeForDisplay(value.startTime);
2805
+ ? \`\${formatTimeForDisplay(value.startTime, showSeconds)} - \${formatTimeForDisplay(value.endTime, showSeconds)}\`
2806
+ : formatTimeForDisplay(value.startTime, showSeconds);
2795
2807
  }
2796
2808
 
2797
- return formatDateForDisplay(value.date, value.startTime);
2809
+ return formatDateForDisplay(value.date, value.startTime, showSeconds);
2798
2810
  }
2799
2811
 
2800
- function getDefaultPlaceholder(variant: DateTimePickerVariant) {
2812
+ function getDefaultPlaceholder(
2813
+ variant: DateTimePickerVariant,
2814
+ showSeconds: boolean
2815
+ ) {
2801
2816
  if (variant === "date-only") return "--/--/----";
2802
- if (variant === "time-only") return "--:-- --";
2817
+ if (variant === "time-only") {
2818
+ return showSeconds ? "--:--:-- --" : "--:-- --";
2819
+ }
2820
+
2821
+ if (showSeconds) return "--/--/---- --:--:-- --";
2803
2822
 
2804
2823
  return DEFAULT_PLACEHOLDER;
2805
2824
  }
@@ -3015,16 +3034,20 @@ function splitTypedDateInput(value: string) {
3015
3034
  };
3016
3035
  }
3017
3036
 
3018
- function isPotentiallyValidTimeDigits(timeDigits: string) {
3037
+ function isPotentiallyValidTimeDigits(timeDigits: string, showSeconds: boolean) {
3019
3038
  const hourValue = timeDigits.slice(0, 2);
3020
3039
  const minuteValue = timeDigits.slice(2, 4);
3040
+ const secondValue = showSeconds ? timeDigits.slice(4, 6) : "";
3021
3041
  const hour = Number(hourValue);
3022
3042
  const minute = Number(minuteValue);
3043
+ const second = Number(secondValue);
3023
3044
 
3024
3045
  if (hourValue.length === 1 && hour > 1) return false;
3025
3046
  if (hourValue.length === 2 && (hour < 1 || hour > 12)) return false;
3026
3047
  if (minuteValue.length === 1 && minute > 5) return false;
3027
3048
  if (minuteValue.length === 2 && minute > 59) return false;
3049
+ if (showSeconds && secondValue.length === 1 && second > 5) return false;
3050
+ if (showSeconds && secondValue.length === 2 && second > 59) return false;
3028
3051
 
3029
3052
  return true;
3030
3053
  }
@@ -3037,21 +3060,25 @@ function formatMeridiemInput(letters: string) {
3037
3060
  return null;
3038
3061
  }
3039
3062
 
3040
- function formatTimeInput(restValue: string) {
3063
+ function formatTimeInput(restValue: string, showSeconds: boolean) {
3041
3064
  const normalizedValue = restValue.toUpperCase();
3042
- const timeDigits = normalizedValue.replace(/\\D/g, "").slice(0, 4);
3065
+ const maxTimeDigits = showSeconds ? 6 : 4;
3066
+ const timeDigits = normalizedValue.replace(/\\D/g, "").slice(0, maxTimeDigits);
3043
3067
  const meridiemLetters = normalizedValue.replace(/[^A-Z]/g, "");
3044
3068
  const meridiem = formatMeridiemInput(meridiemLetters.slice(0, 2));
3045
3069
 
3046
3070
  if (!timeDigits && !meridiem) return "";
3047
- if (!isPotentiallyValidTimeDigits(timeDigits) || meridiem === null) {
3071
+ if (!isPotentiallyValidTimeDigits(timeDigits, showSeconds) || meridiem === null) {
3048
3072
  return null;
3049
3073
  }
3050
3074
 
3051
- const timeValue =
3052
- timeDigits.length <= 2
3053
- ? timeDigits
3054
- : \`\${timeDigits.slice(0, 2)}:\${timeDigits.slice(2, 4)}\`;
3075
+ let timeValue = timeDigits.slice(0, 2);
3076
+ if (timeDigits.length > 2) {
3077
+ timeValue = \`\${timeValue}:\${timeDigits.slice(2, 4)}\`;
3078
+ }
3079
+ if (showSeconds && timeDigits.length > 4) {
3080
+ timeValue = \`\${timeValue}:\${timeDigits.slice(4, 6)}\`;
3081
+ }
3055
3082
 
3056
3083
  return [timeValue, meridiem].filter(Boolean).join(" ");
3057
3084
  }
@@ -3070,20 +3097,28 @@ function sanitizeTypedDateInput(value: string, previousValue: string) {
3070
3097
  return dateValue;
3071
3098
  }
3072
3099
 
3073
- function sanitizeTypedTimeInput(value: string, previousValue: string) {
3100
+ function sanitizeTypedTimeInput(
3101
+ value: string,
3102
+ previousValue: string,
3103
+ showSeconds: boolean
3104
+ ) {
3074
3105
  const trimmedValue = value.trimStart();
3075
3106
  if (/^[A-Za-z]/.test(trimmedValue) && !/^[AP]/i.test(trimmedValue)) {
3076
3107
  return previousValue;
3077
3108
  }
3078
3109
 
3079
3110
  const normalizedValue = value.toUpperCase().replace(/[^0-9\\s:APM]/g, "");
3080
- const formattedTime = formatTimeInput(normalizedValue);
3111
+ const formattedTime = formatTimeInput(normalizedValue, showSeconds);
3081
3112
  if (formattedTime === null) return previousValue;
3082
3113
 
3083
3114
  return formattedTime;
3084
3115
  }
3085
3116
 
3086
- function sanitizeTypedDateTimeInput(value: string, previousValue: string) {
3117
+ function sanitizeTypedDateTimeInput(
3118
+ value: string,
3119
+ previousValue: string,
3120
+ showSeconds: boolean
3121
+ ) {
3087
3122
  const trimmedValue = value.trimStart();
3088
3123
  if (/^[A-Za-z]/.test(trimmedValue)) return previousValue;
3089
3124
 
@@ -3097,22 +3132,59 @@ function sanitizeTypedDateTimeInput(value: string, previousValue: string) {
3097
3132
  if (!limitedDateDigits) return "";
3098
3133
  if (!isValid) return previousValue;
3099
3134
 
3100
- const formattedTime = isComplete ? formatTimeInput(restValue) : "";
3135
+ const formattedTime = isComplete ? formatTimeInput(restValue, showSeconds) : "";
3101
3136
  if (formattedTime === null) return previousValue;
3102
3137
 
3103
3138
  return [dateValue, formattedTime].filter(Boolean).join(" ");
3104
3139
  }
3105
3140
 
3106
- type DateTimeInputSegment = keyof typeof DATE_TIME_INPUT_SEGMENT_RANGES;
3107
-
3108
- function getDateTimeInputSegment(cursorPosition: number): DateTimeInputSegment {
3109
- if (cursorPosition <= DATE_TIME_INPUT_SEGMENT_RANGES.day[1]) return "day";
3110
- if (cursorPosition <= DATE_TIME_INPUT_SEGMENT_RANGES.month[1]) return "month";
3111
- if (cursorPosition <= DATE_TIME_INPUT_SEGMENT_RANGES.year[1]) return "year";
3112
- if (cursorPosition <= DATE_TIME_INPUT_SEGMENT_RANGES.hour[1]) return "hour";
3113
- if (cursorPosition <= DATE_TIME_INPUT_SEGMENT_RANGES.minute[1]) {
3141
+ type DateTimeInputSegment =
3142
+ | "day"
3143
+ | "month"
3144
+ | "year"
3145
+ | "hour"
3146
+ | "minute"
3147
+ | "second"
3148
+ | "meridiem";
3149
+
3150
+ function getDateTimeInputSegmentRanges(showSeconds: boolean) {
3151
+ return showSeconds
3152
+ ? ({
3153
+ day: [0, 2],
3154
+ month: [3, 5],
3155
+ year: [6, 10],
3156
+ hour: [11, 13],
3157
+ minute: [14, 16],
3158
+ second: [17, 19],
3159
+ meridiem: [20, 22],
3160
+ } satisfies Record<DateTimeInputSegment, readonly [number, number]>)
3161
+ : ({
3162
+ day: [0, 2],
3163
+ month: [3, 5],
3164
+ year: [6, 10],
3165
+ hour: [11, 13],
3166
+ minute: [14, 16],
3167
+ second: [17, 19],
3168
+ meridiem: [17, 19],
3169
+ } satisfies Record<DateTimeInputSegment, readonly [number, number]>);
3170
+ }
3171
+
3172
+ function getDateTimeInputSegment(
3173
+ cursorPosition: number,
3174
+ showSeconds: boolean
3175
+ ): DateTimeInputSegment {
3176
+ const ranges = getDateTimeInputSegmentRanges(showSeconds);
3177
+
3178
+ if (cursorPosition <= ranges.day[1]) return "day";
3179
+ if (cursorPosition <= ranges.month[1]) return "month";
3180
+ if (cursorPosition <= ranges.year[1]) return "year";
3181
+ if (cursorPosition <= ranges.hour[1]) return "hour";
3182
+ if (cursorPosition <= ranges.minute[1]) {
3114
3183
  return "minute";
3115
3184
  }
3185
+ if (showSeconds && cursorPosition <= ranges.second[1]) {
3186
+ return "second";
3187
+ }
3116
3188
 
3117
3189
  return "meridiem";
3118
3190
  }
@@ -3125,12 +3197,13 @@ function stepDateTimeInputValue(
3125
3197
  value: string,
3126
3198
  cursorPosition: number,
3127
3199
  direction: 1 | -1,
3128
- fallbackTime: string
3200
+ fallbackTime: string,
3201
+ showSeconds: boolean
3129
3202
  ) {
3130
3203
  const typedDateTime = parseTypedDateTime(value);
3131
3204
  if (!typedDateTime) return null;
3132
3205
 
3133
- const segment = getDateTimeInputSegment(cursorPosition);
3206
+ const segment = getDateTimeInputSegment(cursorPosition, showSeconds);
3134
3207
  const nextDate = new Date(typedDateTime.date);
3135
3208
  const [hourValue = "0", minuteValue = "0", secondValue = "00"] = (
3136
3209
  typedDateTime.startTime ?? fallbackTime
@@ -3167,6 +3240,8 @@ function stepDateTimeInputValue(
3167
3240
  nextDate.setHours(nextDate.getHours() + direction);
3168
3241
  } else if (segment === "minute") {
3169
3242
  nextDate.setMinutes(nextDate.getMinutes() + direction);
3243
+ } else if (segment === "second") {
3244
+ nextDate.setSeconds(nextDate.getSeconds() + direction);
3170
3245
  } else {
3171
3246
  nextDate.setHours(nextDate.getHours() + 12 * direction);
3172
3247
  }
@@ -3189,6 +3264,34 @@ function stepDateTimeInputValue(
3189
3264
  };
3190
3265
  }
3191
3266
 
3267
+ function formatTimeForTimeInput(time: string, showSeconds: boolean) {
3268
+ const normalizedTime = parseTimePart(time) ?? DEFAULT_START_TIME;
3269
+ const [hour = "00", minute = "00", second = "00"] = normalizedTime.split(":");
3270
+
3271
+ return showSeconds ? \`\${hour}:\${minute}:\${second}\` : \`\${hour}:\${minute}\`;
3272
+ }
3273
+
3274
+ function normalizeTimeInputValue(time: string, fallbackTime: string) {
3275
+ return parseTimePart(time) ?? parseTimePart(fallbackTime) ?? DEFAULT_START_TIME;
3276
+ }
3277
+
3278
+ function openNativeTimePicker(input: HTMLInputElement | null) {
3279
+ if (!input) return;
3280
+
3281
+ input.focus();
3282
+
3283
+ const showPicker = (
3284
+ input as HTMLInputElement & { showPicker?: () => void }
3285
+ ).showPicker;
3286
+ if (!showPicker) return;
3287
+
3288
+ try {
3289
+ showPicker.call(input);
3290
+ } catch {
3291
+ input.focus();
3292
+ }
3293
+ }
3294
+
3192
3295
  function parseTypedDateTime(value: string) {
3193
3296
  const typedValue = value.trim();
3194
3297
  if (!typedValue) return undefined;
@@ -3268,6 +3371,85 @@ function FigmaCalendarIcon({ className }: { className?: string }) {
3268
3371
  );
3269
3372
  }
3270
3373
 
3374
+ function CalendarDropdown({
3375
+ id,
3376
+ label,
3377
+ value,
3378
+ options,
3379
+ open,
3380
+ onOpenChange,
3381
+ onValueChange,
3382
+ }: {
3383
+ id: string;
3384
+ label: string;
3385
+ value: string;
3386
+ options: CalendarDropdownOption[];
3387
+ open: boolean;
3388
+ onOpenChange: (open: boolean) => void;
3389
+ onValueChange: (value: string) => void;
3390
+ }) {
3391
+ const selectedOption = options.find((option) => option.value === value);
3392
+
3393
+ return (
3394
+ <div className="relative">
3395
+ <label className="sr-only" htmlFor={id}>
3396
+ {label}
3397
+ </label>
3398
+ <button
3399
+ id={id}
3400
+ type="button"
3401
+ role="combobox"
3402
+ aria-label={label}
3403
+ aria-expanded={open}
3404
+ aria-controls={\`\${id}-options\`}
3405
+ data-value={value}
3406
+ className={cn(
3407
+ CALENDAR_DROPDOWN_TRIGGER_CLASS,
3408
+ "inline-flex items-center justify-between gap-2"
3409
+ )}
3410
+ onClick={() => onOpenChange(!open)}
3411
+ >
3412
+ <span>{selectedOption?.label ?? value}</span>
3413
+ <ChevronRight
3414
+ className={cn("size-3 transition-transform", open && "-rotate-90")}
3415
+ aria-hidden="true"
3416
+ />
3417
+ </button>
3418
+ {open && (
3419
+ <div
3420
+ id={\`\${id}-options\`}
3421
+ role="listbox"
3422
+ aria-label={\`\${label} options\`}
3423
+ className="absolute left-0 top-full z-10 mt-1 max-h-52 min-w-full overflow-y-auto rounded-md border border-solid border-semantic-border-layout bg-semantic-bg-primary p-1 shadow-lg"
3424
+ >
3425
+ {options.map((option) => (
3426
+ <button
3427
+ key={option.value}
3428
+ type="button"
3429
+ role="option"
3430
+ aria-label={option.label}
3431
+ aria-selected={option.value === value}
3432
+ disabled={option.disabled}
3433
+ className={cn(
3434
+ "flex w-full items-center rounded px-2 py-1.5 text-left text-sm text-semantic-text-primary transition-colors hover:bg-semantic-bg-hover disabled:cursor-not-allowed disabled:opacity-40",
3435
+ option.value === value && "bg-semantic-primary text-semantic-text-inverted"
3436
+ )}
3437
+ onClick={() => {
3438
+ if (option.disabled) return;
3439
+
3440
+ onValueChange(option.value);
3441
+ onOpenChange(false);
3442
+ }}
3443
+ >
3444
+ {option.label}
3445
+ </button>
3446
+ ))}
3447
+ </div>
3448
+ )}
3449
+ </div>
3450
+ );
3451
+ }
3452
+
3271
3453
  const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3272
3454
  (
3273
3455
  {
@@ -3283,6 +3465,7 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3283
3465
  readOnly = false,
3284
3466
  name,
3285
3467
  showEndTime = true,
3468
+ showSeconds,
3286
3469
  showClear = true,
3287
3470
  closeOnSelect = false,
3288
3471
  startTimeLabel = "Start Time",
@@ -3305,7 +3488,6 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3305
3488
  const showCalendar = pickerVariant !== "time-only";
3306
3489
  const showTimeFields = pickerVariant !== "date-only";
3307
3490
  const resolvedShowEndTime = showTimeFields && showEndTime;
3308
- const placeholder = placeholderProp ?? getDefaultPlaceholder(pickerVariant);
3309
3491
  const isValueControlled = value !== undefined;
3310
3492
  const isOpenControlled = controlledOpen !== undefined;
3311
3493
  const [internalValue, setInternalValue] = React.useState(() =>
@@ -3321,17 +3503,33 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3321
3503
  const hasTimeValue = isValueControlled
3322
3504
  ? Boolean(value?.date || value?.startTime || value?.endTime)
3323
3505
  : internalHasTimeValue;
3506
+ const resolvedShowSeconds =
3507
+ showSeconds ??
3508
+ (timeHasVisibleSeconds(currentValue.startTime) ||
3509
+ (resolvedShowEndTime && timeHasVisibleSeconds(currentValue.endTime)));
3510
+ const placeholder =
3511
+ placeholderProp ?? getDefaultPlaceholder(pickerVariant, resolvedShowSeconds);
3324
3512
  const open = isOpenControlled ? controlledOpen : internalOpen;
3325
3513
  const [visibleMonth, setVisibleMonth] = React.useState(() =>
3326
3514
  startOfMonth(currentValue.date ?? new Date())
3327
3515
  );
3328
3516
  const [dateInputValue, setDateInputValue] = React.useState(() =>
3329
- formatDateForDisplay(currentValue.date, currentValue.startTime)
3517
+ formatValueForDisplay(
3518
+ currentValue,
3519
+ pickerVariant,
3520
+ resolvedShowEndTime,
3521
+ hasTimeValue,
3522
+ resolvedShowSeconds
3523
+ )
3330
3524
  );
3331
3525
  const [isDateInputFocused, setIsDateInputFocused] = React.useState(false);
3332
3526
  const rootRef = React.useRef<HTMLDivElement | null>(null);
3333
3527
  const triggerRef = React.useRef<HTMLDivElement | null>(null);
3334
3528
  const popoverRef = React.useRef<HTMLDivElement | null>(null);
3529
+ const startTimeInputRef = React.useRef<HTMLInputElement | null>(null);
3530
+ const endTimeInputRef = React.useRef<HTMLInputElement | null>(null);
3531
+ const [openCalendarDropdown, setOpenCalendarDropdown] =
3532
+ React.useState<CalendarDropdownKind | null>(null);
3335
3533
  const usesContainerPortal = portalContainer !== undefined;
3336
3534
  const floatingStrategy: Strategy = usesContainerPortal
3337
3535
  ? "absolute"
@@ -3378,7 +3576,8 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3378
3576
  currentValue,
3379
3577
  pickerVariant,
3380
3578
  resolvedShowEndTime,
3381
- hasTimeValue
3579
+ hasTimeValue,
3580
+ resolvedShowSeconds
3382
3581
  );
3383
3582
  const effectiveMinDate = React.useMemo(() => {
3384
3583
  if (!disablePastDates) return minDate;
@@ -3397,6 +3596,10 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3397
3596
 
3398
3597
  const setOpen = React.useCallback(
3399
3598
  (nextOpen: boolean) => {
3599
+ if (!nextOpen) {
3600
+ setOpenCalendarDropdown(null);
3601
+ }
3602
+
3400
3603
  if (!isOpenControlled) {
3401
3604
  setInternalOpen(nextOpen);
3402
3605
  }
@@ -3561,7 +3764,8 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3561
3764
  if (pickerVariant === "time-only") {
3562
3765
  const nextInputValue = sanitizeTypedTimeInput(
3563
3766
  event.target.value,
3564
- dateInputValue
3767
+ dateInputValue,
3768
+ resolvedShowSeconds
3565
3769
  );
3566
3770
  setDateInputValue(nextInputValue);
3567
3771
 
@@ -3627,7 +3831,8 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3627
3831
 
3628
3832
  const nextInputValue = sanitizeTypedDateTimeInput(
3629
3833
  event.target.value,
3630
- dateInputValue
3834
+ dateInputValue,
3835
+ resolvedShowSeconds
3631
3836
  );
3632
3837
  setDateInputValue(nextInputValue);
3633
3838
 
@@ -3675,7 +3880,8 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3675
3880
  dateInputValue,
3676
3881
  cursorPosition,
3677
3882
  direction,
3678
- currentValue.startTime
3883
+ currentValue.startTime,
3884
+ resolvedShowSeconds
3679
3885
  );
3680
3886
 
3681
3887
  if (!steppedDateTime) return;
@@ -3691,10 +3897,13 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3691
3897
 
3692
3898
  const nextInputValue = formatDateForDisplay(
3693
3899
  steppedDateTime.date,
3694
- steppedDateTime.startTime
3900
+ steppedDateTime.startTime,
3901
+ resolvedShowSeconds
3695
3902
  );
3903
+ const dateTimeInputSegmentRanges =
3904
+ getDateTimeInputSegmentRanges(resolvedShowSeconds);
3696
3905
  const [selectionStart, selectionEnd] =
3697
- DATE_TIME_INPUT_SEGMENT_RANGES[steppedDateTime.segment];
3906
+ dateTimeInputSegmentRanges[steppedDateTime.segment];
3698
3907
  const resolvedInputValue =
3699
3908
  pickerVariant === "date-only"
3700
3909
  ? formatDateOnlyForDisplay(steppedDateTime.date)
@@ -3723,7 +3932,8 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3723
3932
  currentValue,
3724
3933
  pickerVariant,
3725
3934
  resolvedShowEndTime,
3726
- hasTimeValue
3935
+ hasTimeValue,
3936
+ resolvedShowSeconds
3727
3937
  )
3728
3938
  );
3729
3939
  };
@@ -3773,25 +3983,15 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3773
3983
  <ChevronLeft className="size-4" aria-hidden="true" />
3774
3984
  </button>
3775
3985
  <div className="flex min-w-0 items-center gap-1.5">
3776
- <label className="sr-only" htmlFor={\`\${triggerId}-month\`}>
3777
- Month
3778
- </label>
3779
- <select
3986
+ <CalendarDropdown
3780
3987
  id={\`\${triggerId}-month\`}
3781
- aria-label="Month"
3782
- className={CALENDAR_NATIVE_SELECT_CLASS}
3988
+ label="Month"
3783
3989
  value={visibleMonth.getMonth().toString()}
3784
- onChange={(event) => {
3785
- syncCalendarMonthAndValue(
3786
- new Date(
3787
- visibleMonth.getFullYear(),
3788
- Number(event.target.value),
3789
- 1
3790
- )
3791
- );
3792
- }}
3793
- >
3794
- {monthNames.map((monthName, monthIndex) => {
3990
+ open={openCalendarDropdown === "month"}
3991
+ onOpenChange={(nextOpen) =>
3992
+ setOpenCalendarDropdown(nextOpen ? "month" : null)
3993
+ }
3994
+ options={monthNames.map((monthName, monthIndex) => {
3795
3995
  const optionMonth = new Date(
3796
3996
  visibleMonth.getFullYear(),
3797
3997
  monthIndex,
@@ -3802,41 +4002,44 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3802
4002
  isMonthBefore(optionMonth, effectiveMinDate)) ||
3803
4003
  (maxDate && isMonthAfter(optionMonth, maxDate));
3804
4004
 
3805
- return (
3806
- <option
3807
- key={monthName}
3808
- value={monthIndex.toString()}
3809
- disabled={!!isDisabled}
3810
- >
3811
- {monthName}
3812
- </option>
3813
- );
4005
+ return {
4006
+ value: monthIndex.toString(),
4007
+ label: monthName,
4008
+ disabled: !!isDisabled,
4009
+ };
3814
4010
  })}
3815
- </select>
3816
- <label className="sr-only" htmlFor={\`\${triggerId}-year\`}>
3817
- Year
3818
- </label>
3819
- <select
4011
+ onValueChange={(nextMonth) => {
4012
+ syncCalendarMonthAndValue(
4013
+ new Date(
4014
+ visibleMonth.getFullYear(),
4015
+ Number(nextMonth),
4016
+ 1
4017
+ )
4018
+ );
4019
+ }}
4020
+ />
4021
+ <CalendarDropdown
3820
4022
  id={\`\${triggerId}-year\`}
3821
- aria-label="Year"
3822
- className={CALENDAR_NATIVE_SELECT_CLASS}
4023
+ label="Year"
3823
4024
  value={visibleMonth.getFullYear().toString()}
3824
- onChange={(event) => {
4025
+ open={openCalendarDropdown === "year"}
4026
+ onOpenChange={(nextOpen) =>
4027
+ setOpenCalendarDropdown(nextOpen ? "year" : null)
4028
+ }
4029
+ options={yearOptions.map((year) => ({
4030
+ value: year.toString(),
4031
+ label: year.toString(),
4032
+ }))}
4033
+ onValueChange={(nextYear) => {
3825
4034
  syncCalendarMonthAndValue(
3826
4035
  new Date(
3827
- Number(event.target.value),
4036
+ Number(nextYear),
3828
4037
  visibleMonth.getMonth(),
3829
4038
  1
3830
4039
  )
3831
4040
  );
3832
4041
  }}
3833
- >
3834
- {yearOptions.map((year) => (
3835
- <option key={year} value={year.toString()}>
3836
- {year}
3837
- </option>
3838
- ))}
3839
- </select>
4042
+ />
3840
4043
  <div id={\`\${triggerId}-calendar-heading\`} className="sr-only">
3841
4044
  {monthFormatter.format(visibleMonth)}
3842
4045
  </div>
@@ -3929,22 +4132,32 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3929
4132
  >
3930
4133
  {startTimeLabel}
3931
4134
  </label>
3932
- <div className="relative">
4135
+ <div
4136
+ className="relative cursor-pointer"
4137
+ onClick={() => openNativeTimePicker(startTimeInputRef.current)}
4138
+ >
3933
4139
  <Clock2
3934
4140
  className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-semantic-text-muted"
3935
4141
  aria-hidden="true"
3936
4142
  />
3937
4143
  <input
4144
+ ref={startTimeInputRef}
3938
4145
  id={\`\${triggerId}-start-time\`}
3939
4146
  type="time"
3940
- step="1"
3941
- value={currentValue.startTime}
4147
+ step={resolvedShowSeconds ? "1" : "60"}
4148
+ value={formatTimeForTimeInput(
4149
+ currentValue.startTime,
4150
+ resolvedShowSeconds
4151
+ )}
3942
4152
  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)]"
3943
4153
  onChange={(event) =>
3944
4154
  updateValue(
3945
4155
  {
3946
4156
  ...currentValue,
3947
- startTime: event.target.value,
4157
+ startTime: normalizeTimeInputValue(
4158
+ event.target.value,
4159
+ currentValue.startTime
4160
+ ),
3948
4161
  },
3949
4162
  { hasTimeValue: true }
3950
4163
  )
@@ -3961,22 +4174,32 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3961
4174
  >
3962
4175
  {endTimeLabel}
3963
4176
  </label>
3964
- <div className="relative">
4177
+ <div
4178
+ className="relative cursor-pointer"
4179
+ onClick={() => openNativeTimePicker(endTimeInputRef.current)}
4180
+ >
3965
4181
  <Clock2
3966
4182
  className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-semantic-text-muted"
3967
4183
  aria-hidden="true"
3968
4184
  />
3969
4185
  <input
4186
+ ref={endTimeInputRef}
3970
4187
  id={\`\${triggerId}-end-time\`}
3971
4188
  type="time"
3972
- step="1"
3973
- value={currentValue.endTime}
4189
+ step={resolvedShowSeconds ? "1" : "60"}
4190
+ value={formatTimeForTimeInput(
4191
+ currentValue.endTime,
4192
+ resolvedShowSeconds
4193
+ )}
3974
4194
  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)]"
3975
4195
  onChange={(event) =>
3976
4196
  updateValue(
3977
4197
  {
3978
4198
  ...currentValue,
3979
- endTime: event.target.value,
4199
+ endTime: normalizeTimeInputValue(
4200
+ event.target.value,
4201
+ currentValue.endTime
4202
+ ),
3980
4203
  },
3981
4204
  { hasTimeValue: true }
3982
4205
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-mcp",
3
- "version": "0.2.329",
3
+ "version": "0.2.330",
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",