Skip to content

Commit 2678109

Browse files
committed
Added ShadCN color styles button & form components
1 parent 4b2f0d0 commit 2678109

File tree

10 files changed

+308
-26
lines changed

10 files changed

+308
-26
lines changed

packages/ariakit/src/toolbar/ToolbarSelect.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ export function ToolbarSelect(props: ToolbarSelectProps) {
1515
aria-label="Text alignment"
1616
className="button secondary"
1717
render={<Ariakit.ToolbarItem />}>
18-
{selectedItem.icon && <selectedItem.icon />} {selectedItem.text}{" "}
19-
<Ariakit.SelectArrow />
18+
{selectedItem.icon} {selectedItem.text} <Ariakit.SelectArrow />
2019
</Ariakit.Select>
2120
<Ariakit.SelectPopover gutter={4} className="popover">
2221
{props.items.map((option) => (
2322
<Ariakit.SelectItem
2423
key={option.text}
2524
value={option.text}
2625
className="select-item">
27-
{option.icon && <option.icon />} {option.text}
26+
{option.icon}
27+
{option.text}
2828
</Ariakit.SelectItem>
2929
))}
3030
</Ariakit.SelectPopover>

packages/shadcn/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@
5252
"dependencies": {
5353
"@blocknote/core": "*",
5454
"@blocknote/react": "*",
55+
"@hookform/resolvers": "^3.3.4",
5556
"@radix-ui/react-dropdown-menu": "^2.0.6",
57+
"@radix-ui/react-label": "^2.0.2",
58+
"@radix-ui/react-popover": "^1.0.7",
5659
"@radix-ui/react-select": "^2.0.0",
5760
"@radix-ui/react-slot": "^1.0.2",
5861
"@radix-ui/react-tabs": "^1.0.4",
@@ -63,8 +66,10 @@
6366
"lucide-react": "^0.362.0",
6467
"react": "^18",
6568
"react-dom": "^18",
69+
"react-hook-form": "^7.51.2",
6670
"tailwind-merge": "^2.2.2",
67-
"tailwindcss-animate": "^1.0.7"
71+
"tailwindcss-animate": "^1.0.7",
72+
"zod": "^3.22.4"
6873
},
6974
"devDependencies": {
7075
"@radix-ui/colors": "^3.0.0",

packages/shadcn/src/components/ui/dropdown-menu.tsx

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,15 @@ const DropdownMenuContent = React.forwardRef<
5757
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
5858
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
5959
>(({ className, sideOffset = 4, ...props }, ref) => (
60-
<DropdownMenuPrimitive.Portal>
61-
<DropdownMenuPrimitive.Content
62-
ref={ref}
63-
sideOffset={sideOffset}
64-
className={cn(
65-
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
66-
className
67-
)}
68-
{...props}
69-
/>
70-
</DropdownMenuPrimitive.Portal>
60+
<DropdownMenuPrimitive.Content
61+
ref={ref}
62+
sideOffset={sideOffset}
63+
className={cn(
64+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
65+
className
66+
)}
67+
{...props}
68+
/>
7169
));
7270
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
7371

@@ -96,17 +94,17 @@ const DropdownMenuCheckboxItem = React.forwardRef<
9694
<DropdownMenuPrimitive.CheckboxItem
9795
ref={ref}
9896
className={cn(
99-
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
97+
"relative flex cursor-default select-none items-center justify-between rounded-sm gap-2 py-1.5 px-2 w-full text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
10098
className
10199
)}
102100
checked={checked}
103101
{...props}>
104-
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
102+
<span className="flex items-center justify-between gap-2">{children}</span>
103+
<span className="flex h-3.5 w-3.5 items-center justify-center">
105104
<DropdownMenuPrimitive.ItemIndicator>
106105
<Check className="h-4 w-4" />
107106
</DropdownMenuPrimitive.ItemIndicator>
108107
</span>
109-
{children}
110108
</DropdownMenuPrimitive.CheckboxItem>
111109
));
112110
DropdownMenuCheckboxItem.displayName =
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import * as React from "react";
2+
import * as LabelPrimitive from "@radix-ui/react-label";
3+
import { Slot } from "@radix-ui/react-slot";
4+
import {
5+
Controller,
6+
ControllerProps,
7+
FieldPath,
8+
FieldValues,
9+
FormProvider,
10+
useFormContext,
11+
} from "react-hook-form";
12+
13+
import { cn } from "../../lib/utils";
14+
import { Label } from "./label";
15+
16+
const Form = FormProvider;
17+
18+
type FormFieldContextValue<
19+
TFieldValues extends FieldValues = FieldValues,
20+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
21+
> = {
22+
name: TName;
23+
};
24+
25+
const FormFieldContext = React.createContext<FormFieldContextValue>(
26+
{} as FormFieldContextValue
27+
);
28+
29+
const FormField = <
30+
TFieldValues extends FieldValues = FieldValues,
31+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
32+
>({
33+
...props
34+
}: ControllerProps<TFieldValues, TName>) => {
35+
return (
36+
<FormFieldContext.Provider value={{ name: props.name }}>
37+
<Controller {...props} />
38+
</FormFieldContext.Provider>
39+
);
40+
};
41+
42+
const useFormField = () => {
43+
const fieldContext = React.useContext(FormFieldContext);
44+
const itemContext = React.useContext(FormItemContext);
45+
const { getFieldState, formState } = useFormContext();
46+
47+
const fieldState = getFieldState(fieldContext.name, formState);
48+
49+
if (!fieldContext) {
50+
throw new Error("useFormField should be used within <FormField>");
51+
}
52+
53+
const { id } = itemContext;
54+
55+
return {
56+
id,
57+
name: fieldContext.name,
58+
formItemId: `${id}-form-item`,
59+
formDescriptionId: `${id}-form-item-description`,
60+
formMessageId: `${id}-form-item-message`,
61+
...fieldState,
62+
};
63+
};
64+
65+
type FormItemContextValue = {
66+
id: string;
67+
};
68+
69+
const FormItemContext = React.createContext<FormItemContextValue>(
70+
{} as FormItemContextValue
71+
);
72+
73+
const FormItem = React.forwardRef<
74+
HTMLDivElement,
75+
React.HTMLAttributes<HTMLDivElement>
76+
>(({ className, ...props }, ref) => {
77+
const id = React.useId();
78+
79+
return (
80+
<FormItemContext.Provider value={{ id }}>
81+
<div ref={ref} className={cn("space-y-2", className)} {...props} />
82+
</FormItemContext.Provider>
83+
);
84+
});
85+
FormItem.displayName = "FormItem";
86+
87+
const FormLabel = React.forwardRef<
88+
React.ElementRef<typeof LabelPrimitive.Root>,
89+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
90+
>(({ className, ...props }, ref) => {
91+
const { error, formItemId } = useFormField();
92+
93+
return (
94+
<Label
95+
ref={ref}
96+
className={cn(error && "text-destructive", className)}
97+
htmlFor={formItemId}
98+
{...props}
99+
/>
100+
);
101+
});
102+
FormLabel.displayName = "FormLabel";
103+
104+
const FormControl = React.forwardRef<
105+
React.ElementRef<typeof Slot>,
106+
React.ComponentPropsWithoutRef<typeof Slot>
107+
>(({ ...props }, ref) => {
108+
const { error, formItemId, formDescriptionId, formMessageId } =
109+
useFormField();
110+
111+
return (
112+
<Slot
113+
ref={ref}
114+
id={formItemId}
115+
aria-describedby={
116+
!error
117+
? `${formDescriptionId}`
118+
: `${formDescriptionId} ${formMessageId}`
119+
}
120+
aria-invalid={!!error}
121+
{...props}
122+
/>
123+
);
124+
});
125+
FormControl.displayName = "FormControl";
126+
127+
const FormDescription = React.forwardRef<
128+
HTMLParagraphElement,
129+
React.HTMLAttributes<HTMLParagraphElement>
130+
>(({ className, ...props }, ref) => {
131+
const { formDescriptionId } = useFormField();
132+
133+
return (
134+
<p
135+
ref={ref}
136+
id={formDescriptionId}
137+
className={cn("text-sm text-muted-foreground", className)}
138+
{...props}
139+
/>
140+
);
141+
});
142+
FormDescription.displayName = "FormDescription";
143+
144+
const FormMessage = React.forwardRef<
145+
HTMLParagraphElement,
146+
React.HTMLAttributes<HTMLParagraphElement>
147+
>(({ className, children, ...props }, ref) => {
148+
const { error, formMessageId } = useFormField();
149+
const body = error ? String(error?.message) : children;
150+
151+
if (!body) {
152+
return null;
153+
}
154+
155+
return (
156+
<p
157+
ref={ref}
158+
id={formMessageId}
159+
className={cn("text-sm font-medium text-destructive", className)}
160+
{...props}>
161+
{body}
162+
</p>
163+
);
164+
});
165+
FormMessage.displayName = "FormMessage";
166+
167+
export {
168+
useFormField,
169+
Form,
170+
FormItem,
171+
FormLabel,
172+
FormControl,
173+
FormDescription,
174+
FormMessage,
175+
FormField,
176+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as React from "react";
2+
import * as LabelPrimitive from "@radix-ui/react-label";
3+
import { cva, type VariantProps } from "class-variance-authority";
4+
5+
import { cn } from "../../lib/utils";
6+
7+
const labelVariants = cva(
8+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
9+
);
10+
11+
const Label = React.forwardRef<
12+
React.ElementRef<typeof LabelPrimitive.Root>,
13+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
14+
VariantProps<typeof labelVariants>
15+
>(({ className, ...props }, ref) => (
16+
<LabelPrimitive.Root
17+
ref={ref}
18+
className={cn(labelVariants(), className)}
19+
{...props}
20+
/>
21+
));
22+
Label.displayName = LabelPrimitive.Root.displayName;
23+
24+
export { Label };
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as React from "react";
2+
import * as PopoverPrimitive from "@radix-ui/react-popover";
3+
4+
import { cn } from "../../lib/utils";
5+
6+
const Popover = PopoverPrimitive.Root;
7+
8+
const PopoverTrigger = PopoverPrimitive.Trigger;
9+
10+
const PopoverContent = React.forwardRef<
11+
React.ElementRef<typeof PopoverPrimitive.Content>,
12+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
13+
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
14+
<PopoverPrimitive.Content
15+
ref={ref}
16+
align={align}
17+
sideOffset={sideOffset}
18+
className={cn(
19+
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
20+
className
21+
)}
22+
{...props}
23+
/>
24+
));
25+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
26+
27+
export { Popover, PopoverTrigger, PopoverContent };

