@foxglove/embed-react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/cjs/FoxgloveViewer.d.ts +43 -0
- package/dist/cjs/FoxgloveViewer.d.ts.map +1 -0
- package/dist/cjs/FoxgloveViewer.js +87 -0
- package/dist/cjs/FoxgloveViewer.js.map +1 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/tsconfig.build.cjs.tsbuildinfo +1 -0
- package/dist/cjs/useShouldNotChangeOften.d.ts +3 -0
- package/dist/cjs/useShouldNotChangeOften.d.ts.map +1 -0
- package/dist/cjs/useShouldNotChangeOften.js +22 -0
- package/dist/cjs/useShouldNotChangeOften.js.map +1 -0
- package/dist/esm/FoxgloveViewer.d.ts +43 -0
- package/dist/esm/FoxgloveViewer.d.ts.map +1 -0
- package/dist/esm/FoxgloveViewer.js +84 -0
- package/dist/esm/FoxgloveViewer.js.map +1 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/tsconfig.build.tsbuildinfo +1 -0
- package/dist/esm/useShouldNotChangeOften.d.ts +3 -0
- package/dist/esm/useShouldNotChangeOften.d.ts.map +1 -0
- package/dist/esm/useShouldNotChangeOften.js +19 -0
- package/dist/esm/useShouldNotChangeOften.js.map +1 -0
- package/package.json +41 -0
- package/src/FoxgloveViewer.tsx +150 -0
- package/src/index.ts +1 -0
- package/src/useShouldNotChangeOften.ts +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# @foxglove/embed-react
|
|
2
|
+
|
|
3
|
+
This package provides the `<FoxgloveViewer />` component. It wraps the [`FoxgloveViewer` class](https://docs.foxglove.dev/docs/embed/foxglove-embed) to provide a React API for embedding [Foxglove](https://foxglove.dev/) in your application.
|
|
4
|
+
|
|
5
|
+
See https://docs.foxglove.dev/docs/embed/foxglove-embed-react
|
|
6
|
+
|
|
7
|
+
## Stay in touch
|
|
8
|
+
|
|
9
|
+
Join us in [Discord](https://foxglove.dev/chat) to ask questions, share feedback, and stay up to date on what our team is working on.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type DataSource, type OpaqueLayoutData } from "@foxglove/embed";
|
|
3
|
+
type Props = {
|
|
4
|
+
/** The URL where the Foxglove app is hosted. Defaults to `https://embed.foxglove.dev/`. */
|
|
5
|
+
src?: string;
|
|
6
|
+
/**
|
|
7
|
+
* The Foxglove organization the user should be signed into. Passing this parameter is
|
|
8
|
+
* recommended so the user can be prompted to switch to the correct org if necessary.
|
|
9
|
+
*/
|
|
10
|
+
orgSlug?: string;
|
|
11
|
+
/** The data source to visualize in the Foxglove app. */
|
|
12
|
+
data?: DataSource;
|
|
13
|
+
/** The layout to use in the Foxglove app. */
|
|
14
|
+
layoutData?: OpaqueLayoutData;
|
|
15
|
+
/**
|
|
16
|
+
* The extensions to install in the Foxglove app.
|
|
17
|
+
*
|
|
18
|
+
* This property is not supported by all Foxglove versions and plans, so it may result in an `onError` callback.
|
|
19
|
+
*/
|
|
20
|
+
extensions?: Array<string | File>;
|
|
21
|
+
/** The color scheme to use in the Foxglove app. "auto" will use the system color scheme. */
|
|
22
|
+
colorScheme?: "light" | "dark" | "auto";
|
|
23
|
+
/** The style to apply to the container. */
|
|
24
|
+
style?: React.CSSProperties;
|
|
25
|
+
/** The class name to apply to the container. */
|
|
26
|
+
className?: string;
|
|
27
|
+
/** A callback function that is called when the Foxglove app is ready. */
|
|
28
|
+
onReady?: () => void;
|
|
29
|
+
/**
|
|
30
|
+
* A callback function that is called when the Foxglove app encounters an error when trying to
|
|
31
|
+
* update the data source, layout, or extensions.
|
|
32
|
+
* */
|
|
33
|
+
onError?: (errorMsg: string) => void;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* A component that renders an iframe with the Foxglove app.
|
|
37
|
+
*
|
|
38
|
+
* This component is a wrapper around the `FoxgloveViewer` class that provides a more
|
|
39
|
+
* convenient API for embedding the Foxglove app in a React application.
|
|
40
|
+
*/
|
|
41
|
+
export declare function FoxgloveViewer(props: Props): ReactNode;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=FoxgloveViewer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FoxgloveViewer.d.ts","sourceRoot":"","sources":["../../src/FoxgloveViewer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,EACL,KAAK,UAAU,EAEf,KAAK,gBAAgB,EACtB,MAAM,iBAAiB,CAAC;AAIzB,KAAK,KAAK,GAAG;IACX,2FAA2F;IAC3F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAClC,4FAA4F;IAC5F,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,2CAA2C;IAC3C,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;SAGK;IACL,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAiGtD"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FoxgloveViewer = FoxgloveViewer;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const embed_1 = require("@foxglove/embed");
|
|
7
|
+
const useShouldNotChangeOften_1 = require("./useShouldNotChangeOften");
|
|
8
|
+
/**
|
|
9
|
+
* A component that renders an iframe with the Foxglove app.
|
|
10
|
+
*
|
|
11
|
+
* This component is a wrapper around the `FoxgloveViewer` class that provides a more
|
|
12
|
+
* convenient API for embedding the Foxglove app in a React application.
|
|
13
|
+
*/
|
|
14
|
+
function FoxgloveViewer(props) {
|
|
15
|
+
const { src, orgSlug, className, style, data, layoutData, extensions, colorScheme } = props;
|
|
16
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
17
|
+
const elem = (0, react_1.useRef)(null);
|
|
18
|
+
const frame = (0, react_1.useRef)();
|
|
19
|
+
// Store the callbacks in refs to avoid re-rendering the component when the callbacks change.
|
|
20
|
+
const onReady = (0, react_1.useRef)(props.onReady ?? undefined);
|
|
21
|
+
const onError = (0, react_1.useRef)(props.onError ?? undefined);
|
|
22
|
+
(0, useShouldNotChangeOften_1.useShouldNotChangeOften)(data, () => {
|
|
23
|
+
console.warn(`[FoxgloveViewer] "data" is changing too frequently. This may cause performance issues, consider memoizing this value.`);
|
|
24
|
+
});
|
|
25
|
+
(0, useShouldNotChangeOften_1.useShouldNotChangeOften)(layoutData, () => {
|
|
26
|
+
console.warn(`[FoxgloveViewer] "layoutData" is changing too frequently. This may cause performance issues, consider memoizing this value.`);
|
|
27
|
+
});
|
|
28
|
+
(0, useShouldNotChangeOften_1.useShouldNotChangeOften)(extensions, () => {
|
|
29
|
+
console.warn(`[FoxgloveViewer] "extensions" is changing too frequently. This may cause performance issues, consider memoizing this value.`);
|
|
30
|
+
});
|
|
31
|
+
// Update onReady and onError refs when the props change.
|
|
32
|
+
(0, react_1.useEffect)(() => {
|
|
33
|
+
onReady.current = props.onReady;
|
|
34
|
+
}, [props.onReady]);
|
|
35
|
+
(0, react_1.useEffect)(() => {
|
|
36
|
+
onError.current = props.onError;
|
|
37
|
+
}, [props.onError]);
|
|
38
|
+
(0, react_1.useEffect)(() => {
|
|
39
|
+
if (!elem.current) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const handleReady = () => {
|
|
43
|
+
onReady.current?.();
|
|
44
|
+
};
|
|
45
|
+
const handleError = (errorMsg) => {
|
|
46
|
+
onError.current?.(errorMsg.detail);
|
|
47
|
+
};
|
|
48
|
+
frame.current = new embed_1.FoxgloveViewer({
|
|
49
|
+
parent: elem.current,
|
|
50
|
+
src,
|
|
51
|
+
orgSlug,
|
|
52
|
+
});
|
|
53
|
+
frame.current.addEventListener("ready", handleReady);
|
|
54
|
+
frame.current.addEventListener("error", handleError);
|
|
55
|
+
return () => {
|
|
56
|
+
frame.current?.removeEventListener("ready", handleReady);
|
|
57
|
+
frame.current?.removeEventListener("error", handleError);
|
|
58
|
+
frame.current?.destroy();
|
|
59
|
+
};
|
|
60
|
+
}, [src, orgSlug]);
|
|
61
|
+
(0, react_1.useEffect)(() => {
|
|
62
|
+
if (data == undefined || frame.current == undefined) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
frame.current.setDataSource(data);
|
|
66
|
+
}, [data]);
|
|
67
|
+
(0, react_1.useEffect)(() => {
|
|
68
|
+
if (layoutData == undefined || frame.current == undefined) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
frame.current.setLayoutData(layoutData);
|
|
72
|
+
}, [layoutData]);
|
|
73
|
+
(0, react_1.useEffect)(() => {
|
|
74
|
+
if (extensions == undefined || frame.current == undefined) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
frame.current.installExtensions(extensions);
|
|
78
|
+
}, [extensions]);
|
|
79
|
+
(0, react_1.useEffect)(() => {
|
|
80
|
+
if (colorScheme == undefined || frame.current == undefined) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
frame.current.setColorScheme(colorScheme);
|
|
84
|
+
}, [colorScheme]);
|
|
85
|
+
return (0, jsx_runtime_1.jsx)("div", { ref: elem, style: style, className: className });
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=FoxgloveViewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FoxgloveViewer.js","sourceRoot":"","sources":["../../src/FoxgloveViewer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,2DAAiC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../../src/FoxgloveViewer.tsx","../../src/index.ts","../../src/useShouldNotChangeOften.ts"],"version":"5.9.2"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useShouldNotChangeOften.d.ts","sourceRoot":"","sources":["../../src/useShouldNotChangeOften.ts"],"names":[],"mappings":"AAKA,+DAA+D;AAC/D,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,IAAI,GAAG,CAAC,CAexE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file is copied from @foxglove/viz/hooks/useShouldNotChangeOften.ts
|
|
3
|
+
// We copy the file to avoid depending on the viz package.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.useShouldNotChangeOften = useShouldNotChangeOften;
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
/** Call _warn_ if a given value changes twice within 200 ms */
|
|
8
|
+
function useShouldNotChangeOften(value, warn) {
|
|
9
|
+
const prev = (0, react_1.useRef)(value);
|
|
10
|
+
const prevPrev = (0, react_1.useRef)(value);
|
|
11
|
+
const lastTime = (0, react_1.useRef)(Date.now());
|
|
12
|
+
if (value !== prev.current &&
|
|
13
|
+
prev.current !== prevPrev.current &&
|
|
14
|
+
Date.now() - lastTime.current < 200) {
|
|
15
|
+
warn();
|
|
16
|
+
}
|
|
17
|
+
prevPrev.current = prev.current;
|
|
18
|
+
prev.current = value;
|
|
19
|
+
lastTime.current = Date.now();
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=useShouldNotChangeOften.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useShouldNotChangeOften.js","sourceRoot":"","sources":["../../src/useShouldNotChangeOften.ts"],"names":[],"mappings":";AAAA,0EAA0E;AAC1E,0DAA0D;;AAK1D,0DAeC;AAlBD,iCAA+B;AAE/B,+DAA+D;AAC/D,SAAgB,uBAAuB,CAAI,KAAQ,EAAE,IAAgB;IACnE,MAAM,IAAI,GAAG,IAAA,cAAM,EAAC,KAAK,CAAC,CAAC;IAC3B,MAAM,QAAQ,GAAG,IAAA,cAAM,EAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,IAAA,cAAM,EAAS,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5C,IACE,KAAK,KAAK,IAAI,CAAC,OAAO;QACtB,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO;QACjC,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,OAAO,GAAG,GAAG,EACnC,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC;IACD,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAChC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACrB,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC9B,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type DataSource, type OpaqueLayoutData } from "@foxglove/embed";
|
|
3
|
+
type Props = {
|
|
4
|
+
/** The URL where the Foxglove app is hosted. Defaults to `https://embed.foxglove.dev/`. */
|
|
5
|
+
src?: string;
|
|
6
|
+
/**
|
|
7
|
+
* The Foxglove organization the user should be signed into. Passing this parameter is
|
|
8
|
+
* recommended so the user can be prompted to switch to the correct org if necessary.
|
|
9
|
+
*/
|
|
10
|
+
orgSlug?: string;
|
|
11
|
+
/** The data source to visualize in the Foxglove app. */
|
|
12
|
+
data?: DataSource;
|
|
13
|
+
/** The layout to use in the Foxglove app. */
|
|
14
|
+
layoutData?: OpaqueLayoutData;
|
|
15
|
+
/**
|
|
16
|
+
* The extensions to install in the Foxglove app.
|
|
17
|
+
*
|
|
18
|
+
* This property is not supported by all Foxglove versions and plans, so it may result in an `onError` callback.
|
|
19
|
+
*/
|
|
20
|
+
extensions?: Array<string | File>;
|
|
21
|
+
/** The color scheme to use in the Foxglove app. "auto" will use the system color scheme. */
|
|
22
|
+
colorScheme?: "light" | "dark" | "auto";
|
|
23
|
+
/** The style to apply to the container. */
|
|
24
|
+
style?: React.CSSProperties;
|
|
25
|
+
/** The class name to apply to the container. */
|
|
26
|
+
className?: string;
|
|
27
|
+
/** A callback function that is called when the Foxglove app is ready. */
|
|
28
|
+
onReady?: () => void;
|
|
29
|
+
/**
|
|
30
|
+
* A callback function that is called when the Foxglove app encounters an error when trying to
|
|
31
|
+
* update the data source, layout, or extensions.
|
|
32
|
+
* */
|
|
33
|
+
onError?: (errorMsg: string) => void;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* A component that renders an iframe with the Foxglove app.
|
|
37
|
+
*
|
|
38
|
+
* This component is a wrapper around the `FoxgloveViewer` class that provides a more
|
|
39
|
+
* convenient API for embedding the Foxglove app in a React application.
|
|
40
|
+
*/
|
|
41
|
+
export declare function FoxgloveViewer(props: Props): ReactNode;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=FoxgloveViewer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FoxgloveViewer.d.ts","sourceRoot":"","sources":["../../src/FoxgloveViewer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,EACL,KAAK,UAAU,EAEf,KAAK,gBAAgB,EACtB,MAAM,iBAAiB,CAAC;AAIzB,KAAK,KAAK,GAAG;IACX,2FAA2F;IAC3F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAClC,4FAA4F;IAC5F,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,2CAA2C;IAC3C,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;SAGK;IACL,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAiGtD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { FoxgloveViewer as FoxgloveViewerClass, } from "@foxglove/embed";
|
|
4
|
+
import { useShouldNotChangeOften } from "./useShouldNotChangeOften";
|
|
5
|
+
/**
|
|
6
|
+
* A component that renders an iframe with the Foxglove app.
|
|
7
|
+
*
|
|
8
|
+
* This component is a wrapper around the `FoxgloveViewer` class that provides a more
|
|
9
|
+
* convenient API for embedding the Foxglove app in a React application.
|
|
10
|
+
*/
|
|
11
|
+
export function FoxgloveViewer(props) {
|
|
12
|
+
const { src, orgSlug, className, style, data, layoutData, extensions, colorScheme } = props;
|
|
13
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
14
|
+
const elem = useRef(null);
|
|
15
|
+
const frame = useRef();
|
|
16
|
+
// Store the callbacks in refs to avoid re-rendering the component when the callbacks change.
|
|
17
|
+
const onReady = useRef(props.onReady ?? undefined);
|
|
18
|
+
const onError = useRef(props.onError ?? undefined);
|
|
19
|
+
useShouldNotChangeOften(data, () => {
|
|
20
|
+
console.warn(`[FoxgloveViewer] "data" is changing too frequently. This may cause performance issues, consider memoizing this value.`);
|
|
21
|
+
});
|
|
22
|
+
useShouldNotChangeOften(layoutData, () => {
|
|
23
|
+
console.warn(`[FoxgloveViewer] "layoutData" is changing too frequently. This may cause performance issues, consider memoizing this value.`);
|
|
24
|
+
});
|
|
25
|
+
useShouldNotChangeOften(extensions, () => {
|
|
26
|
+
console.warn(`[FoxgloveViewer] "extensions" is changing too frequently. This may cause performance issues, consider memoizing this value.`);
|
|
27
|
+
});
|
|
28
|
+
// Update onReady and onError refs when the props change.
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
onReady.current = props.onReady;
|
|
31
|
+
}, [props.onReady]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
onError.current = props.onError;
|
|
34
|
+
}, [props.onError]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!elem.current) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const handleReady = () => {
|
|
40
|
+
onReady.current?.();
|
|
41
|
+
};
|
|
42
|
+
const handleError = (errorMsg) => {
|
|
43
|
+
onError.current?.(errorMsg.detail);
|
|
44
|
+
};
|
|
45
|
+
frame.current = new FoxgloveViewerClass({
|
|
46
|
+
parent: elem.current,
|
|
47
|
+
src,
|
|
48
|
+
orgSlug,
|
|
49
|
+
});
|
|
50
|
+
frame.current.addEventListener("ready", handleReady);
|
|
51
|
+
frame.current.addEventListener("error", handleError);
|
|
52
|
+
return () => {
|
|
53
|
+
frame.current?.removeEventListener("ready", handleReady);
|
|
54
|
+
frame.current?.removeEventListener("error", handleError);
|
|
55
|
+
frame.current?.destroy();
|
|
56
|
+
};
|
|
57
|
+
}, [src, orgSlug]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (data == undefined || frame.current == undefined) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
frame.current.setDataSource(data);
|
|
63
|
+
}, [data]);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (layoutData == undefined || frame.current == undefined) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
frame.current.setLayoutData(layoutData);
|
|
69
|
+
}, [layoutData]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (extensions == undefined || frame.current == undefined) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
frame.current.installExtensions(extensions);
|
|
75
|
+
}, [extensions]);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (colorScheme == undefined || frame.current == undefined) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
frame.current.setColorScheme(colorScheme);
|
|
81
|
+
}, [colorScheme]);
|
|
82
|
+
return _jsx("div", { ref: elem, style: style, className: className });
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=FoxgloveViewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FoxgloveViewer.js","sourceRoot":"","sources":["../../src/FoxgloveViewer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../../src/FoxgloveViewer.tsx","../../src/index.ts","../../src/useShouldNotChangeOften.ts"],"version":"5.9.2"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useShouldNotChangeOften.d.ts","sourceRoot":"","sources":["../../src/useShouldNotChangeOften.ts"],"names":[],"mappings":"AAKA,+DAA+D;AAC/D,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,IAAI,GAAG,CAAC,CAexE"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// This file is copied from @foxglove/viz/hooks/useShouldNotChangeOften.ts
|
|
2
|
+
// We copy the file to avoid depending on the viz package.
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
/** Call _warn_ if a given value changes twice within 200 ms */
|
|
5
|
+
export function useShouldNotChangeOften(value, warn) {
|
|
6
|
+
const prev = useRef(value);
|
|
7
|
+
const prevPrev = useRef(value);
|
|
8
|
+
const lastTime = useRef(Date.now());
|
|
9
|
+
if (value !== prev.current &&
|
|
10
|
+
prev.current !== prevPrev.current &&
|
|
11
|
+
Date.now() - lastTime.current < 200) {
|
|
12
|
+
warn();
|
|
13
|
+
}
|
|
14
|
+
prevPrev.current = prev.current;
|
|
15
|
+
prev.current = value;
|
|
16
|
+
lastTime.current = Date.now();
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=useShouldNotChangeOften.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useShouldNotChangeOften.js","sourceRoot":"","sources":["../../src/useShouldNotChangeOften.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,0DAA0D;AAE1D,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE/B,+DAA+D;AAC/D,MAAM,UAAU,uBAAuB,CAAI,KAAQ,EAAE,IAAgB;IACnE,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,MAAM,CAAS,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5C,IACE,KAAK,KAAK,IAAI,CAAC,OAAO;QACtB,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO;QACjC,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,OAAO,GAAG,GAAG,EACnC,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC;IACD,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAChC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACrB,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC9B,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@foxglove/embed-react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Foxglove Technologies",
|
|
7
|
+
"email": "support@foxglove.dev"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"foxglove",
|
|
11
|
+
"robotics",
|
|
12
|
+
"ros",
|
|
13
|
+
"visualization",
|
|
14
|
+
"react"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://foxglove.dev/",
|
|
17
|
+
"module": "dist/esm/index.js",
|
|
18
|
+
"main": "dist/cjs/index.js",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"src"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"clean": "rimraf dist",
|
|
25
|
+
"prepack": "yarn build",
|
|
26
|
+
"build": "yarn workspace @foxglove/embed build && tsc -b tsconfig.build.json tsconfig.build.cjs.json"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@foxglove/embed": "workspace:*",
|
|
30
|
+
"tslib": "2.8.1"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"react": ">=18",
|
|
34
|
+
"react-dom": ">=18"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@foxglove/tsconfig": "2.0.0",
|
|
38
|
+
"@types/react": "18.3.5",
|
|
39
|
+
"typescript": "5.9.2"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// foxglove-depcheck-used: tslib
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { useEffect, useRef } from "react";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type DataSource,
|
|
8
|
+
FoxgloveViewer as FoxgloveViewerClass,
|
|
9
|
+
type OpaqueLayoutData,
|
|
10
|
+
} from "@foxglove/embed";
|
|
11
|
+
|
|
12
|
+
import { useShouldNotChangeOften } from "./useShouldNotChangeOften";
|
|
13
|
+
|
|
14
|
+
type Props = {
|
|
15
|
+
/** The URL where the Foxglove app is hosted. Defaults to `https://embed.foxglove.dev/`. */
|
|
16
|
+
src?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The Foxglove organization the user should be signed into. Passing this parameter is
|
|
19
|
+
* recommended so the user can be prompted to switch to the correct org if necessary.
|
|
20
|
+
*/
|
|
21
|
+
orgSlug?: string;
|
|
22
|
+
/** The data source to visualize in the Foxglove app. */
|
|
23
|
+
data?: DataSource;
|
|
24
|
+
/** The layout to use in the Foxglove app. */
|
|
25
|
+
layoutData?: OpaqueLayoutData;
|
|
26
|
+
/**
|
|
27
|
+
* The extensions to install in the Foxglove app.
|
|
28
|
+
*
|
|
29
|
+
* This property is not supported by all Foxglove versions and plans, so it may result in an `onError` callback.
|
|
30
|
+
*/
|
|
31
|
+
extensions?: Array<string | File>;
|
|
32
|
+
/** The color scheme to use in the Foxglove app. "auto" will use the system color scheme. */
|
|
33
|
+
colorScheme?: "light" | "dark" | "auto";
|
|
34
|
+
/** The style to apply to the container. */
|
|
35
|
+
style?: React.CSSProperties;
|
|
36
|
+
/** The class name to apply to the container. */
|
|
37
|
+
className?: string;
|
|
38
|
+
/** A callback function that is called when the Foxglove app is ready. */
|
|
39
|
+
onReady?: () => void;
|
|
40
|
+
/**
|
|
41
|
+
* A callback function that is called when the Foxglove app encounters an error when trying to
|
|
42
|
+
* update the data source, layout, or extensions.
|
|
43
|
+
* */
|
|
44
|
+
onError?: (errorMsg: string) => void;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* A component that renders an iframe with the Foxglove app.
|
|
49
|
+
*
|
|
50
|
+
* This component is a wrapper around the `FoxgloveViewer` class that provides a more
|
|
51
|
+
* convenient API for embedding the Foxglove app in a React application.
|
|
52
|
+
*/
|
|
53
|
+
export function FoxgloveViewer(props: Props): ReactNode {
|
|
54
|
+
const { src, orgSlug, className, style, data, layoutData, extensions, colorScheme } = props;
|
|
55
|
+
|
|
56
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
57
|
+
const elem = useRef<HTMLIFrameElement | null>(null);
|
|
58
|
+
const frame = useRef<FoxgloveViewerClass>();
|
|
59
|
+
|
|
60
|
+
// Store the callbacks in refs to avoid re-rendering the component when the callbacks change.
|
|
61
|
+
const onReady = useRef<(() => void) | undefined>(props.onReady ?? undefined);
|
|
62
|
+
const onError = useRef<((errorMsg: string) => void) | undefined>(props.onError ?? undefined);
|
|
63
|
+
|
|
64
|
+
useShouldNotChangeOften(data, () => {
|
|
65
|
+
console.warn(
|
|
66
|
+
`[FoxgloveViewer] "data" is changing too frequently. This may cause performance issues, consider memoizing this value.`,
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
useShouldNotChangeOften(layoutData, () => {
|
|
71
|
+
console.warn(
|
|
72
|
+
`[FoxgloveViewer] "layoutData" is changing too frequently. This may cause performance issues, consider memoizing this value.`,
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
useShouldNotChangeOften(extensions, () => {
|
|
77
|
+
console.warn(
|
|
78
|
+
`[FoxgloveViewer] "extensions" is changing too frequently. This may cause performance issues, consider memoizing this value.`,
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Update onReady and onError refs when the props change.
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
onReady.current = props.onReady;
|
|
85
|
+
}, [props.onReady]);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
onError.current = props.onError;
|
|
88
|
+
}, [props.onError]);
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!elem.current) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const handleReady = () => {
|
|
96
|
+
onReady.current?.();
|
|
97
|
+
};
|
|
98
|
+
const handleError = (errorMsg: CustomEvent<string>) => {
|
|
99
|
+
onError.current?.(errorMsg.detail);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
frame.current = new FoxgloveViewerClass({
|
|
103
|
+
parent: elem.current,
|
|
104
|
+
src,
|
|
105
|
+
orgSlug,
|
|
106
|
+
});
|
|
107
|
+
frame.current.addEventListener("ready", handleReady);
|
|
108
|
+
frame.current.addEventListener("error", handleError);
|
|
109
|
+
|
|
110
|
+
return () => {
|
|
111
|
+
frame.current?.removeEventListener("ready", handleReady);
|
|
112
|
+
frame.current?.removeEventListener("error", handleError);
|
|
113
|
+
frame.current?.destroy();
|
|
114
|
+
};
|
|
115
|
+
}, [src, orgSlug]);
|
|
116
|
+
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (data == undefined || frame.current == undefined) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
frame.current.setDataSource(data);
|
|
123
|
+
}, [data]);
|
|
124
|
+
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (layoutData == undefined || frame.current == undefined) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
frame.current.setLayoutData(layoutData);
|
|
131
|
+
}, [layoutData]);
|
|
132
|
+
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
if (extensions == undefined || frame.current == undefined) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
frame.current.installExtensions(extensions);
|
|
139
|
+
}, [extensions]);
|
|
140
|
+
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (colorScheme == undefined || frame.current == undefined) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
frame.current.setColorScheme(colorScheme);
|
|
147
|
+
}, [colorScheme]);
|
|
148
|
+
|
|
149
|
+
return <div ref={elem} style={style} className={className} />;
|
|
150
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./FoxgloveViewer";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// This file is copied from @foxglove/viz/hooks/useShouldNotChangeOften.ts
|
|
2
|
+
// We copy the file to avoid depending on the viz package.
|
|
3
|
+
|
|
4
|
+
import { useRef } from "react";
|
|
5
|
+
|
|
6
|
+
/** Call _warn_ if a given value changes twice within 200 ms */
|
|
7
|
+
export function useShouldNotChangeOften<T>(value: T, warn: () => void): T {
|
|
8
|
+
const prev = useRef(value);
|
|
9
|
+
const prevPrev = useRef(value);
|
|
10
|
+
const lastTime = useRef<number>(Date.now());
|
|
11
|
+
if (
|
|
12
|
+
value !== prev.current &&
|
|
13
|
+
prev.current !== prevPrev.current &&
|
|
14
|
+
Date.now() - lastTime.current < 200
|
|
15
|
+
) {
|
|
16
|
+
warn();
|
|
17
|
+
}
|
|
18
|
+
prevPrev.current = prev.current;
|
|
19
|
+
prev.current = value;
|
|
20
|
+
lastTime.current = Date.now();
|
|
21
|
+
return value;
|
|
22
|
+
}
|