easyblockshq / easyblocks

The open-source visual builder framework.
https://easyblocks.io
GNU Affero General Public License v3.0
241 stars 33 forks source link

Feature: default for component collections #47

Open timoconnellaus opened 1 month ago

timoconnellaus commented 1 month ago

Motivation: In some cases you want to repeat the same item in a component collection over and over. It seems redundant to ask the user each time which component they want - especially when there is only one item in the component collection to choose from

{
  type: "select"
  prop: "type",
  params: {
    options: [{value: "icon-and-text", label: "Icon And Text"}, {value: "text-only", label: "Text"}]
  }
},
{
  type: "component-collection",
  prop: "data",
  label: "List Items",
  accepts: (value) => value.type, // optionally can be dynamically set
  autoPopulate: true // when there is only one value in accepts it will autopopulate
}
r00dY commented 1 month ago

Absolutely agree. You're raising 2 important topics: one is auto-populate and another one is dynamic accepts.

Auto Populate

Auto-population is very much needed and I think we could make it even in a more future-proof way. Right now you can pick your template picker with picker property already (described here). There are 3 picker variants but they're hardcoded and in the end it's always going to be inflexible. If we opened up template pickers API, we could easily pass parameters to the picker and even provide custom template pickers. That could easily solve auto-population problem without extending the core API and allow for much more use cases in the future:

// Legacy API
{
  type: "component-collection",
  prop: "data",
  label: "List Items",
  picker: "large" // or "large-3" | "compact"
}

// New API 
{
  type: "component-collection",
  prop: "data",
  label: "List Items",
  picker: {
    id: "@easyblocks/large", // id could be optional and default to @easyblocks/large as it is now
    autoPopulate: true,
    numberOfCardsInARow: 2, // could be also 3 potentially
  }
}

// New API for custom pickers
{
  type: "component-collection",
  prop: "data",
  label: "List Items",
  picker: {
    id: "MyCustomPicker",
    param1: "a",
    param2: "b"
  }
}

// and then for EasyblocksEditor component
<EasyblocksEditor components={{...}} widgtets={{...}} pickers={{ MyCustomPicker: component }} />

Such API is much more powerful, it allows for example for dynamic template fetching in a custom picker, custom UI etc. Each picker could decide whether it allows for autoPopulate or not depending on its specific constraints.

What do you think?

Dynamic accepts

This is tricky. If we go with too much dynamism on accepts then we must have some resolution strategy when component X could be added to a slot some time ago, but the conditions changed and now it shouldn't be there even though it is in the canvas. Can you please elaborate on your use case?

timoconnellaus commented 1 month ago

The motivation for dynamic accepts is you might only allow certain components to be added to the collection based on a choice in the parent component. This could be where you want the items in the collection to always be the same type - so after he first one is chosen you restrict the others so they are the same.

I think with dynamic accepts it's more like allowed selection. The accepts can have multiple items but based on a condition - only A out of A, B and C components can be picked based on the condition.

r00dY commented 1 month ago

Got it. Question:

What if, based on condition in parent, only component A is allowed. User adds A,A,A. Then the condition changes and only B is allowed. What happens to A,A,A? What do you think the resolution strategy here should be?

timoconnellaus commented 1 month ago

I think it's about what component can be added next, not what components already exist. For example, you might use it to say "you've already added A, now you can only add B". So it's down to the developer to decide how it works

r00dY commented 1 month ago

Actually it's not how we think about it. We think of accepts as a validator for data integrity. It's almost like interfaces in typed languages, you say that certain slot can only accept visual elements of a certain type (like buttons), that satisfy certain type of props (I'm talking about props passing in no-code components) etc.

If you only want to constraint the templates user sees when template picker is displayed then my suggestion with opening up picker API is much simpler, easier and more practical. Which is good news :)