@mhome/ui 0.1.5 → 0.1.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhome/ui",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "description": "mHome UI Component Library",
6
6
  "main": "dist/index.cjs.js",
@@ -26,6 +26,8 @@ const DatePicker = React.forwardRef(
26
26
  const inputRef = React.useRef(null);
27
27
  const yearPickerRef = React.useRef(null);
28
28
  const currentYearButtonRef = React.useRef(null);
29
+ const [viewMode, setViewMode] = React.useState("month"); // "month" or "year"
30
+ const [showYearPicker, setShowYearPicker] = React.useState(false);
29
31
 
30
32
  React.useImperativeHandle(ref, () => inputRef.current);
31
33
 
@@ -52,7 +54,7 @@ const DatePicker = React.forwardRef(
52
54
  }
53
55
  };
54
56
 
55
- const handleClickOutside = (event) => {
57
+ const handleClickOutside = React.useCallback((event) => {
56
58
  if (
57
59
  calendarRef.current &&
58
60
  !calendarRef.current.contains(event.target) &&
@@ -65,7 +67,7 @@ const DatePicker = React.forwardRef(
65
67
  onOpenChange(false);
66
68
  }
67
69
  }
68
- };
70
+ }, [onOpenChange]);
69
71
 
70
72
  React.useEffect(() => {
71
73
  if (open) {
@@ -74,12 +76,10 @@ const DatePicker = React.forwardRef(
74
76
  document.removeEventListener("mousedown", handleClickOutside);
75
77
  };
76
78
  }
77
- }, [open]);
79
+ }, [open, handleClickOutside]);
78
80
 
79
81
  const textFieldProps = slotProps?.textField || {};
80
82
  const displayValue = selectedDate ? selectedDate.format("YYYY-MM-DD") : "";
81
- const [viewMode, setViewMode] = React.useState("month"); // "month" or "year"
82
- const [showYearPicker, setShowYearPicker] = React.useState(false);
83
83
 
84
84
  // Scroll to current year when year picker opens
