mui / toolpad

Toolpad: Full stack components and low-code builder for dashboards and internal apps.
https://mui.com/toolpad/
MIT License
1.27k stars 282 forks source link

Support map component in Toolpad #864

Open prakhargupta1 opened 2 years ago

prakhargupta1 commented 2 years ago

Duplicates

Latest version

Summary 💡

I want to create an app that uses a map component to show start location and destination.

Note: Currently, it is supported through custom components: https://mui.com/toolpad/studio/how-to-guides/map-display/

Examples 🌈

No response

Motivation 🔦

No response

bharatkashyap commented 2 years ago

There's a way to use the Custom components to build a Map component from scratch inside Toolpad. Here's an example using Leaflet:

import * as React from "react";
import { createComponent } from "@mui/toolpad-core";

import * as L from "https://esm.sh/leaflet";

export interface LeafletProps {
  lat: number;
  long: number;
  zoom: number;
}

async function createLeafletStyles(doc) {
  let styles = doc.getElementById("leaflet-css");
  if (styles) {
    return;
  }
  const res = await fetch("https://esm.sh/leaflet/dist/leaflet.css");
  if (!res.ok) {
    throw new Error(`HTTP ${res.status}: ${res.statusText}`);
  }
  const css = await res.text();
  styles = doc.createElement("style");
  styles.id = "leaflet-css";
  styles.appendChild(doc.createTextNode(css));
  doc.head.appendChild(styles);
}

function Leaflet({ lat, long, zoom }: LeafletProps) {
  const root = React.useRef(null);
  const mapRef = React.useRef<any>();
  const [stylesInitialized, setStylesIitialized] = React.useState(false);
  const [error, setError] = React.useState<Error>();

  React.useEffect(() => {
    const doc = root.current.ownerDocument;
    createLeafletStyles(doc).then(
      () => setStylesIitialized(true),
      (err) => setError(err)
    );
  }, []);

  React.useEffect(() => {
    if (!mapRef.current && stylesInitialized) {
      mapRef.current = L.map(root.current);
      L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
        maxZoom: 19,
        attribution: "© OpenStreetMap",
      }).addTo(mapRef.current);
    }

    if (mapRef.current) {
      mapRef.current.setView([lat, long], zoom);
    }
  }, [stylesInitialized, lat, long, zoom]);

  return (
    <div style={{ height: 400, width: 600 }}>
      {error ? (
        error.message
      ) : (
        <div style={{ width: "100%", height: "100%" }} ref={root} />
      )}
    </div>
  );
}

export default createComponent(Leaflet, {
  argTypes: {
    lat: {
      typeDef: { type: "number" },
      defaultValue: 51.505,
    },
    long: {
      typeDef: { type: "number" },
      defaultValue: -0.09,
    },
    zoom: {
      typeDef: { type: "number" },
      defaultValue: 13,
    },
  },
});
prakhargupta1 commented 2 years ago

Nice!! Thanks, I'll check it out.

prakhargupta1 commented 1 year ago

Can we make it permanent in Toolpad's component library? Another solution could be that we provide it as a Custom component by default. It will give user a better idea of what custom component is.

A user shared that he got confused what Components was, until he discovered component library. This can give users an example to understand about custom components.

I am opening this issue.

Janpot commented 1 year ago

Sure, some parts that are missing in this implementation that probably must make it into a native component

casi-z commented 8 months ago

top