🏠 back to Observable

Embeding a notebook cell in a resizable and draggable react component ?

I’m trying to embed a notebook cell in a resizable React Component.
I have difficulties making the notebook cell chart responsive to the React Component Size.

I have been looking at https://observablehq.com/@observablehq/downloading-and-embedding-notebooks, which suggest that If I’am able to get the width of the resizable React container I could inject it in the notebook as the new width for my embed chart.

I also looked at https://observablehq.com/@toja/custom-notebook-width but could not make it work in or aside the React component

I also looked at this https://observablehq.com/@mbostock/embedded-notebook?collection=@observablehq/techniques (but here again I have no clue on how to mix that code with React)

Here is the code that is roughly working (displaying the chart but not resizing it)…

import React from "react";
import ReactDOM from "react-dom";

import { Runtime, Inspector, Library } from "@observablehq/runtime";
import define from "@maliky/3-filtres-pour-les-donnees-des-mooc-v3";

import { ResizableBox } from "react-resizable";

const mountNode = document.getElementById("react-root");
var runtime = new Runtime();

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { animationRef: React.createRef() };
  }
  componentDidMount() {
    runtime.module(define, name => {
      if (name === "viewof link_strength") {
        return new Inspector.into(document.querySelector("#obs1"))();
        /* return new Inspector.into(this.state.animationRef)(); */
      }
    });
  }
  render() {
    return (
      <div id="root">
        <ResizableBox width={400} height={300}>
          <div id="obs1"></div>
          {/* <div ref={this.state.animationRef}></div> */}
        </ResizableBox>
      </div>
    );
  }
}

ReactDOM.render(<App />, mountNode);

you may notice that I need to add parentheses at the end of
new Inspector.into(...) I’m not sure why. I don’t see this in other people’s code I’ve read.
Edit: Checked again the docs… The reason is that I’m returning new Inspector.into(foo) and it is different from returning new Inspector(foo)
new Inspector.into(foo)(baz) Is returning a new Inspector(foo) into the baz DOM element, but if baz is empty – i.e. new Inspector.into(foo)() – then, I guess, it does the same as new Inspector(foo)

What I would like is been able to export the main svg as

<svg viewBox="0,0,953,400" width="100%" height="100%">... 

instead of

<svg viewBox="0,0,953,400" width="953" height="400">...

where the svg attributes are a translation of DOM.svg(dim.W, dim.H)

But, maybe it is the way I am building and thinking my charts that is not correct.
Could you please tell me what you think about my approach to set the basics elements for my chart ?

… I know this is a stupid question, I’m sorry. There’s still basic stuff I don’t get about viewBox too although I am checking the MDN docs.

Edit:
I finally go the iframe technique working thanks to bgchen and his answer to my previous question and it does enable the chart to resize base on its containers’s width.
All there is to do is really to change the link of the import define to match your chart with js at the end.
endEdit:

I have a better solution than the previous one,
https://codesandbox.io/s/twilight-rain-hjp3r (tested with chromium)

here’s the code in case the link is broken

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import { Runtime, Inspector } from "@observablehq/runtime";
import notebook from "./notebook/1949747ba1654b78@406";

/* import Draggable from "react-draggable"; */
import { Resizable } from "react-resizable";
import Draggable from "react-draggable";
const runtime = new Runtime();
var obsheight = 100,
  obswidth = 100;

class ResizableChartCard extends React.Component {
  constructor(props) {
    super(props);
    this.refreshInitID = 0;
    this.chartRef = React.createRef();
    this.state = {
      width: 399,
      height: 299,
      dragStartX: 0,
      dragStartY: 0,
      dragEndX: 0,
      dragEndY: 0
    };
    this.margins = { X: 80, Y: 80 };
  }

  static getDerivedStateFromProps(nextProps, nextState) {
    return null;
  }

  componentDidMount() {
    runtime.module(notebook, name => {
      if (name === "chart") {
        return new Inspector(this.chartRef.current);
      }

      if (name === "mutable mheight") {
        return {
          fulfilled: value => {
            obsheight = value;
          }
        };
      }

      if (name === "mutable mwidth") {
        return {
          fulfilled: value => {
            obswidth = value;
          }
        };
      }
    });
    // Pour forcer la mise à jour des variable de taille.  Laisser au moins 10ms
    this.refreshInitID = setInterval(() => this.refresh_init(), 10);
  }

  componentDidUpdate(nextProps, nextState) {}

  refresh_init() {
    this.setState({
      width: obswidth.value,
      height: obsheight.value
    });
    clearInterval(this.refreshInitID);
  }

  refresh() {
    debugger;
    this.setState({
      width: obswidth.value,
      height: obsheight.value
    });
    //clearInterval(this.refreshInitID);
  }

  onResizeStart = (event, data) => {
    //debugger;
    this.setState({ dragStartX: event.clientX, dragStartY: event.clientY });
  };
  onResizeStop = (event, data) => {
    let new_width = this.state.width + event.clientX - this.state.dragStartX;
    let new_height = this.state.height + event.clientY - this.state.dragStartY;
    obswidth.value = new_width;
    obsheight.value = new_height;
    this.setState({
      dragEndX: event.clientX,
      dragEndY: event.clientY,
      widht: new_width,
      height: new_height
    });
    //debugger;
  };

  render() {
    const new_width = this.state.width + "px",
      new_height = this.state.height + "px";
    return (
      <Draggable
        width={this.state.width}
        height={this.state.height}
        handle=".drag-handle"
      >
        <Resizable
          width={this.state.width}
          height={this.state.height}
          onResizeStart={this.onResizeStart}
          onResizeStop={this.onResizeStop}
          {...this.props}
        >
          <div id="my-card" style={{ width: new_width, height: new_height }}>
            <div
              id="my-card-header"
              className="drag-handle"
              style={{ background: "grey" }}
            >
              Cliquez moi pour déplacer le graph
            </div>
            <div id="my-card-content" className="nodrag">
              <div ref={this.chartRef} />
            </div>
          </div>
        </Resizable>
      </Draggable>
    );
  }
}

ReactDOM.render(<ResizableChartCard />, document.getElementById("root"));

The css is retreivable here

2 Likes