@imfusion/sdk-react 0.7.0-dev.31.g0ba92924
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 +51 -0
- package/dist/ImFusionCanvas.d.ts +26 -0
- package/dist/ImFusionCanvas.js +51 -0
- package/dist/ImFusionError.d.ts +37 -0
- package/dist/ImFusionError.js +41 -0
- package/dist/ImFusionLoading.d.ts +25 -0
- package/dist/ImFusionLoading.js +30 -0
- package/dist/ImFusionProvider.d.ts +56 -0
- package/dist/ImFusionProvider.js +88 -0
- package/dist/ImFusionReady.d.ts +25 -0
- package/dist/ImFusionReady.js +30 -0
- package/dist/context.d.ts +14 -0
- package/dist/context.js +3 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +17 -0
- package/dist/useAnnotations.d.ts +33 -0
- package/dist/useAnnotations.js +67 -0
- package/dist/useContextMenu.d.ts +21 -0
- package/dist/useContextMenu.js +30 -0
- package/dist/useData.d.ts +26 -0
- package/dist/useData.js +40 -0
- package/dist/useDataLoader.d.ts +50 -0
- package/dist/useDataLoader.js +70 -0
- package/dist/useDisplayOptions.d.ts +59 -0
- package/dist/useDisplayOptions.js +87 -0
- package/dist/useFrameSelection.d.ts +32 -0
- package/dist/useFrameSelection.js +43 -0
- package/dist/useImFusion.d.ts +22 -0
- package/dist/useImFusion.js +32 -0
- package/dist/useImFusionError.d.ts +21 -0
- package/dist/useImFusionError.js +32 -0
- package/dist/useImFusionState.d.ts +31 -0
- package/dist/useImFusionState.js +32 -0
- package/dist/useLayoutMode.d.ts +29 -0
- package/dist/useLayoutMode.js +38 -0
- package/dist/useStableHandle.d.ts +15 -0
- package/dist/useStableHandle.js +12 -0
- package/dist/useViewVisibility.d.ts +30 -0
- package/dist/useViewVisibility.js +45 -0
- package/dist/useVisibleData.d.ts +21 -0
- package/dist/useVisibleData.js +32 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# ImFusion Web SDK React Bindings
|
|
2
|
+
React components and hooks for integrating the ImFusion Web SDK into React applications.
|
|
3
|
+
|
|
4
|
+
## Getting Started
|
|
5
|
+
|
|
6
|
+
Wrap your app with `ImFusionProvider`, add a canvas, and use the lifecycle components:
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import {
|
|
10
|
+
ImFusionProvider,
|
|
11
|
+
ImFusionCanvas,
|
|
12
|
+
ImFusionReady,
|
|
13
|
+
ImFusionLoading,
|
|
14
|
+
ImFusionError,
|
|
15
|
+
} from '@imfusion/sdk-react';
|
|
16
|
+
|
|
17
|
+
function App() {
|
|
18
|
+
return (
|
|
19
|
+
<ImFusionProvider>
|
|
20
|
+
<ImFusionCanvas style={{ width: '100%', height: '100%' }} />
|
|
21
|
+
<ImFusionLoading>Initializing SDK...</ImFusionLoading>
|
|
22
|
+
<ImFusionError>{error => <p>Failed: {error.message}</p>}</ImFusionError>
|
|
23
|
+
<ImFusionReady>
|
|
24
|
+
<Viewer />
|
|
25
|
+
</ImFusionReady>
|
|
26
|
+
</ImFusionProvider>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Inside `ImFusionReady`, use hooks to interact with the SDK:
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { useImFusion, useDataLoader, useData } from '@imfusion/sdk-react';
|
|
35
|
+
|
|
36
|
+
function Viewer() {
|
|
37
|
+
const imf = useImFusion();
|
|
38
|
+
const { loadUrl, isLoading, progress } = useDataLoader();
|
|
39
|
+
const datasets = useData();
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div>
|
|
43
|
+
<button onClick={() => loadUrl('/data/scan.nii.gz')} disabled={isLoading}>
|
|
44
|
+
{isLoading ? 'Loading...' : 'Load'}
|
|
45
|
+
</button>
|
|
46
|
+
<p>{datasets.length} dataset(s) loaded</p>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type CanvasHTMLAttributes } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Renders a canvas element and registers it with the {@link ImFusionProvider}.
|
|
4
|
+
*
|
|
5
|
+
* When this component mounts, it triggers SDK initialization by registering
|
|
6
|
+
* the canvas element with the parent provider. All standard HTML canvas
|
|
7
|
+
* attributes are passed through to the underlying `<canvas>` element.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <ImFusionProvider>
|
|
12
|
+
* <ImFusionCanvas
|
|
13
|
+
* style={{ width: '100%', height: '100vh' }}
|
|
14
|
+
* className="viewer-canvas"
|
|
15
|
+
* />
|
|
16
|
+
* </ImFusionProvider>
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* // Accessing the canvas element via ref
|
|
22
|
+
* const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
23
|
+
* <ImFusionCanvas ref={canvasRef} width={800} height={600} />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare const ImFusionCanvas: import("react").ForwardRefExoticComponent<CanvasHTMLAttributes<HTMLCanvasElement> & import("react").RefAttributes<HTMLCanvasElement>>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useContext, useEffect, useRef, forwardRef } from 'react';
|
|
3
|
+
import { ImFusionContext } from './context.js';
|
|
4
|
+
/**
|
|
5
|
+
* Renders a canvas element and registers it with the {@link ImFusionProvider}.
|
|
6
|
+
*
|
|
7
|
+
* When this component mounts, it triggers SDK initialization by registering
|
|
8
|
+
* the canvas element with the parent provider. All standard HTML canvas
|
|
9
|
+
* attributes are passed through to the underlying `<canvas>` element.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* <ImFusionProvider>
|
|
14
|
+
* <ImFusionCanvas
|
|
15
|
+
* style={{ width: '100%', height: '100vh' }}
|
|
16
|
+
* className="viewer-canvas"
|
|
17
|
+
* />
|
|
18
|
+
* </ImFusionProvider>
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* // Accessing the canvas element via ref
|
|
24
|
+
* const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
25
|
+
* <ImFusionCanvas ref={canvasRef} width={800} height={600} />
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export const ImFusionCanvas = forwardRef(function ImFusionCanvas(props, forwardedRef) {
|
|
29
|
+
const context = useContext(ImFusionContext);
|
|
30
|
+
const internalRef = useRef(null);
|
|
31
|
+
if (!context) {
|
|
32
|
+
throw new Error('ImFusionCanvas must be used within an ImFusionProvider');
|
|
33
|
+
}
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const canvas = internalRef.current;
|
|
36
|
+
if (canvas) {
|
|
37
|
+
context.registerCanvas(canvas);
|
|
38
|
+
}
|
|
39
|
+
}, [context.registerCanvas]);
|
|
40
|
+
// Merge the internal ref with the forwarded ref
|
|
41
|
+
const setRefs = (element) => {
|
|
42
|
+
internalRef.current = element;
|
|
43
|
+
if (typeof forwardedRef === 'function') {
|
|
44
|
+
forwardedRef(element);
|
|
45
|
+
}
|
|
46
|
+
else if (forwardedRef) {
|
|
47
|
+
forwardedRef.current = element;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
return _jsx("canvas", { ref: setRefs, ...props });
|
|
51
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface ImFusionErrorProps {
|
|
3
|
+
/** Content to render when an initialization error has occurred. */
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Renders children when SDK initialization has failed.
|
|
8
|
+
*
|
|
9
|
+
* Use {@link useImFusionError} inside children to access the error object.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* <ImFusionProvider>
|
|
14
|
+
* <ImFusionCanvas />
|
|
15
|
+
* <ImFusionLoading>
|
|
16
|
+
* <Spinner />
|
|
17
|
+
* </ImFusionLoading>
|
|
18
|
+
* <ImFusionError>
|
|
19
|
+
* <ErrorDisplay />
|
|
20
|
+
* </ImFusionError>
|
|
21
|
+
* <ImFusionReady>
|
|
22
|
+
* <App />
|
|
23
|
+
* </ImFusionReady>
|
|
24
|
+
* </ImFusionProvider>
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* ```tsx
|
|
28
|
+
* function ErrorDisplay() {
|
|
29
|
+
* const error = useImFusionError();
|
|
30
|
+
* return <p>Failed to initialize: {error.message}</p>;
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @see {@link useImFusionError}
|
|
35
|
+
* @see {@link useImFusionState}
|
|
36
|
+
*/
|
|
37
|
+
export declare function ImFusionError({ children }: ImFusionErrorProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { ImFusionContext } from './context.js';
|
|
4
|
+
/**
|
|
5
|
+
* Renders children when SDK initialization has failed.
|
|
6
|
+
*
|
|
7
|
+
* Use {@link useImFusionError} inside children to access the error object.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <ImFusionProvider>
|
|
12
|
+
* <ImFusionCanvas />
|
|
13
|
+
* <ImFusionLoading>
|
|
14
|
+
* <Spinner />
|
|
15
|
+
* </ImFusionLoading>
|
|
16
|
+
* <ImFusionError>
|
|
17
|
+
* <ErrorDisplay />
|
|
18
|
+
* </ImFusionError>
|
|
19
|
+
* <ImFusionReady>
|
|
20
|
+
* <App />
|
|
21
|
+
* </ImFusionReady>
|
|
22
|
+
* </ImFusionProvider>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* function ErrorDisplay() {
|
|
27
|
+
* const error = useImFusionError();
|
|
28
|
+
* return <p>Failed to initialize: {error.message}</p>;
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @see {@link useImFusionError}
|
|
33
|
+
* @see {@link useImFusionState}
|
|
34
|
+
*/
|
|
35
|
+
export function ImFusionError({ children }) {
|
|
36
|
+
const context = useContext(ImFusionContext);
|
|
37
|
+
if (!context) {
|
|
38
|
+
throw new Error('ImFusionError must be used within an ImFusionProvider');
|
|
39
|
+
}
|
|
40
|
+
return context.error ? _jsx(_Fragment, { children: children }) : null;
|
|
41
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface ImFusionLoadingProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Renders children while the SDK is initializing.
|
|
7
|
+
*
|
|
8
|
+
* Automatically hides when the SDK becomes ready or when an error occurs.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <ImFusionProvider>
|
|
13
|
+
* <ImFusionCanvas />
|
|
14
|
+
* <ImFusionLoading>
|
|
15
|
+
* <div className="spinner">Loading SDK...</div>
|
|
16
|
+
* </ImFusionLoading>
|
|
17
|
+
* <ImFusionReady>
|
|
18
|
+
* <App />
|
|
19
|
+
* </ImFusionReady>
|
|
20
|
+
* </ImFusionProvider>
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @see {@link useImFusionState}
|
|
24
|
+
*/
|
|
25
|
+
export declare function ImFusionLoading({ children }: ImFusionLoadingProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { ImFusionContext } from './context.js';
|
|
4
|
+
/**
|
|
5
|
+
* Renders children while the SDK is initializing.
|
|
6
|
+
*
|
|
7
|
+
* Automatically hides when the SDK becomes ready or when an error occurs.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <ImFusionProvider>
|
|
12
|
+
* <ImFusionCanvas />
|
|
13
|
+
* <ImFusionLoading>
|
|
14
|
+
* <div className="spinner">Loading SDK...</div>
|
|
15
|
+
* </ImFusionLoading>
|
|
16
|
+
* <ImFusionReady>
|
|
17
|
+
* <App />
|
|
18
|
+
* </ImFusionReady>
|
|
19
|
+
* </ImFusionProvider>
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @see {@link useImFusionState}
|
|
23
|
+
*/
|
|
24
|
+
export function ImFusionLoading({ children }) {
|
|
25
|
+
const context = useContext(ImFusionContext);
|
|
26
|
+
if (!context) {
|
|
27
|
+
throw new Error('ImFusionLoading must be used within an ImFusionProvider');
|
|
28
|
+
}
|
|
29
|
+
return !context.imf && !context.error ? _jsx(_Fragment, { children: children }) : null;
|
|
30
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type ImFusion, type InitOptions } from '@imfusion/sdk';
|
|
3
|
+
export interface ImFusionProviderProps {
|
|
4
|
+
/**
|
|
5
|
+
* Options passed to {@link @imfusion/sdk!init | init} when the SDK is initialized.
|
|
6
|
+
*
|
|
7
|
+
* @see {@link @imfusion/sdk!InitOptions | InitOptions}
|
|
8
|
+
*/
|
|
9
|
+
options?: InitOptions;
|
|
10
|
+
/**
|
|
11
|
+
* Called after the SDK has been initialized successfully.
|
|
12
|
+
*/
|
|
13
|
+
onReady?: (imf: ImFusion) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Called if SDK initialization fails.
|
|
16
|
+
*/
|
|
17
|
+
onError?: (error: Error) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Content that should have access to the ImFusion SDK.
|
|
20
|
+
*
|
|
21
|
+
* Render an {@link ImFusionCanvas} somewhere inside this provider so the SDK
|
|
22
|
+
* has a canvas to initialize against.
|
|
23
|
+
*/
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Initializes the ImFusion SDK and provides it to descendant components.
|
|
28
|
+
*
|
|
29
|
+
* Place an {@link ImFusionCanvas} inside this provider to trigger initialization.
|
|
30
|
+
* Children are rendered immediately; use the state components to show different
|
|
31
|
+
* UI for each initialization phase.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <ImFusionProvider options={{ autoResize: true }}>
|
|
36
|
+
* <ImFusionCanvas style={{ width: '100%', height: '100vh' }} />
|
|
37
|
+
*
|
|
38
|
+
* <ImFusionLoading>
|
|
39
|
+
* <Spinner />
|
|
40
|
+
* </ImFusionLoading>
|
|
41
|
+
*
|
|
42
|
+
* <ImFusionError>
|
|
43
|
+
* <ErrorMessage />
|
|
44
|
+
* </ImFusionError>
|
|
45
|
+
*
|
|
46
|
+
* <ImFusionReady>
|
|
47
|
+
* <MyApp />
|
|
48
|
+
* </ImFusionReady>
|
|
49
|
+
* </ImFusionProvider>
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @remarks
|
|
53
|
+
* The SDK is initialized once per provider instance. Changes to `options` do
|
|
54
|
+
* not reconfigure an already initialized SDK.
|
|
55
|
+
*/
|
|
56
|
+
export declare function ImFusionProvider({ options, onReady, onError, children }: ImFusionProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
3
|
+
import { init } from '@imfusion/sdk';
|
|
4
|
+
import { ImFusionContext } from './context.js';
|
|
5
|
+
/**
|
|
6
|
+
* Initializes the ImFusion SDK and provides it to descendant components.
|
|
7
|
+
*
|
|
8
|
+
* Place an {@link ImFusionCanvas} inside this provider to trigger initialization.
|
|
9
|
+
* Children are rendered immediately; use the state components to show different
|
|
10
|
+
* UI for each initialization phase.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <ImFusionProvider options={{ autoResize: true }}>
|
|
15
|
+
* <ImFusionCanvas style={{ width: '100%', height: '100vh' }} />
|
|
16
|
+
*
|
|
17
|
+
* <ImFusionLoading>
|
|
18
|
+
* <Spinner />
|
|
19
|
+
* </ImFusionLoading>
|
|
20
|
+
*
|
|
21
|
+
* <ImFusionError>
|
|
22
|
+
* <ErrorMessage />
|
|
23
|
+
* </ImFusionError>
|
|
24
|
+
*
|
|
25
|
+
* <ImFusionReady>
|
|
26
|
+
* <MyApp />
|
|
27
|
+
* </ImFusionReady>
|
|
28
|
+
* </ImFusionProvider>
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* The SDK is initialized once per provider instance. Changes to `options` do
|
|
33
|
+
* not reconfigure an already initialized SDK.
|
|
34
|
+
*/
|
|
35
|
+
export function ImFusionProvider({ options, onReady, onError, children }) {
|
|
36
|
+
const [canvas, setCanvas] = useState(null);
|
|
37
|
+
const [imf, setImf] = useState(null);
|
|
38
|
+
const [error, setError] = useState(null);
|
|
39
|
+
// Capture init promise to prevent double WASM creation in StrictMode
|
|
40
|
+
const initPromiseRef = useRef(null);
|
|
41
|
+
// Capture the first canvas; warn if a different one tries to register
|
|
42
|
+
const initialCanvasRef = useRef(null);
|
|
43
|
+
// Capture initial options only (never updated)
|
|
44
|
+
const optionsRef = useRef(options);
|
|
45
|
+
const onReadyRef = useRef(onReady);
|
|
46
|
+
const onErrorRef = useRef(onError);
|
|
47
|
+
onReadyRef.current = onReady;
|
|
48
|
+
onErrorRef.current = onError;
|
|
49
|
+
const registerCanvas = useCallback((canvasElement) => {
|
|
50
|
+
if (initialCanvasRef.current) {
|
|
51
|
+
if (initialCanvasRef.current !== canvasElement) {
|
|
52
|
+
console.warn('ImFusionProvider: A different canvas element was registered after SDK initialization. ' +
|
|
53
|
+
'The SDK remains bound to the original canvas.');
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
initialCanvasRef.current = canvasElement;
|
|
58
|
+
setCanvas(canvasElement);
|
|
59
|
+
}, []);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!canvas)
|
|
62
|
+
return;
|
|
63
|
+
let cancelled = false;
|
|
64
|
+
if (!initPromiseRef.current) {
|
|
65
|
+
initPromiseRef.current = init(canvas, optionsRef.current);
|
|
66
|
+
}
|
|
67
|
+
initPromiseRef.current
|
|
68
|
+
.then((instance) => {
|
|
69
|
+
if (!cancelled) {
|
|
70
|
+
setImf(instance);
|
|
71
|
+
onReadyRef.current?.(instance);
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
.catch((err) => {
|
|
75
|
+
if (!cancelled) {
|
|
76
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
77
|
+
setError(error);
|
|
78
|
+
onErrorRef.current?.(error);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return () => {
|
|
82
|
+
cancelled = true;
|
|
83
|
+
initPromiseRef.current?.then((instance) => instance.destroy());
|
|
84
|
+
};
|
|
85
|
+
}, [canvas]);
|
|
86
|
+
const contextValue = useMemo(() => ({ imf, error, registerCanvas }), [imf, error, registerCanvas]);
|
|
87
|
+
return _jsx(ImFusionContext.Provider, { value: contextValue, children: children });
|
|
88
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface ImFusionReadyProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Renders children only after the SDK has successfully initialized.
|
|
7
|
+
*
|
|
8
|
+
* Components inside can safely call {@link useImFusion}. Multiple instances
|
|
9
|
+
* can be placed independently throughout the tree.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* <ImFusionProvider>
|
|
14
|
+
* <ImFusionCanvas />
|
|
15
|
+
* <ImFusionReady>
|
|
16
|
+
* <Sidebar />
|
|
17
|
+
* <Toolbar />
|
|
18
|
+
* </ImFusionReady>
|
|
19
|
+
* </ImFusionProvider>
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @see {@link useImFusion}
|
|
23
|
+
* @see {@link useImFusionState}
|
|
24
|
+
*/
|
|
25
|
+
export declare function ImFusionReady({ children }: ImFusionReadyProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { ImFusionContext } from './context.js';
|
|
4
|
+
/**
|
|
5
|
+
* Renders children only after the SDK has successfully initialized.
|
|
6
|
+
*
|
|
7
|
+
* Components inside can safely call {@link useImFusion}. Multiple instances
|
|
8
|
+
* can be placed independently throughout the tree.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <ImFusionProvider>
|
|
13
|
+
* <ImFusionCanvas />
|
|
14
|
+
* <ImFusionReady>
|
|
15
|
+
* <Sidebar />
|
|
16
|
+
* <Toolbar />
|
|
17
|
+
* </ImFusionReady>
|
|
18
|
+
* </ImFusionProvider>
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @see {@link useImFusion}
|
|
22
|
+
* @see {@link useImFusionState}
|
|
23
|
+
*/
|
|
24
|
+
export function ImFusionReady({ children }) {
|
|
25
|
+
const context = useContext(ImFusionContext);
|
|
26
|
+
if (!context) {
|
|
27
|
+
throw new Error('ImFusionReady must be used within an ImFusionProvider');
|
|
28
|
+
}
|
|
29
|
+
return context.imf ? _jsx(_Fragment, { children: children }) : null;
|
|
30
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ImFusion } from '@imfusion/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Internal context value shared between provider, canvas, and hooks.
|
|
4
|
+
*
|
|
5
|
+
* @internal Use {@link useImFusion},
|
|
6
|
+
* {@link useImFusionError}, or {@link useImFusionState} instead.
|
|
7
|
+
*/
|
|
8
|
+
export interface ImFusionContextValue {
|
|
9
|
+
imf: ImFusion | null;
|
|
10
|
+
error: Error | null;
|
|
11
|
+
registerCanvas: (canvas: HTMLCanvasElement) => void;
|
|
12
|
+
}
|
|
13
|
+
/** @internal */
|
|
14
|
+
export declare const ImFusionContext: import("react").Context<ImFusionContextValue | null>;
|
package/dist/context.js
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { ImFusionProvider, type ImFusionProviderProps } from './ImFusionProvider.js';
|
|
2
|
+
export { ImFusionCanvas } from './ImFusionCanvas.js';
|
|
3
|
+
export { ImFusionReady, type ImFusionReadyProps } from './ImFusionReady.js';
|
|
4
|
+
export { ImFusionLoading, type ImFusionLoadingProps } from './ImFusionLoading.js';
|
|
5
|
+
export { ImFusionError, type ImFusionErrorProps } from './ImFusionError.js';
|
|
6
|
+
export { useImFusion } from './useImFusion.js';
|
|
7
|
+
export { useImFusionError } from './useImFusionError.js';
|
|
8
|
+
export { useImFusionState, type ImFusionState } from './useImFusionState.js';
|
|
9
|
+
export { useData } from './useData.js';
|
|
10
|
+
export { useVisibleData } from './useVisibleData.js';
|
|
11
|
+
export { useDataLoader, type DataLoaderProgress } from './useDataLoader.js';
|
|
12
|
+
export { useAnnotations } from './useAnnotations.js';
|
|
13
|
+
export { useLayoutMode } from './useLayoutMode.js';
|
|
14
|
+
export { useViewVisibility } from './useViewVisibility.js';
|
|
15
|
+
export { useDisplayOptions2d, useDisplayOptions3d } from './useDisplayOptions.js';
|
|
16
|
+
export { useFrameSelection } from './useFrameSelection.js';
|
|
17
|
+
export { useContextMenu } from './useContextMenu.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { ImFusionProvider } from './ImFusionProvider.js';
|
|
2
|
+
export { ImFusionCanvas } from './ImFusionCanvas.js';
|
|
3
|
+
export { ImFusionReady } from './ImFusionReady.js';
|
|
4
|
+
export { ImFusionLoading } from './ImFusionLoading.js';
|
|
5
|
+
export { ImFusionError } from './ImFusionError.js';
|
|
6
|
+
export { useImFusion } from './useImFusion.js';
|
|
7
|
+
export { useImFusionError } from './useImFusionError.js';
|
|
8
|
+
export { useImFusionState } from './useImFusionState.js';
|
|
9
|
+
export { useData } from './useData.js';
|
|
10
|
+
export { useVisibleData } from './useVisibleData.js';
|
|
11
|
+
export { useDataLoader } from './useDataLoader.js';
|
|
12
|
+
export { useAnnotations } from './useAnnotations.js';
|
|
13
|
+
export { useLayoutMode } from './useLayoutMode.js';
|
|
14
|
+
export { useViewVisibility } from './useViewVisibility.js';
|
|
15
|
+
export { useDisplayOptions2d, useDisplayOptions3d } from './useDisplayOptions.js';
|
|
16
|
+
export { useFrameSelection } from './useFrameSelection.js';
|
|
17
|
+
export { useContextMenu } from './useContextMenu.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Annotation, Data, HandleArray } from '@imfusion/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a reactive list of annotations, optionally filtered by dataset.
|
|
4
|
+
*
|
|
5
|
+
* Subscribes to {@link @imfusion/sdk!AnnotationModel.onAnnotationAdded | AnnotationModel.onAnnotationAdded},
|
|
6
|
+
* {@link @imfusion/sdk!AnnotationModel.onAnnotationAboutToBeRemoved | AnnotationModel.onAnnotationAboutToBeRemoved}, and
|
|
7
|
+
* {@link @imfusion/sdk!AnnotationModel.onAnnotationEvent | AnnotationModel.onAnnotationEvent}.
|
|
8
|
+
*
|
|
9
|
+
* When `data` is provided, only annotations belonging to that dataset are
|
|
10
|
+
* returned.
|
|
11
|
+
*
|
|
12
|
+
* @param data - Optional dataset to filter annotations by
|
|
13
|
+
* @returns A {@link @imfusion/sdk!HandleArray | HandleArray} of annotations
|
|
14
|
+
* @throws If called outside of an {@link ImFusionProvider}
|
|
15
|
+
* @throws If called before the SDK has finished initializing
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* function AnnotationList() {
|
|
20
|
+
* const annotations = useAnnotations();
|
|
21
|
+
* return <p>Total annotations: {annotations.length}</p>;
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* function DataAnnotations({ data }: { data: Data }) {
|
|
28
|
+
* const annotations = useAnnotations(data);
|
|
29
|
+
* return <p>Annotations on {data.name}: {annotations.length}</p>;
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function useAnnotations(data?: Data): HandleArray<Annotation>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { useImFusion } from './useImFusion.js';
|
|
3
|
+
import { useStableHandle } from './useStableHandle.js';
|
|
4
|
+
/**
|
|
5
|
+
* Returns a reactive list of annotations, optionally filtered by dataset.
|
|
6
|
+
*
|
|
7
|
+
* Subscribes to {@link @imfusion/sdk!AnnotationModel.onAnnotationAdded | AnnotationModel.onAnnotationAdded},
|
|
8
|
+
* {@link @imfusion/sdk!AnnotationModel.onAnnotationAboutToBeRemoved | AnnotationModel.onAnnotationAboutToBeRemoved}, and
|
|
9
|
+
* {@link @imfusion/sdk!AnnotationModel.onAnnotationEvent | AnnotationModel.onAnnotationEvent}.
|
|
10
|
+
*
|
|
11
|
+
* When `data` is provided, only annotations belonging to that dataset are
|
|
12
|
+
* returned.
|
|
13
|
+
*
|
|
14
|
+
* @param data - Optional dataset to filter annotations by
|
|
15
|
+
* @returns A {@link @imfusion/sdk!HandleArray | HandleArray} of annotations
|
|
16
|
+
* @throws If called outside of an {@link ImFusionProvider}
|
|
17
|
+
* @throws If called before the SDK has finished initializing
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* function AnnotationList() {
|
|
22
|
+
* const annotations = useAnnotations();
|
|
23
|
+
* return <p>Total annotations: {annotations.length}</p>;
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* function DataAnnotations({ data }: { data: Data }) {
|
|
30
|
+
* const annotations = useAnnotations(data);
|
|
31
|
+
* return <p>Annotations on {data.name}: {annotations.length}</p>;
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function useAnnotations(data) {
|
|
36
|
+
const imf = useImFusion();
|
|
37
|
+
const stableData = useStableHandle(data);
|
|
38
|
+
const readAnnotations = useCallback(() => {
|
|
39
|
+
return stableData ? imf.annotationModel.dataAnnotations(stableData) : imf.annotationModel.annotations();
|
|
40
|
+
}, [imf, stableData]);
|
|
41
|
+
const [annotations, setAnnotations] = useState(readAnnotations);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
setAnnotations(readAnnotations());
|
|
44
|
+
const refresh = () => {
|
|
45
|
+
setAnnotations((prev) => {
|
|
46
|
+
const next = readAnnotations();
|
|
47
|
+
if (prev.length !== next.length)
|
|
48
|
+
return next;
|
|
49
|
+
for (let i = 0; i < prev.length; i++) {
|
|
50
|
+
if (!prev[i].isAliasOf(next[i]))
|
|
51
|
+
return next;
|
|
52
|
+
}
|
|
53
|
+
return prev;
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const deferredRefresh = () => queueMicrotask(refresh);
|
|
57
|
+
const offAdded = imf.annotationModel.onAnnotationAdded(refresh);
|
|
58
|
+
const offRemoved = imf.annotationModel.onAnnotationAboutToBeRemoved(deferredRefresh);
|
|
59
|
+
const offEvent = imf.annotationModel.onAnnotationEvent(refresh);
|
|
60
|
+
return () => {
|
|
61
|
+
offAdded();
|
|
62
|
+
offRemoved();
|
|
63
|
+
offEvent();
|
|
64
|
+
};
|
|
65
|
+
}, [imf, readAnnotations]);
|
|
66
|
+
return annotations;
|
|
67
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Menu } from '@imfusion/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Registers a context menu handler that fires when the SDK produces a
|
|
4
|
+
* context menu from an input event.
|
|
5
|
+
*
|
|
6
|
+
* @param callback - Receives the {@link @imfusion/sdk!Menu | Menu} and the DOM {@link Event} that triggered it
|
|
7
|
+
* @throws If called outside of an {@link ImFusionProvider}
|
|
8
|
+
* @throws If called before the SDK has finished initializing
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* function ContextMenuHandler() {
|
|
13
|
+
* useContextMenu((menu, event) => {
|
|
14
|
+
* event.preventDefault();
|
|
15
|
+
* // Build a custom menu UI from menu.items()
|
|
16
|
+
* });
|
|
17
|
+
* return null;
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function useContextMenu(callback: (menu: Menu, event: Event) => void): void;
|