Skip to content

Commit e8f7eca

Browse files
committed
Refactor input and calendar components, add email/password fields
Refactored the input renderer to a standalone component and registered new 'email' and 'password' input types. Updated the OTP input renderer for dynamic slot rendering and improved value handling. Simplified and restyled the calendar and button components for consistency with the design system.
1 parent a7f0d37 commit e8f7eca

File tree

5 files changed

+198
-313
lines changed

5 files changed

+198
-313
lines changed

packages/components/src/renderers/form/input-otp.tsx

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,32 @@ import type { InputOTPSchema } from '@object-ui/types';
33
import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from '../../ui';
44

55
ComponentRegistry.register('input-otp',
6-
({ schema, className, ...props }: { schema: InputOTPSchema; className?: string; [key: string]: any }) => (
7-
<InputOTP maxLength={schema.length || 6} className={className} {...props}>
8-
<InputOTPGroup>
9-
<InputOTPSlot index={0} />
10-
<InputOTPSlot index={1} />
11-
<InputOTPSlot index={2} />
12-
</InputOTPGroup>
13-
<InputOTPSeparator />
14-
<InputOTPGroup>
15-
<InputOTPSlot index={3} />
16-
<InputOTPSlot index={4} />
17-
<InputOTPSlot index={5} />
18-
</InputOTPGroup>
19-
</InputOTP>
20-
),
6+
({ schema, className, onChange, value, ...props }: { schema: InputOTPSchema; className?: string; [key: string]: any }) => {
7+
const length = schema.maxLength || 6;
8+
const slots = Array.from({ length });
9+
10+
const handleChange = (val: string) => {
11+
if (onChange) {
12+
onChange(val);
13+
}
14+
};
15+
16+
return (
17+
<InputOTP
18+
maxLength={length}
19+
className={className}
20+
value={value ?? schema.value}
21+
onChange={handleChange}
22+
{...props}
23+
>
24+
<InputOTPGroup>
25+
{slots.map((_, i) => (
26+
<InputOTPSlot key={i} index={i} />
27+
))}
28+
</InputOTPGroup>
29+
</InputOTP>
30+
);
31+
},
2132
{
2233
label: 'Input OTP',
2334
inputs: [

packages/components/src/renderers/form/input.tsx

Lines changed: 81 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,58 +4,57 @@ import type { InputSchema } from '@object-ui/types';
44
import { Input, Label } from '../../ui';
55
import { cn } from '../../lib/utils';
66

7-
ComponentRegistry.register('input',
8-
({ schema, className, onChange, value, ...props }: { schema: InputSchema; className?: string; onChange?: (val: any) => void; value?: any; [key: string]: any }) => {
9-
10-
// Handle change for both raw inputs and form-bound inputs
11-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
12-
if (onChange) {
13-
onChange(e.target.value);
14-
}
15-
};
7+
const InputRenderer = ({ schema, className, onChange, value, ...props }: { schema: InputSchema; className?: string; onChange?: (val: any) => void; value?: any; [key: string]: any }) => {
8+
// Handle change for both raw inputs and form-bound inputs
9+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
10+
if (onChange) {
11+
onChange(e.target.value);
12+
}
13+
};
1614

17-
// Extract designer-related props to apply to the wrapper
18-
// These props are injected by SchemaRenderer for designer interaction
19-
const {
20-
'data-obj-id': dataObjId,
21-
'data-obj-type': dataObjType,
22-
style,
23-
...inputProps
24-
} = props;
15+
// Extract designer-related props to apply to the wrapper
16+
// These props are injected by SchemaRenderer for designer interaction
17+
const {
18+
'data-obj-id': dataObjId,
19+
'data-obj-type': dataObjType,
20+
style,
21+
...inputProps
22+
} = props;
2523

26-
return (
27-
<div
28-
className={cn("grid w-full items-center gap-1.5", schema.wrapperClass)}
29-
data-obj-id={dataObjId}
30-
data-obj-type={dataObjType}
31-
style={style}
32-
>
33-
{schema.label && <Label htmlFor={schema.id} className={cn(schema.required && "text-destructive after:content-['*'] after:ml-0.5")}>{schema.label}</Label>}
34-
<Input
35-
type={schema.inputType || 'text'}
36-
id={schema.id}
37-
name={schema.name}
38-
placeholder={schema.placeholder}
39-
className={className}
40-
required={schema.required}
41-
disabled={schema.disabled}
42-
readOnly={schema.readOnly}
43-
value={value ?? schema.value ?? ''} // Controlled if value provided
44-
defaultValue={value === undefined ? schema.defaultValue : undefined}
45-
onChange={handleChange}
46-
min={schema.min}
47-
max={schema.max}
48-
step={schema.step}
49-
maxLength={schema.maxLength}
50-
pattern={schema.pattern}
51-
{...inputProps}
52-
/>
53-
{schema.description && <p className="text-sm text-muted-foreground">{schema.description}</p>}
54-
{schema.error && <p className="text-sm font-medium text-destructive">{schema.error}</p>}
55-
</div>
56-
);
57-
},
58-
{
24+
return (
25+
<div
26+
className={cn("grid w-full items-center gap-1.5", schema.wrapperClass)}
27+
data-obj-id={dataObjId}
28+
data-obj-type={dataObjType}
29+
style={style}
30+
>
31+
{schema.label && <Label htmlFor={schema.id} className={cn(schema.required && "text-destructive after:content-['*'] after:ml-0.5")}>{schema.label}</Label>}
32+
<Input
33+
type={schema.inputType || 'text'}
34+
id={schema.id}
35+
name={schema.name}
36+
placeholder={schema.placeholder}
37+
className={className}
38+
required={schema.required}
39+
disabled={schema.disabled}
40+
readOnly={schema.readOnly}
41+
value={value ?? schema.value ?? ''} // Controlled if value provided
42+
defaultValue={value === undefined ? schema.defaultValue : undefined}
43+
onChange={handleChange}
44+
min={schema.min}
45+
max={schema.max}
46+
step={schema.step}
47+
maxLength={schema.maxLength}
48+
pattern={schema.pattern}
49+
{...inputProps}
50+
/>
51+
{schema.description && <p className="text-sm text-muted-foreground">{schema.description}</p>}
52+
{schema.error && <p className="text-sm font-medium text-destructive">{schema.error}</p>}
53+
</div>
54+
);
55+
};
56+
57+
ComponentRegistry.register('input', InputRenderer, {
5958
label: 'Input Field',
6059
inputs: [
6160
{ name: 'label', type: 'string', label: 'Label' },
@@ -77,3 +76,35 @@ ComponentRegistry.register('input',
7776
}
7877
}
7978
);
79+
80+
ComponentRegistry.register('email',
81+
(props: any) => <InputRenderer {...props} schema={{ ...props.schema, inputType: 'email' }} />,
82+
{
83+
label: 'Email Input',
84+
icon: 'mail',
85+
inputs: [
86+
{ name: 'label', type: 'string', label: 'Label' },
87+
{ name: 'name', type: 'string', label: 'Field Name' },
88+
{ name: 'placeholder', type: 'string', label: 'Placeholder' },
89+
{ name: 'required', type: 'boolean', label: 'Required' },
90+
{ name: 'disabled', type: 'boolean', label: 'Disabled' },
91+
{ name: 'description', type: 'string', label: 'Description' }
92+
]
93+
}
94+
);
95+
96+
ComponentRegistry.register('password',
97+
(props: any) => <InputRenderer {...props} schema={{ ...props.schema, inputType: 'password' }} />,
98+
{
99+
label: 'Password Input',
100+
icon: 'lock',
101+
inputs: [
102+
{ name: 'label', type: 'string', label: 'Label' },
103+
{ name: 'name', type: 'string', label: 'Field Name' },
104+
{ name: 'placeholder', type: 'string', label: 'Placeholder' },
105+
{ name: 'required', type: 'boolean', label: 'Required' },
106+
{ name: 'disabled', type: 'boolean', label: 'Disabled' },
107+
{ name: 'description', type: 'string', label: 'Description' }
108+
]
109+
}
110+
);

packages/components/src/ui/button.tsx

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,28 @@ import { cva, type VariantProps } from "class-variance-authority"
55
import { cn } from "../lib/utils"
66

77
const buttonVariants = cva(
8-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-sm text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-1 focus-visible:ring-cyan-400/50 tracking-wide",
8+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
99
{
1010
variants: {
1111
variant: {
1212
default:
13-
"bg-cyan-950/80 text-cyan-50 border border-cyan-500/50 shadow-[0_0_10px_-3px_rgba(6,182,212,0.3)] hover:bg-cyan-900 hover:border-cyan-400 hover:shadow-[0_0_15px_rgba(6,182,212,0.5)] active:scale-95",
13+
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
1414
destructive:
15-
"bg-red-950/80 text-red-50 border border-red-500/50 hover:bg-red-900 hover:border-red-400 hover:shadow-[0_0_15px_rgba(239,68,68,0.5)]",
15+
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
1616
outline:
17-
"border border-cyan-800 bg-background/50 hover:bg-cyan-950/30 hover:border-cyan-500/50 hover:text-cyan-100 text-muted-foreground",
17+
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
1818
secondary:
19-
"bg-slate-800/80 text-secondary-foreground hover:bg-slate-700/80 border border-slate-700",
20-
ghost:
21-
"hover:bg-cyan-950/20 hover:text-cyan-400 data-[state=open]:bg-cyan-950/20 data-[state=open]:text-cyan-400",
22-
link: "text-cyan-400 underline-offset-4 hover:underline hover:text-cyan-300",
19+
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20+
ghost: "hover:bg-accent hover:text-accent-foreground",
21+
link: "text-primary underline-offset-4 hover:underline",
2322
},
2423
size: {
25-
default: "h-9 px-4 py-2 has-[>svg]:px-3",
26-
sm: "h-8 rounded-sm gap-1.5 px-3 has-[>svg]:px-2.5 text-xs",
27-
lg: "h-10 rounded-sm px-6 has-[>svg]:px-4",
28-
icon: "size-9 rounded-sm",
29-
"icon-sm": "size-8 rounded-sm",
30-
"icon-lg": "size-10 rounded-sm",
24+
default: "h-9 px-4 py-2",
25+
sm: "h-8 rounded-md px-3 text-xs",
26+
lg: "h-10 rounded-md px-8",
27+
icon: "h-9 w-9",
28+
"icon-sm": "h-8 w-8",
29+
"icon-lg": "h-10 w-10",
3130
},
3231
},
3332
defaultVariants: {
@@ -37,29 +36,24 @@ const buttonVariants = cva(
3736
}
3837
)
3938

40-
const Button = React.forwardRef<HTMLButtonElement, React.ComponentProps<"button"> &
41-
VariantProps<typeof buttonVariants> & {
42-
asChild?: boolean
43-
}>(({
44-
className,
45-
variant = "default",
46-
size = "default",
47-
asChild = false,
48-
...props
49-
}, ref) => {
50-
const Comp = asChild ? Slot : "button"
39+
export interface ButtonProps
40+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
41+
VariantProps<typeof buttonVariants> {
42+
asChild?: boolean
43+
}
5144

52-
return (
53-
<Comp
54-
ref={ref}
55-
data-slot="button"
56-
data-variant={variant}
57-
data-size={size}
58-
className={cn(buttonVariants({ variant, size, className }))}
59-
{...props}
60-
/>
61-
)
62-
})
45+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
46+
({ className, variant, size, asChild = false, ...props }, ref) => {
47+
const Comp = asChild ? Slot : "button"
48+
return (
49+
<Comp
50+
className={cn(buttonVariants({ variant, size, className }))}
51+
ref={ref}
52+
{...props}
53+
/>
54+
)
55+
}
56+
)
6357
Button.displayName = "Button"
6458

6559
export { Button, buttonVariants }

0 commit comments

Comments
 (0)