Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ should change the heading of the (upcoming) version to include a major version b

## @rjsf/shadcn

- Update `README.md` with picture of the theme!
- Bump `@shadcn/ui` components to use latest version from https://ui.shadcn.com/
- Bump `tailwindcss` to using v4 and css compiling process to use latest `@tailwindcss/cli`
- Remove `postcss` due to new Oxide compiler of tailwindcss
- Update playground themes with `default`, `Amethyst Haze`, `Caffeine`, `Claude`, `Neo Brutalism`, `Pastel Dreams`, `Soft Pop`, `Twitter`, `Vercel`
- Radio widget labels are now accessible and can be clicked on to select the associated option.
- Allow passing `className` props to `AddButton`, `BaseInputTemplate`, `CheckboxWidget`, `CheckboxesWidget`, `RadioWidget`, `SelectWidget`, `SubmitButton`, `TextareaWidget` for extra Tailwind CSS customization through `ui:className`

## @rjsf/utils

Expand Down
8 changes: 6 additions & 2 deletions packages/shadcn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
<a href="https://github.com/rjsf-team/react-jsonschema-form/issues">Request Feature</a>
</p>


<p align="center">
<img src="https://github.com/tuanphung2308/rjsf-shadcn-css/blob/main/shadcn-demo.png?raw=true" alt="Logo" width="720" height="240">
</p>

<!-- TABLE OF CONTENTS -->

## Table of Contents
Expand Down Expand Up @@ -134,8 +139,7 @@ Supported colors are:
#### Coloring

