@oceanum/eidos
v0.9.5
Published
A lightweight, reactive JavaScript library for embedding and controlling EIDOS visualizations in web applications. Built with Valtio for natural object mutations and AJV for comprehensive schema validation.
Readme
EIDOS JavaScript Bindings
A lightweight, reactive JavaScript library for embedding and controlling EIDOS visualizations in web applications. Built with Valtio for natural object mutations and AJV for comprehensive schema validation.
Features
- 🚀 Natural API: Mutate EIDOS specs using standard JavaScript object assignment
- ⚡ Reactive Updates: Changes automatically propagate to the renderer via JSON patches
- 🔍 Schema Validation: Comprehensive validation using AJV with detailed error messages
- 🎯 Framework Agnostic: Works with React, Vue, Svelte, and vanilla JavaScript
- 📦 Lightweight: Small bundle size with efficient reactivity
- 🔒 Type Safe: Full TypeScript support
Quick Start
Installation
npm install @oceanum/eidosReact Usage (Recommended)
import { EidosProvider, useEidosSpec } from "@oceanum/eidos";
// Define your EIDOS specification
const initialSpec = {
version: "0.9",
id: "my-app",
name: "My Visualization",
root: {
id: "root",
nodeType: "world",
children: [],
},
data: [],
};
function App() {
return (
<EidosProvider
initialSpec={initialSpec}
options={{
renderer: "https://render.eidos.oceanum.io",
eventListener: (event) => console.log("Event:", event),
}}
>
<YourComponents />
</EidosProvider>
);
}
// In any child component
function YourComponent() {
const spec = useEidosSpec();
const addLayer = () => {
// Mutate spec directly - changes propagate automatically to iframe
spec.root.children.push({
id: "new-layer",
nodeType: "worldlayer",
layerType: "track",
});
};
return <button onClick={addLayer}>Add Layer</button>;
}Vanilla JavaScript Usage
import { render } from "@oceanum/eidos";
// Define your EIDOS specification
const spec = {
version: "0.9",
id: "my-app",
name: "My Visualization",
root: {
id: "root",
nodeType: "world",
children: [],
},
data: [],
};
// Render in a container element
const container = document.getElementById("eidos-container");
const result = await render(container, spec, {
renderer: "https://render.eidos.oceanum.io",
eventListener: (event) => {
console.log("Received event:", event);
},
});
// Mutate the spec naturally - changes propagate automatically
result.spec.name = "Updated Visualization";
result.spec.root.children.push({
id: "layer-1",
nodeType: "worldlayer",
layerType: "track",
});
// Clean up when done
result.destroy();Advanced Usage
Managing Multiple EIDOS Instances
The React Context API allows you to easily manage multiple EIDOS instances in the same application. Each EidosProvider creates its own isolated context with its own iframe and spec.
Option 1: Multiple Providers (Recommended)
import { EidosProvider, useEidosSpec } from "@oceanum/eidos";
function App() {
return (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
{/* Map view */}
<EidosProvider
initialSpec={mapSpec}
options={{ renderer: EIDOS_URL }}
>
<MapControls />
</EidosProvider>
{/* Chart view */}
<EidosProvider
initialSpec={chartSpec}
options={{ renderer: EIDOS_URL }}
>
<ChartControls />
</EidosProvider>
</div>
);
}
function MapControls() {
const spec = useEidosSpec(); // Gets mapSpec from nearest provider
// Mutations only affect this instance
const zoomIn = () => {
spec.root.viewState.zoom += 1;
};
return <button onClick={zoomIn}>Zoom In</button>;
}
function ChartControls() {
const spec = useEidosSpec(); // Gets chartSpec from nearest provider
// Completely isolated from MapControls
const updateData = () => {
spec.data[0].dataSpec = newData;
};
return <button onClick={updateData}>Update Data</button>;
}Option 2: Low-Level Render API
For more control, use the render() function directly:
import { render } from "@oceanum/eidos";
import { useEffect, useRef, useState } from "react";
function MultiInstance() {
const container1 = useRef(null);
const container2 = useRef(null);
const [specs, setSpecs] = useState({ map: null, chart: null });
useEffect(() => {
const init = async () => {
const map = await render(container1.current, mapSpec, { renderer: EIDOS_URL });
const chart = await render(container2.current, chartSpec, { renderer: EIDOS_URL });
setSpecs({ map: map.spec, chart: chart.spec });
return () => {
map.destroy();
chart.destroy();
};
};
init();
}, []);
const updateMap = () => {
if (specs.map) {
specs.map.root.viewState.zoom += 1;
}
};
return (
<div>
<div ref={container1} style={{ width: '50%', height: '100%' }} />
<div ref={container2} style={{ width: '50%', height: '100%' }} />
<button onClick={updateMap}>Update Map</button>
</div>
);
}Key Points for Multiple Instances:
- ✅ Each instance has its own iframe and spec proxy
- ✅ Changes to one instance don't affect others
- ✅
useEidosSpec()always returns the spec from the nearestEidosProviderancestor - ✅ Use unique
idvalues in your specs to avoid conflicts - ✅ Each instance can connect to the same or different renderer URLs
Framework Integration
- React Integration - Hooks, components, and patterns
- Vue.js Integration - Composition API and component examples
- Svelte Integration - Stores and reactive patterns
- Vanilla JavaScript - Pure JavaScript examples
API Reference
React Components and Hooks
<EidosProvider>
Renders an EIDOS iframe and provides the spec proxy to all child components via React Context.
Props:
initialSpec(required): The EIDOS specification objectoptions(optional): Render optionsrenderer(string): URL of the EIDOS renderer (default:https://render.eidos.oceanum.io)eventListener(function): Callback for events from the rendererauthToken(string | function): Authentication token for data fetching
containerStyle(optional): CSS styles for the iframe container (default:{ width: '100%', height: '100%', position: 'absolute' })onInitialized(optional): Callback called with the spec proxy after initialization
Example:
<EidosProvider
initialSpec={mySpec}
options={{
renderer: "https://render.eidos.oceanum.io",
eventListener: (event) => console.log(event),
}}
containerStyle={{ width: '100vw', height: '100vh' }}
onInitialized={(spec) => console.log('Ready!', spec)}
>
<YourComponents />
</EidosProvider>useEidosSpec()
Hook that returns the EIDOS spec proxy from the nearest EidosProvider ancestor.
Returns: Proxy<EidosSpec> - The spec proxy (read and mutate directly)
Throws: Error if not used within an EidosProvider
Example:
function MyComponent() {
const spec = useEidosSpec();
// Read from spec
const layerCount = spec.root.children.length;
// Mutate spec - changes sync to iframe automatically
const addLayer = () => {
spec.root.children.push({
id: 'new-layer',
nodeType: 'worldlayer',
});
};
return <button onClick={addLayer}>Add Layer ({layerCount})</button>;
}Core Functions
render(element, spec, options)
Low-level function to render EIDOS in a DOM element. Use this for vanilla JavaScript or when you need more control than EidosProvider offers.
Parameters:
element(HTMLElement): Container element for the iframespec(EidosSpec): The EIDOS specificationoptions(RenderOptions): Configuration optionsrenderer(string): Renderer URLeventListener(function): Event callbackauthToken(string | function): Auth tokenid(string): Optional override for spec ID
Returns: Promise<RenderResult>
spec: The reactive spec proxyiframe: The iframe elementupdateAuth(token): Function to update auth tokendestroy(): Cleanup function
Example:
const result = await render(container, spec, {
renderer: EIDOS_URL,
eventListener: (e) => console.log(e),
});
result.spec.data.push(newData);
// Clean up
result.destroy();Additional Documentation
- Core API - Complete API documentation
- Events - Event handling and communication
- Validation - Schema validation details
Examples
Check out the examples directory for complete working examples in different frameworks.
Contributing
This library is part of the oceanum-js project. Please see the main repository for contribution guidelines.
License
MIT License - see the main oceanum-js repository for details.
