Code Monkey home page Code Monkey logo

puck's Introduction

Puck

The visual editor for React.

Demo

Visit https://demo.puckeditor.com/edit to try the demo.

Documentation

Visit https://puckeditor.com to view the full documentation.

Quick start

Install the package:

npm i @measured/puck --save # or npx create-puck-app my-app

Render the editor:

// Editor.jsx
import { Puck } from "@measured/puck";
import "@measured/puck/puck.css";

// Create Puck component config
const config = {
  components: {
    HeadingBlock: {
      fields: {
        children: {
          type: "text",
        },
      },
      render: ({ children }) => {
        return <h1>{children}</h1>;
      },
    },
  },
};

// Describe the initial data
const initialData = {};

// Save the data to your database
const save = (data) => {};

// Render Puck editor
export function Editor() {
  return <Puck config={config} data={initialData} onPublish={save} />;
}

Render the page:

// Page.jsx
import { Render } from "@measured/puck";
import "@measured/puck/puck.css";

export function Page() {
  return <Render config={config} data={data} />;
}

Recipes

Use create-puck-app to quickly spin up a a pre-configured app based on our provided recipes:

npx create-puck-app my-app

Available recipes include:

  • next: Next.js 13 app example, using App Router and static page generation
  • remix: Remix Run v2 app example, using dynamic routes at root-level

Community

Hire the Puck team

Puck is developed and maintained by Measured, a small group of industry veterans with decades of experience helping companies solve hard UI problems. We offer consultancy and development services for scale-ups, SMEs and enterprises.

If you need support integrating Puck or creating a beautiful component library, please reach out via the Measured website.

License

MIT © Measured Corporation Ltd

puck's People

Contributors

4leite avatar abhishek-exists avatar ahmedrowaihi avatar anglepoised avatar bwat-dev avatar chrisvxd avatar danielbayley avatar dependabot[bot] avatar earthlingdavey avatar eltociear avatar estephan avatar freddieridell avatar gothfemme avatar hjbrand avatar jabba-the-bug avatar jakesidsmith avatar jonduarte avatar jperasmus avatar leweyse avatar matthewlynch avatar mfakhrys avatar monospaced avatar nguyendhst avatar philipodev avatar prashanthvdckap avatar sagarchoudhary96 avatar soudasuwa avatar turbobot-temp avatar xaviemirmon avatar yuddomack avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

puck's Issues

Consider standalone app framework, `puck-app`

At it's core, puck is a mechanism to build visual editors into existing applications.

We could also consider making a standalone application framework, puck-app, that wraps Next.js, includes a database configuration and authentication via something like auth0. This will provide OOTB support for basic marketing sites with no dynamic content.

i.e.

yarn create puck-app

Spits out

  • puck.config.ts
  • package.json - contains scripts like
    • eject - eject from Puck to a standalone Next.js application
  • eslint etc

Questions / notes

  • Add built in auth0?
  • Add built in database?

--

This might be better as a generator, but I thought this could be a clean way to hide away all the noise from the end user.

Feature Request: Sub objects

It would be nice to be able to add sub objects pretty much exactly the way arrayFields currently behaves just without the 'add item' UI!

For example in the demo site we have full blocks like Hero which only have some theme/config options but we also have components like H1 on the page at the same time.

The Hero likely contains a H1 and it would be nice to be able to edit the H1 as a dedicated object.

Export-1694337157447

Undo/redo history

  • Add Undo/Redo buttons to header using react-feather and IconButton component
  • Avoid storing entire state
  • Include keyboard shortcuts, if possible

create-app throws 404 without database.json

I followed the intrusions and created a new app using

npx create-puck-app my-app

Running yarn dev and accessing http://localhost:3000 throws 404. Looking at app/[...puckPath]/page.tsx it looks like it throws a 404 when database.json is not found. I tried creating an empty file but the same issue persists. What should go in this file when bootstrapping the editor for the first time?

Add / deploy demo application

We need a hosted demo that showcases how puck can integrate with different component libraries.

  • Add a new application called demo to /apps.
  • Integrate demo with one or more component libraries
  • Add UI to switch between libraries
  • Allow public editing, but generate random URL and delete it after 1 hour
  • Add custom library
  • Consider making custom library responsive
  • Ensure fonts are loaded

Ability to reorder array items

