myoperator-mcp 0.2.328 → 0.2.329

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 +440 -243
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2520,13 +2520,6 @@ import { cva, type VariantProps } from "class-variance-authority";
2520
2520
  import { ChevronLeft, ChevronRight, Clock2, X } from "lucide-react";
2521
2521
 
2522
2522
  import { cn } from "@/lib/utils";
2523
- import {
2524
- Select,
2525
- SelectContent,
2526
- SelectItem,
2527
- SelectTrigger,
2528
- SelectValue,
2529
- } from "./select";
2530
2523
 
2531
2524
  const DEFAULT_START_TIME = "10:30:00";
2532
2525
  const DEFAULT_END_TIME = "12:30:00";
@@ -2540,14 +2533,8 @@ const POPOVER_WIDTH_VAR = "--date-time-picker-popover-width";
2540
2533
  const CALENDAR_PLACEMENT: Placement = "bottom-start";
2541
2534
  const YEAR_RANGE_BEFORE = 100;
2542
2535
  const YEAR_RANGE_AFTER = 10;
2543
- const CALENDAR_SELECT_CONTENT_SELECTOR =
2544
- "[data-date-time-picker-calendar-select]";
2545
- const CALENDAR_SELECT_TRIGGER_SELECTOR =
2546
- "[data-date-time-picker-calendar-select-trigger]";
2547
- const CALENDAR_SELECT_TRIGGER_CLASS = "!w-[90px] !min-w-[90px] !gap-[6px] !px-3";
2548
- const CALENDAR_SELECT_CONTENT_CLASS =
2549
- "z-[10060] w-[var(--radix-select-trigger-width)] min-w-[var(--radix-select-trigger-width)] max-h-[min(16rem,var(--radix-select-content-available-height))]";
2550
- type CalendarSelect = "month" | "year";
2536
+ const CALENDAR_NATIVE_SELECT_CLASS =
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)]";
2551
2538
  const DATE_TIME_INPUT_SEGMENT_RANGES = {
2552
2539
  day: [0, 2],
2553
2540
  month: [3, 5],
@@ -2570,6 +2557,11 @@ const monthFormatter = new Intl.DateTimeFormat("en-US", {
2570
2557
 
2571
2558
  const dateTimePickerVariants = cva("relative inline-block w-full max-w-full", {
2572
2559
  variants: {
2560
+ variant: {
2561
+ "date-time": "",
2562
+ "date-only": "",
2563
+ "time-only": "",
2564
+ },
2573
2565
  size: {
2574
2566
  sm: "sm:w-[280px]",
2575
2567
  default: "sm:w-[336px]",
@@ -2577,6 +2569,7 @@ const dateTimePickerVariants = cva("relative inline-block w-full max-w-full", {
2577
2569
  },
2578
2570
  },
2579
2571
  defaultVariants: {
2572
+ variant: "date-time",
2580
2573
  size: "default",
2581
2574
  },
2582
2575
  });
@@ -2635,6 +2628,10 @@ export interface DateTimePickerProps
2635
2628
  portalContainer?: HTMLElement | null;
2636
2629
  }
2637
2630
 
2631
+ type DateTimePickerVariant = NonNullable<
2632
+ VariantProps<typeof dateTimePickerVariants>["variant"]
2633
+ >;
2634
+
2638
2635
  function normalizeValue(
2639
2636
  value?: Partial<DateTimePickerValue>
2640
2637
  ): DateTimePickerValue {
@@ -2694,6 +2691,13 @@ function clampMonth(date: Date, minDate?: Date, maxDate?: Date) {
2694
2691
  return startOfMonth(date);
2695
2692
  }
2696
2693
 
2694
+ function isSelectableDay(date: Date, minDate?: Date, maxDate?: Date) {
2695
+ return (
2696
+ !(minDate && isBeforeDay(date, minDate)) &&
2697
+ !(maxDate && isAfterDay(date, maxDate))
2698
+ );
2699
+ }
2700
+
2697
2701
  function getYearOptions(visibleMonth: Date, minDate?: Date, maxDate?: Date) {
2698
2702
  const currentYear = new Date().getFullYear();
2699
2703
  const selectedYear = visibleMonth.getFullYear();
@@ -2742,12 +2746,6 @@ function isPointerInsideElement(
2742
2746
  return false;
2743
2747
  }
2744
2748
 
2745
- function isPointerInsideSelector(event: MouseEvent, selector: string) {
2746
- const target = event.target;
2747
-
2748
- return target instanceof Element && target.closest(selector) !== null;
2749
- }
2750
-
2751
2749
  function formatTimeForDisplay(time: string) {
2752
2750
  const [hour = "0", minute = "0"] = time.split(":");
2753
2751
  const hourNumber = Number(hour);
@@ -2768,6 +2766,44 @@ function formatDateForDisplay(date?: Date, time?: string) {
2768
2766
  return \`\${day}/\${month}/\${year} \${formattedTime}\`;
2769
2767
  }
2770
2768
 
2769
+ function formatDateOnlyForDisplay(date?: Date) {
2770
+ if (!date) return "";
2771
+
2772
+ const month = (date.getMonth() + 1).toString().padStart(2, "0");
2773
+ const day = date.getDate().toString().padStart(2, "0");
2774
+ const year = date.getFullYear();
2775
+
2776
+ return \`\${day}/\${month}/\${year}\`;
2777
+ }
2778
+
2779
+ function formatValueForDisplay(
2780
+ value: DateTimePickerValue,
2781
+ variant: DateTimePickerVariant,
2782
+ showEndTime: boolean,
2783
+ hasTimeValue: boolean
2784
+ ) {
2785
+ if (variant === "date-only") {
2786
+ return formatDateOnlyForDisplay(value.date);
2787
+ }
2788
+
2789
+ if (variant === "time-only") {
2790
+ if (!hasTimeValue) return "";
2791
+
2792
+ return showEndTime
2793
+ ? \`\${formatTimeForDisplay(value.startTime)} - \${formatTimeForDisplay(value.endTime)}\`
2794
+ : formatTimeForDisplay(value.startTime);
2795
+ }
2796
+
2797
+ return formatDateForDisplay(value.date, value.startTime);
2798
+ }
2799
+
2800
+ function getDefaultPlaceholder(variant: DateTimePickerVariant) {
2801
+ if (variant === "date-only") return "--/--/----";
2802
+ if (variant === "time-only") return "--:-- --";
2803
+
2804
+ return DEFAULT_PLACEHOLDER;
2805
+ }
2806
+
2771
2807
  function parseDatePart(datePart: string) {
2772
2808
  const isoMatch = datePart.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/);
2773
2809
  const dayFirstMatch = datePart.match(/^(\\d{1,2})[/-](\\d{1,2})[/-](\\d{4})$/);
@@ -3020,6 +3056,33 @@ function formatTimeInput(restValue: string) {
3020
3056
  return [timeValue, meridiem].filter(Boolean).join(" ");
3021
3057
  }
3022
3058
 
3059
+ function sanitizeTypedDateInput(value: string, previousValue: string) {
3060
+ const trimmedValue = value.trimStart();
3061
+ if (/^[A-Za-z]/.test(trimmedValue)) return previousValue;
3062
+
3063
+ const normalizedValue = value.toUpperCase().replace(/[^0-9\\s/-]/g, "");
3064
+ const { dateDigits, dateValue, isValid } = splitTypedDateInput(normalizedValue);
3065
+ const limitedDateDigits = dateDigits.slice(0, 8);
3066
+
3067
+ if (!limitedDateDigits) return "";
3068
+ if (!isValid) return previousValue;
3069
+
3070
+ return dateValue;
3071
+ }
3072
+
3073
+ function sanitizeTypedTimeInput(value: string, previousValue: string) {
3074
+ const trimmedValue = value.trimStart();
3075
+ if (/^[A-Za-z]/.test(trimmedValue) && !/^[AP]/i.test(trimmedValue)) {
3076
+ return previousValue;
3077
+ }
3078
+
3079
+ const normalizedValue = value.toUpperCase().replace(/[^0-9\\s:APM]/g, "");
3080
+ const formattedTime = formatTimeInput(normalizedValue);
3081
+ if (formattedTime === null) return previousValue;
3082
+
3083
+ return formattedTime;
3084
+ }
3085
+
3023
3086
  function sanitizeTypedDateTimeInput(value: string, previousValue: string) {
3024
3087
  const trimmedValue = value.trimStart();
3025
3088
  if (/^[A-Za-z]/.test(trimmedValue)) return previousValue;
@@ -3144,9 +3207,40 @@ function parseTypedDateTime(value: string) {
3144
3207
  return { date, startTime };
3145
3208
  }
3146
3209
 
3147
- function formatHiddenValue(value: DateTimePickerValue) {
3210
+ function parseTypedDate(value: string) {
3211
+ const typedValue = value.trim();
3212
+ if (!typedValue) return undefined;
3213
+
3214
+ const typedMatch = typedValue.match(
3215
+ /^(\\d{4}-\\d{1,2}-\\d{1,2}|\\d{1,2}[/-]\\d{1,2}[/-]\\d{4})$/
3216
+ );
3217
+ if (!typedMatch) return null;
3218
+
3219
+ return parseDatePart(typedValue);
3220
+ }
3221
+
3222
+ function formatHiddenValue(
3223
+ value: DateTimePickerValue,
3224
+ variant: DateTimePickerVariant,
3225
+ showEndTime: boolean,
3226
+ hasTimeValue: boolean
3227
+ ) {
3228
+ if (variant === "time-only") {
3229
+ if (!hasTimeValue) return "";
3230
+
3231
+ return showEndTime ? \`\${value.startTime}/\${value.endTime}\` : value.startTime;
3232
+ }
3233
+
3148
3234
  if (!value.date) return "";
3149
3235
 
3236
+ if (variant === "date-only") {
3237
+ const month = (value.date.getMonth() + 1).toString().padStart(2, "0");
3238
+ const day = value.date.getDate().toString().padStart(2, "0");
3239
+ const year = value.date.getFullYear();
3240
+
3241
+ return \`\${year}-\${month}-\${day}\`;
3242
+ }
3243
+
3150
3244
  const month = (value.date.getMonth() + 1).toString().padStart(2, "0");
3151
3245
  const day = value.date.getDate().toString().padStart(2, "0");
3152
3246
  const year = value.date.getFullYear();
@@ -3178,12 +3272,13 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3178
3272
  (
3179
3273
  {
3180
3274
  className,
3275
+ variant,
3181
3276
  size,
3182
3277
  state,
3183
3278
  value,
3184
3279
  defaultValue,
3185
3280
  onValueChange,
3186
- placeholder = DEFAULT_PLACEHOLDER,
3281
+ placeholder: placeholderProp,
3187
3282
  disabled = false,
3188
3283
  readOnly = false,
3189
3284
  name,
@@ -3206,15 +3301,26 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3206
3301
  ) => {
3207
3302
  const generatedId = React.useId();
3208
3303
  const triggerId = id ?? generatedId;
3304
+ const pickerVariant = variant ?? "date-time";
3305
+ const showCalendar = pickerVariant !== "time-only";
3306
+ const showTimeFields = pickerVariant !== "date-only";
3307
+ const resolvedShowEndTime = showTimeFields && showEndTime;
3308
+ const placeholder = placeholderProp ?? getDefaultPlaceholder(pickerVariant);
3209
3309
  const isValueControlled = value !== undefined;
3210
3310
  const isOpenControlled = controlledOpen !== undefined;
3211
3311
  const [internalValue, setInternalValue] = React.useState(() =>
3212
3312
  normalizeValue(defaultValue)
3213
3313
  );
3314
+ const [internalHasTimeValue, setInternalHasTimeValue] = React.useState(() =>
3315
+ Boolean(defaultValue?.date || defaultValue?.startTime || defaultValue?.endTime)
3316
+ );
3214
3317
  const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
3215
3318
  const currentValue = normalizeValue(
3216
3319
  isValueControlled ? value : internalValue
3217
3320
  );
3321
+ const hasTimeValue = isValueControlled
3322
+ ? Boolean(value?.date || value?.startTime || value?.endTime)
3323
+ : internalHasTimeValue;
3218
3324
  const open = isOpenControlled ? controlledOpen : internalOpen;
3219
3325
  const [visibleMonth, setVisibleMonth] = React.useState(() =>
3220
3326
  startOfMonth(currentValue.date ?? new Date())
@@ -3223,8 +3329,6 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3223
3329
  formatDateForDisplay(currentValue.date, currentValue.startTime)
3224
3330
  );
3225
3331
  const [isDateInputFocused, setIsDateInputFocused] = React.useState(false);
3226
- const [openCalendarSelect, setOpenCalendarSelect] =
3227
- React.useState<CalendarSelect | null>(null);
3228
3332
  const rootRef = React.useRef<HTMLDivElement | null>(null);
3229
3333
  const triggerRef = React.useRef<HTMLDivElement | null>(null);
3230
3334
  const popoverRef = React.useRef<HTMLDivElement | null>(null);
@@ -3270,9 +3374,11 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3270
3374
  () => getCalendarDays(visibleMonth),
3271
3375
  [visibleMonth]
3272
3376
  );
3273
- const displayValue = formatDateForDisplay(
3274
- currentValue.date,
3275
- currentValue.startTime
3377
+ const displayValue = formatValueForDisplay(
3378
+ currentValue,
3379
+ pickerVariant,
3380
+ resolvedShowEndTime,
3381
+ hasTimeValue
3276
3382
  );
3277
3383
  const effectiveMinDate = React.useMemo(() => {
3278
3384
  if (!disablePastDates) return minDate;
@@ -3295,39 +3401,11 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3295
3401
  setInternalOpen(nextOpen);
3296
3402
  }
3297
3403
 
3298
- if (!nextOpen) {
3299
- setOpenCalendarSelect(null);
3300
- }
3301
-
3302
3404
  onOpenChange?.(nextOpen);
3303
3405
  },
3304
3406
  [isOpenControlled, onOpenChange]
3305
3407
  );
3306
3408
 
3307
- const handleCalendarSelectOpenChange = React.useCallback(
3308
- (select: CalendarSelect, nextOpen: boolean) => {
3309
- setOpenCalendarSelect((currentSelect) => {
3310
- if (nextOpen) return select;
3311
-
3312
- return currentSelect === select ? null : currentSelect;
3313
- });
3314
- },
3315
- []
3316
- );
3317
-
3318
- const handleCalendarSelectTriggerPointerDown = React.useCallback(
3319
- (
3320
- select: CalendarSelect,
3321
- event: React.PointerEvent<HTMLButtonElement>
3322
- ) => {
3323
- if (openCalendarSelect !== select) return;
3324
-
3325
- event.preventDefault();
3326
- setOpenCalendarSelect(null);
3327
- },
3328
- [openCalendarSelect]
3329
- );
3330
-
3331
3409
  const setTriggerRef = React.useCallback(
3332
3410
  (node: HTMLDivElement | null) => {
3333
3411
  triggerRef.current = node;
@@ -3356,29 +3434,20 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3356
3434
  }
3357
3435
  }, [displayValue, isDateInputFocused]);
3358
3436
 
3359
- React.useEffect(() => {
3360
- if (!open) {
3361
- setOpenCalendarSelect(null);
3362
- }
3363
- }, [open]);
3364
-
3365
3437
  React.useEffect(() => {
3366
3438
  if (!open) return;
3367
3439
 
3368
3440
  const handlePointerDown = (event: MouseEvent) => {
3369
3441
  if (
3370
3442
  !isPointerInsideElement(event, rootRef.current) &&
3371
- !isPointerInsideElement(event, popoverRef.current) &&
3372
- !isPointerInsideSelector(event, CALENDAR_SELECT_CONTENT_SELECTOR)
3443
+ !isPointerInsideElement(event, popoverRef.current)
3373
3444
  ) {
3374
- setOpenCalendarSelect(null);
3375
3445
  setOpen(false);
3376
3446
  }
3377
3447
  };
3378
3448
 
3379
3449
  const handleKeyDown = (event: KeyboardEvent) => {
3380
3450
  if (event.key === "Escape") {
3381
- setOpenCalendarSelect(null);
3382
3451
  setOpen(false);
3383
3452
  }
3384
3453
  };
@@ -3393,9 +3462,15 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3393
3462
  }, [open, setOpen]);
3394
3463
 
3395
3464
  const updateValue = React.useCallback(
3396
- (nextValue: DateTimePickerValue) => {
3465
+ (
3466
+ nextValue: DateTimePickerValue,
3467
+ options?: { hasTimeValue?: boolean }
3468
+ ) => {
3397
3469
  if (!isValueControlled) {
3398
3470
  setInternalValue(nextValue);
3471
+ if (options?.hasTimeValue !== undefined) {
3472
+ setInternalHasTimeValue(options.hasTimeValue);
3473
+ }
3399
3474
  }
3400
3475
 
3401
3476
  onValueChange?.(nextValue);
@@ -3412,7 +3487,7 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3412
3487
  date: undefined,
3413
3488
  startTime: currentValue.startTime,
3414
3489
  endTime: currentValue.endTime,
3415
- });
3490
+ }, { hasTimeValue: false });
3416
3491
  };
3417
3492
 
3418
3493
  const yearOptions = React.useMemo(
@@ -3427,9 +3502,129 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3427
3502
  [effectiveMinDate, maxDate]
3428
3503
  );
3429
3504
 
3505
+ const syncCalendarMonthAndValue = React.useCallback(
3506
+ (nextMonth: Date) => {
3507
+ const clampedMonth = clampMonth(nextMonth, effectiveMinDate, maxDate);
3508
+ setVisibleMonth(clampedMonth);
3509
+
3510
+ if (pickerVariant === "time-only" || !currentValue.date) return;
3511
+
3512
+ const selectedDay = currentValue.date.getDate();
3513
+ const daysInTargetMonth = getDaysInMonth(
3514
+ clampedMonth.getFullYear(),
3515
+ clampedMonth.getMonth() + 1
3516
+ );
3517
+
3518
+ if (selectedDay > daysInTargetMonth) {
3519
+ const fallbackDate = new Date(
3520
+ clampedMonth.getFullYear(),
3521
+ clampedMonth.getMonth(),
3522
+ 1
3523
+ );
3524
+
3525
+ if (!isSelectableDay(fallbackDate, effectiveMinDate, maxDate)) {
3526
+ updateValue({ ...currentValue, date: undefined });
3527
+ return;
3528
+ }
3529
+
3530
+ updateValue({ ...currentValue, date: fallbackDate });
3531
+ return;
3532
+ }
3533
+
3534
+ const nextSelectedDate = new Date(
3535
+ clampedMonth.getFullYear(),
3536
+ clampedMonth.getMonth(),
3537
+ selectedDay
3538
+ );
3539
+
3540
+ if (!isSelectableDay(nextSelectedDate, effectiveMinDate, maxDate)) {
3541
+ updateValue({ ...currentValue, date: undefined });
3542
+ return;
3543
+ }
3544
+
3545
+ if (!isSameDay(currentValue.date, nextSelectedDate)) {
3546
+ updateValue({ ...currentValue, date: nextSelectedDate });
3547
+ }
3548
+ },
3549
+ [
3550
+ currentValue,
3551
+ effectiveMinDate,
3552
+ maxDate,
3553
+ pickerVariant,
3554
+ updateValue,
3555
+ ]
3556
+ );
3557
+
3430
3558
  const handleTypedDateChange = (
3431
3559
  event: React.ChangeEvent<HTMLInputElement>
3432
3560
  ) => {
3561
+ if (pickerVariant === "time-only") {
3562
+ const nextInputValue = sanitizeTypedTimeInput(
3563
+ event.target.value,
3564
+ dateInputValue
3565
+ );
3566
+ setDateInputValue(nextInputValue);
3567
+
3568
+ if (
3569
+ nextInputValue === dateInputValue &&
3570
+ event.target.value !== dateInputValue
3571
+ ) {
3572
+ event.currentTarget.value = nextInputValue;
3573
+ return;
3574
+ }
3575
+
3576
+ const typedTime = parseTimePart(nextInputValue);
3577
+ if (typedTime === undefined) {
3578
+ updateValue({ ...currentValue, date: undefined }, { hasTimeValue: false });
3579
+ return;
3580
+ }
3581
+
3582
+ if (typedTime) {
3583
+ updateValue(
3584
+ {
3585
+ ...currentValue,
3586
+ startTime: typedTime,
3587
+ },
3588
+ { hasTimeValue: true }
3589
+ );
3590
+ }
3591
+
3592
+ return;
3593
+ }
3594
+
3595
+ if (pickerVariant === "date-only") {
3596
+ const nextInputValue = sanitizeTypedDateInput(
3597
+ event.target.value,
3598
+ dateInputValue
3599
+ );
3600
+ setDateInputValue(nextInputValue);
3601
+
3602
+ if (
3603
+ nextInputValue === dateInputValue &&
3604
+ event.target.value !== dateInputValue
3605
+ ) {
3606
+ event.currentTarget.value = nextInputValue;
3607
+ return;
3608
+ }
3609
+
3610
+ const typedDate = parseTypedDate(nextInputValue);
3611
+ if (typedDate === undefined) {
3612
+ updateValue({ ...currentValue, date: undefined });
3613
+ return;
3614
+ }
3615
+
3616
+ if (
3617
+ typedDate &&
3618
+ !(effectiveMinDate && isBeforeDay(typedDate, effectiveMinDate)) &&
3619
+ !(maxDate && isAfterDay(typedDate, maxDate))
3620
+ ) {
3621
+ updateValue({ ...currentValue, date: typedDate });
3622
+ updateVisibleMonth(typedDate);
3623
+ }
3624
+
3625
+ return;
3626
+ }
3627
+
3433
3628
  const nextInputValue = sanitizeTypedDateTimeInput(
3434
3629
  event.target.value,
3435
3630
  dateInputValue
@@ -3469,6 +3664,7 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3469
3664
  const handleTypedDateKeyDown = (
3470
3665
  event: React.KeyboardEvent<HTMLInputElement>
3471
3666
  ) => {
3667
+ if (pickerVariant === "time-only") return;
3472
3668
  if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
3473
3669
 
3474
3670
  const direction = event.key === "ArrowUp" ? 1 : -1;
@@ -3499,12 +3695,19 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3499
3695
  );
3500
3696
  const [selectionStart, selectionEnd] =
3501
3697
  DATE_TIME_INPUT_SEGMENT_RANGES[steppedDateTime.segment];
3698
+ const resolvedInputValue =
3699
+ pickerVariant === "date-only"
3700
+ ? formatDateOnlyForDisplay(steppedDateTime.date)
3701
+ : nextInputValue;
3502
3702
 
3503
- setDateInputValue(nextInputValue);
3703
+ setDateInputValue(resolvedInputValue);
3504
3704
  updateValue({
3505
3705
  ...currentValue,
3506
3706
  date: steppedDateTime.date,
3507
- startTime: steppedDateTime.startTime,
3707
+ startTime:
3708
+ pickerVariant === "date-only"
3709
+ ? currentValue.startTime
3710
+ : steppedDateTime.startTime,
3508
3711
  });
3509
3712
  updateVisibleMonth(steppedDateTime.date);
3510
3713
 
@@ -3516,7 +3719,12 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3516
3719
  const handleTypedDateBlur = () => {
3517
3720
  setIsDateInputFocused(false);
3518
3721
  setDateInputValue(
3519
- formatDateForDisplay(currentValue.date, currentValue.startTime)
3722
+ formatValueForDisplay(
3723
+ currentValue,
3724
+ pickerVariant,
3725
+ resolvedShowEndTime,
3726
+ hasTimeValue
3727
+ )
3520
3728
  );
3521
3729
  };
3522
3730
 
@@ -3530,7 +3738,8 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3530
3738
  ref={setPopoverRef}
3531
3739
  role="dialog"
3532
3740
  aria-modal="false"
3533
- aria-labelledby={\`\${triggerId}-calendar-heading\`}
3741
+ aria-labelledby={showCalendar ? \`\${triggerId}-calendar-heading\` : undefined}
3742
+ aria-label={showCalendar ? undefined : "Time picker"}
3534
3743
  className={cn(
3535
3744
  "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",
3536
3745
  "[scrollbar-gutter:stable] [scrollbar-width:thin] [scrollbar-color:var(--semantic-border-secondary)_transparent]",
@@ -3545,64 +3754,42 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3545
3754
  zIndex: 10050,
3546
3755
  visibility: isPositioned ? undefined : "hidden",
3547
3756
  }}
3548
- onPointerDown={(event) => {
3549
- if (
3550
- event.target instanceof Element &&
3551
- !event.target.closest(CALENDAR_SELECT_TRIGGER_SELECTOR)
3552
- ) {
3553
- setOpenCalendarSelect(null);
3554
- }
3555
-
3556
- event.stopPropagation();
3557
- }}
3757
+ onPointerDown={(event) => event.stopPropagation()}
3558
3758
  onMouseDown={(event) => event.stopPropagation()}
3559
3759
  onWheel={(event) => event.stopPropagation()}
3560
3760
  onTouchMove={(event) => event.stopPropagation()}
3561
3761
  >
3562
- <div className="p-3 touch-pan-y">
3563
- <div className="mb-3 flex items-center justify-between gap-2">
3564
- <button
3565
- type="button"
3566
- aria-label="Previous month"
3567
- className="p-1 rounded hover:bg-semantic-bg-hover text-semantic-text-secondary transition-colors"
3568
- onClick={() => updateVisibleMonth(addMonths(visibleMonth, -1))}
3569
- >
3570
- <ChevronLeft className="size-4" aria-hidden="true" />
3571
- </button>
3572
- <div className="flex min-w-0 items-center gap-1.5">
3573
- <label className="sr-only" htmlFor={\`\${triggerId}-month\`}>
3574
- Month
3575
- </label>
3576
- <Select
3577
- open={openCalendarSelect === "month"}
3578
- onOpenChange={(nextOpen) =>
3579
- handleCalendarSelectOpenChange("month", nextOpen)
3580
- }
3581
- value={visibleMonth.getMonth().toString()}
3582
- onValueChange={(nextMonth) =>
3583
- updateVisibleMonth(
3584
- new Date(
3585
- visibleMonth.getFullYear(),
3586
- Number(nextMonth),
3587
- 1
3588
- )
3589
- )
3762
+ {showCalendar && (
3763
+ <div className="p-3 touch-pan-y">
3764
+ <div className="mb-3 flex items-center justify-between gap-2">
3765
+ <button
3766
+ type="button"
3767
+ aria-label="Previous month"
3768
+ className="p-1 rounded hover:bg-semantic-bg-hover text-semantic-text-secondary transition-colors"
3769
+ onClick={() =>
3770
+ syncCalendarMonthAndValue(addMonths(visibleMonth, -1))
3590
3771
  }
3591
3772
  >
3592
- <SelectTrigger
3773
+ <ChevronLeft className="size-4" aria-hidden="true" />
3774
+ </button>
3775
+ <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
3593
3780
  id={\`\${triggerId}-month\`}
3594
- data-date-time-picker-calendar-select-trigger=""
3595
3781
  aria-label="Month"
3596
- className={CALENDAR_SELECT_TRIGGER_CLASS}
3597
- onPointerDown={(event) =>
3598
- handleCalendarSelectTriggerPointerDown("month", event)
3599
- }
3600
- >
3601
- <SelectValue />
3602
- </SelectTrigger>
3603
- <SelectContent
3604
- data-date-time-picker-calendar-select=""
3605
- className={CALENDAR_SELECT_CONTENT_CLASS}
3782
+ className={CALENDAR_NATIVE_SELECT_CLASS}
3783
+ 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
+ }}
3606
3793
  >
3607
3794
  {monthNames.map((monthName, monthIndex) => {
3608
3795
  const optionMonth = new Date(
@@ -3616,133 +3803,125 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3616
3803
  (maxDate && isMonthAfter(optionMonth, maxDate));
3617
3804
 
3618
3805
  return (
3619
- <SelectItem
3806
+ <option
3620
3807
  key={monthName}
3621
3808
  value={monthIndex.toString()}
3622
3809
  disabled={!!isDisabled}
3623
3810
  >
3624
3811
  {monthName}
3625
- </SelectItem>
3812
+ </option>
3626
3813
  );
3627
3814
  })}
3628
- </SelectContent>
3629
- </Select>
3630
- <label className="sr-only" htmlFor={\`\${triggerId}-year\`}>
3631
- Year
3632
- </label>
3633
- <Select
3634
- open={openCalendarSelect === "year"}
3635
- onOpenChange={(nextOpen) =>
3636
- handleCalendarSelectOpenChange("year", nextOpen)
3637
- }
3638
- value={visibleMonth.getFullYear().toString()}
3639
- onValueChange={(nextYear) =>
3640
- updateVisibleMonth(
3641
- new Date(
3642
- Number(nextYear),
3643
- visibleMonth.getMonth(),
3644
- 1
3645
- )
3646
- )
3647
- }
3648
- >
3649
- <SelectTrigger
3815
+ </select>
3816
+ <label className="sr-only" htmlFor={\`\${triggerId}-year\`}>
3817
+ Year
3818
+ </label>
3819
+ <select
3650
3820
  id={\`\${triggerId}-year\`}
3651
- data-date-time-picker-calendar-select-trigger=""
3652
3821
  aria-label="Year"
3653
- className={CALENDAR_SELECT_TRIGGER_CLASS}
3654
- onPointerDown={(event) =>
3655
- handleCalendarSelectTriggerPointerDown("year", event)
3656
- }
3657
- >
3658
- <SelectValue />
3659
- </SelectTrigger>
3660
- <SelectContent
3661
- data-date-time-picker-calendar-select=""
3662
- className={CALENDAR_SELECT_CONTENT_CLASS}
3822
+ className={CALENDAR_NATIVE_SELECT_CLASS}
3823
+ value={visibleMonth.getFullYear().toString()}
3824
+ onChange={(event) => {
3825
+ syncCalendarMonthAndValue(
3826
+ new Date(
3827
+ Number(event.target.value),
3828
+ visibleMonth.getMonth(),
3829
+ 1
3830
+ )
3831
+ );
3832
+ }}
3663
3833
  >
3664
3834
  {yearOptions.map((year) => (
3665
- <SelectItem key={year} value={year.toString()}>
3835
+ <option key={year} value={year.toString()}>
3666
3836
  {year}
3667
- </SelectItem>
3837
+ </option>
3668
3838
  ))}
3669
- </SelectContent>
3670
- </Select>
3671
- <div id={\`\${triggerId}-calendar-heading\`} className="sr-only">
3672
- {monthFormatter.format(visibleMonth)}
3839
+ </select>
3840
+ <div id={\`\${triggerId}-calendar-heading\`} className="sr-only">
3841
+ {monthFormatter.format(visibleMonth)}
3842
+ </div>
3673
3843
  </div>
3674
- </div>
3675
- <button
3676
- type="button"
3677
- aria-label="Next month"
3678
- className="p-1 rounded hover:bg-semantic-bg-hover text-semantic-text-secondary transition-colors"
3679
- onClick={() => updateVisibleMonth(addMonths(visibleMonth, 1))}
3680
- >
3681
- <ChevronRight className="size-4" aria-hidden="true" />
3682
- </button>
3683
- </div>
3684
-
3685
- <div className="grid grid-cols-7">
3686
- {weekDays.map((day) => (
3687
- <div
3688
- key={day}
3689
- className="flex size-8 items-center justify-center text-xs font-medium text-semantic-text-muted"
3844
+ <button
3845
+ type="button"
3846
+ aria-label="Next month"
3847
+ className="p-1 rounded hover:bg-semantic-bg-hover text-semantic-text-secondary transition-colors"
3848
+ onClick={() =>
3849
+ syncCalendarMonthAndValue(addMonths(visibleMonth, 1))
3850
+ }
3690
3851
  >
3691
- {day}
3692
- </div>
3693
- ))}
3694
- {calendarDays.map((day) => {
3695
- const isCurrentMonth =
3696
- day.getMonth() === visibleMonth.getMonth();
3697
- const isSelected = isSameDay(day, currentValue.date);
3698
- const isToday = isSameDay(day, new Date());
3699
- const isDisabled =
3700
- (effectiveMinDate && isBeforeDay(day, effectiveMinDate)) ||
3701
- (maxDate && isAfterDay(day, maxDate));
3702
- const dayLabel = day.toLocaleDateString("en-US", {
3703
- month: "long",
3704
- day: "numeric",
3705
- year: "numeric",
3706
- });
3707
-
3708
- return (
3709
- <button
3710
- key={day.toISOString()}
3711
- type="button"
3712
- aria-label={dayLabel}
3713
- aria-pressed={isSelected}
3714
- aria-current={isToday ? "date" : undefined}
3715
- disabled={!!isDisabled}
3716
- className={cn(
3717
- "relative flex items-center justify-center size-8 mx-auto rounded-full text-xs transition-colors",
3718
- isSelected
3719
- ? "bg-semantic-primary text-semantic-text-inverted font-semibold"
3720
- : isCurrentMonth
3721
- ? "text-semantic-text-primary hover:bg-semantic-bg-hover"
3722
- : "text-semantic-text-muted hover:bg-semantic-bg-hover",
3723
- isDisabled &&
3724
- "opacity-40 cursor-not-allowed pointer-events-none"
3725
- )}
3726
- onClick={() => {
3727
- if (isDisabled) return;
3852
+ <ChevronRight className="size-4" aria-hidden="true" />
3853
+ </button>
3854
+ </div>
3728
3855
 
3729
- updateValue({ ...currentValue, date: day });
3730
- if (closeOnSelect) {
3731
- setOpen(false);
3732
- }
3733
- }}
3856
+ <div className="grid grid-cols-7">
3857
+ {weekDays.map((day) => (
3858
+ <div
3859
+ key={day}
3860
+ className="flex size-8 items-center justify-center text-xs font-medium text-semantic-text-muted"
3734
3861
  >
3735
- {day.getDate()}
3736
- {isToday && !isSelected && (
3737
- <span className="absolute bottom-0.5 left-1/2 -translate-x-1/2 size-1 rounded-full bg-semantic-primary" />
3738
- )}
3739
- </button>
3740
- );
3741
- })}
3862
+ {day}
3863
+ </div>
3864
+ ))}
3865
+ {calendarDays.map((day) => {
3866
+ const isCurrentMonth =
3867
+ day.getMonth() === visibleMonth.getMonth();
3868
+ const isSelected = isSameDay(day, currentValue.date);
3869
+ const isToday = isSameDay(day, new Date());
3870
+ const isDisabled =
3871
+ (effectiveMinDate && isBeforeDay(day, effectiveMinDate)) ||
3872
+ (maxDate && isAfterDay(day, maxDate));
3873
+ const dayLabel = day.toLocaleDateString("en-US", {
3874
+ month: "long",
3875
+ day: "numeric",
3876
+ year: "numeric",
3877
+ });
3878
+
3879
+ return (
3880
+ <button
3881
+ key={day.toISOString()}
3882
+ type="button"
3883
+ aria-label={dayLabel}
3884
+ aria-pressed={isSelected}
3885
+ aria-current={isToday ? "date" : undefined}
3886
+ disabled={!!isDisabled}
3887
+ className={cn(
3888
+ "relative flex items-center justify-center size-8 mx-auto rounded-full text-xs transition-colors",
3889
+ isSelected
3890
+ ? "bg-semantic-primary text-semantic-text-inverted font-semibold"
3891
+ : isCurrentMonth
3892
+ ? "text-semantic-text-primary hover:bg-semantic-bg-hover"
3893
+ : "text-semantic-text-muted hover:bg-semantic-bg-hover",
3894
+ isDisabled &&
3895
+ "opacity-40 cursor-not-allowed pointer-events-none"
3896
+ )}
3897
+ onClick={() => {
3898
+ if (isDisabled) return;
3899
+
3900
+ updateValue({ ...currentValue, date: day });
3901
+ if (closeOnSelect) {
3902
+ setOpen(false);
3903
+ }
3904
+ }}
3905
+ >
3906
+ {day.getDate()}
3907
+ {isToday && !isSelected && (
3908
+ <span className="absolute bottom-0.5 left-1/2 -translate-x-1/2 size-1 rounded-full bg-semantic-primary" />
3909
+ )}
3910
+ </button>
3911
+ );
3912
+ })}
3913
+ </div>
3742
3914
  </div>
3743
- </div>
3915
+ )}
3744
3916
 
3745
- <div className="space-y-3 border-t border-solid border-semantic-border-layout bg-semantic-bg-primary p-3">
3917
+ {showTimeFields && (
3918
+ <div
3919
+ className={cn(
3920
+ "space-y-3 bg-semantic-bg-primary p-3",
3921
+ showCalendar &&
3922
+ "border-t border-solid border-semantic-border-layout"
3923
+ )}
3924
+ >
3746
3925
  <div className="flex flex-col gap-1.5">
3747
3926
  <label
3748
3927
  htmlFor={\`\${triggerId}-start-time\`}
@@ -3762,16 +3941,19 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3762
3941
  value={currentValue.startTime}
3763
3942
  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)]"
3764
3943
  onChange={(event) =>
3765
- updateValue({
3766
- ...currentValue,
3767
- startTime: event.target.value,
3768
- })
3944
+ updateValue(
3945
+ {
3946
+ ...currentValue,
3947
+ startTime: event.target.value,
3948
+ },
3949
+ { hasTimeValue: true }
3950
+ )
3769
3951
  }
3770
3952
  />
3771
3953
  </div>
3772
3954
  </div>
3773
3955
 
3774
- {showEndTime && (
3956
+ {resolvedShowEndTime && (
3775
3957
  <div className="flex flex-col gap-1.5">
3776
3958
  <label
3777
3959
  htmlFor={\`\${triggerId}-end-time\`}
@@ -3791,16 +3973,20 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3791
3973
  value={currentValue.endTime}
3792
3974
  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)]"
3793
3975
  onChange={(event) =>
3794
- updateValue({
3795
- ...currentValue,
3796
- endTime: event.target.value,
3797
- })
3976
+ updateValue(
3977
+ {
3978
+ ...currentValue,
3979
+ endTime: event.target.value,
3980
+ },
3981
+ { hasTimeValue: true }
3982
+ )
3798
3983
  }
3799
3984
  />
3800
3985
  </div>
3801
3986
  </div>
3802
3987
  )}
3803
- </div>
3988
+ </div>
3989
+ )}
3804
3990
  </div>,
3805
3991
  portalMount
3806
3992
  );
@@ -3824,7 +4010,12 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3824
4010
  <input
3825
4011
  type="hidden"
3826
4012
  name={name}
3827
- value={formatHiddenValue(currentValue)}
4013
+ value={formatHiddenValue(
4014
+ currentValue,
4015
+ pickerVariant,
4016
+ resolvedShowEndTime,
4017
+ hasTimeValue
4018
+ )}
3828
4019
  />
3829
4020
  )}
3830
4021
  <div
@@ -3846,7 +4037,13 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3846
4037
  placeholder={placeholder}
3847
4038
  aria-haspopup="dialog"
3848
4039
  aria-expanded={open}
3849
- aria-label="Date and time"
4040
+ aria-label={
4041
+ pickerVariant === "date-only"
4042
+ ? "Date"
4043
+ : pickerVariant === "time-only"
4044
+ ? "Time"
4045
+ : "Date and time"
4046
+ }
3850
4047
  className="min-w-0 flex-1 bg-transparent text-sm text-semantic-text-primary outline-none placeholder:text-semantic-text-placeholder disabled:cursor-not-allowed read-only:cursor-not-allowed"
3851
4048
  onFocus={() => {
3852
4049
  setIsDateInputFocused(true);
@@ -3870,7 +4067,7 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
3870
4067
  <button
3871
4068
  type="button"
3872
4069
  disabled={disabled || readOnly}
3873
- aria-label="Open calendar"
4070
+ aria-label={showCalendar ? "Open calendar" : "Open time picker"}
3874
4071
  className="inline-flex shrink-0 items-center justify-center rounded text-semantic-text-muted hover:bg-semantic-bg-hover hover:text-semantic-text-primary disabled:cursor-not-allowed"
3875
4072
  onClick={() => setOpen(!open)}
3876
4073
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-mcp",
3
- "version": "0.2.328",
3
+ "version": "0.2.329",
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",