- Generate a theme from [official shadCN site](https://ui.shadcn.com/themes)
or [zippy starter's shadcn/ui theme generator](https://zippystarter.com/tools/shadcn-ui-theme-generator)
or [Railly](https://customizer.railly.dev/)
or [tweakcn](https://tweakcn.com/editor/theme)
- Navigate to shadcn/css, create a new file called [your-theme].css
- Replace the base layer code with your new color
- Follow the next section to build your CSS file
Expand Down
7 changes: 6 additions & 1 deletion packages/shadcn/src/AddButton/AddButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@ import { FormContextType, IconButtonProps, RJSFSchema, StrictRJSFSchema, Transla
import { PlusCircle } from 'lucide-react';

import { Button } from '../components/ui/button';
import { cn } from '../lib/utils';

/**
* A button component for adding new items in a form
* @param uiSchema - The UI schema for the form, which can include custom properties
* @param registry - The registry object containing the form's configuration and utilities
* @param className - Allow custom class names to be passed for Tailwind CSS styling
* @param props - The component properties
*/
export default function AddButton<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>({
uiSchema,
registry,
className,
...props
}: IconButtonProps<T, S, F>) {
const { translateString } = registry;
return (
<div className='p-0 m-0'>
<Button {...props} className='w-fit gap-2' variant='outline' type='button'>
<Button {...props} className={cn('w-fit gap-2', className)} variant='outline' type='button'>
<PlusCircle size={16} /> {translateString(TranslatableString.AddItemButton)}
</Button>
</div>
Expand Down
3 changes: 2 additions & 1 deletion packages/shadcn/src/BaseInputTemplate/BaseInputTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default function BaseInputTemplate<
rawErrors = [],
children,
extraProps,
className,
}: BaseInputTemplateProps<T, S, F>) {
const inputProps = {
...extraProps,
Expand All @@ -61,7 +62,7 @@ export default function BaseInputTemplate<
required={required}
disabled={disabled}
readOnly={readonly}
className={cn({ 'border-destructive focus-visible:ring-0': rawErrors.length > 0 })}
className={cn({ 'border-destructive focus-visible:ring-0': rawErrors.length > 0 }, className)}
list={schema.examples ? examplesId<T>(id) : undefined}
{...inputProps}
value={value || value === 0 ? value : ''}
Expand Down
2 changes: 2 additions & 0 deletions packages/shadcn/src/CheckboxWidget/CheckboxWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default function CheckboxWidget<
onFocus,
registry,
uiSchema,
className,
} = props;
// Because an unchecked checkbox will cause html5 validation to fail, only add
// the "required" attribute if the field value must be "true", due to the
Expand Down Expand Up @@ -78,6 +79,7 @@ export default function CheckboxWidget<
onCheckedChange={_onChange}
onBlur={_onBlur}
onFocus={_onFocus}
className={className}
/>
<Label className='leading-tight' htmlFor={id}>
{labelValue(label, hideLabel || !label)}
Expand Down
15 changes: 14 additions & 1 deletion packages/shadcn/src/CheckboxesWidget/CheckboxesWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,19 @@ export default function CheckboxesWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any,
>({ id, disabled, options, value, autofocus, readonly, required, onChange, onBlur, onFocus }: WidgetProps<T, S, F>) {
>({
id,
disabled,
options,
value,
autofocus,
readonly,
required,
onChange,
onBlur,
onFocus,
className,
}: WidgetProps<T, S, F>) {
const { enumOptions, enumDisabled, inline, emptyValue } = options;
const checkboxesValues = Array.isArray(value) ? value : [value];

Expand Down Expand Up @@ -56,6 +68,7 @@ export default function CheckboxesWidget<
onChange(enumOptionsDeselectValue<S>(index, checkboxesValues, enumOptions));
}
}}
className={className}
checked={checked}
autoFocus={autofocus && index === 0}
onBlur={_onBlur}
Expand Down
3 changes: 2 additions & 1 deletion packages/shadcn/src/RadioWidget/RadioWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default function RadioWidget<T = any, S extends StrictRJSFSchema = RJSFSc
onChange,
onBlur,
onFocus,
className,
}: WidgetProps<T, S, F>) {
const { enumOptions, enumDisabled, emptyValue } = options;

Expand All @@ -53,7 +54,7 @@ export default function RadioWidget<T = any, S extends StrictRJSFSchema = RJSFSc
onFocus={_onFocus}
aria-describedby={ariaDescribedByIds<T>(id)}
orientation={inline ? 'horizontal' : 'vertical'}
className={cn('flex flex-wrap', { 'flex-col': !inline })}
className={cn('flex flex-wrap', { 'flex-col': !inline }, className)}
>
{Array.isArray(enumOptions) &&
enumOptions.map((option, index) => {
Expand Down
7 changes: 5 additions & 2 deletions packages/shadcn/src/SelectWidget/SelectWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default function SelectWidget<
defaultValue,
placeholder,
rawErrors = [],
className,
}: WidgetProps<T, S, F>) {
const { enumOptions, enumDisabled, emptyValue: optEmptyValue } = options;

Expand All @@ -54,6 +55,8 @@ export default function SelectWidget<
disabled: Array.isArray(enumDisabled) && enumDisabled.includes(value),
}));

const cnClassName = cn({ 'border-destructive': rawErrors.length > 0 }, className);

return (
<div className='p-0.5'>
{!multiple ? (
Expand All @@ -67,7 +70,7 @@ export default function SelectWidget<
disabled={disabled || readonly}
required={required}
placeholder={placeholder}
className={cn({ 'border-destructive': rawErrors.length > 0 })}
className={cnClassName}
onFocus={_onFancyFocus}
onBlur={_onFancyBlur}
ariaDescribedby={ariaDescribedByIds<T>(id)}
Expand All @@ -78,7 +81,7 @@ export default function SelectWidget<
autoFocus={autofocus}
disabled={disabled || readonly}
multiple
className={rawErrors.length > 0 ? 'border-destructive' : ''}
className={cnClassName}
items={items}
selected={value}
onValueChange={(values) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/shadcn/src/SubmitButton/SubmitButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FormContextType, getSubmitButtonOptions, RJSFSchema, StrictRJSFSchema, SubmitButtonProps } from '@rjsf/utils';

import { Button } from '../components/ui/button';
import { cn } from '../lib/utils';

/** The `SubmitButton` renders a button that represent the `Submit` action on a form
*/
Expand All @@ -13,7 +14,7 @@ export default function SubmitButton<T = any, S extends StrictRJSFSchema = RJSFS
}
return (
<div>
<Button type='submit' {...submitButtonProps} className='my-2'>
<Button type='submit' {...submitButtonProps} className={cn('my-2', submitButtonProps?.className)}>
{submitText}
</Button>
</div>
Expand Down
2 changes: 2 additions & 0 deletions packages/shadcn/src/TextareaWidget/TextareaWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default function TextareaWidget<
onFocus,
onChange,
options,
className,
}: CustomWidgetProps<T, S, F>) {
const _onChange = ({ target: { value } }: ChangeEvent<HTMLTextAreaElement>) =>
onChange(value === '' ? options.emptyValue : value);
Expand All @@ -53,6 +54,7 @@ export default function TextareaWidget<
onBlur={_onBlur}
onFocus={_onFocus}
aria-describedby={ariaDescribedByIds<T>(id)}
className={className}
/>
</div>
);
Expand Down
Loading