braebo / gooey

floating gui library for the web
https://gooey.braebo.dev
4 stars 1 forks source link

Convert `Folder.inputs` to a typesafe pojo #35

Open braebo opened 2 months ago

braebo commented 2 months ago

This is a follow up after talking through #29, which was motivated by the need to track changes when using addMany (not bindMany) with an inline object.

@ghostdevv The problem is encountered when doing this:

gooey
  .addMany({
    a: 1,
    b: { c: 2 }
  })
  .on('change', (PAYLOAD) => { ... })

And you were asking for the value of PAYLOAD to be a reference to the object that was changed.

This isn't a thing because there is no "object" internally anymore, just a tree of folders and inputs.

The 'change' event is emitted by every folder, and it's used to react to any change in any input or subfolder input in the folder emitting the event. This is independent in many ways from the concept of addMany.

In order to access the values corresponding to the input object, you'd need to use the allInputs map of the folder being added to:

gooey.addMany({ a: 1, b: { c: 2 }}).on('change', PAYLOAD => { 
    const a = gooey.allInputs.get('a').value // this sucks tbf (should be a typesafe object instead of a map)
    // do something with `a`

    // or

    if (PAYLOAD.title === 'a') {
        // do something with `a`
    }
})

I've yet to try that, as I'd normally be storing references as needed:

const params = { a: 1, b: { c: 2 }}

gooey.bindMany(params).on('change', PAYLOAD => { 
    const { a } = params // up to date values 👍
})

Nonetheless, this highlights one of the biggest unsolved annoyances I do have with the current API:

Getting the inputs back out of the gooey object kinda sucks thanks to the type-unsafe nature of a Map:

const a = gooey.inputs.get('a') // => ValidInput | PizzaDelivery | MovieTicket | QuantumMultiverse

We could:

  1. Bully typescript till it does proper inference on Maps (done it before but it's kinda ghetto)
  2. Construct and track an internal object that maps each key to its input

Option 2 seems better to me.

So. Currently, there are 3 ways to get a reference to an input:

If inputs is converted to a typesafe POJO, it would feel even more grody constructing it every time allInputs is referenced. So I guess my question is:

Whats the best way to construct and store this POJO map in a recursive tree structure like this one while maintaining the current API?

Off the dome, I suppose we could:

  1. Store it on the Folder.root or Gooey instance
  2. Update it inside of the Folder._registerInput() method (luckily all inputs go through it)
  3. Make each folders' allInputs getter point to its position in the tree.

I'm not sure if this is the best way to go about it, but it's the first thing that comes to mind. I'll try it out and see how it feels. If it works, it would solve both this and #29

ghostdevv commented 2 months ago

doesn't addMany just use bindMany under the hood? If so could you do something like

function addMany(obj) {
    const value = structuredClone(obj);
    this.bindMany(value ....
}

then just return this value in the onChange?

braebo commented 2 months ago

because onChange is a method on Folder that is used lots of places by stuff other than addMany, and addMany could be called any number of times, and there's no way to link a specific onChange event to a specific addMany callsite