gosling-lang / gosling.js

Grammar of Scalable Linked Interactive Nucleotide Graphics
https://gosling.js.org
MIT License
157 stars 27 forks source link

Add basic navigation/interactive components like HiGlass view headers #911

Open sehilyi opened 1 year ago

sehilyi commented 1 year ago

We can add basic navigation/interactive components, such as search boxes for genomic regions, to the Gosling Component. While they are not "visualizations" that fit into the grammar of Gosling, they will be beneficial to have since they are essential for genomics data analysis. Some potential components:

We can show such components as a header of the Gosling visualization.

Screenshot 2023-05-30 at 15 56 10

I don't think we have to edit the schema (the Gosling grammar). Instead, we can use options for the Gosling Component:

<GoslingComponent ... header={true}/>

cc @ngehlenborg @manzt @etowahadams

sehilyi commented 7 months ago
manzt commented 7 months ago

This sounds like a proposal to add additional UI components on top of Gosling's API, and we could run into the same HiGlass issue where folks want to turn off/on header.

Rather than expanding the GoslingComponent with additional props, we could consider a more React-like API for composing UI components for the Gosling API.

let GoslingContext = React.createContext(null);

function GoslingProvider({ children }) {
  let ref = React.useRef();
  return <GoslingContext.Provider value={ref}>{children}</GoslingContext.Provider>
}

function useGoslingApi() {
  let ref = React.useContext(GoslingContext);
  if (!ref) throw Error("useGoslingApi must be called within GoslingProvider");
  return ref;
}

function ExportButtons() {
  let ref = useGoslingApi();
  return (
     <div>
       <button onClick={() => ref.current?.api.exportPng()}>Save PNG</button>
       <button onClick={() => ref.current?.api.exportPdf()}>Save PDF</button>
     </div>
  )
}

function GoslingNavigation() {
  let ref = useGoslingApi();
  function handleInput() { /* ... */ }
  return <input type="text" onInput={handleInput} />
}

function GoslingComponentWithMyControls() {
  return (
    <GoslingProvider>
      <div>
        <GoslingNavigation />
        <div>
          <ExportButtons />
          <GoslingComponent spec={spec} />
        </div>
      </div>
    </GoslingProvider>
  )
}

gosling.embed could use one of these higher level components (e.g., GoslingComponentWithMyControls), but it would also allow Gosling app developers to create their own Gosling UI with React.

The existing GoslingComponent would just need to aware of the context:

const GoslingComponent = React.forwardRef((props, ref) => {
  ref = ref ?? React.useContext(GoslingContext);
  /* ... */
})
manzt commented 7 months ago

I wonder if @thomcsmits would have any thoughts with relation to AltGosling and using a container/hook like this.