85
85
  React.useEffect(() => {
@@ -363,17 +363,7 @@ DialogFooter.displayName = "DialogFooter";
363
363
 
364
364
  const DialogTitle = React.forwardRef(
365
365
  ({ className, style, sx, ...props }, ref) => {
366
- const actualMode = useIsDarkMode();
367
- const backgroundType = useRecoilValue(backgroundTypeState);
368
- const bgColor = getBackground(backgroundType, actualMode === "dark");
369
- const isGradient =
370
- bgColor &&
371
- typeof bgColor === "string" &&
372
- (bgColor.startsWith("linear-gradient") ||
373
- bgColor.startsWith("radial-gradient") ||
374
- bgColor.startsWith("conic-gradient"));
375
-
376
- // Check if DialogTitle is inside DialogHeader
366
+ // Check if DialogTitle is inside DialogHeader (DialogHeader already has padding)
377
367
  const isInsideDialogHeader = React.useContext(DialogHeaderContext);
378
368
 
379
369
  // Convert sx prop to style if provided, handling MUI spacing
@@ -456,42 +446,6 @@ const DialogTitle = React.forwardRef(
456
446
  return converted;
457
447
  }, [sx]);
458
448
 
459
- // Extract padding from style to allow override
460
- const {
461
- padding,
462
- paddingTop,
463
- paddingBottom,
464
- paddingLeft,
465
- paddingRight,
466
- backgroundColor,
467
- ...restStyle
468
- } = style || {};
469
-
470
- // Check if padding is explicitly set in style, sxStyles, or className
471
- // Check for padding-related Tailwind classes in className
472
- const hasPaddingInClassName = className && (
473
- /\b(p|px|py|pt|pb|pl|pr)-\d+/.test(className) ||
474
- /\bpadding/.test(className)
475
- );
476
-
477
- const hasAnyPadding =
478
- padding !== undefined ||
479
- paddingTop !== undefined ||
480
- paddingBottom !== undefined ||
481
- paddingLeft !== undefined ||
482
- paddingRight !== undefined ||
483
- sxStyles.padding !== undefined ||
484
- sxStyles.paddingTop !== undefined ||
485
- sxStyles.paddingBottom !== undefined ||
486
- sxStyles.paddingLeft !== undefined ||
487
- sxStyles.paddingRight !== undefined ||
488
- hasPaddingInClassName;
489
-
490
- // DialogTitle should have default padding when used standalone (not inside DialogHeader)
491
- // When inside DialogHeader, DialogHeader provides px-6 pt-6, so DialogTitle doesn't need padding
492
- // When used standalone, DialogTitle should have px-6 pt-6 to match DialogHeader's padding
493
- const defaultPaddingClass = !isInsideDialogHeader && !hasAnyPadding ? "px-6 pt-6" : "";
494
-
495
449
  return (
496
450
  <h2
497
451
  ref={ref}
@@ -499,28 +453,14 @@ const DialogTitle = React.forwardRef(
499
453
  "rounded-t-[inherit]",
500
454
  "text-lg font-semibold leading-none tracking-tight",
501
455
  "text-foreground",
502
- "text-left", // Always left align
503
- defaultPaddingClass,
456
+ "text-left",
457
+ !isInsideDialogHeader && "px-6 pt-6", // Default padding only when not inside DialogHeader
504
458
  className
505
459
  )}
506
460
  style={{
507
- // Only set background if explicitly provided, otherwise inherit from parent
508
- ...(backgroundColor !== undefined
509
- ? backgroundColor.startsWith("linear-gradient") ||
510
- backgroundColor.startsWith("radial-gradient") ||
511
- backgroundColor.startsWith("conic-gradient")
512
- ? { background: backgroundColor }
513
- : { backgroundColor }
514
- : {}),
515
- // Allow padding override via style prop (for standalone use)
516
- padding: padding,
517
- paddingLeft: paddingLeft,
518
- paddingRight: paddingRight,
519
- paddingTop: paddingTop,
520
- paddingBottom: paddingBottom,
521
- textAlign: "left", // Ensure left alignment
461
+ textAlign: "left",
522
462
  ...sxStyles,
523
- ...restStyle,
463
+ ...style,
524
464
  }}
525
465
  {...props}
526
466
  />
@@ -70,8 +70,9 @@ const IconButton = React.forwardRef(
70
70
  }, [children, size]);
71
71
 
72
72
  // Warn if no aria-label is provided (accessibility best practice)
73
+ // Only warn in development mode
73
74
  React.useEffect(() => {
74
- if (!ariaLabel && !props["aria-labelledby"]) {
75
+ if (process.env.NODE_ENV === "development" && !ariaLabel && !props["aria-labelledby"]) {
75
76
  console.warn(
76
77
  "IconButton: Missing aria-label or aria-labelledby. Icon-only buttons should have accessible labels."
77
78
  );
@@ -28,6 +28,8 @@ const Select = React.forwardRef(
28
28
  multiple = false,
29
29
  sx,
30
30
  style,
31
+ menuStyle, // Style for the dropdown menu (the open listbox)
32
+ menuClassName, // ClassName for the dropdown menu
31
33
  ...props
32
34
  },
33
35
  ref
@@ -154,7 +156,7 @@ const Select = React.forwardRef(
154
156
  paddingRight: endAdornment ? "8px" : "14px",
155
157
  paddingTop: "6px",
156
158
  paddingBottom: "6px",
157
- // If borderRadius is provided in props.style, use it; otherwise let Tailwind's rounded-3xl class handle it
159
+ // If borderRadius is provided in props.style, use it; otherwise let Tailwind classes handle it
158
160
  ...(style?.borderRadius !== undefined
159
161
  ? { borderRadius: style.borderRadius }
160
162
  : {}),
@@ -194,16 +196,21 @@ const Select = React.forwardRef(
194
196
  id={`${selectId}-listbox`}
195
197
  role="listbox"
196
198
  aria-label={label || "Options"}
197
- className="absolute z-50 w-full mt-1 rounded-lg shadow-lg"
199
+ className={cn(
200
+ "absolute z-50 w-full mt-1 shadow-lg",
201
+ menuClassName
202
+ )}
198
203
  style={{
199
204
  backgroundColor: "hsl(var(--card))",
200
205
  boxShadow: isDark
201
- ? "0px 4px 12px rgba(0, 0, 0, 0.4), 0px 0px 0px 1px rgba(255, 255, 255, 0.1)"
206
+ ? "0px 4px 12px rgba(0, 0, 0, 0.4)"
202
207
  : "0px 4px 12px rgba(0, 0, 0, 0.15)",
203
208
  border: isDark
204
209
  ? "1px solid rgba(255, 255, 255, 0.1)"
205
210
  : "1px solid hsl(var(--border))",
206
211
  borderRadius: "8px",
212
+ overflow: "hidden",
213
+ ...menuStyle,
207
214
  }}
208
215
  >
209
216
  {React.Children.map(children, (child) => {
@@ -218,7 +218,7 @@ const Tab = React.forwardRef(
218
218
  }
219
219
  }}
220
220
  className={cn(
221
- "cursor-pointer transition-all duration-200 text-sm border-none bg-transparent focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
221
+ "cursor-pointer transition-all duration-200 text-sm border-none bg-transparent focus:outline-none",
222
222
  defaultPaddingClass,
223
223
  "min-w-[72px] min-h-[48px] flex-1",
224
224
  selected ? "font-semibold" : "font-medium",
@@ -320,7 +320,7 @@ const Tab = React.forwardRef(
320
320
  }
321
321
  }}
322
322
  className={cn(
323
- "cursor-pointer transition-all duration-200 min-h-[32px] min-w-[100px] rounded-2xl text-sm border-none focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
323
+ "cursor-pointer transition-all duration-200 min-h-[32px] min-w-[100px] rounded-2xl text-sm border-none focus:outline-none",
324
324
  defaultPaddingClass,
325
325
  defaultMarginClass,
326
326
  selected ? "font-semibold" : "font-medium",
@@ -74,7 +74,7 @@ const TextField = React.forwardRef(
74
74
  } else {
75
75
  // outlined (default)
76
76
  return focused
77
- ? getComponentBgColor("hsl(var(--input))")
77
+ ? getComponentBgColor("hsl(var(--background))")
78
78
  : "transparent";
79
79
  }
80
80
  };
@@ -46,7 +46,7 @@ const typographyVariants = cva("", {
46
46
 
47
47
  const Typography = React.forwardRef(
48
48
  (
49
- { className, variant = "body1", component, style, color, noWrap, ...props },
49
+ { className, variant = "body1", component, style, color, noWrap, gutterBottom, ...props },
50
50
  ref
51
51
  ) => {
52
52
  // Map MUI color props to shadcn colors
@@ -68,6 +68,7 @@ const Typography = React.forwardRef(
68
68
  )}
69
69
  style={{
70
70
  ...(noWrap && { whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }),
71
+ ...(gutterBottom && { marginBottom: "0.35em" }),
71
72
  ...style,
72
73
  }}
73
74
  {...props}