packages/shadcn/src/form/Form.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { ReactNode } from "react";
2+
3+
export const Form = (props: { children: ReactNode }) => {
4+
return <Form>{props.children}</Form>;
5+
};

packages/shadcn/src/index.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
ComponentsContextValue,
55
} from "@blocknote/react";
66
import { ComponentProps } from "react";
7+
import { Form } from "./form/Form";
78
import {
89
Menu,
910
MenuDivider,
@@ -17,8 +18,14 @@ import { PanelButton } from "./panel/PanelButton";
1718
import { PanelFileInput } from "./panel/PanelFileInput";
1819
import { PanelTab } from "./panel/PanelTab";
1920
import { PanelTextInput } from "./panel/PanelTextInput";
20-
import "./style.css";
2121
import { Toolbar, ToolbarButton, ToolbarSelect } from "./toolbar/Toolbar";
22+
import {
23+
Popover,
24+
PopoverContent,
25+
PopoverTrigger,
26+
} from "./components/ui/popover";
27+
import { TextInput } from "./input/TextInput";
28+
import "./style.css";
2229

2330
export const components: ComponentsContextValue = {
2431
Toolbar,
@@ -35,11 +42,11 @@ export const components: ComponentsContextValue = {
3542
PanelFileInput,
3643
PanelTab,
3744
PanelTextInput,
38-
Popover: () => null,
39-
PopoverContent: () => null,
40-
PopoverTrigger: () => null,
41-
TextInput: () => null,
42-
Form: (props) => <div {...props} />,
45+
Popover: Popover,
46+
PopoverContent: PopoverContent,
47+
PopoverTrigger: PopoverTrigger,
48+
TextInput: TextInput,
49+
Form: Form,
4350
// SuggestionMenuLoader: () => (
4451
// <Loader className={"bn-slash-menu-loader"} type="dots" />
4552
// ),

0 commit comments

Comments
 (0)