-
- {children}
+export const Section = forwardRef
(
+ ({ children, className, maxWidth = "1280px", style = {} }, ref) => {
+ return (
+
-
- );
-};
+ );
+ }
+);
diff --git a/apps/demo/config/components/Section/styles.module.css b/apps/demo/config/components/Section/styles.module.css
index abcf3646fe..d26292c530 100644
--- a/apps/demo/config/components/Section/styles.module.css
+++ b/apps/demo/config/components/Section/styles.module.css
@@ -10,8 +10,14 @@
}
}
-.Section:not(.Section .Section) .Section-inner {
+.Section-inner {
margin-left: auto;
margin-right: auto;
+ height: 100%;
width: 100%;
}
+
+.Section .Section .Section-inner {
+ margin-left: 0;
+ margin-right: 0;
+}
diff --git a/apps/demo/config/index.tsx b/apps/demo/config/index.tsx
index 1639c68099..ee9b287454 100644
--- a/apps/demo/config/index.tsx
+++ b/apps/demo/config/index.tsx
@@ -1,30 +1,30 @@
import { Config, Data } from "@/core";
-import { ButtonGroup, ButtonGroupProps } from "./blocks/ButtonGroup";
+import { Button, ButtonProps } from "./blocks/Button";
import { Card, CardProps } from "./blocks/Card";
-import { Columns, ColumnsProps } from "./blocks/Columns";
+import { Grid, GridProps } from "./blocks/Grid";
import { Hero, HeroProps } from "./blocks/Hero";
import { Heading, HeadingProps } from "./blocks/Heading";
import { Flex, FlexProps } from "./blocks/Flex";
import { Logos, LogosProps } from "./blocks/Logos";
import { Stats, StatsProps } from "./blocks/Stats";
import { Text, TextProps } from "./blocks/Text";
-import { VerticalSpace, VerticalSpaceProps } from "./blocks/VerticalSpace";
+import { Space, SpaceProps } from "./blocks/Space";
import Root, { RootProps } from "./root";
export type { RootProps } from "./root";
export type Props = {
- ButtonGroup: ButtonGroupProps;
+ Button: ButtonProps;
Card: CardProps;
- Columns: ColumnsProps;
+ Grid: GridProps;
Hero: HeroProps;
Heading: HeadingProps;
Flex: FlexProps;
Logos: LogosProps;
Stats: StatsProps;
Text: TextProps;
- VerticalSpace: VerticalSpaceProps;
+ Space: SpaceProps;
};
export type UserConfig = Config<
@@ -45,27 +45,31 @@ export const conf: UserConfig = {
},
categories: {
layout: {
- components: ["Columns", "Flex", "VerticalSpace"],
+ components: ["Grid", "Flex", "Space"],
},
typography: {
components: ["Heading", "Text"],
},
interactive: {
title: "Actions",
- components: ["ButtonGroup"],
+ components: ["Button"],
+ },
+ other: {
+ title: "Other",
+ components: ["Card", "Hero", "Logos", "Stats"],
},
},
components: {
- ButtonGroup,
+ Button,
Card,
- Columns,
+ Grid,
Hero,
Heading,
Flex,
Logos,
Stats,
Text,
- VerticalSpace,
+ Space,
},
};
@@ -93,10 +97,15 @@ export const initialData: Record
= {
padding: "128px",
align: "left",
},
+ readOnly: { title: false, description: false },
},
{
- type: "VerticalSpace",
- props: { size: "96px", id: "VerticalSpace-1687298109536" },
+ type: "Space",
+ props: {
+ size: "96px",
+ id: "Space-1687298109536",
+ direction: "vertical",
+ },
},
{
type: "Heading",
@@ -104,49 +113,61 @@ export const initialData: Record = {
align: "center",
level: "2",
text: "Drag-and-drop your own React components",
- padding: "0px",
+ layout: { padding: "0px" },
size: "xxl",
id: "Heading-1687297593514",
},
},
{
- type: "VerticalSpace",
- props: { size: "8px", id: "VerticalSpace-1687284122744" },
+ type: "Space",
+ props: {
+ size: "8px",
+ id: "Space-1687284122744",
+ direction: "vertical",
+ },
},
{
type: "Text",
props: {
align: "center",
text: "Configure Puck with your own components to make change for your marketing pages without a developer.",
- padding: "0px",
+ layout: { padding: "0px" },
size: "m",
id: "Text-1687297621556",
color: "muted",
},
},
{
- type: "VerticalSpace",
- props: { size: "40px", id: "VerticalSpace-1687296179388" },
+ type: "Space",
+ props: {
+ size: "40px",
+ id: "Space-1687296179388",
+ direction: "vertical",
+ },
},
{
- type: "Columns",
+ type: "Grid",
props: {
- columns: [
- { id: "aed487d8-640d-4788-834f-39d282b48dbe" },
- { id: "b225c7ba-c90c-4290-9faf-46809aeb2469" },
- { id: "0059e9d0-1bd2-4262-afd8-841b7099cbaa" },
- ],
- distribution: "auto",
- id: "Columns-2d650a8ceb081a2c04f3a2d17a7703ca6efb0d06",
+ id: "Grid-c4cd99ae-8c5e-4cdb-87d2-35a639f5163e",
+ gap: 24,
+ numColumns: 3,
},
},
{
- type: "VerticalSpace",
- props: { size: "96px", id: "VerticalSpace-1687287070296" },
+ type: "Space",
+ props: {
+ size: "96px",
+ id: "Space-1687287070296",
+ direction: "vertical",
+ },
},
{
- type: "VerticalSpace",
- props: { size: "96px", id: "VerticalSpace-1687298110602" },
+ type: "Space",
+ props: {
+ size: "96px",
+ id: "Space-1687298110602",
+ direction: "vertical",
+ },
},
{
type: "Heading",
@@ -154,21 +175,25 @@ export const initialData: Record = {
align: "center",
level: "2",
text: "The numbers",
- padding: "0px",
+ layout: { padding: "0px" },
size: "xxl",
id: "Heading-1687296574110",
},
},
{
- type: "VerticalSpace",
- props: { size: "16px", id: "VerticalSpace-1687284283005" },
+ type: "Space",
+ props: {
+ size: "16px",
+ id: "Space-1687284283005",
+ direction: "vertical",
+ },
},
{
type: "Text",
props: {
align: "center",
text: 'This page demonstrates Puck configured with a custom component library. This component is called "Stats", and contains some made-up numbers. You can configure any page by adding "/edit" onto the URL.',
- padding: "0px",
+ layout: { padding: "0px" },
size: "m",
id: "Text-1687284565722",
color: "muted",
@@ -176,8 +201,12 @@ export const initialData: Record = {
},
},
{
- type: "VerticalSpace",
- props: { size: "96px", id: "VerticalSpace-1687297618253" },
+ type: "Space",
+ props: {
+ size: "96px",
+ id: "Space-1687297618253",
+ direction: "vertical",
+ },
},
{
type: "Stats",
@@ -192,8 +221,12 @@ export const initialData: Record = {
},
},
{
- type: "VerticalSpace",
- props: { size: "120px", id: "VerticalSpace-1687297589663" },
+ type: "Space",
+ props: {
+ size: "120px",
+ id: "Space-1687297589663",
+ direction: "vertical",
+ },
},
{
type: "Heading",
@@ -201,21 +234,25 @@ export const initialData: Record = {
align: "center",
level: "2",
text: "Extending Puck",
- padding: "0px",
+ layout: { padding: "0px" },
size: "xxl",
id: "Heading-1687296184321",
},
},
{
- type: "VerticalSpace",
- props: { size: "8px", id: "VerticalSpace-1687296602860" },
+ type: "Space",
+ props: {
+ size: "8px",
+ id: "Space-1687296602860",
+ direction: "vertical",
+ },
},
{
type: "Text",
props: {
align: "center",
text: "Puck can also be extended with plugins and headless CMS content fields, transforming Puck into the perfect tool for your Content Ops.",
- padding: "0px",
+ layout: { padding: "0px" },
size: "m",
id: "Text-1687296579834",
color: "muted",
@@ -223,27 +260,28 @@ export const initialData: Record = {
},
},
{
- type: "VerticalSpace",
- props: { size: "96px", id: "VerticalSpace-1687299311382" },
+ type: "Space",
+ props: {
+ size: "96px",
+ id: "Space-1687299311382",
+ direction: "vertical",
+ },
},
{
- type: "Columns",
+ type: "Grid",
props: {
- columns: [
- { span: 4, id: "f6baf6a3-3702-427f-a427-81c14855ad7f" },
- { span: 4, id: "2d7e1c2e-5b50-4412-9aee-27441e99bc1e" },
- { span: 4, id: "a23fcb9d-e0c4-4f07-9c03-d30f901d87c4" },
- { span: 4, id: "ec906bd1-fce6-4386-893b-1840dfa8c3df" },
- { span: 4, id: "812e374a-a5e0-4c45-95b4-0f9494d3d473" },
- { span: 4, id: "71c77d92-2a30-4e0c-86d9-8dbdb017ee89" },
- ],
- id: "Columns-3c2ca5b045ee26535fcdf0eddf409a6308764634",
- distribution: "manual",
+ gap: 24,
+ numColumns: 3,
+ id: "Grid-2da28e88-7b7b-4152-9da0-9f93f41213b6",
},
},
{
- type: "VerticalSpace",
- props: { size: "96px", id: "VerticalSpace-1687299315421" },
+ type: "Space",
+ props: {
+ size: "96px",
+ id: "Space-1687299315421",
+ direction: "vertical",
+ },
},
{
type: "Heading",
@@ -251,175 +289,189 @@ export const initialData: Record = {
align: "center",
level: "2",
text: "Get started",
- padding: "0px",
+ layout: { padding: "0px" },
size: "xxl",
id: "Heading-1687299303766",
},
},
{
- type: "VerticalSpace",
- props: { size: "16px", id: "VerticalSpace-1687299318902" },
+ type: "Space",
+ props: {
+ size: "16px",
+ id: "Space-1687299318902",
+ direction: "vertical",
+ },
},
{
type: "Text",
props: {
align: "center",
text: "Browse the Puck GitHub to get started, or try editing this page",
- padding: "0px",
+ layout: { padding: "0px" },
size: "m",
id: "Text-1687299305686",
color: "muted",
},
},
{
- type: "VerticalSpace",
- props: { size: "24px", id: "VerticalSpace-1687299335149" },
+ type: "Space",
+ props: {
+ size: "24px",
+ id: "Space-1687299335149",
+ direction: "vertical",
+ },
},
{
- type: "ButtonGroup",
+ type: "Flex",
props: {
- buttons: [
- {
- label: "Visit GitHub",
- href: "https://github.com/measuredco/puck",
- variant: "primary",
- },
- { label: "Edit this page", href: "/edit", variant: "secondary" },
- ],
- id: "ButtonGroup-1687299235545",
- align: "center",
+ justifyContent: "center",
+ direction: "row",
+ gap: 24,
+ wrap: "wrap",
+ layout: { spanCol: 1, spanRow: 1, padding: "0px" },
+ id: "Flex-7d63d5ff-bd42-4354-b05d-681b16436fd6",
},
},
{
- type: "VerticalSpace",
- props: { size: "96px", id: "VerticalSpace-1687284290127" },
+ type: "Space",
+ props: {
+ size: "96px",
+ id: "Space-1687284290127",
+ direction: "vertical",
+ },
},
],
root: { props: { title: "Puck Example" } },
zones: {
- "Columns-2d650a8ceb081a2c04f3a2d17a7703ca6efb0d06:column-aed487d8-640d-4788-834f-39d282b48dbe":
- [
- {
- type: "Card",
- props: {
- title: "Built for content teams",
- description:
- "Puck enables content teams to make changes to their content without a developer or breaking the UI.",
- icon: "pen-tool",
- mode: "flat",
- id: "Card-0d9077e00e0ad66c34c62ab6986967e1ce04f9e4",
- },
+ "Grid-c4cd99ae-8c5e-4cdb-87d2-35a639f5163e:grid": [
+ {
+ type: "Card",
+ props: {
+ title: "Built for content teams",
+ description:
+ "Puck enables content teams to make changes to their content without a developer or breaking the UI.",
+ icon: "pen-tool",
+ mode: "flat",
+ layout: { grow: true, spanCol: 1, spanRow: 1, padding: "0px" },
+ id: "Card-66ab42c9-d1da-4c44-9dba-5d7d72f2178d",
},
- ],
- "Columns-2d650a8ceb081a2c04f3a2d17a7703ca6efb0d06:column-b225c7ba-c90c-4290-9faf-46809aeb2469":
- [
- {
- type: "Card",
- props: {
- title: "Easy to integrate",
- description:
- "Front-end developers can easily integrate their own components using a familiar React API.",
- icon: "git-merge",
- mode: "flat",
- id: "Card-978bef5d136d4b0d9855f5272429986ceb22e5a6",
- },
+ },
+ {
+ type: "Card",
+ props: {
+ title: "Easy to integrate",
+ description:
+ "Front-end developers can easily integrate their own components using a familiar React API.",
+ icon: "git-merge",
+ mode: "flat",
+ layout: { grow: true, spanCol: 1, spanRow: 1, padding: "0px" },
+ id: "Card-0012a293-8ef3-4e7c-9d7c-7da0a03d97ae",
},
- ],
- "Columns-2d650a8ceb081a2c04f3a2d17a7703ca6efb0d06:column-0059e9d0-1bd2-4262-afd8-841b7099cbaa":
- [
- {
- type: "Card",
- props: {
- title: "No vendor lock-in",
- description:
- "Completely open-source, Puck is designed to be integrated into your existing React application.",
- icon: "github",
- mode: "flat",
- id: "Card-133a61826f0019841aec6f0aec011bf07e6bc6de",
- },
+ },
+ {
+ type: "Card",
+ props: {
+ title: "No vendor lock-in",
+ description:
+ "Completely open-source, Puck is designed to be integrated into your existing React application.",
+ icon: "github",
+ mode: "flat",
+ layout: { grow: true, spanCol: 1, spanRow: 1, padding: "0px" },
+ id: "Card-09efb3f3-f58d-4e07-a481-7238d7e57ad6",
},
- ],
- "Columns-3c2ca5b045ee26535fcdf0eddf409a6308764634:column-f6baf6a3-3702-427f-a427-81c14855ad7f":
- [
- {
- type: "Card",
- props: {
- title: "plugin-heading-analyzer",
- description:
- "Analyze the document structure and identify WCAG 2.1 issues with your heading hierarchy.",
- icon: "align-left",
- mode: "card",
- id: "Card-e2e757b0b4a579d5f87564dfa9b4442f9794b45b",
- },
+ },
+ ],
+ "Grid-2da28e88-7b7b-4152-9da0-9f93f41213b6:grid": [
+ {
+ type: "Card",
+ props: {
+ title: "plugin-heading-analyzer",
+ description:
+ "Analyze the document structure and identify WCAG 2.1 issues with your heading hierarchy.",
+ icon: "align-left",
+ mode: "card",
+ layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" },
+ id: "Card-b0e8407d-9fbb-4e76-aa32-d32f655c11d3",
},
- ],
- "Columns-3c2ca5b045ee26535fcdf0eddf409a6308764634:column-2d7e1c2e-5b50-4412-9aee-27441e99bc1e":
- [
- {
- type: "Card",
- props: {
- title: "External data",
- description:
- "Connect your components with an existing data source, like Strapi.js.",
- icon: "feather",
- mode: "card",
- id: "Card-4eea28543d13c41c30934c3e4c4c95a75017a89c",
- },
+ },
+ {
+ type: "Card",
+ props: {
+ title: "External data",
+ description:
+ "Connect your components with an existing data source, like Strapi.js.",
+ icon: "feather",
+ mode: "card",
+ layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" },
+ id: "Card-f8ebd568-3a30-4099-a068-22cabae4691b",
},
- ],
- "Columns-3c2ca5b045ee26535fcdf0eddf409a6308764634:column-a23fcb9d-e0c4-4f07-9c03-d30f901d87c4":
- [
- {
- type: "Card",
- props: {
- title: "Custom plugins",
- description:
- "Create your own plugin to extend Puck for your use case using React.",
- icon: "feather",
- mode: "card",
- id: "Card-3314e8b24aa52843ce22ab7424b8f3b8064acfdf",
- },
+ },
+ {
+ type: "Card",
+ props: {
+ title: "Custom plugins",
+ description:
+ "Create your own plugin to extend Puck for your use case using React.",
+ icon: "plug",
+ mode: "card",
+ layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" },
+ id: "Card-9c3b0acc-ee42-4a4a-8cc7-1b22d98493f1",
},
- ],
- "Columns-3c2ca5b045ee26535fcdf0eddf409a6308764634:column-ec906bd1-fce6-4386-893b-1840dfa8c3df":
- [
- {
- type: "Card",
- props: {
- title: "Title",
- description: "Description",
- icon: "feather",
- mode: "card",
- id: "Card-49b11940784cfe8dc1a2b2facc5ac2bcf797792f",
- },
+ },
+ {
+ type: "Card",
+ props: {
+ title: "Title",
+ description: "Description",
+ icon: "Feather",
+ mode: "card",
+ layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" },
+ id: "Card-dbec4ae9-8208-49bf-8910-3347ff13d957",
},
- ],
- "Columns-3c2ca5b045ee26535fcdf0eddf409a6308764634:column-812e374a-a5e0-4c45-95b4-0f9494d3d473":
- [
- {
- type: "Card",
- props: {
- title: "Title",
- description: "Description",
- icon: "feather",
- mode: "card",
- id: "Card-efb0a1ed06cc4152a7861376aafbe62b0445382d",
- },
+ },
+ {
+ type: "Card",
+ props: {
+ title: "Title",
+ description: "Description",
+ icon: "Feather",
+ mode: "card",
+ layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" },
+ id: "Card-e807464c-4974-4dbb-b1c9-989deabce58d",
},
- ],
- "Columns-3c2ca5b045ee26535fcdf0eddf409a6308764634:column-71c77d92-2a30-4e0c-86d9-8dbdb017ee89":
- [
- {
- type: "Card",
- props: {
- title: "Title",
- description: "Description",
- icon: "feather",
- mode: "card",
- id: "Card-513cfb17d07ba4b6e0212d931571c0760839f029",
- },
+ },
+ {
+ type: "Card",
+ props: {
+ title: "Title",
+ description: "Description",
+ icon: "Feather",
+ mode: "card",
+ layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" },
+ id: "Card-3b4b7d53-2124-4d7a-a67e-36b24fd765b4",
+ },
+ },
+ ],
+ "Flex-7d63d5ff-bd42-4354-b05d-681b16436fd6:flex": [
+ {
+ type: "Button",
+ props: {
+ label: "Visit GitHub",
+ href: "https://github.com/measuredco/puck",
+ variant: "primary",
+ id: "Button-bd41007c-6627-414d-839a-e261d470d8f9",
},
- ],
+ },
+ {
+ type: "Button",
+ props: {
+ label: "Edit this page",
+ href: "/edit",
+ variant: "secondary",
+ id: "Button-6a5fa26c-8a2d-4b08-a756-c46079877127",
+ },
+ },
+ ],
},
},
"/pricing": {
diff --git a/apps/demo/config/root.tsx b/apps/demo/config/root.tsx
index a21b256174..36ea734b2e 100644
--- a/apps/demo/config/root.tsx
+++ b/apps/demo/config/root.tsx
@@ -1,4 +1,4 @@
-import { DefaultRootProps } from "@/core";
+import { DefaultRootProps, DropZone } from "@/core";
import { Footer } from "./components/Footer";
import { Header } from "./components/Header";
@@ -6,7 +6,9 @@ export type RootProps = DefaultRootProps;
function Root({ children, puck }: RootProps) {
return (
- <>
+
{children}
- >
+
);
}
diff --git a/apps/demo/next.config.js b/apps/demo/next.config.js
index 7bbb3f641c..2d9d22a35e 100644
--- a/apps/demo/next.config.js
+++ b/apps/demo/next.config.js
@@ -1,4 +1,4 @@
module.exports = {
- reactStrictMode: false,
+ reactStrictMode: true,
transpilePackages: ["@measured/puck", "lucide-react"],
};
diff --git a/apps/docs/pages/docs/api-reference/components.mdx b/apps/docs/pages/docs/api-reference/components.mdx
index 072eb661fb..dfed00732b 100644
--- a/apps/docs/pages/docs/api-reference/components.mdx
+++ b/apps/docs/pages/docs/api-reference/components.mdx
@@ -20,6 +20,7 @@ Puck provides several components to support different integration approaches.
- [\](components/action-bar) - An action bar containing a series of actions, normally used with the [actionBar override](/docs/api-reference/overrides/action-bar).
- [\](components/action-bar-action) - An action for use within the ActionBar component.
- [\](components/action-bar-group) - A group of actions for use within the ActionBar component.
+- [\](components/action-bar-label) - A label for use within the ActionBar component.
- [\](components/drawer) - A reference list of items that can be dragged into a droppable area, normally [``](components/puck-preview).
- [\](components/drawer-item) - An item that can be dragged from a [``](components/drawer).
- [\](components/field-label) - Render a styled `label` when creating [`custom` fields](/docs/api-reference/fields/custom).
diff --git a/apps/docs/pages/docs/api-reference/components/_meta.js b/apps/docs/pages/docs/api-reference/components/_meta.js
index 68666054af..ce7930087e 100644
--- a/apps/docs/pages/docs/api-reference/components/_meta.js
+++ b/apps/docs/pages/docs/api-reference/components/_meta.js
@@ -2,6 +2,7 @@ const menu = {
"action-bar": {},
"action-bar-action": {},
"action-bar-group": {},
+ "action-bar-label": {},
"auto-field": {},
drawer: {},
"drawer-item": {},
diff --git a/apps/docs/pages/docs/api-reference/components/action-bar-action.mdx b/apps/docs/pages/docs/api-reference/components/action-bar-action.mdx
index 429d4e0cd3..bf0d726ca2 100644
--- a/apps/docs/pages/docs/api-reference/components/action-bar-action.mdx
+++ b/apps/docs/pages/docs/api-reference/components/action-bar-action.mdx
@@ -7,26 +7,18 @@ import { ActionBar } from "@/core/components/ActionBar";
# \
-Render an action button in the [``](action-bar). Normally used inside an [``](action-bar-group).
-
-```tsx showLineNumbers {3-5} copy
-
-
- console.log("Clicked!")}>
- ★
-
-
+Render an action button in the [``](action-bar). Often used inside an [``](action-bar-group).
+
+```tsx showLineNumbers {2} copy
+
+ console.log("Clicked!")}>★
```
-
-
- console.log("Clicked!")}>
- ★
-
-
+
+ console.log("Clicked!")}>★
diff --git a/apps/docs/pages/docs/api-reference/components/action-bar-label.mdx b/apps/docs/pages/docs/api-reference/components/action-bar-label.mdx
new file mode 100644
index 0000000000..a17d4339d4
--- /dev/null
+++ b/apps/docs/pages/docs/api-reference/components/action-bar-label.mdx
@@ -0,0 +1,44 @@
+---
+title:
+---
+
+import { PuckPreview } from "@/docs/components/Preview";
+import { ActionBar } from "@/core/components/ActionBar";
+
+# \
+
+Render a label in the [``](action-bar) or an [``](action-bar-group).
+
+```tsx showLineNumbers {2,4} copy
+
+
+
+
+ ★
+
+
+```
+
+
+
+
+
+## Props
+
+| Prop | Example | Type | Status |
+| ----------------- | --------- | ------ | -------- |
+| [`label`](#label) | `"Label"` | String | Required |
+
+## Required Props
+
+### `label`
+
+The label text.
diff --git a/apps/docs/pages/docs/api-reference/components/action-bar.mdx b/apps/docs/pages/docs/api-reference/components/action-bar.mdx
index fd15713fad..3be609255a 100644
--- a/apps/docs/pages/docs/api-reference/components/action-bar.mdx
+++ b/apps/docs/pages/docs/api-reference/components/action-bar.mdx
@@ -23,9 +23,7 @@ Render the Puck ActionBar. Use this when overriding the [actionBar](/docs/api-re
- console.log("Clicked!")}>
- ★
-
+ ★
diff --git a/apps/docs/pages/docs/api-reference/components/drawer-item.mdx b/apps/docs/pages/docs/api-reference/components/drawer-item.mdx
index b0d86f8cd9..6dd8582a30 100644
--- a/apps/docs/pages/docs/api-reference/components/drawer-item.mdx
+++ b/apps/docs/pages/docs/api-reference/components/drawer-item.mdx
@@ -16,7 +16,7 @@ export function Editor() {
return (
-
+
);
@@ -25,12 +25,12 @@ export function Editor() {
## Props
-| Prop | Example | Type | Status |
-| ----------------------- | ------------------------- | -------- | -------- |
-| [`name`](#name) | `name: "Orange"` | String | Required |
-| [`index`](#index) | `index: 0` | Number | Required |
-| [`children`](#children) | `children: () => ` | Function | - |
-| [`id`](#id) | `id: "OrangeComponent"` | String | - |
+| Prop | Example | Type | Status |
+| ----------------------------------- | ------------------------- | -------- | -------- |
+| [`name`](#name) | `name: "Orange"` | String | Required |
+| [`children`](#children) | `children: () => ` | Function | - |
+| [`id`](#id) | `id: "OrangeComponent"` | String | - |
+| [`isDragDisabled`](#isdragdisabled) | `isDragDisabled: false` | Boolean | - |
## Required props
@@ -41,13 +41,6 @@ The name of this drawer item.
- This will be rendered on the item by default.
- Will be used as the `id`, unless otherwise specified
-### `index`
-
-A numerical index for each item in the drawer.
-
-- Must be sequential, i.e. `0`, `1`, `2`.
-- Must start at `0`.
-
## Optional props
### `children`
@@ -61,9 +54,7 @@ export function Editor() {
return (
-
- {() => Orange 🍊
}
-
+ {() => Orange 🍊
}
);
@@ -72,9 +63,7 @@ export function Editor() {
-
- {() => Orange 🍊
}
-
+ {() => Orange 🍊
}
@@ -93,3 +82,7 @@ The original node for the drawer item.
A unique id for this drawer item. Defaults to the value of [`name`](#name).
If using the `` as a component list to be dragged into ``, this should be the key of a component defined in the [Config](/docs/api-reference/configuration/config).
+
+### `isDragDisabled`
+
+Whether or not this item is disabled.
diff --git a/apps/docs/pages/docs/api-reference/components/drawer.mdx b/apps/docs/pages/docs/api-reference/components/drawer.mdx
index 166db8ba8a..5ecc88b12c 100644
--- a/apps/docs/pages/docs/api-reference/components/drawer.mdx
+++ b/apps/docs/pages/docs/api-reference/components/drawer.mdx
@@ -8,14 +8,14 @@ import { Puck } from "@/core/components/Puck";
# \
-A vertical or horizontal list of items that can be dragged into a [``](puck-preview). Used for composing custom Puck UIs.
+A list of items that can be dragged into a [``](puck-preview). Used for composing custom Puck UIs.
Orange
} } }}
data={{ root: { props: {} }, content: [] }}
>
-
-
+
+
@@ -26,7 +26,7 @@ export function Editor() {
return (
-
+
);
@@ -35,31 +35,12 @@ export function Editor() {
## Props
-| Param | Example | Type | Status |
-| ----------------------------- | --------------------------- | ------------------------ | -------- |
-| [`children`](#children) | `children: ` | ReactNode | Required |
-| [`direction`](#direction) | `direction: "horizontal"` | `horizontal`, `vertical` | - |
-| [`droppableId`](#droppableId) | `droppableId: "my-drawer"` | String | - |
+| Param | Example | Type | Status |
+| ----------------------- | --------------------------- | --------- | -------- |
+| [`children`](#children) | `children: ` | ReactNode | Required |
## Required props
### `children`
A React node representing the contents of the ``. Will likely contain [``](drawer-item) nodes.
-
-## Optional props
-
-### `direction`
-
-Set the direction of the drawer.
-
-Defaults to `vertical`.
-
-### `droppableId`
-
-Set a custom ID for the underlying [Droppable](https://github.com/hello-pangea/dnd/blob/main/docs/api/droppable.md).
-
-- Must be unique.
-- Will be prefixed with `component-list:`.
-
-Defaults to `default`.
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 ee97aabad6..2055759305 100644
--- a/apps/docs/pages/docs/api-reference/components/drop-zone.mdx
+++ b/apps/docs/pages/docs/api-reference/components/drop-zone.mdx
@@ -26,11 +26,16 @@ const config = {
## Props
-| Param | Example | Type | Status |
-| ----------------------- | ---------------------------- | ------ | -------- |
-| [`zone`](#zone) | `zone: "my-zone"` | String | Required |
-| [`allow`](#allow) | `allow: ["HeadingBlock"]` | Array | |
-| [`disallow`](#disallow) | `disallow: ["HeadingBlock"]` | Array | |
+| Param | Example | Type | Status |
+| ----------------------------------- | ---------------------------- | ------------- | -------- |
+| [`zone`](#zone) | `zone: "my-zone"` | String | Required |
+| [`allow`](#allow) | `allow: ["HeadingBlock"]` | Array | - |
+| [`className`](#className) | `className: "MyClass"` | String | - |
+| [`collisionAxis`](#collisionAxis) | `collisionAxis: "x"` | String | - |
+| [`disallow`](#disallow) | `disallow: ["HeadingBlock"]` | Array | - |
+| [`minEmptyHeight`](#minEmptyHeight) | `minEmptyHeight: 256` | Number | - |
+| [`ref`](#ref) | `ref: ref` | Ref | - |
+| [`style`](#style) | `style: {display: "flex"}` | CSSProperties | - |
## Required props
@@ -78,6 +83,59 @@ const config = {
};
```
+### `className`
+
+Provide a className to the DropZone component. The default DropZone styles will still be applied.
+
+```tsx copy {7}
+const config = {
+ components: {
+ Example: {
+ render: () => {
+ return (
+
+
+
+ );
+ },
+ },
+ },
+};
+```
+
+### `collisionAxis`
+
+Configure which axis Puck will use for overlap collision detection.
+
+Options:
+
+- `x` - detect collisions based their x-axis overlap
+- `y` - detect collisions based their y-axis overlap
+- `dynamic` - automatically choose an axis based on the direction of travel
+
+The defaults are set based on the CSS layout of the parent:
+
+- grid: `dynamic`
+- flex (row): `x`
+- inline/inline-block: `x`
+- Everything else: `y`
+
+```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`.
@@ -98,6 +156,68 @@ const config = {
};
```
+### `minEmptyHeight`
+
+The minimum height of the DropZone when empty, in pixels. Defaults to `128`.
+
+```tsx copy {7}
+const config = {
+ components: {
+ Example: {
+ render: () => {
+ return (
+
+
+
+ );
+ },
+ },
+ },
+};
+```
+
+### `ref`
+
+A [React ref](https://react.dev/learn/manipulating-the-dom-with-refs), assigned to the root node of the DropZone.
+
+```tsx copy {9}
+const config = {
+ components: {
+ Example: {
+ render: () => {
+ const ref = useRef();
+
+ return (
+
+
+
+ );
+ },
+ },
+ },
+};
+```
+
+### `style`
+
+Provide a style attribute to the DropZone. The default DropZone styles will still be applied.
+
+```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/api-reference/configuration/component-config.mdx b/apps/docs/pages/docs/api-reference/configuration/component-config.mdx
index a479a41047..f3696cea7c 100644
--- a/apps/docs/pages/docs/api-reference/configuration/component-config.mdx
+++ b/apps/docs/pages/docs/api-reference/configuration/component-config.mdx
@@ -31,6 +31,7 @@ const config = {
| [`render()`](#renderprops) | `render: () => ` | Function | Required |
| [`fields`](#fields) | `fields: { title: { type: "text"} }` | Object | - |
| [`defaultProps`](#defaultprops) | `defaultProps: { title: "Hello, world" }` | Object | - |
+| [`inline`](#inline) | `inline: true` | Boolean | - |
| [`label`](#label) | `label: "Heading Block"` | String | - |
| [`permissions()`](#permissions) | `permissions: { delete: false }` | Object | - |
| [`resolveData()`](#resolvedatadata-params) | `resolveData: async ({ props }) => ({ props })` | Object | - |
@@ -58,6 +59,7 @@ const config = {
| Arg | Example | Type |
| -------------------------------------------- | ------------- | -------- |
| [`id`](#id) | `button-1234` | String |
+| [`puck.dragRef`](#puckdragref) | `null` | Function |
| [`puck.isEditing`](#puckisediting) | `false` | Boolean |
| [`puck.renderDropZone`](#puckrenderdropzone) | `() => {}` | Function |
| [`...props`](#props) | `{}` | Object |
@@ -66,6 +68,23 @@ const config = {
A unique ID generated by Puck for this component. You can optionally apply this, or use your own ID.
+##### `puck.dragRef`
+
+A `ref` that tells Puck which element is draggable. Apply this to your components when using the [`inline` parameter](#inline) for advanced CSS layouts.
+
+```tsx {5} /renderDropZone/1 copy
+const config = {
+ components: {
+ Example: {
+ inline: true,
+ render: ({ puck: { dragRef } }) => {
+ return Hello, world
;
+ },
+ },
+ },
+};
+```
+
##### `puck.isEditing`
A boolean describing whether or not this component is being rendered in the `` component.
@@ -165,6 +184,45 @@ const config = {
}}
/>
+### `inline`
+
+Render your component without a wrapping element. Use this to [create advanced CSS layouts](/docs/integrating-puck/multi-column-layouts#advanced-css-layouts). Defaults to `false`.
+
+When `true`, you must to specify which item is draggable via the [`puck.dragRef` prop](#puckdragref).
+
+```tsx {4-5} copy showLineNumbers
+const config = {
+ components: {
+ HeadingBlock: {
+ inline: true,
+ render: ({ puck }) => Hello, World
,
+ },
+ },
+};
+```
+
+ {
+ return (
+
+ {title}
+
+ );
+ },
+ }}
+/>
+
### `label`
A label to show when referring to your component within the Puck editor. Defaults to the key of your component.
@@ -182,7 +240,7 @@ const config = {
-
+
diff --git a/apps/docs/pages/docs/api-reference/overrides/action-bar.mdx b/apps/docs/pages/docs/api-reference/overrides/action-bar.mdx
index dfbb450521..4a08df3f77 100644
--- a/apps/docs/pages/docs/api-reference/overrides/action-bar.mdx
+++ b/apps/docs/pages/docs/api-reference/overrides/action-bar.mdx
@@ -10,8 +10,8 @@ Override the action bar. Use the [`` component](/docs/api-reference/c
import { ActionBar } from "@measured/puck";
const overrides = {
- actionBar: ({ children }) => (
-
+ actionBar: ({ children, label }) => (
+
{children}
),
@@ -20,10 +20,20 @@ const overrides = {
## Props
-| Prop | Example | Type |
-| ----------------------- | --------- | --------- |
-| [`children`](#children) | `` | ReactNode |
+| Prop | Example | Type |
+| ------------------------------- | ---------------- | --------- |
+| [`children`](#children) | `` | ReactNode |
+| [`label`](#label) | `"HeadingBlock"` | String |
+| [`parentAction`](#parentAction) | `` | ReactNode |
### `children`
A fragment containing the default [actions](/docs/api-reference/components/action-bar-action). This should normally be rendered inside an [``](/docs/api-reference/components/action-bar-group).
+
+### `label`
+
+The default label for the action bar.
+
+### `parentAction`
+
+A single [``](/docs/api-reference/components/action-bar-action) to select the current component's parent.
diff --git a/apps/docs/pages/docs/index.mdx b/apps/docs/pages/docs/index.mdx
index e8ef0ab4da..3c8805cf25 100644
--- a/apps/docs/pages/docs/index.mdx
+++ b/apps/docs/pages/docs/index.mdx
@@ -16,7 +16,7 @@ Puck is also licensed under MIT, making it suitable for both internal systems an
| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| [Component Configuration](/docs/integrating-puck/component-configuration) | Integrate your own components with Puck by providing render functions and configuring fields that map to your props. |
| [Root Configuration](/docs/integrating-puck/root-configuration) | Customize the root component that wraps all other Puck components. |
-| [Multi-column Layouts](/docs/integrating-puck/multi-column-layouts) | Use DropZones to build more multi-column layouts by nesting components. |
+| [Multi-column Layouts](/docs/integrating-puck/multi-column-layouts) | Create multi-column layouts using nested components. Now supports advanced CSS layouts. |
| [Categories](/docs/integrating-puck/categories) | Group your components in the side bar. |
| [Dynamic Props](/docs/integrating-puck/dynamic-props) | Dynamically set props after user input and mark fields as read-only |
| [Dynamic Fields](/docs/integrating-puck/dynamic-fields) | Dynamically set fields based on user input |
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 9f3452fbae..7fce4d214b 100644
--- a/apps/docs/pages/docs/integrating-puck/multi-column-layouts.mdx
+++ b/apps/docs/pages/docs/integrating-puck/multi-column-layouts.mdx
@@ -7,15 +7,16 @@ import { Puck } from "@/core";
# Multi-column Layouts
-Multi-column and other complex layouts can be achieved by nesting components.
+Multi-column layouts can be achieved with two Puck APIs:
-## Using DropZones
+1. The [`` component](/docs/api-reference/components/drop-zone), enabling you to nest components
+2. The [`inline` parameter](/docs/api-reference/configuration/component-config#inline), enable multi-directional drag-and-drop
-Puck provides support for nesting components via the [`` API](/docs/api-reference/components/drop-zone).
+## Nesting components with DropZones
-### Nesting components
+Puck provides support for nesting components via the [`` API](/docs/api-reference/components/drop-zone).
-The `` component can be rendered anywhere within your component.
+The `` component can be rendered anywhere within your component, creating a zone that you can drop components into.
```tsx {1,9} showLineNumbers copy
import { DropZone } from "@measured/puck";
@@ -31,8 +32,8 @@ const config = {
);
},
},
- HeadingBlock: {
- render: () => Hello, world
,
+ Card: {
+ render: () => Hello, world
,
},
},
};
@@ -51,9 +52,20 @@ const config = {
);
},
},
- HeadingBlock: {
+ Card: {
render: () => {
- return Hello, world
;
+ return (
+
+ Hello, world
+
+ );
},
},
},
@@ -62,18 +74,16 @@ const config = {
content: [{ type: "Example", props: { id: "Example-1" } }],
root: { props: {} },
zones: {
- "Example-1:my-content": [
- { type: "HeadingBlock", props: { id: "Example-2" } },
- ],
+ "Example-1:my-content": [{ type: "Card", props: { id: "Example-2" } }],
},
}}
>
-### Creating multi-column layouts
+The [`zone` identifier](/docs/api-reference/components/drop-zone#zone) is unique to the component, and can be reused in different components.
-Combine multiple `` components with something like [CSS Grid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout) or [Flexbox](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox) to achieve multi-column layouts.
+You can combine multiple DropZones to achieve fixed layouts:
```tsx {9,11,12} showLineNumbers copy
import { DropZone } from "@measured/puck";
@@ -92,8 +102,8 @@ const config = {
);
},
},
- HeadingBlock: {
- render: ({ text }) => {text}
,
+ Card: {
+ render: () => Hello, world
,
},
},
};
@@ -102,6 +112,11 @@ const config = {
(
+
+ ),
+ },
components: {
Example: {
render: ({ puck: { renderDropZone: DropZone } }) => {
@@ -111,7 +126,6 @@ const config = {
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: 16,
- padding: 32,
}}
>
@@ -120,9 +134,20 @@ const config = {
);
},
},
- HeadingBlock: {
+ Card: {
render: ({ content }) => {
- return {content}
;
+ return (
+
+ {content}
+
+ );
},
},
},
@@ -133,14 +158,14 @@ const config = {
zones: {
"Example-1:left-column": [
{
- type: "HeadingBlock",
- props: { id: "Example-2", content: "Left column" },
+ type: "Card",
+ props: { id: "Example-2", content: "1" },
},
],
"Example-1:right-column": [
{
- type: "HeadingBlock",
- props: { id: "Example-3", content: "Right column" },
+ type: "Card",
+ props: { id: "Example-3", content: "2" },
},
],
},
@@ -149,47 +174,210 @@ const config = {
-### Reusing the `zone`
+## Advanced CSS layouts
-The [`zone` identifier](/docs/api-reference/components/drop-zone#zone) is unique to the component, and can be reused in different components.
+By default, Puck will wrap your components in a `div` element for a vertical `block` layout, restricting drag-and-drop to the y-axis.
-```tsx {3-8,16,25} showLineNumbers copy
-import { DropZone } from "@measured/puck";
+The [`inline` component parameter](/docs/api-reference/configuration/component-config#inline) allows you to eliminate the wrapping element. Combining this with DropZones enables advanced CSS layouts with multi-directional drag-and-drop.
+
+### Basic CSS grid example
-const Columns = () => (
-
-
-
-
-);
+For example, set `inline: true` to implement CSS grid layouts. When using inline mode, you must specify which element is draggable by passing the [`puck.dragRef` prop](/docs/api-reference/configuration/component-config#puckdragref) to your element's `ref` prop.
+
+```tsx {9,14,17} showLineNumbers copy
+import { DropZone } from "@measured/puck";
const config = {
components: {
Example: {
- render: () => {
- return (
-
-
-
- );
+ render: () => (
+
+ ),
+ },
+ Card: {
+ inline: true, // Enable inline mode, removing the Puck wrapper
+ render: ({ text, puck }) => (
+
+ {text}
+
+ ),
+ },
+ },
+};
+```
+
+ (
+
+ ),
+ },
+ components: {
+ Card: {
+ inline: true,
+ render: ({ content, puck }) => {
+ return (
+
+ {content}
+
+ );
+ },
},
},
- Alternative: {
- render: () => {
- return (
-
-
-
- );
+ }}
+ data={{
+ content: [
+ {
+ type: "Card",
+ props: { id: "Example-2", content: "1" },
},
+ {
+ type: "Card",
+ props: { id: "Example-3", content: "2" },
+ },
+ ],
+ root: { props: {} },
+ }}
+>
+
+
+
+### Advanced CSS grid example
+
+You can create more sophisticated layouts, too. Here's an advanced CSS grid example, with items spanning multiple rows and columns.
+
+```tsx {11-12,24-25} showLineNumbers copy
+import { DropZone } from "@measured/puck";
+
+const config = {
+ components: {
+ Example: {
+ render: () => (
+
+ ),
+ },
+ Card: {
+ inline: true, // Enable inline mode, removing the Puck wrapper
+ render: ({ text, puck }) => (
+
+ {text}
+
+ ),
},
},
};
```
-### Restricting components
+ (
+
+ ),
+ },
+ components: {
+ Card: {
+ inline: true,
+ render: ({ content, spanCol, spanRow, puck }) => {
+ return (
+
+ {content}
+
+ );
+ },
+ },
+ },
+ }}
+ data={{
+ content: [
+ {
+ type: "Card",
+ props: { id: "Example-1", content: "1", spanCol: 2, spanRow: 2 },
+ },
+ {
+ type: "Card",
+ props: { id: "Example-2", content: "2", spanCol: 1, spanRow: 1 },
+ },
+ {
+ type: "Card",
+ props: { id: "Example-3", content: "3", spanCol: 1, spanRow: 1 },
+ },
+ {
+ type: "Card",
+ props: { id: "Example-4", content: "4", spanCol: 2, spanRow: 1 },
+ },
+ {
+ type: "Card",
+ props: { id: "Example-5", content: "5", spanCol: 1, spanRow: 1 },
+ },
+
+ ],
+ root: { props: {} },
+
+}}
+
+>
+
+
+
+
+## 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.
+The [`allow`](/docs/api-reference/components/drop-zone#allow) and [`disallow`](/docs/api-reference/components/drop-zone#disallow) DropZone props allow you to restrict which components can be dragged into a DropZone.
```tsx {9} showLineNumbers copy
import { DropZone } from "@measured/puck";
@@ -200,7 +388,7 @@ const config = {
render: () => {
return (
-
+
);
},
@@ -211,13 +399,13 @@ const config = {
This can be combined with [categories](/docs/integrating-puck/categories) to restrict based on your existing groups:
-```tsx {4-8,14} showLineNumbers copy
+```tsx {4-8,16} showLineNumbers copy
import { DropZone } from "@measured/puck";
const config = {
categories: {
typography: {
- components: ["HeadingBlock"],
+ components: ["Card"],
},
},
components: {
@@ -240,5 +428,6 @@ const config = {
## Further reading
- [The `` API](/docs/api-reference/components/drop-zone)
+- [The `inline` component config](/docs/api-reference/configuration/component-config#inline)
- [How DropZones store data](/docs/api-reference/data#zones)
- [DropZones and React Server Components](/docs/integrating-puck/server-components)
diff --git a/package.json b/package.json
index 7cb16f9700..ebc5d9b2b1 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"private": true,
"scripts": {
"build": "turbo run build",
- "dev": "turbo run dev --filter=demo",
+ "dev": "rm -rf packages/core/dist && turbo run dev --filter=demo",
"lint": "turbo run lint",
"format:check": "prettier --check \"**/*.{ts,tsx,md,mdx,css}\"",
"format": "prettier --write \"**/*.{ts,tsx,md,mdx,css}\"",
@@ -10,6 +10,7 @@
"release:prepare": "git fetch --tags && conventional-recommended-bump -p angular | xargs yarn version:auto $1",
"release:canary": "yarn release:prepare && node scripts/get-unstable-version canary | xargs yarn version:auto $1",
"release-commit": "git add -u && git commit -m \"release: v${npm_package_version}\"",
+ "smoke": "node scripts/e2e/smoke.mjs",
"test": "turbo run test",
"version": "lerna version --force-publish -y --no-push --no-changelog --no-git-tag-version $npm_package_version",
"version:auto": "yarn version --no-git-tag-version --new-version $1",
@@ -17,12 +18,14 @@
},
"devDependencies": {
"@turbo/gen": "^2.3.3",
+ "asciichart": "^1.5.25",
"conventional-changelog-angular": "^4.0.0",
"conventional-recommended-bump": "^6.0.5",
"eslint": "^7.32.0",
"eslint-config-custom": "*",
"lerna": "^7.1.1",
"prettier": "^2.5.1",
+ "puppeteer": "^23.11.1",
"standard-changelog": "^2.0.21",
"turbo": "^2.3.3"
},
diff --git a/packages/core/components/ActionBar/index.tsx b/packages/core/components/ActionBar/index.tsx
index f13c53e799..35fb9ad177 100644
--- a/packages/core/components/ActionBar/index.tsx
+++ b/packages/core/components/ActionBar/index.tsx
@@ -10,8 +10,17 @@ export const ActionBar = ({
label?: string;
children?: ReactNode;
}) => (
-
- {label &&
{label}
}
+
{
+ e.stopPropagation();
+ }}
+ >
+ {label && (
+
+ {label}
+
+ )}
{children}
);
@@ -39,5 +48,10 @@ export const Group = ({ children }: { children: ReactNode }) => (
{children}
);
+export const Label = ({ label }: { label: string }) => (
+
{label}
+);
+
ActionBar.Action = Action;
+ActionBar.Label = Label;
ActionBar.Group = Group;
diff --git a/packages/core/components/ActionBar/styles.module.css b/packages/core/components/ActionBar/styles.module.css
index de7f1279a5..35886d039b 100644
--- a/packages/core/components/ActionBar/styles.module.css
+++ b/packages/core/components/ActionBar/styles.module.css
@@ -1,40 +1,51 @@
.ActionBar {
+ align-items: center;
+ cursor: default;
display: flex;
width: auto;
padding: 4px;
- padding-right: 4px;
+ padding-left: 0;
+ padding-right: 0;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
border-radius: 8px;
background: var(--puck-color-grey-01);
color: var(--puck-color-white);
font-family: var(--puck-font-family);
- gap: 4px;
min-height: 26px;
}
-.ActionBar-actionsLabel {
+.ActionBar-label {
color: var(--puck-color-grey-08);
- display: flex;
font-size: var(--puck-font-size-xxxs);
font-weight: 500;
- justify-content: center;
- align-items: center;
padding-left: 8px;
padding-right: 8px;
+ margin-left: 4px;
+ margin-right: 4px;
text-overflow: ellipsis;
white-space: nowrap;
}
+.ActionBar-action + .ActionBar-label {
+ padding-left: 0;
+}
+
+.ActionBar-label + .ActionBar-action {
+ margin-left: -4px;
+}
+
.ActionBar-group {
+ align-items: center;
border-inline-start: 0.5px solid var(--puck-color-grey-05); /* Fractional value required due to scaling */
display: flex;
+ height: 100%;
padding-left: 4px;
padding-right: 4px;
}
-.ActionBar-group:last-of-type {
- padding-right: 0;
+.ActionBar-group:first-of-type {
+ border-inline-start: 0;
}
.ActionBar-group:empty {
@@ -47,6 +58,8 @@
color: var(--puck-color-grey-08);
cursor: pointer;
padding: 6px 8px;
+ margin-left: 4px;
+ margin-right: 4px;
border-radius: 4px;
overflow: hidden;
display: flex;
@@ -55,6 +68,10 @@
transition: color 50ms ease-in;
}
+.ActionBar-action svg {
+ max-width: none !important; /* Explicit definition to prevent some SVG style pollution */
+}
+
.ActionBar-action:focus-visible {
outline: 2px solid var(--puck-color-azure-05);
outline-offset: -2px;
@@ -71,3 +88,7 @@
color: var(--puck-color-azure-07);
transition: none;
}
+
+.ActionBar-group * {
+ margin: 0;
+}
diff --git a/packages/core/components/AutoField/fields/ArrayField/index.tsx b/packages/core/components/AutoField/fields/ArrayField/index.tsx
index f9faea1a24..4de1ad784c 100644
--- a/packages/core/components/AutoField/fields/ArrayField/index.tsx
+++ b/packages/core/components/AutoField/fields/ArrayField/index.tsx
@@ -4,13 +4,11 @@ import { Copy, List, Plus, Trash } from "lucide-react";
import { AutoFieldPrivate, FieldPropsInternal } from "../..";
import { IconButton } from "../../../IconButton";
import { reorder, replace } from "../../../../lib";
-import { Droppable } from "../../../Droppable";
-import { Draggable } from "../../../Draggable";
import { useCallback, useEffect, useState } from "react";
import { DragIcon } from "../../../DragIcon";
import { ArrayState, ItemWithId } from "../../../../types";
import { useAppContext } from "../../../Puck/context";
-import { DragDropContext } from "../../../DragDropContext";
+import { Sortable, SortableProvider } from "../../../Sortable";
const getClassName = getClassNameFactory("ArrayField", styles);
const getClassNameItem = getClassNameFactory("ArrayFieldItem", styles);
@@ -102,8 +100,6 @@ export const ArrayField = ({
}
}, []);
- const [hovering, setHovering] = useState(false);
-
const forceReadOnly = getPermissions({ item: selectedItem }).edit === false;
if (field.type !== "array" || !field.arrayFields) {
@@ -122,249 +118,211 @@ export const ArrayField = ({
el="div"
readOnly={readOnly}
>
-
{
- if (event.destination) {
- const newValue = reorder(
- value,
- event.source.index,
- event.destination?.index
- );
-
- const newArrayStateItems: ItemWithId[] = reorder(
- arrayState.items,
- event.source.index,
- event.destination?.index
- );
-
- const newUi = {
- arrayState: {
- ...state.ui.arrayState,
- [id]: { ...arrayState, items: newArrayStateItems },
- },
- };
-
- setUi(newUi, false);
-
- onChange(newValue, newUi);
-
- setLocalState({
- value: newValue,
- arrayState: { ...arrayState, items: newArrayStateItems },
- });
- }
+ {
+ const newValue = reorder(value, move.source, move.target);
+ const newArrayStateItems: ItemWithId[] = reorder(
+ arrayState.items,
+ move.source,
+ move.target
+ );
+ const newUi = {
+ arrayState: {
+ ...state.ui.arrayState,
+ [id]: { ...arrayState, items: newArrayStateItems },
+ },
+ };
+ setUi(newUi, false);
+ onChange(newValue, newUi);
+ setLocalState({
+ value: newValue,
+ arrayState: { ...arrayState, items: newArrayStateItems },
+ });
}}
>
-
- {(provided, snapshot) => {
+ 0,
+ addDisabled,
+ })}
+ onClick={(e) => {
+ e.preventDefault();
+ }}
+ >
+ {localState.arrayState.items.map((item, i) => {
+ const { _arrayId = `${id}-${i}`, _originalIndex = i } = item;
+ const data: any = Array.from(localState.value || [])[i] || {};
+
return (
-
0,
- addDisabled,
- })}
- onMouseOver={(e) => {
- e.stopPropagation();
- setHovering(true);
- }}
- onMouseOut={(e) => {
- e.stopPropagation();
- setHovering(false);
- }}
- onClick={(e) => {
- e.preventDefault();
- }}
+
- {localState.arrayState.items.map((item, i) => {
- const { _arrayId = `${id}-${i}`, _originalIndex = i } = item;
- const data: any = Array.from(localState.value || [])[i] || {};
-
- return (
-
- getClassNameItem({
- isExpanded: arrayState.openId === _arrayId,
- isDragging: snapshot?.isDragging,
- readOnly,
- })
- }
- isDragDisabled={readOnly || !hovering}
+ {({ status, ref }) => (
+
+
{
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (arrayState.openId === _arrayId) {
+ setUi(
+ mapArrayStateToUi({
+ openId: "",
+ })
+ );
+ } else {
+ setUi(
+ mapArrayStateToUi({
+ openId: _arrayId,
+ })
+ );
+ }
+ }}
+ className={getClassNameItem("summary")}
>
- {() => (
- <>
-
{
- if (arrayState.openId === _arrayId) {
- setUi(
- mapArrayStateToUi({
- openId: "",
- })
- );
- } else {
- setUi(
- mapArrayStateToUi({
- openId: _arrayId,
- })
- );
- }
- }}
- className={getClassNameItem("summary")}
- >
- {field.getItemSummary
- ? field.getItemSummary(data, i)
- : `Item #${_originalIndex}`}
-
- {!readOnly && (
-
-
- {
- e.stopPropagation();
-
- const existingValue = [
- ...(value || []),
- ];
-
- existingValue.splice(
- i,
- 0,
- existingValue[i]
- );
-
- onChange(
- existingValue,
- mapArrayStateToUi(
- regenerateArrayState(existingValue)
- )
- );
- }}
- title="Duplicate"
- >
-
-
-
-
- =
- localState.arrayState.items.length
- }
- onClick={(e) => {
- e.stopPropagation();
-
- const existingValue = [
- ...(value || []),
- ];
-
- const existingItems = [
- ...(arrayState.items || []),
- ];
-
- existingValue.splice(i, 1);
- existingItems.splice(i, 1);
-
- onChange(
- existingValue,
- mapArrayStateToUi({
- items: existingItems,
- })
- );
- }}
- title="Delete"
- >
-
-
-
-
- )}
-
-
-
-
-
-
-
+
+
+
+
)}
-
+
);
- }}
-
-
+ })}
+
+ {!addDisabled && (
+
+ )}
+
+
);
};
diff --git a/packages/core/components/AutoField/fields/ArrayField/styles.module.css b/packages/core/components/AutoField/fields/ArrayField/styles.module.css
index 9e7007aeaf..0b3ffdd077 100644
--- a/packages/core/components/AutoField/fields/ArrayField/styles.module.css
+++ b/packages/core/components/AutoField/fields/ArrayField/styles.module.css
@@ -5,7 +5,7 @@
.ArrayField {
display: flex;
flex-direction: column;
- background-color: var(--puck-color-grey-09);
+ background: var(--puck-color-azure-11);
border: 1px solid var(--puck-color-grey-09);
border-radius: 4px;
}
diff --git a/packages/core/components/AutoFrame/index.tsx b/packages/core/components/AutoFrame/index.tsx
index 16fdf6f6e9..f04c01b7d0 100644
--- a/packages/core/components/AutoFrame/index.tsx
+++ b/packages/core/components/AutoFrame/index.tsx
@@ -1,6 +1,7 @@
import {
createContext,
ReactNode,
+ RefObject,
useContext,
useEffect,
useRef,
@@ -303,6 +304,7 @@ export type AutoFrameProps = {
debug?: boolean;
id?: string;
onStylesLoaded?: () => void;
+ frameRef: RefObject
;
};
type AutoFrameContext = {
@@ -320,31 +322,33 @@ function AutoFrame({
debug,
id,
onStylesLoaded,
+ frameRef,
...props
}: AutoFrameProps) {
const [loaded, setLoaded] = useState(false);
const [ctx, setCtx] = useState({});
- const ref = useRef(null);
const [mountTarget, setMountTarget] = useState();
useEffect(() => {
- if (ref.current) {
+ if (frameRef.current) {
setCtx({
- document: ref.current.contentDocument || undefined,
- window: ref.current.contentWindow || undefined,
+ document: frameRef.current.contentDocument || undefined,
+ window: frameRef.current.contentWindow || undefined,
});
- setMountTarget(ref.current.contentDocument?.getElementById("frame-root"));
+ setMountTarget(
+ frameRef.current.contentDocument?.getElementById("frame-root")
+ );
}
- }, [ref, loaded]);
+ }, [frameRef, loaded]);
return (