Skip to main content

Wikidata Explorer

Example demonstrating large graph exploration capabilities on public Wikidata dataset.

/src/examples/PlaygroundWikidata.tsx
import * as React from 'react';
import * as Reactodia from '@reactodia/workspace';

import { ExampleToolbarMenu } from './ExampleCommon';

const Layouts = Reactodia.defineLayoutWorker(() => new Worker(
new URL('@reactodia/workspace/layout.worker', import.meta.url)
));

export function PlaygroundWikidata() {
const {defaultLayout} = Reactodia.useWorker(Layouts);

const {onMount} = Reactodia.useLoadedWorkspace(async ({context, signal}) => {
const {model, getCommandBus} = context;

const sparqlProvider = new Reactodia.SparqlDataProvider(
{
endpointUrl: 'https://query.wikidata.org/sparql',
imagePropertyUris: [
'http://www.wikidata.org/prop/direct/P18',
'http://www.wikidata.org/prop/direct/P154',
]
},
{
...Reactodia.WikidataSettings,
filterOnlyLanguages: ['de', 'en', 'es', 'ru', 'zh'],
// Public Wikidata endpoint is too overloaded for the connection statistics
linkTypesStatisticsQuery: '',
});

const dataProvider = new Reactodia.IndexedDbCachedProvider({
baseProvider: sparqlProvider,
dbName: 'reactodia-wikidata-cache',
closeSignal: signal,
});

await model.importLayout({ dataProvider, signal });

getCommandBus(Reactodia.UnifiedSearchTopic)
.trigger('focus', {sectionKey: 'entities'});
}, []);

const suggestProperties = React.useCallback<Reactodia.PropertySuggestionHandler>(params => {
const scores = params.properties.map((iri, index): Reactodia.PropertyScore => {
// Assumption is smaller P-properties were created earlier and are more interesting
const match = /P([0-9]+)$/.exec(iri);
return {
propertyIri: iri,
score: match ? -Number(match[1]) : (params.properties.length - index),
};
});
return Promise.resolve(scores);
}, []);

return (
<Reactodia.Workspace ref={onMount}
defaultLayout={defaultLayout}>
<Reactodia.DefaultWorkspace
menu={
<>
<ExampleToolbarMenu />
<ClearWikidataCacheAction />
</>
}
connectionsMenu={{suggestProperties}}
languages={[
{ code: 'de', label: 'Deutsch' },
{ code: 'en', label: 'english' },
{ code: 'es', label: 'español' },
{ code: 'ru', label: 'русский' },
{ code: 'zh', label: '汉语' },
]}
/>
</Reactodia.Workspace>
);
}

function ClearWikidataCacheAction() {
const {model} = Reactodia.useWorkspace();
return (
<Reactodia.ToolbarAction
title='Clear locally-cached data previously fetched from Wikidata'
onSelect={() => {
const {dataProvider} = model;
if (dataProvider instanceof Reactodia.IndexedDbCachedProvider) {
dataProvider.clearCache();
}
}}>
Clear Wikidata cache
</Reactodia.ToolbarAction>
);
}

ExampleCommon.tsx
/src/examples/ExampleCommon.tsx
import * as React from 'react';
import * as Reactodia from '@reactodia/workspace';
import { saveAs } from 'file-saver';

export function ExampleToolbarMenu() {
const {model, editor, overlay} = Reactodia.useWorkspace();
return (
<>
<Reactodia.ToolbarActionOpen
fileAccept='.json'
onSelect={async file => {
const preloadedElements = new Map<Reactodia.ElementIri, Reactodia.ElementModel>();
for (const element of model.elements) {
for (const data of Reactodia.iterateEntitiesOf(element)) {
preloadedElements.set(data.id, data);
}
}

const task = overlay.startTask({title: 'Importing a layout from file'});
try {
const json = await file.text();
const diagramLayout = JSON.parse(json);
await model.importLayout({
dataProvider: model.dataProvider,
diagram: diagramLayout,
preloadedElements,
validateLinks: true,
});
} catch (err) {
task.setError(new Error(
'Failed to load specified file with a diagram layout.',
{cause: err}
));
} finally {
task.end();
}
}}>
Open diagram from file
</Reactodia.ToolbarActionOpen>
<Reactodia.ToolbarActionSave mode='layout'
onSelect={() => {
const diagramLayout = model.exportLayout();
const layoutString = JSON.stringify(diagramLayout);
const blob = new Blob([layoutString], {type: 'application/json'});
const timestamp = new Date().toISOString().replaceAll(/[Z\s:-]/g, '');
saveAs(blob, `reactodia-diagram-${timestamp}.json`);
}}>
Save diagram to file
</Reactodia.ToolbarActionSave>
{editor.inAuthoringMode ? (
<Reactodia.ToolbarActionSave mode='authoring'
onSelect={() => {
const state = editor.authoringState;
console.log('Authoring state:', state);
alert('Please check browser console for result');
}}>
Persist changes to data
</Reactodia.ToolbarActionSave>
) : null}
<Reactodia.ToolbarActionClearAll />
<Reactodia.ToolbarActionExport kind='exportRaster' />
<Reactodia.ToolbarActionExport kind='exportSvg' />
<Reactodia.ToolbarActionExport kind='print' />
</>
);
}