#How has other solved default wrappers for components, props you want all your components to have

1 messages · Page 1 of 1 (latest)

frigid vault
#

type DefaultSizing = "xl" | "lg" | "md" | "sm";

export type DefaultLayoutProps = {
marginLeft?: DefaultSizing;
marginRight?: DefaultSizing;
...
};

type DefaultWrapperProps = DefaultLayoutProps & { children?: ReactNode };

export type DefaultComponentConfig<TProps> = ComponentConfig<
TProps & DefaultLayoutProps

;

const defaultLayoutFields = {
marginLeft: {
type: "select" as const,
options: [
{ label: "Small", value: "sm" },
{ label: "Medium", value: "md" },
{ label: "Large", value: "lg" },
{ label: "Extra Large", value: "xl" },
],
},
marginRight: {
type: "select" as const,
options: [
...
],
},
},
} satisfies ComponentConfig<DefaultLayoutProps>["fields"];

const defaultLayoutProps: DefaultLayoutProps = {
marginLeft: "md",
marginRight: "md",
marginTop: "md",
...
};

const DefaultWrapper = ({
children,
marginBottom,
marginLeft,
marginRight,
...
}: DefaultWrapperProps) => {
const className = [
marginBottom && mb-${marginBottom},
marginTop && mt-${marginTop},
...
]
.filter(Boolean)
.join(" ");

return <div className={className}>{children}</div>;
};

export type HeaderBlockProps = {
title: string;
};

type HeaderBlockRenderProps = HeaderBlockProps & DefaultWrapperProps;

export const HeaderBlock: ComponentConfig<
HeaderBlockRenderProps,
HeaderBlockProps & DefaultLayoutProps

= {
fields: {
title: { type: "text" as const },
...defaultLayoutFields,
},
defaultProps: {
...defaultLayoutProps,
title: "Heading",
},
render: ({ title, children, ...spacing }) => (
<DefaultWrapper {...spacing}>
<HeadingComponent title={title} />
{children}
</DefaultWrapper>
),
};

#

I feel like there should be a better way to pass default props to all components, like creating a new sub type isch

frigid vault
#

I converted it to a function using withLayout isch, and that works but looks ugly, unless someone has a good solution i am creating a div with a slot that have thease settings

covert flax
#

Hey @frigid vault!

This is indeed one of those things that depends heavily on your setup. The main thing is that Puck configs are just objects, so one solution is using decorators like the one you mentioned above—though that approach does have its limitations (such as field ordering, logic flow control, etc.).

I've also seen people take a more OOP-oriented approach, where they wrap their configs with classes and use patterns like builders, or use creator functions with inheritance.

From the library's perspective, we took a more unopinionated approach, since some users need very basic setups while others require more advanced ones. Providing these patterns out of the box might be overkill, too complex for the simpler use cases, or lead people to use patterns they're not using internally.

That said, this is something I’ve always wanted to explore further. I'm curious to hear your thoughts: how would you like subtyping to work? What would you envision that looking like in code? Would you want to define it directly within the config?

frigid vault
# covert flax Hey <@1034898612024529007>! This is indeed one of those things that depends hea...

Hey Fede,

I have been thinking it over, and i think i have a structure that could work, here me out

we have the default component lets take this as an example:

  TypographyProps,
} from "@/components/typography/Typography";
import { ComponentConfig } from "@measured/puck";

export type TypographyBlockProps = {
  title: string;
  type: TypographyProps["type"];
  color: string;
};

export const TypographyBlock: ComponentConfig<TypographyBlockProps> = {
  fields: {
    title: { type: "text" },
    type: {
      type: "select",
      options: [
        { label: "Header", value: "header" },
        { label: "SubHeader", value: "subheader" },
        { label: "Body", value: "body" },
        { label: "Caption", value: "caption" },
      ],
    },
    color: {
      type: "custom",
      render: ({ value, onChange }) => (
        <input
          type="color"
          value={value || "#000000"}
          onChange={(e) => onChange(e.target.value)}
          className="w-16 h-8 p-0 border-0"
        />
      ),
    },
  },
  defaultProps: {
    title: "Heading",
    type: "header",
    color: "#000000",
  },
  render: ({ title, type, color }) => (
    <Typography color={color} type={type} text={title} />
  ),
};
#

I think Component config, should have an optional field called "parent", adding a parentConfig would basically put the current config in this case the typography block inside that parent block, all the props etc can be defined in the editor for both the parent and the typography block, idealy the parent blocks would be in a subsection of its own in the editor aswell.

#

I will start coding a poc on this to se if i can get it to work, i think it basically boils down to a class builder as you said

#

I have been playing with wrapper functions aswell but yeah, i felt that it got a bit complex that way

#

And the "parent" component should also be a ComponentConfig, making them recursivly inherit their parents indefinently

covert flax
#

So basically you'd have class inheritance in a way, but in this case it would be so that you inherit all the configuration properties? The parent would be its own draggable component in the drawer as well?
How would you expect resolveData, resolveFields, and resolvePermissions to work with this approach?

frigid vault
#

Yeah exactly, the parent would be its own compnent, so resolvedata,fields and permissions should be handled in the configuration for the parent

#

i mean the parent could be draggble if it is passed to the root config, but does not have to

covert flax
frigid vault
covert flax
#

Thank you!

frigid vault
#

How long does this process usually take ? (To to stress you guys) i am going on a vaccation for 3 weeks soon so wondering if stuff would start before i get back <.<