@reactodia/workspace
Version:
Reactodia Workspace -- library for visual interaction with graphs in a form of a diagram.
183 lines (169 loc) • 6.48 kB
text/typescript
import type { Translation } from '../coreUtils/i18n';
import { ElementModel, PropertyTypeIri, isEncodedBlank } from '../data/model';
import * as Rdf from '../data/rdf/rdfModel';
import { rdfs, schema } from '../data/rdf/vocabulary';
import type { DataDiagramModel } from './dataDiagramModel';
/**
* Provides the methods to format the graph data according to the current language.
*
* @see {@link DefaultDataLocaleFormatter}
*/
export interface DataLocaleProvider {
/**
* Selects a preferred set of localized labels for an entity.
*/
selectEntityLabel(entity: ElementModel): readonly Rdf.Literal[];
/**
* Selects a preferred image URL for an entity.
*/
selectEntityImageUrl(entity: ElementModel): string | undefined;
/**
* Formats an IRI (unique identifier) for a graph content item.
*/
formatIri(iri: string): string;
/**
* Formats a graph entity label.
*
* @param entity entity to format label for
* @param language target language code
*/
formatEntityLabel(entity: ElementModel, language: string): string;
/**
* Formats a graph entity types into a list.
*
* @param entity entity to format type list for
* @param language target language code
*/
formatEntityTypeList(entity: ElementModel, language: string): string;
}
/**
* Options for {@link DefaultDataLocaleProvider}.
*/
export interface DefaultDataLocaleProviderOptions {
/**
* Provided {@link DataDiagramModel} with the diagram content.
*/
readonly model: DataDiagramModel;
/**
* Provided {@link Translation} for i18n strings and language-based selection.
*/
readonly translation: Translation;
/**
* Property IRIs to select labels from for an entity.
*
* The values of a first property in the sequence with a non-empty value set are selected.
*
* @default ["http://www.w3.org/2000/01/rdf-schema#label"]
*/
readonly labelProperties?: readonly PropertyTypeIri[];
/**
* Property IRIs to select image URL from for an entity.
*
* The first found value of a property in the sequence is selected as an entity image.
*
* @default ["http://schema.org/thumbnailUrl"]
*/
readonly imageProperties?: readonly PropertyTypeIri[];
}
/**
* Provides a default graph data locale provider implementation.
*
* The default provider uses {@link rdfs.label} and {@link schema.thumbnailUrl}
* properties to get labels and image URLs unless overridden with options.
*/
export class DefaultDataLocaleProvider implements DataLocaleProvider {
protected readonly model: DataDiagramModel;
protected readonly translation: Translation;
private readonly labelProperties: readonly PropertyTypeIri[];
private readonly imageProperties: readonly PropertyTypeIri[];
private readonly EMPTY_LABELS: readonly Rdf.Literal[] = [];
constructor(options: DefaultDataLocaleProviderOptions) {
const {
model,
translation,
labelProperties = [rdfs.label],
imageProperties = [schema.thumbnailUrl],
} = options;
this.model = model;
this.translation = translation;
this.labelProperties = labelProperties;
this.imageProperties = imageProperties;
}
selectEntityLabel(entity: ElementModel): readonly Rdf.Literal[] {
for (const property of this.labelProperties) {
if (Object.prototype.hasOwnProperty.call(entity.properties, property)) {
const values = entity.properties[property];
const literals = values ? filterInLiterals(values) : this.EMPTY_LABELS;
if (literals.length > 0) {
return literals;
}
}
}
return this.EMPTY_LABELS;
}
selectEntityImageUrl(entity: ElementModel): string | undefined {
for (const property of this.imageProperties) {
if (Object.prototype.hasOwnProperty.call(entity.properties, property)) {
const values = entity.properties[property];
if (values && values.length > 0) {
return values[0].value;
}
}
}
return undefined;
}
/**
* Formats an IRI (unique identifier) for a graph content item.
*
* **By default**:
* - usual IRIs are enclosed in `<IRI>`;
* - anonymous element IRIs displayed as `(blank node)`.
*/
formatIri(iri: string): string {
if (isEncodedBlank(iri)) {
return this.translation.text('default_data_locale.iri_blank', {
value: iri,
});
}
return this.translation.text('default_data_locale.iri', {
value: iri,
});
}
/**
* Formats a graph entity label.
*
* **By default**: uses {@link selectEntityLabel} to get entity labels and
* {@link Translation.formatLabel} to select one based on the {@link DiagramModel.language}.
*
* @param entity entity to format label for
* @param language target language code
*/
formatEntityLabel(entity: ElementModel, language: string): string {
const labels = this.selectEntityLabel(entity);
return this.translation.formatLabel(labels, entity.id, language);
}
/**
* Formats a graph entity types into a list.
*
* **By default**: returns a sorted comma-separated list of formatted type labels.
*
* @param entity entity to format label for
* @param language target language code
*/
formatEntityTypeList(entity: ElementModel, language: string): string {
const labelList = entity.types.map(iri => {
const labels = this.model.getElementType(iri)?.data?.label;
return this.translation.formatLabel(labels, iri, language);
});
labelList.sort();
return labelList.join(', ');
}
}
function filterInLiterals(terms: ReadonlyArray<Rdf.NamedNode | Rdf.Literal>): readonly Rdf.Literal[] {
for (const term of terms) {
if (term.termType !== 'Literal') {
return terms.filter((t): t is Rdf.Literal => t.termType === 'Literal');
}
}
return terms as readonly Rdf.Literal[];
}