Skip to content

Commit

Permalink
feat: add support for custom fields
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisvxd committed Sep 14, 2023
1 parent f57eb86 commit b46b721
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 2 deletions.
39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,37 @@ const myPlugin = {
};
```

## Custom fields

Puck supports custom fields using the `custom` field type and `render` method.

In this example, we optionally add the `<FieldLabel>` component to add a label:

```tsx
import { FieldLabel } from "@measured/puck";

export const MyComponent: ComponentConfig = {
fields: {
myField: {
type: "custom",
render: ({ field, name, onChange, value }) => {
return (
<FieldLabel label={field.label || name}>
<input
placeholder="Enter text..."
type="text"
name={name}
defaultValue={value}
onChange={(e) => onChange(e.currentTarget.value)}
></input>
</FieldLabel>
);
},
},
},
};
```

## Reference

### `<Puck>`
Expand Down Expand Up @@ -152,7 +183,7 @@ The `Config` object describes which components Puck should render, how they shou

A `Field` represents a user input field shown in the Puck interface.

- **type** (`text` | `textarea` | `number` | `select` | `radio` | `external` | `array`): The input type to render
- **type** (`text` | `textarea` | `number` | `select` | `radio` | `external` | `array` | `custom`): The input type to render
- **label** (`text` [optional]): A label for the input. Will use the key if not provided.
- **arrayFields** (`object`): Object describing sub-fields for items in an `array` input
- **[fieldName]** (`Field`): The Field objects describing the input data for each item
Expand All @@ -163,6 +194,12 @@ A `Field` represents a user input field shown in the Puck interface.
- **value** (`string` | `number` | `boolean`)
- **adaptor** (`Adaptor`): Content adaptor if using the `external` input type
- **adaptorParams** (`object`): Paramaters passed to the adaptor
- **render** (`Component`): Render a custom field. Receives the props:
- **field** (`Field`): Field configuration
- **name** (`string`): Name of the field
- **value** (`any`): Value for the field
- **onChange** (`(value: any) => void`): Callback to change the value
- **readOnly** (`boolean` | `undefined`): Whether or not the field should be in readOnly mode

### `Data`

Expand Down
39 changes: 39 additions & 0 deletions packages/core/components/InputOrGroup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,30 @@ import {
Hash,
} from "react-feather";
import { IconButton } from "../IconButton";
import { ReactNode } from "react";

const getClassName = getClassNameFactory("Input", styles);

export const FieldLabel = ({
children,
icon,
label,
}: {
children?: ReactNode;
icon?: ReactNode;
label: string;
}) => {
return (
<label>
<div className={getClassName("label")}>
{icon && <div className={getClassName("labelIcon")}></div>}
{label}
</div>
{children}
</label>
);
};

export const InputOrGroup = ({
name,
field,
Expand Down Expand Up @@ -237,6 +258,24 @@ export const InputOrGroup = ({
);
}

if (field.type === "custom") {
if (!field.render) {
return null;
}

return (
<div className={getClassName()}>
{field.render({
field,
name,
value,
onChange,
readOnly,
})}
</div>
);
}

return (
<label className={getClassName({ readOnly })}>
<div className={getClassName("label")}>
Expand Down
2 changes: 2 additions & 0 deletions packages/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export * from "./components/Button";
export * from "./components/IconButton";
export * from "./components/Puck";
export * from "./components/Render";

export { FieldLabel } from "./components/InputOrGroup";
10 changes: 9 additions & 1 deletion packages/core/types/Config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export type Field<
| "select"
| "array"
| "external"
| "radio";
| "radio"
| "custom";
label?: string;
adaptor?: Adaptor;
adaptorParams?: object;
Expand All @@ -27,6 +28,13 @@ export type Field<
};
getItemSummary?: (item: Props, index?: number) => string;
defaultItemProps?: Props;
render?: (props: {
field: Field;
name: string;
value: any;
onChange: (value: any) => void;
readOnly?: boolean;
}) => ReactElement;
options?: {
label: string;
value: string | number | boolean;
Expand Down

0 comments on commit b46b721

Please sign in to comment.