Using it in a framework
The renderer is framework-free: it needs an <svg> element in the page and a Well. In a component framework the pattern is always the same:
- Render an empty
<svg>with a unique id. - After mount, import the package, create the renderer, call
prepareSvg()anddraw(). - Redraw when the well changes; rebuild when the size or theme changes.
- Never run it during server-side rendering.
Vue 3
<script setup lang="ts">
import type { Well } from '@welldot/core';
import type { WellRenderer } from '@welldot/render';
import { onMounted, ref, useId, watch } from 'vue';
const props = defineProps<{ well: Well; height?: number }>();
const id = `well-${useId()}`;
const container = ref<HTMLElement | null>(null);
let renderer: WellRenderer | null = null;
async function build() {
const { WellRenderer, INTERACTIVE_RENDER_CONFIG, applyRenderLocale } =
await import('@welldot/render');
const margins = { top: 24, right: 16, bottom: 20, left: 48 };
const width =
(container.value?.clientWidth ?? 400) - margins.left - margins.right;
renderer = new WellRenderer(
[{ selector: `#${id}`, width, height: props.height ?? 600, margins }],
{
locale: 'en',
renderConfig: {
...applyRenderLocale(INTERACTIVE_RENDER_CONFIG, 'en'),
zoom: false,
pan: false,
},
onError: e => console.error(e),
},
);
await renderer.prepareSvg();
renderer.draw(props.well);
}
onMounted(build);
watch(
() => props.well,
well => renderer?.draw(well),
{ deep: true },
);
</script>
<template>
<div ref="container">
<svg :id="id" />
</div>
</template>
onMounted never runs on the server, so this component is safe in Nuxt as it is. The dynamic import() keeps D3 out of the server bundle. In Nuxt, wrapping it in <ClientOnly> also avoids a hydration warning for the empty SVG.
React
import type { Well } from '@welldot/core';
import type { WellRenderer } from '@welldot/render';
import { useEffect, useId, useRef } from 'react';
export function WellProfile({
well,
height = 600,
}: {
well: Well;
height?: number;
}) {
const id = `well-${useId().replace(/:/g, '')}`;
const container = useRef<HTMLDivElement>(null);
const renderer = useRef<WellRenderer | null>(null);
useEffect(() => {
let cancelled = false;
(async () => {
const { WellRenderer, INTERACTIVE_RENDER_CONFIG, applyRenderLocale } =
await import('@welldot/render');
if (cancelled) return;
const margins = { top: 24, right: 16, bottom: 20, left: 48 };
const width =
(container.current?.clientWidth ?? 400) - margins.left - margins.right;
const r = new WellRenderer(
[{ selector: `#${id}`, width, height, margins }],
{
locale: 'en',
renderConfig: {
...applyRenderLocale(INTERACTIVE_RENDER_CONFIG, 'en'),
zoom: false,
pan: false,
},
},
);
await r.prepareSvg();
renderer.current = r;
r.draw(well);
})();
return () => {
cancelled = true;
renderer.current?.clear();
};
}, [id, height]);
useEffect(() => {
renderer.current?.draw(well);
}, [well]);
return (
<div ref={container}>
<svg id={id} />
</div>
);
}
In Next.js, mark the file 'use client'. React's useId returns ids with colons, which are not valid in a CSS selector, hence the replace.
Resizing and themes
The renderer draws at the size it was created with. To follow the container, observe it and rebuild after a short pause; to follow a dark mode switch, rebuild with the other theme (Theming). Rebuilding is cheap: create the new renderer, prepareSvg(), draw().
Validation first
Parse with parseWell before handing data to the renderer, and show the errors when it fails, as the live previews on this site do. The renderer expects a valid Well; it reports drawing problems through onError but does not validate.