From 82ed63a737e3f9ace114b1a287144ce639776dc7 Mon Sep 17 00:00:00 2001 From: Chris Villa Date: Fri, 24 Nov 2023 10:55:01 +0000 Subject: [PATCH 1/2] fix: enable user to pass in config without casting Closes #185 --- packages/core/components/Puck/index.tsx | 2 +- packages/core/components/Render/index.tsx | 8 +++++++- packages/core/lib/resolve-all-data.ts | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/core/components/Puck/index.tsx b/packages/core/components/Puck/index.tsx index 6cc571b0e8..fdc7f93c37 100644 --- a/packages/core/components/Puck/index.tsx +++ b/packages/core/components/Puck/index.tsx @@ -98,7 +98,7 @@ export function Puck({ headerTitle, headerPath, }: { - config: Config; + config: Config; data: Data; onChange?: (data: Data) => void; onPublish: (data: Data) => void; diff --git a/packages/core/components/Render/index.tsx b/packages/core/components/Render/index.tsx index cbcdcb9ec6..48dc0783d3 100644 --- a/packages/core/components/Render/index.tsx +++ b/packages/core/components/Render/index.tsx @@ -4,7 +4,13 @@ import { rootDroppableId } from "../../lib/root-droppable-id"; import { Config, Data } from "../../types/Config"; import { DropZone, DropZoneProvider } from "../DropZone"; -export function Render({ config, data }: { config: Config; data: Data }) { +export function Render({ + config, + data, +}: { + config: Config; + data: Data; +}) { // DEPRECATED const rootProps = data.root.props || data.root; diff --git a/packages/core/lib/resolve-all-data.ts b/packages/core/lib/resolve-all-data.ts index 5ee83c6d14..252471b7e9 100644 --- a/packages/core/lib/resolve-all-data.ts +++ b/packages/core/lib/resolve-all-data.ts @@ -4,7 +4,7 @@ import { resolveRootData } from "./resolve-root-data"; export const resolveAllData = async ( data: Data, - config: Config, + config: Config, onResolveStart?: (item: MappedItem) => void, onResolveEnd?: (item: MappedItem) => void ) => { From b7e04c8690bc1ec1ccda201c7e044e87f9cde973 Mon Sep 17 00:00:00 2001 From: Chris Villa Date: Fri, 24 Nov 2023 10:57:05 +0000 Subject: [PATCH 2/2] feat: add APIs to restrict components dropped in DropZones --- apps/demo/config/blocks/Columns/index.tsx | 5 +- apps/demo/config/index.tsx | 6 ++- .../api-reference/components/drop-zone.mdx | 50 +++++++++++++++++-- .../docs/integrating-puck/categories.mdx | 16 ++++++ .../integrating-puck/multi-column-layouts.mdx | 47 +++++++++++++++++ packages/core/components/DropZone/index.tsx | 25 +++++++++- packages/core/components/Puck/index.tsx | 4 +- 7 files changed, 145 insertions(+), 8 deletions(-) diff --git a/apps/demo/config/blocks/Columns/index.tsx b/apps/demo/config/blocks/Columns/index.tsx index a2ecf804cc..bd0837f333 100644 --- a/apps/demo/config/blocks/Columns/index.tsx +++ b/apps/demo/config/blocks/Columns/index.tsx @@ -71,7 +71,10 @@ export const Columns: ComponentConfig = { : "", }} > - + ))} diff --git a/apps/demo/config/index.tsx b/apps/demo/config/index.tsx index a431a62552..fcd0b650ef 100644 --- a/apps/demo/config/index.tsx +++ b/apps/demo/config/index.tsx @@ -26,7 +26,11 @@ type Props = { }; // We avoid the name config as next gets confused -export const conf: Config = { +export const conf: Config< + Props, + RootProps, + "layout" | "typography" | "interactive" +> = { root: { render: Root, }, diff --git a/apps/docs/pages/docs/api-reference/components/drop-zone.mdx b/apps/docs/pages/docs/api-reference/components/drop-zone.mdx index 7c83ae7b58..ee97aabad6 100644 --- a/apps/docs/pages/docs/api-reference/components/drop-zone.mdx +++ b/apps/docs/pages/docs/api-reference/components/drop-zone.mdx @@ -26,9 +26,11 @@ const config = { ## Props -| Param | Example | Type | Status | -| --------------- | ----------------- | ------ | -------- | -| [`zone`](#zone) | `zone: "my-zone"` | String | Required | +| Param | Example | Type | Status | +| ----------------------- | ---------------------------- | ------ | -------- | +| [`zone`](#zone) | `zone: "my-zone"` | String | Required | +| [`allow`](#allow) | `allow: ["HeadingBlock"]` | Array | | +| [`disallow`](#disallow) | `disallow: ["HeadingBlock"]` | Array | | ## Required props @@ -54,6 +56,48 @@ const config = { }; ``` +## Optional props + +### `allow` + +Only allow specific components to be dragged into the DropZone: + +```tsx copy {7} +const config = { + components: { + Example: { + render: () => { + return ( +
+ +
+ ); + }, + }, + }, +}; +``` + +### `disallow` + +Allow all but specific components to be dragged into the DropZone. Any items in `allow` will override `disallow`. + +```tsx copy {7} +const config = { + components: { + Example: { + render: () => { + return ( +
+ +
+ ); + }, + }, + }, +}; +``` + ## Restrictions You can't drag between DropZones that don't share a parent component. diff --git a/apps/docs/pages/docs/integrating-puck/categories.mdx b/apps/docs/pages/docs/integrating-puck/categories.mdx index 077660e99e..7caffc1484 100644 --- a/apps/docs/pages/docs/integrating-puck/categories.mdx +++ b/apps/docs/pages/docs/integrating-puck/categories.mdx @@ -70,6 +70,22 @@ const config = { }; ``` +## TypeScript + +You can pass in available category names to the `Config` type if using TypeScript + +```tsx copy {3} +import type { Config } from "@measured/puck"; + +const config: Config<{}, {}, "typography" | "interactive"> = { + categories: { + typography: {}, + interactive: {}, + }, + // ... +}; +``` + ## Further reading - [`categories` API reference](/docs/api-reference/configuration/config#categories) diff --git a/apps/docs/pages/docs/integrating-puck/multi-column-layouts.mdx b/apps/docs/pages/docs/integrating-puck/multi-column-layouts.mdx index 46f63a279b..b08bc90c42 100644 --- a/apps/docs/pages/docs/integrating-puck/multi-column-layouts.mdx +++ b/apps/docs/pages/docs/integrating-puck/multi-column-layouts.mdx @@ -95,6 +95,53 @@ const config = { }; ``` +### Restricting components + +The [`allow`](/docs/api-reference/components/drop-zone#allow) and [`disallow`](/docs/api-reference/components/drop-zone#disallow) props allow us to restrict which components can be dragged into a DropZone. + +```tsx {9} showLineNumbers copy +import { DropZone } from "@measured/puck"; + +const config = { + components: { + Example: { + render: () => { + return ( +
+ +
+ ); + }, + }, + }, +}; +``` + +This can be combined with [categories](/docs/integrating-puck/categories) to restrict based on your existing groups: + +```tsx {4-8,14} showLineNumbers copy +import { DropZone } from "@measured/puck"; + +const config = { + config: { + typography: { + components: ["HeadingBlock"], + }, + }, + components: { + Example: { + render: () => { + return ( +
+ +
+ ); + }, + }, + }, +}; +``` + ## Further reading - [The `` API](/docs/api-reference/components/drop-zone) diff --git a/packages/core/components/DropZone/index.tsx b/packages/core/components/DropZone/index.tsx index 5c62e0747e..efc4f5ed76 100644 --- a/packages/core/components/DropZone/index.tsx +++ b/packages/core/components/DropZone/index.tsx @@ -16,10 +16,12 @@ export { DropZoneProvider, dropZoneContext } from "./context"; type DropZoneProps = { zone: string; + allow?: string[]; + disallow?: string[]; style?: CSSProperties; }; -function DropZoneEdit({ zone, style }: DropZoneProps) { +function DropZoneEdit({ zone, allow, disallow, style }: DropZoneProps) { const appContext = useAppContext(); const ctx = useContext(dropZoneContext); @@ -133,6 +135,27 @@ function DropZoneEdit({ zone, style }: DropZoneProps) { } } + if (isEnabled && userIsDragging && (allow || disallow)) { + const [_, componentType] = draggedItem.draggableId.split("::"); + + if (disallow) { + const defaultedAllow = allow || []; + + // remove any explicitly allowed items from disallow + const filteredDisallow = (disallow || []).filter( + (item) => defaultedAllow.indexOf(item) === -1 + ); + + if (filteredDisallow.indexOf(componentType) !== -1) { + isEnabled = false; + } + } else if (allow) { + if (allow.indexOf(componentType) === -1) { + isEnabled = false; + } + } + } + const selectedItem = itemSelector ? getItem(itemSelector, data) : null; const isAreaSelected = selectedItem && zoneArea === selectedItem.props.id; diff --git a/packages/core/components/Puck/index.tsx b/packages/core/components/Puck/index.tsx index fdc7f93c37..7bab6e3dfd 100644 --- a/packages/core/components/Puck/index.tsx +++ b/packages/core/components/Puck/index.tsx @@ -354,11 +354,11 @@ export function Puck({ droppedItem.source.droppableId.startsWith("component-list") && droppedItem.destination ) { - const [_, componentId] = droppedItem.draggableId.split("::"); + const [_, componentType] = droppedItem.draggableId.split("::"); dispatch({ type: "insert", - componentType: componentId || droppedItem.draggableId, + componentType: componentType || droppedItem.draggableId, destinationIndex: droppedItem.destination!.index, destinationZone: droppedItem.destination.droppableId, });