Would be great if subitems (like the features inside of a featureList) could be reordered via drag and drop. (Even if it's only sorting the list in the righthand sidebar.) As I understand it, the only way now to reorder sub items is to delete them and re-enter each one.

How to use it with Vite?

I tried using the demo in Vite but I am stuck on "Failed to resolve import "@measured/puck/components/Puck""

This file doesn't exist on the NPM package's dist folder, what am I missing?

Viewport previewing

Users may wish to preview their page for different viewport sizes without leaving Puck.

Viewport previews are typically done by resizing an iframe containing the content. However, Puck doesn't currently use frames because of their historical performance issues and the challenges supporting a robust drag-and-drop interface.

We're looking for alternative solutions. Please use this thread to propose ideas and discuss!

Build failing with Cannot find module '@measured/puck/types/Config' and Collecting page data

Hi!
I'm running into a few errors when trying to build the next app:
The first one is a type error in ./app/[...puckPath]/page.tsx:

Type error: Cannot find module '@measured/puck/types/Config' or its corresponding type declarations.

  3 | import resolvePuckPath from "./resolve-puck-path";
  4 | import { Metadata } from "next";
> 5 | import { Data } from "@measured/puck/types/Config";
    |                      ^
  6 | 
  7 | export async function generateMetadata({
  8 |   params,

I think this can be fixed by changing:

import { Data } from "@measured/puck/types/Config";
// to ->
import { Data } from "@measured/puck";

and changing line 27 from:

return {
  title: data?.page?.title, // Property 'page' does not exist on type 'Data'
};
// to ->
return {
  title: data?.root?.title, 
};

This gets me further into the build process, but it errors out on the Collecting page data step:

pnpm run build

> [email protected] build /Users/maantje/programmeren/Full webapps/test_app_puck_v2
> next build

- info Creating an optimized production build  
- info Compiled successfully
- info Linting and checking validity of types  
- info Collecting page data  
[    ] - info Generating static pages (0/3)TypeError: fetch failed
    at Object.fetch (node:internal/deps/undici/undici:11576:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  cause: AggregateError
      at internalConnectMultiple (node:net:1111:18)
      at afterConnectMultiple (node:net:1663:5)
      at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
    code: 'ECONNREFUSED',
    [errors]: [ [Error], [Error] ]
  }
}
TypeError: fetch failed
    at Object.fetch (node:internal/deps/undici/undici:11576:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  cause: AggregateError
      at internalConnectMultiple (node:net:1111:18)
      at afterConnectMultiple (node:net:1663:5)
      at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
    code: 'ECONNREFUSED',
    [errors]: [ [Error], [Error] ]
  }
}

Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error
TypeError: fetch failed
    at Object.fetch (node:internal/deps/undici/undici:11576:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
- info Generating static pages (3/3)

> Export encountered errors on following paths:
        /page: /

I also tried with yarn, which gave me the same results.

Steps to reproduce:

  1. Run npx create-puck-app and use default options
  2. Open in code editor and fix type error according to code above
  3. Run npm run dev or yarn dev
  4. Create the / page and give it a title and heading with random words
  5. Close the dev server and run npm run build or yarn build

I found this(https://stackoverflow.com/questions/74165121/next-js-fetch-request-gives-error-typeerror-fetch-failed) which can maybe help, but I didn't really figure it out. I think it has something to do with the fetch to localhost:3000 in ./app/[...puckPath]/page.tsx.

edit: just occurred to me that this should probably be 2 different issues

Publish GitHub releases

We don't currently publish GitHub releases. We should.

Current process

Our release process currently goes:

  1. Make changes with conventional commit
  2. Release author runs yarn release. This bumps all monorepo packages and updates the changeling.
  3. Release author pushes branch up for review
  4. Release is reviewed. Version and changelog can be tweaked at this point.
  5. When release is merged, CI picks up the release: commit and publishes all packages

Suggestions

There are 2 options that come to mind:

  1. Manually publish the releases
  2. Automate!

If going with automation (my preferred option), we should update the CI that runs on step 5 to create the release via an API and update it with the changelog for that release.

Notes

  • Steps 1 -> 4 should not change
  • The release notes are compiled into 1 large CHANGELOG.md when running yarn release. We could either parse these back out of the CHANGELOG, or consider a separate CHANGELOG directory (but retain a CHANGELOG.md in here containing all releases. I think I prefer the former.
  • The CI is a GitHub workflow

Adding support to sort Component List

Current the puck Editor renders the Components List by map Object.keys(config.components). it will be great if we can have ability to control the order of rendering this List.

Inline CSS

Users shouldn't have to worry about configuring CSS. We should inline it, if possible.

Add more behaviour to plugins

  • setData prop to allow plugins to override the data
  • A mechanism to block page publishing (for example, if the heading outline is invalid in plugin-heading-analyzer) Moved to #164

Puck shouldn't add extraneous elements

I am trying to make an email template editor using Puck and React Email. However, Puck is adding a few extra elements between the root element and the component (see screenshot) which is causing styles such as:

#my-email-container {
  display: flex;
  flex-flow: column;
  align-items: center;
}

to not work as expected.

Example code:

const config = {
  root: {
    render({ children }) {
      return <div className="my-email-container">{children}</div>;
    },
  },
  components: {
    Button: {
      fields: { ... },
      render({ content, url, bgColor }) {
        return (
          <Button
            pX={20}
            pY={12}
            href={url}
            style={{ background: bgColor, color: "#000" }}
          >
            {content}
          </Button>
        );
      },
    },
  },
};

const initialData = {
  root: {
    title: "Title",
  },
  content: [],
};

export default function EmailEditorPage() {
  return (
        <Puck
          config={config}
          data={initialData}
        />
  );
}

Add more core features

Large tracker for lots of features:

  • Add proper button styling (including Publish button)
  • Write basic docs to README
  • Add group input types
  • Support page metadata, like title etc

Image uploader

Unclear how this would work, as image uploads are largely a user-land problem and it's unclear how much value a generic solution would provide.

Suggestions:

  • Provide a generic and configurable field type for this
  • Support generic image hosting providers OOTB (cloudinary etc)
  • Enable configuration of field type to support other providers

Explore breaking up config

@monospaced suggested we could look into making the config more modular.

Some thoughts:

  • Co-locate config alongside components

This doesn't necessarily require a first-party API, and may be more "recipe" based.

Generate templates from recipes

The code in /recipes and /packages/create-puck-app/templates is currently duplicated. We should reconcile these to prevent users from committing changes to the wrong directory. See #120 for example.

Solution

Suggest we should add a script that either:

  1. (Preferred) Generate the template from the recipe, as recipes are the logical entry point for people to make changes.
  2. Generate the recipe from the template

Considerations

  • If going with option 1, preserve the handlebars template files (package.json, anything else)
  • Add a CI check to make sure there is no diff (run the script and check for working changes. None should be present)

Defining a props as an Object.

i have a Component with props as

{
 name: string;
price: {"total": number; "string": number}
}

@chrisvxd
how can i define the config for the price prop for puck editor. i see we added support for "custom" type but not sure if this can be done with it .

Add mechanism for prop migration

Problem

Source: https://news.ycombinator.com/item?id=37392861

Sometimes a user might need to rename or remove a property provided to a component. For example, remaining description to subtitle.

The user currently has two options:

  1. Manually migrate their saved data before updating their Puck configuration
  2. (Preferred) Retain the old prop and map it to the new prop inside the render function.

These solutions either require repetitive work, or require the user to republish each page manually.

Proposal

Add a first-party mechanism for transforming data on a per-component basis.

This would probably live under a separate package (/packages/transform or /packages/migrate) and provide a user with a mechanism to 1) patch their data on the fly in a standardised manor and 2) batch transform their puck data consistently to adhere to the new props.

Create transformer

import { createTransform } from "@measured/puck-transform";

const transform = createTransform({
  HeadingBlock: ({ subtitle, ...props }) => {
    return {
      ...props,
      description: subtitle,
    };
  },
});

Patch data inline

// Pull from backend
const data = {
  type: "HeadingBlock",
  props: {
    subtitle: "Hello, world",
  },
};

export default () => <Puck data={transform(data)} config={config} />;

Patch all existing data

// Example data
const allData = [
  {
    path: "/",
    data: {
      root: {},
      content: [
        {
          type: "HeadingBlock",
          props: {
            subtitle: "Hello, world",
          },
        },
      ],
    },
  },
];

// Run the transformer
const patchedData = allData.map((page) => ({
  ...page,
  data: transform(page.data),
}));

// Write to your db
const save = () => {};
save(patchedData);

Considerations

  • TypeScript - How to tightly manage the types?

Ensure code generated by create-puck-app can be built

The code generated by the generator doesn't get built on publish, which makes it easy to publish code that will fail when the user tries to build it. We should build all recipes as generated by the generator as part of a GitHub workflow for PRs, failing if any build fails.

To test generators, we need to:

  • either generate a new app and compile that
  • or share code between the recipes and generator, and build the recipe (see #10)

Example: #46

"Cannot find package 'glob'" when running npx create-puck-app my-app

Hi!
What an amazing project! I was trying to get it to work locally but it seems like the recipe doesn't work on my machine. I get this error when I run it:

> npx create-puck-app my-app

node:internal/errors:496
    ErrorCaptureStackTrace(err);
    ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'glob' imported from /Users/maantje/.npm/_npx/039b3c3c47fc2298/node_modules/create-puck-app/index.js
    at new NodeError (node:internal/errors:405:5)
    at packageResolve (node:internal/modules/esm/resolve:781:9)
    at moduleResolve (node:internal/modules/esm/resolve:830:20)
    at defaultResolve (node:internal/modules/esm/resolve:1035:11)
    at DefaultModuleLoader.resolve (node:internal/modules/esm/loader:251:12)
    at DefaultModuleLoader.getModuleJob (node:internal/modules/esm/loader:140:32)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:33)
    at link (node:internal/modules/esm/module_job:75:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Node.js v20.4.0

I'm using Node v20.4.0, I also tried with pnpm and got the same error. I sadly don't know enough about npm to fix this.

Ability to group components

When navigating a large collection of components, it would be useful to have them grouped into some form of hierarchy (rather than a single list) so they could be found more easily.

State Between Components in the Specified Configuration

Is there a way to share state between components in the config? Other than abusing dom tricks? Or is the state of each component isolated?

From my understanding, it would likely be the latter, and that makes more sense for what Puck is trying to achieve, but for my use case I would be hoping that there might be a way to share state. Global states would work as well.

How do you want to engage with Puck?

The last few days have been a blast - we've gone from 8 stars on Tuesday morning to 2.1k and counting on Thursday.

We've been thinking a lot about the best way to engage with you all.

Currently we're considering:

  1. GitHub only (issues, discussions and releases)
  2. X/Twitter (via the @hellomeasuredco account, or a new dedicated account)
  3. Discord
  4. Mailing list

Are there any things we're missing, or anything you'd like us to consider? Free-form responses welcome.

Add docs site

Let's use this issue to crib out structure for a proper docs site

Build all packages for distribution

Right now, some packages may not be correctly built for distribution. Known issues:

  • plugin-heading-analyzer - does not currently compile from TS into JS
  • adaptors

change page background color

Suppose I have a page, I can edit its title. Is there any way I can edit and assign any custom colour to the background currently, it is white only ?

it would be great if we can add prop for page background color below title

Screenshot 2023-09-11 at 12 09 27 PM

Deno Preact support?

Amazing project! I'm very excited at the prospect of software creators and designers in particular being able to use a component library directly in this highly intuitive manner. Well done!

I'm curious what the path might look like in supporting preact, specifically for deno/fresh?

Great concept, well executed — thanks for putting this out there!

Add fieldsets and tabs to fields API

Currently, we have no way to create groups of fields. #62 adds object support, and spun out this discussion.

Inline fieldsets at the top-level (with headings) are something I've considered as a way to break the form up into separate areas of related fields. It strikes me that the Heading scenario here might also work with an inline Heading fieldset.

Proposals

Option 1

Add renderFields method

Example adding a Style fieldset to Heading

export const Heading: ComponentConfig<HeadingProps> = {
  renderFields: () => {
    return <>
      <Field name="text" />
      <Field name="level" />
      <Fieldset title="Style">
        <Field name="size" />
        <Field name="align" />
        <Field name="padding" />
      </Fieldset>
    </>
  },
  fields: {
    text: { type: "text" },
    size: {
      type: "select",
      options: sizeOptions,
    },
    level: {
      type: "select",
      options: levelOptions,
    },
    align: {
      type: "radio",
      options: [
        { label: "Left", value: "left" },
        { label: "Center", value: "center" },
        { label: "Right", value: "right" },
      ],
    },
    padding: { type: "text" },
  },
}

Option 2

Change fields API or add new fieldsets API

export const Heading: ComponentConfig<HeadingProps> = {
  fieldsets: [
    {
      title: "",
      fields: {
        text: { type: "text" },
        level: {
          type: "select",
          options: levelOptions,
        },
      },
    },
    {
      title: "Style",
      fields: {
        size: {
          type: "select",
          options: sizeOptions,
        },

        align: {
          type: "radio",
          options: [
            { label: "Left", value: "left" },
            { label: "Center", value: "center" },
            { label: "Right", value: "right" },
          ],
        },
        padding: { type: "text" },
      },
    },
  ],

Option 3

Something else; could be some combination of the above or another proposal.


Related #62

Custom field type and custom field input

How can I create a new field type and it's editor?

The current plugin system doesn't seem to support this, only allowing us to reimplement the whole sidebar editing?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.