nathancahill / split

Unopinionated utilities for resizeable split views
https://split.js.org/
MIT License
6.15k stars 450 forks source link

Use a React component for gutter #320

Open drazik opened 4 years ago

drazik commented 4 years ago

Hello,

When reading the react-split docs, I found that the gutter prop is a function that should return an html element (https://github.com/nathancahill/split/tree/master/packages/react-split#gutter). I was wondering if there is a way to make it work with a React component. My use case is that I will need to show an icon in the gutter, and a tooltip when the mouse is hovering this icon. Since I have all the components ready to show this icon and tooltip, it would be great that I can use them in a Gutter component that I would give as the gutter prop of the Split component.

If this is not possible to do this right now, do you think it would be good for the library ? I can help doing the changes.

Thanks !

nathancahill commented 3 years ago

Great idea. I'd love to support that.

colmanhumphrey commented 3 years ago

This would be really cool. @drazik do you have any thought about how much work it could be?

drazik commented 3 years ago

I didn't look into it finally, sorry.

adamsussman commented 2 years ago

fwiw, I have had some success with:

<Split
     gutter={() => {
        const gutterWrapper = document.createElement("div");
        const MyComponent = (
          <div
            style={{background: "red", height: "100%"}}
            onClick={() => console.log("clicked")}
            etc...
          >
             Gutter contents or more components...
          </div>
        );
        ReactDOM.render(MyComponent, gutterWrapper);
        return gutterWrapper;
      }}
>

But note that in this usage, communications between components is one way (upwards from the gutter to the container). You can pass in "onDoSomething" props to the gutter component that it can use to send signal to the component containing the Split but you can't pass mutable state from the container into the gutter component and get it to re-render the gutter on change. Right now, it seems that react-split caches the gutter on mount and does not re-render it afterwards, although there may be some trick to it that I am missing since there is a notion of certain props forcing re-creation of the split object.

Might be doable with a context. I haven't explored that too much.

kalkrueger commented 1 year ago

I struggled with this on a piece of legacy code I have been working on.

The work around I found was to use createPortal from react-dom. I also had to return a null html element if the dom rendered before the gutter was set or I was getting a react error (there is an explanation for this in the createPortal docs).

I mocked up the general idea of the code that ended up working for me:

const InnerGutterElement = ({ clickEvent }) => {
  const handleClick = () => {
    clickEvent();
  };

  return (
      <div className={`gutter-element}`} onClick={() => handleClick()}></div>
  );
};

const PanelComponent = () => {
  const [gutterElement, setGutter] = useState(false);

  return (
    <div>
      <Split
        gutter = (_, direction) => {
          const gutter = document.createElement('div');
          gutter.className = `gutter gutter-${direction}`;

           //sets gutter component to be used by createPortal when gutter is initialized
          setGutter(gutter);

         return gutter;
       };
      >
     {gutterElement ? createPortal(<InnerGutterElemen clickEvent={clickEvent}/>, gutterElement) : null}
    </div>
  )
}
lehovec commented 11 months ago

It's possible to use Portals to have custom gutter component:

export const CustomGutterSplit = () => {
  const gutterContainer = useRef<HTMLElement>(document.createElement("div"));
  return (
    <>
      <Split
        gutter={() => gutterContainer.current}
      >
        <Container1 />
        <Container2 />
      </Split>
      {createPortal(<div>my custom gutter</div>, gutterContainer.current)}
    </>
  );
};