brianzinn / react-babylonjs

React for Babylon 3D engine
https://brianzinn.github.io/react-babylonjs/
818 stars 105 forks source link

grid - usable in react #173

Closed hupeky closed 2 years ago

hupeky commented 2 years ago

moved from q&a

I would like to use the grid component but it apprears to not be functional as declarative in react at this stage.


brian wrote: The GUI components call ie: addControl(...) automatically behind the scenes. This is an example markup - appreciate any feedback:

<grid>
  <columnDefinition width={100} isPixel />
  <columnDefinition width={0.5} />
  <columnDefinition width={0.5} />
  <columnDefinition width={100} isPixel />
  <rowDefinition height={0.5}>...</rowDefinition>
  <rowDefinition height={0.5}>...</rowDefinition>
</grid>

I've never used the grid control before, so if you can prove the imperative code example then maybe we can work backwards. It looks like those methods are fluent (and return a grid), so I don't see a way to update ie: column width by updating <columnDefnition .../>. So, I'm not familiar with a good way to make it responsive to attaching width to state. Also, looking for how to declaratively add row content.


ps. here is a link to the playground with a working grid https://playground.babylonjs.com/#KX33X8#1

brianzinn commented 2 years ago

@kyehuelin would you mind sharing an imperative playground that is similar to the images you shared in the discussion? I really like the sliders and switch buttons you have there.

The addControl method is overridden and adds 2 new parameters row and column. Currently controls are transparently added by react-babylonjs, so we need a way to declare grid position to place in correct spot.

Here are possible options:

<grid>
  ...
  <stackPanel gridColumn={1} gridRow={1}>
    ....
  </stackPanel>
</grid>

This way the control would pick up it's position, but it feels less intuitive.

<grid>
  ...
  <gridControl column={1} row={1}>
      <stackPanel ... />
  </gridControl>
</grid>

This seems ok as well as reduces chance of property name collision (when I choose custom properties they can't collide with babylon API properties).

<grid>
  ...
  <stackPanel gridPosition={{row: 1, column: 1}}>
  </stackPanel>
</grid>

I think I like the first way. Do you have a proposal - you have probably met more use-cases.

Also, how do you do pagination - do you just call removeControl() and then when you addControl(...) it will replace the contents and also if you are adding and removing rowDefinitions as you are paging then could make for an interesting challenge.

hupeky commented 2 years ago

Hi brian, thanks for the feedback

Im just working on an implementation of the design using babyon components right now, once its done i can share a playground example.

at the moment i have a material ui implementation of the ui which works from the same store (zustand as per your recomendation) Ill loop back to you once i have the babylon implementation and share a playground. sorry i cant give you more now

hupeky commented 2 years ago

ps in answer to your question about how i do pagination i just map over an array of arrays and build a bunch of stackPanels liek this

    <>
      {pageData.filesGrid.map((filesRow, i) => (
        <stackPanel
          verticalAlignment={0}
          horizontalAlignment={0}
          key={filesRow[0].id}
          isVertical={false}
          adaptHeightToChildren
          paddingTopInPixels={i === 0 ? padding * 2 : 0}
        >
          {filesRow.map((file) => {
            return (
              <File
                my={file.userId !== undefined && id !== undefined && file.userId === id}
                padding={padding}
                key={file.id}
                selected={file.id === selectedItem?.id}
                onClick={() => onSelectFile(file)}
                label={file.name}
                heightInPixels={fileDimensions.heightInPixels}
                widthInPixels={fileDimensions.widthInPixels}
                loadable={file.loadable}
              />
            );
          })}
        </stackPanel>
      ))}
    </>
hupeky commented 2 years ago

I like all your proposals tbh the first way seems good yes.

brianzinn commented 2 years ago

oh, I see now it looks like you are converting from Material UI to babylon grid. The question about pagination was how you replace the content - looks like need to call removeControl, but I suspect you haven't written that part yet and you are porting from Material UI to Babylonjs. I think the way you use key prop that it will be handled transparently.

brianzinn commented 2 years ago

I've got a start on it. There have been lots of road closures where I live and I couldn't get home for 10 days - that's why I haven't been doing much lately. Anyway I created a branch to get started: https://github.com/brianzinn/react-babylonjs/tree/add-grid

I ended up having trouble updating the column and row definitions as props. I started a discussion here: https://forum.babylonjs.com/t/valueandunit-dynamic-update/25484

If you don't need to have dynamic row and column definitions then I can probably get the rest going, but otherwise it looks like I will need to make a PR on @babylonjs/gui to support.

brianzinn commented 2 years ago

ok. i pushed the branch even though it's still WIP. Right now you can do this:

<grid width='500px'>
  <rowDefinition height={1} isPixel />
</grid>

I am going to make a PR for babylon/core, so row/column definitions will support reconciler prop updates.

Then what I need to do is add optional parameters gridColumn and gridRow for all 2D controls to specify the row and column for when addControl(...) is called - that part is easy.

brianzinn commented 2 years ago

ok the PR is approved to @babylonjs/gui, so should have something by EOW. The row definitions will be assignable from props to dynamically change width/height via renderer.

brianzinn commented 2 years ago

ok, so this:

<adtFullscreenUi name='ui1'>
  <rectangle name="main" background='#222222' width='80%' thickness={3}>
    <grid name='grid1' background={background} width='500px'>
      <rowDefinition value={0.5} />
      <rowDefinition value={0.5} />
      <columnDefinition value={100} unit={ValueAndUnit.UNITMODE_PIXEL} />
      <columnDefinition value={0.5} />
      <columnDefinition value={0.5} />
      <columnDefinition value={100} unit={ValueAndUnit.UNITMODE_PIXEL} />
      <rectangle name="rect-0-1" background="green" thickness={0} gridRow={0} gridColumn={1} />
      <rectangle name="rect-1-2" background="red" thickness={0} gridRow={1} gridColumn={2} />
      <rectangle name="rect-0-2" background="yellow" thickness={0} gridRow={0} gridColumn={2} />
    </grid>
  </rectangle>
</adtFullscreenUi>

looks like this: image

I know before we had width and height instead of value and isPixel instead of unit, but above API on @babylonjs/gui@5 alpha 63+ will allow props to update the value and units accordingly.

Check it on 3.0.31. It should work as long as you aren't removing columns/rows not at the end - otherwise you will need to be creative with keys on the grid probably.

brianzinn commented 2 years ago

closing for housekeeping purposes. please re-open if not solved for you. this repo will be regenerated to go with babylon v5 now that it's out on beta.