@mhome/ui 0.1.0
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/README.md +188 -0
- package/dist/index.cjs.js +9 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.css +2 -0
- package/dist/index.esm.js +9 -0
- package/dist/index.esm.js.map +1 -0
- package/package.json +54 -0
- package/src/common/adaptive-theme-provider.js +19 -0
- package/src/components/accordion.jsx +306 -0
- package/src/components/alert.jsx +137 -0
- package/src/components/app-bar.jsx +105 -0
- package/src/components/autocomplete.jsx +347 -0
- package/src/components/avatar.jsx +160 -0
- package/src/components/box.jsx +165 -0
- package/src/components/button.jsx +104 -0
- package/src/components/card.jsx +156 -0
- package/src/components/checkbox.jsx +63 -0
- package/src/components/chip.jsx +137 -0
- package/src/components/collapse.jsx +188 -0
- package/src/components/container.jsx +67 -0
- package/src/components/date-picker.jsx +528 -0
- package/src/components/dialog-content-text.jsx +27 -0
- package/src/components/dialog.jsx +584 -0
- package/src/components/divider.jsx +192 -0
- package/src/components/drawer.jsx +255 -0
- package/src/components/form-control-label.jsx +89 -0
- package/src/components/form-group.jsx +32 -0
- package/src/components/form-label.jsx +54 -0
- package/src/components/grid.jsx +135 -0
- package/src/components/icon-button.jsx +101 -0
- package/src/components/index.js +78 -0
- package/src/components/input-adornment.jsx +43 -0
- package/src/components/input-label.jsx +55 -0
- package/src/components/list.jsx +239 -0
- package/src/components/menu.jsx +370 -0
- package/src/components/paper.jsx +173 -0
- package/src/components/radio-group.jsx +76 -0
- package/src/components/radio.jsx +108 -0
- package/src/components/select.jsx +308 -0
- package/src/components/slider.jsx +382 -0
- package/src/components/stack.jsx +110 -0
- package/src/components/table.jsx +243 -0
- package/src/components/tabs.jsx +363 -0
- package/src/components/text-field.jsx +289 -0
- package/src/components/toggle-button.jsx +209 -0
- package/src/components/toolbar.jsx +48 -0
- package/src/components/tooltip.jsx +127 -0
- package/src/components/typography.jsx +77 -0
- package/src/global-state.js +29 -0
- package/src/index.css +110 -0
- package/src/index.js +6 -0
- package/src/lib/useMediaQuery.js +37 -0
- package/src/lib/utils.js +113 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
|
|
4
|
+
const Divider = React.forwardRef(
|
|
5
|
+
(
|
|
6
|
+
{
|
|
7
|
+
className,
|
|
8
|
+
style = {},
|
|
9
|
+
orientation = "horizontal",
|
|
10
|
+
variant = "fullWidth",
|
|
11
|
+
flexItem = false,
|
|
12
|
+
light = false,
|
|
13
|
+
textAlign = "center",
|
|
14
|
+
children,
|
|
15
|
+
...props
|
|
16
|
+
},
|
|
17
|
+
ref
|
|
18
|
+
) => {
|
|
19
|
+
const isVertical = orientation === "vertical";
|
|
20
|
+
const isInset = variant === "inset";
|
|
21
|
+
const isMiddle = variant === "middle";
|
|
22
|
+
|
|
23
|
+
// Extract margin values from style prop
|
|
24
|
+
const styleMarginLeft = style?.marginLeft;
|
|
25
|
+
const styleMarginRight = style?.marginRight;
|
|
26
|
+
|
|
27
|
+
// Get divider styles
|
|
28
|
+
const getDividerStyles = () => {
|
|
29
|
+
const baseStyles = {
|
|
30
|
+
borderColor: light ? "rgba(0, 0, 0, 0.08)" : "hsl(var(--border))",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (isVertical) {
|
|
34
|
+
// For vertical divider, use marginLeft and marginRight for horizontal spacing
|
|
35
|
+
// Default spacing: 8px on both sides, unless variant or style specifies otherwise
|
|
36
|
+
const defaultMargin = "8px";
|
|
37
|
+
const marginLeft = styleMarginLeft || (isInset ? "16px" : defaultMargin);
|
|
38
|
+
const marginRight = styleMarginRight || (isInset ? "16px" : defaultMargin);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
...baseStyles,
|
|
42
|
+
borderLeftWidth: "1px",
|
|
43
|
+
borderLeftStyle: "solid",
|
|
44
|
+
height: "100%",
|
|
45
|
+
width: "1px",
|
|
46
|
+
marginLeft: marginLeft,
|
|
47
|
+
marginRight: marginRight,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Horizontal divider
|
|
52
|
+
// Calculate width based on margins to prevent overflow
|
|
53
|
+
// inset: only left margin, middle: both margins, fullWidth: no margins
|
|
54
|
+
const marginLeft =
|
|
55
|
+
styleMarginLeft || (isInset ? "16px" : isMiddle ? "16px" : "0px");
|
|
56
|
+
const marginRight =
|
|
57
|
+
styleMarginRight || (isInset ? "0px" : isMiddle ? "16px" : "0px");
|
|
58
|
+
|
|
59
|
+
// Parse margin values to calculate total margin
|
|
60
|
+
const parseMargin = (margin) => {
|
|
61
|
+
if (!margin || margin === "0px" || margin === "0") return 0;
|
|
62
|
+
if (typeof margin === "number") return margin;
|
|
63
|
+
const match = margin.toString().match(/([\d.]+)/);
|
|
64
|
+
return match ? parseFloat(match[1]) : 0;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const marginLeftNum = parseMargin(marginLeft);
|
|
68
|
+
const marginRightNum = parseMargin(marginRight);
|
|
69
|
+
const totalMargin = marginLeftNum + marginRightNum;
|
|
70
|
+
|
|
71
|
+
const styles = {
|
|
72
|
+
...baseStyles,
|
|
73
|
+
borderTopWidth: "1px",
|
|
74
|
+
borderTopStyle: "solid",
|
|
75
|
+
boxSizing: "border-box",
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Set width and margins based on variant
|
|
79
|
+
if (flexItem) {
|
|
80
|
+
styles.width = "1px";
|
|
81
|
+
} else if (variant === "fullWidth") {
|
|
82
|
+
// fullWidth: 100% width, no horizontal margins
|
|
83
|
+
styles.width = "100%";
|
|
84
|
+
styles.marginLeft = "0px";
|
|
85
|
+
styles.marginRight = "0px";
|
|
86
|
+
} else {
|
|
87
|
+
// inset or middle: calculate width based on margins
|
|
88
|
+
styles.width = totalMargin > 0 ? `calc(100% - ${totalMargin}px)` : "100%";
|
|
89
|
+
if (marginLeft !== "0px" && marginLeft !== "0") {
|
|
90
|
+
styles.marginLeft = marginLeft;
|
|
91
|
+
}
|
|
92
|
+
if (marginRight !== "0px" && marginRight !== "0") {
|
|
93
|
+
styles.marginRight = marginRight;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return styles;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// If has children, render as wrapper with text
|
|
101
|
+
if (children) {
|
|
102
|
+
// Calculate margin for variant
|
|
103
|
+
// inset: only left margin, middle: both margins, fullWidth: no margins
|
|
104
|
+
const leftMargin = styleMarginLeft || (isInset ? "16px" : isMiddle ? "16px" : "0px");
|
|
105
|
+
const rightMargin = styleMarginRight || (isInset ? "0px" : isMiddle ? "16px" : "0px");
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div
|
|
109
|
+
ref={ref}
|
|
110
|
+
className={cn("flex items-center", className)}
|
|
111
|
+
style={{
|
|
112
|
+
width: "100%",
|
|
113
|
+
margin: "8px 0",
|
|
114
|
+
...style,
|
|
115
|
+
}}
|
|
116
|
+
{...props}
|
|
117
|
+
>
|
|
118
|
+
<div
|
|
119
|
+
style={{
|
|
120
|
+
flex: 1,
|
|
121
|
+
borderTopWidth: "1px",
|
|
122
|
+
borderTopStyle: "solid",
|
|
123
|
+
borderColor: light ? "rgba(0, 0, 0, 0.08)" : "hsl(var(--border))",
|
|
124
|
+
marginLeft: leftMargin !== "0px" ? leftMargin : undefined,
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
<div
|
|
128
|
+
style={{
|
|
129
|
+
paddingLeft: "16px",
|
|
130
|
+
paddingRight: "16px",
|
|
131
|
+
textAlign: textAlign,
|
|
132
|
+
color: "hsl(var(--muted-foreground))",
|
|
133
|
+
fontSize: "0.875rem",
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
{children}
|
|
137
|
+
</div>
|
|
138
|
+
<div
|
|
139
|
+
style={{
|
|
140
|
+
flex: 1,
|
|
141
|
+
borderTopWidth: "1px",
|
|
142
|
+
borderTopStyle: "solid",
|
|
143
|
+
borderColor: light ? "rgba(0, 0, 0, 0.08)" : "hsl(var(--border))",
|
|
144
|
+
marginRight: rightMargin !== "0px" ? rightMargin : undefined,
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Simple divider without text
|
|
152
|
+
const dividerStyles = getDividerStyles();
|
|
153
|
+
// Add default vertical spacing for horizontal dividers if not specified
|
|
154
|
+
if (!isVertical && !flexItem) {
|
|
155
|
+
if (style.marginTop === undefined && dividerStyles.marginTop === undefined) {
|
|
156
|
+
dividerStyles.marginTop = "8px";
|
|
157
|
+
}
|
|
158
|
+
if (style.marginBottom === undefined && dividerStyles.marginBottom === undefined) {
|
|
159
|
+
dividerStyles.marginBottom = "8px";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// For fullWidth, ensure no horizontal margins override the divider styles
|
|
164
|
+
const finalStyle = { ...dividerStyles, ...style };
|
|
165
|
+
if (!isVertical && variant === "fullWidth" && !flexItem) {
|
|
166
|
+
finalStyle.width = "100%";
|
|
167
|
+
finalStyle.marginLeft = style.marginLeft ?? "0px";
|
|
168
|
+
finalStyle.marginRight = style.marginRight ?? "0px";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div
|
|
173
|
+
ref={ref}
|
|
174
|
+
className={cn(
|
|
175
|
+
isVertical ? "inline-block" : "block",
|
|
176
|
+
flexItem && "self-stretch",
|
|
177
|
+
className
|
|
178
|
+
)}
|
|
179
|
+
style={{
|
|
180
|
+
padding: 0,
|
|
181
|
+
display: isVertical ? "inline-block" : "block",
|
|
182
|
+
...finalStyle,
|
|
183
|
+
}}
|
|
184
|
+
{...props}
|
|
185
|
+
/>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
Divider.displayName = "Divider";
|
|
191
|
+
|
|
192
|
+
export { Divider };
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
|
|
4
|
+
const Drawer = React.forwardRef(
|
|
5
|
+
(
|
|
6
|
+
{
|
|
7
|
+
anchor = "left",
|
|
8
|
+
open = false,
|
|
9
|
+
onClose,
|
|
10
|
+
onOpenChange,
|
|
11
|
+
children,
|
|
12
|
+
className,
|
|
13
|
+
sx,
|
|
14
|
+
PaperProps,
|
|
15
|
+
transitionDuration = 300,
|
|
16
|
+
keepMounted = false,
|
|
17
|
+
variant = "temporary",
|
|
18
|
+
ModalProps,
|
|
19
|
+
SlideProps,
|
|
20
|
+
...props
|
|
21
|
+
},
|
|
22
|
+
ref
|
|
23
|
+
) => {
|
|
24
|
+
const drawerRef = React.useRef(null);
|
|
25
|
+
|
|
26
|
+
React.useImperativeHandle(ref, () => drawerRef.current);
|
|
27
|
+
|
|
28
|
+
// Handle backdrop click - only for temporary variant
|
|
29
|
+
const handleBackdropClick = (e) => {
|
|
30
|
+
if (variant === "temporary" && e.target === e.currentTarget && onClose) {
|
|
31
|
+
onClose(e, "backdropClick");
|
|
32
|
+
if (onOpenChange) {
|
|
33
|
+
onOpenChange(false);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Handle open state changes
|
|
39
|
+
React.useEffect(() => {
|
|
40
|
+
if (onOpenChange) {
|
|
41
|
+
onOpenChange(open);
|
|
42
|
+
}
|
|
43
|
+
}, [open, onOpenChange]);
|
|
44
|
+
|
|
45
|
+
// Handle escape key
|
|
46
|
+
React.useEffect(() => {
|
|
47
|
+
if (!open) return;
|
|
48
|
+
|
|
49
|
+
const handleEscape = (e) => {
|
|
50
|
+
if (e.key === "Escape" && onClose) {
|
|
51
|
+
onClose(e, "escapeKeyDown");
|
|
52
|
+
if (onOpenChange) {
|
|
53
|
+
onOpenChange(false);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
document.addEventListener("keydown", handleEscape);
|
|
59
|
+
return () => {
|
|
60
|
+
document.removeEventListener("keydown", handleEscape);
|
|
61
|
+
};
|
|
62
|
+
}, [open, onClose]);
|
|
63
|
+
|
|
64
|
+
// Merge sx styles - handle root and paper selectors
|
|
65
|
+
const { rootStyles, paperStyles: sxPaperStyles } = React.useMemo(() => {
|
|
66
|
+
if (!sx) return { rootStyles: {}, paperStyles: {} };
|
|
67
|
+
const sxObj = typeof sx === "function" ? sx({}) : sx;
|
|
68
|
+
|
|
69
|
+
// Extract root-level styles (direct properties)
|
|
70
|
+
const rootStyles = {};
|
|
71
|
+
// Extract paper styles from "& .MuiDrawer-paper" selector
|
|
72
|
+
const paperStyles = {};
|
|
73
|
+
|
|
74
|
+
Object.keys(sxObj).forEach((key) => {
|
|
75
|
+
if (key === "& .MuiDrawer-paper" || key === "&.MuiDrawer-paper") {
|
|
76
|
+
Object.assign(paperStyles, sxObj[key]);
|
|
77
|
+
} else if (key.startsWith("&")) {
|
|
78
|
+
// Other selectors - could be handled here if needed
|
|
79
|
+
} else {
|
|
80
|
+
// Direct style properties go to root
|
|
81
|
+
rootStyles[key] = sxObj[key];
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return { rootStyles, paperStyles };
|
|
86
|
+
}, [sx]);
|
|
87
|
+
|
|
88
|
+
// Get PaperProps styles
|
|
89
|
+
const paperPropsStyles = React.useMemo(() => {
|
|
90
|
+
const paperSx = PaperProps?.sx;
|
|
91
|
+
if (!paperSx) return {};
|
|
92
|
+
return typeof paperSx === "function" ? paperSx({}) : paperSx;
|
|
93
|
+
}, [PaperProps]);
|
|
94
|
+
|
|
95
|
+
// Get anchor styles
|
|
96
|
+
const getAnchorStyles = () => {
|
|
97
|
+
const baseStyles = {
|
|
98
|
+
position: "fixed",
|
|
99
|
+
zIndex: parseInt(
|
|
100
|
+
getComputedStyle(document.documentElement).getPropertyValue(
|
|
101
|
+
"--z-index-drawer"
|
|
102
|
+
) || "1200",
|
|
103
|
+
10
|
|
104
|
+
),
|
|
105
|
+
transition: `transform ${transitionDuration}ms cubic-bezier(0.4, 0, 0.2, 1)`,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
switch (anchor) {
|
|
109
|
+
case "left":
|
|
110
|
+
return {
|
|
111
|
+
...baseStyles,
|
|
112
|
+
top: 0,
|
|
113
|
+
left: 0,
|
|
114
|
+
bottom: 0,
|
|
115
|
+
transform: open ? "translateX(0)" : "translateX(-100%)",
|
|
116
|
+
};
|
|
117
|
+
case "right":
|
|
118
|
+
return {
|
|
119
|
+
...baseStyles,
|
|
120
|
+
top: 0,
|
|
121
|
+
right: 0,
|
|
122
|
+
bottom: 0,
|
|
123
|
+
transform: open ? "translateX(0)" : "translateX(100%)",
|
|
124
|
+
};
|
|
125
|
+
case "top":
|
|
126
|
+
return {
|
|
127
|
+
...baseStyles,
|
|
128
|
+
top: 0,
|
|
129
|
+
left: 0,
|
|
130
|
+
right: 0,
|
|
131
|
+
width: "100%",
|
|
132
|
+
transform: open ? "translateY(0)" : "translateY(-100%)",
|
|
133
|
+
};
|
|
134
|
+
case "bottom":
|
|
135
|
+
return {
|
|
136
|
+
...baseStyles,
|
|
137
|
+
bottom: 0,
|
|
138
|
+
left: 0,
|
|
139
|
+
right: 0,
|
|
140
|
+
width: "100%",
|
|
141
|
+
transform: open ? "translateY(0)" : "translateY(100%)",
|
|
142
|
+
};
|
|
143
|
+
default:
|
|
144
|
+
return baseStyles;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const anchorStyles = getAnchorStyles();
|
|
149
|
+
|
|
150
|
+
// Early return after all hooks
|
|
151
|
+
// For permanent variant, always render if keepMounted is true
|
|
152
|
+
if (!open && !keepMounted && variant !== "permanent") return null;
|
|
153
|
+
|
|
154
|
+
// For permanent variant, always show
|
|
155
|
+
const isPermanent = variant === "permanent";
|
|
156
|
+
const shouldShowBackdrop = variant === "temporary" || variant === "persistent";
|
|
157
|
+
const shouldShowDrawer = isPermanent || open || keepMounted;
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<>
|
|
161
|
+
{/* Backdrop - only for temporary and persistent variants */}
|
|
162
|
+
{shouldShowBackdrop && shouldShowDrawer && (
|
|
163
|
+
<div
|
|
164
|
+
className="fixed inset-0 bg-black/50"
|
|
165
|
+
style={{
|
|
166
|
+
zIndex:
|
|
167
|
+
parseInt(
|
|
168
|
+
getComputedStyle(document.documentElement).getPropertyValue(
|
|
169
|
+
"--z-index-drawer"
|
|
170
|
+
) || "1200",
|
|
171
|
+
10
|
|
172
|
+
) - 1,
|
|
173
|
+
opacity: open ? 1 : 0,
|
|
174
|
+
transition: `opacity ${transitionDuration}ms cubic-bezier(0.4, 0, 0.2, 1)`,
|
|
175
|
+
pointerEvents: open ? "auto" : "none",
|
|
176
|
+
}}
|
|
177
|
+
onClick={handleBackdropClick}
|
|
178
|
+
aria-hidden="true"
|
|
179
|
+
/>
|
|
180
|
+
)}
|
|
181
|
+
|
|
182
|
+
{/* Drawer */}
|
|
183
|
+
<div
|
|
184
|
+
ref={drawerRef}
|
|
185
|
+
className={cn(
|
|
186
|
+
"MuiDrawer-root",
|
|
187
|
+
"fixed",
|
|
188
|
+
anchor === "left" || anchor === "right" ? "h-full" : "w-full",
|
|
189
|
+
anchor === "top" || anchor === "bottom" ? "h-auto" : "w-auto",
|
|
190
|
+
!open && "pointer-events-none",
|
|
191
|
+
className
|
|
192
|
+
)}
|
|
193
|
+
style={{
|
|
194
|
+
...anchorStyles,
|
|
195
|
+
...rootStyles,
|
|
196
|
+
display: shouldShowDrawer ? "block" : "none",
|
|
197
|
+
// For permanent variant, always visible
|
|
198
|
+
transform: isPermanent ? "translateX(0)" : anchorStyles.transform,
|
|
199
|
+
...props.style,
|
|
200
|
+
}}
|
|
201
|
+
role="presentation"
|
|
202
|
+
{...Object.fromEntries(
|
|
203
|
+
Object.entries(props).filter(([key]) => key !== "style")
|
|
204
|
+
)}
|
|
205
|
+
>
|
|
206
|
+
{/* Paper (drawer content) */}
|
|
207
|
+
<div
|
|
208
|
+
className={cn(
|
|
209
|
+
"MuiDrawer-paper",
|
|
210
|
+
"MuiPaper-root",
|
|
211
|
+
anchor === "left" || anchor === "right" ? "h-full" : "",
|
|
212
|
+
"w-full",
|
|
213
|
+
anchor === "left" || anchor === "right"
|
|
214
|
+
? "overflow-y-auto"
|
|
215
|
+
: anchor === "top" || anchor === "bottom"
|
|
216
|
+
? "overflow-y-auto"
|
|
217
|
+
: "",
|
|
218
|
+
PaperProps?.className
|
|
219
|
+
)}
|
|
220
|
+
style={{
|
|
221
|
+
backgroundColor: "hsl(var(--card))",
|
|
222
|
+
boxShadow:
|
|
223
|
+
sxPaperStyles.boxShadow !== undefined
|
|
224
|
+
? sxPaperStyles.boxShadow
|
|
225
|
+
: paperPropsStyles.boxShadow !== undefined
|
|
226
|
+
? paperPropsStyles.boxShadow
|
|
227
|
+
: PaperProps?.style?.boxShadow !== undefined
|
|
228
|
+
? PaperProps.style.boxShadow
|
|
229
|
+
: open
|
|
230
|
+
? "0px 8px 10px -5px rgba(0,0,0,0.2), 0px 16px 24px 2px rgba(0,0,0,0.14), 0px 6px 30px 5px rgba(0,0,0,0.12)"
|
|
231
|
+
: "none", // No shadow when closed
|
|
232
|
+
...(anchor === "left" || anchor === "right"
|
|
233
|
+
? {
|
|
234
|
+
height: "100%",
|
|
235
|
+
width: PaperProps?.style?.width || sxPaperStyles.width || paperPropsStyles.width || "300px",
|
|
236
|
+
minWidth: PaperProps?.style?.minWidth || sxPaperStyles.minWidth || paperPropsStyles.minWidth || "300px",
|
|
237
|
+
maxWidth: PaperProps?.style?.maxWidth || sxPaperStyles.maxWidth || paperPropsStyles.maxWidth || "90vw"
|
|
238
|
+
}
|
|
239
|
+
: { width: "100%" }),
|
|
240
|
+
...sxPaperStyles,
|
|
241
|
+
...paperPropsStyles,
|
|
242
|
+
...PaperProps?.style,
|
|
243
|
+
}}
|
|
244
|
+
>
|
|
245
|
+
{children}
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
</>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
Drawer.displayName = "Drawer";
|
|
254
|
+
|
|
255
|
+
export { Drawer };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
|
|
4
|
+
const FormControlLabel = React.forwardRef(
|
|
5
|
+
(
|
|
6
|
+
{
|
|
7
|
+
control,
|
|
8
|
+
label,
|
|
9
|
+
labelPlacement = "end",
|
|
10
|
+
disabled = false,
|
|
11
|
+
value,
|
|
12
|
+
className,
|
|
13
|
+
sx,
|
|
14
|
+
...props
|
|
15
|
+
},
|
|
16
|
+
ref
|
|
17
|
+
) => {
|
|
18
|
+
// Merge sx styles
|
|
19
|
+
const mergedSx = React.useMemo(() => {
|
|
20
|
+
if (!sx) return {};
|
|
21
|
+
return typeof sx === "function" ? sx({}) : sx;
|
|
22
|
+
}, [sx]);
|
|
23
|
+
|
|
24
|
+
const isLabelStart = labelPlacement === "start";
|
|
25
|
+
const isLabelTop = labelPlacement === "top";
|
|
26
|
+
const isLabelBottom = labelPlacement === "bottom";
|
|
27
|
+
|
|
28
|
+
const controlWithValue = React.useMemo(() => {
|
|
29
|
+
if (value !== undefined && React.isValidElement(control)) {
|
|
30
|
+
return React.cloneElement(control, {
|
|
31
|
+
...control.props,
|
|
32
|
+
value,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return control;
|
|
36
|
+
}, [control, value]);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<label
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={cn(
|
|
42
|
+
"inline-flex items-center cursor-pointer",
|
|
43
|
+
disabled && "opacity-50 cursor-not-allowed",
|
|
44
|
+
isLabelTop && "flex-col items-start",
|
|
45
|
+
isLabelBottom && "flex-col-reverse items-start",
|
|
46
|
+
isLabelStart && "flex-row-reverse items-center",
|
|
47
|
+
!isLabelTop && !isLabelBottom && !isLabelStart && "items-center",
|
|
48
|
+
className
|
|
49
|
+
)}
|
|
50
|
+
style={mergedSx}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
{/* Control */}
|
|
54
|
+
<span
|
|
55
|
+
className={cn(
|
|
56
|
+
"inline-flex items-center justify-center shrink-0",
|
|
57
|
+
isLabelTop && "mb-1",
|
|
58
|
+
isLabelBottom && "mt-1",
|
|
59
|
+
isLabelStart && "ml-2",
|
|
60
|
+
!isLabelStart && !isLabelTop && !isLabelBottom && "mr-2"
|
|
61
|
+
)}
|
|
62
|
+
style={{
|
|
63
|
+
minWidth: "20px",
|
|
64
|
+
minHeight: "20px",
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
{controlWithValue}
|
|
68
|
+
</span>
|
|
69
|
+
|
|
70
|
+
{/* Label */}
|
|
71
|
+
{label && (
|
|
72
|
+
<span
|
|
73
|
+
className={cn(
|
|
74
|
+
"text-sm text-foreground",
|
|
75
|
+
disabled && "text-muted-foreground opacity-50",
|
|
76
|
+
isLabelStart && "mr-2"
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
{label}
|
|
80
|
+
</span>
|
|
81
|
+
)}
|
|
82
|
+
</label>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
FormControlLabel.displayName = "FormControlLabel";
|
|
88
|
+
|
|
89
|
+
export { FormControlLabel };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
|
|
4
|
+
const FormGroup = React.forwardRef(
|
|
5
|
+
({ children, className, sx, style, row = false, ...props }, ref) => {
|
|
6
|
+
const mergedSx = React.useMemo(() => {
|
|
7
|
+
if (!sx) return {};
|
|
8
|
+
return typeof sx === "function" ? sx({}) : sx;
|
|
9
|
+
}, [sx]);
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div
|
|
13
|
+
ref={ref}
|
|
14
|
+
className={cn(
|
|
15
|
+
"flex",
|
|
16
|
+
row ? "flex-row flex-wrap gap-4" : "flex-col gap-3",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
style={{
|
|
20
|
+
...mergedSx,
|
|
21
|
+
...style,
|
|
22
|
+
}}
|
|
23
|
+
{...props}
|
|
24
|
+
>
|
|
25
|
+
{children}
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
FormGroup.displayName = "FormGroup";
|
|
31
|
+
|
|
32
|
+
export { FormGroup };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
|
|
4
|
+
const FormLabel = React.forwardRef(
|
|
5
|
+
(
|
|
6
|
+
{
|
|
7
|
+
children,
|
|
8
|
+
component = "label",
|
|
9
|
+
className,
|
|
10
|
+
sx,
|
|
11
|
+
style,
|
|
12
|
+
required = false,
|
|
13
|
+
disabled = false,
|
|
14
|
+
error = false,
|
|
15
|
+
focused = false,
|
|
16
|
+
...props
|
|
17
|
+
},
|
|
18
|
+
ref
|
|
19
|
+
) => {
|
|
20
|
+
const mergedSx = React.useMemo(() => {
|
|
21
|
+
if (!sx) return {};
|
|
22
|
+
return typeof sx === "function" ? sx({}) : sx;
|
|
23
|
+
}, [sx]);
|
|
24
|
+
|
|
25
|
+
const Component = component === "legend" ? "legend" : "label";
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Component
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cn(
|
|
31
|
+
"block text-sm font-medium mb-1",
|
|
32
|
+
error
|
|
33
|
+
? "text-destructive"
|
|
34
|
+
: focused
|
|
35
|
+
? "text-primary"
|
|
36
|
+
: "text-foreground",
|
|
37
|
+
disabled && "opacity-50 cursor-not-allowed",
|
|
38
|
+
className
|
|
39
|
+
)}
|
|
40
|
+
style={{
|
|
41
|
+
...mergedSx,
|
|
42
|
+
...style,
|
|
43
|
+
}}
|
|
44
|
+
{...props}
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
{required && <span className="ml-1 text-destructive">*</span>}
|
|
48
|
+
</Component>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
FormLabel.displayName = "FormLabel";
|
|
53
|
+
|
|
54
|
+
export { FormLabel };
|