import { useState } from "preact/hooks"; import FNote from "../../entities/fnote"; import "./PromotedAttributesDisplay.css"; import { useTriliumEvent } from "../react/hooks"; import attributes from "../../services/attributes"; import { DefinitionObject } from "../../services/promoted_attribute_definition_parser"; import { formatDateTime } from "../../utils/formatters"; import { ComponentChildren } from "preact"; import Icon from "../react/Icon"; interface PromotedAttributesDisplayProps { note: FNote; ignoredAttributes?: string[]; } interface AttributeWithDefinitions { friendlyName: string; name: string; type: string; value: string; def: DefinitionObject; } export default function PromotedAttributesDisplay({ note, ignoredAttributes }: PromotedAttributesDisplayProps) { const promotedDefinitionAttributes = useNoteAttributesWithDefinitions(note, ignoredAttributes); return promotedDefinitionAttributes?.length > 0 && (
{promotedDefinitionAttributes?.map((attr) => { return ( {formatLabelValue(attr)} ); } )}
) } function useNoteAttributesWithDefinitions(note: FNote, attributesToIgnore: string[] = []): AttributeWithDefinitions[] { const [ promotedDefinitionAttributes, setPromotedDefinitionAttributes ] = useState(getAttributesWithDefinitions(note, attributesToIgnore)); useTriliumEvent("entitiesReloaded", ({ loadResults }) => { if (loadResults.getAttributeRows().some(attr => attributes.isAffecting(attr, note))) { setPromotedDefinitionAttributes(getAttributesWithDefinitions(note, attributesToIgnore)); } }); return promotedDefinitionAttributes; } function formatLabelValue(attr: AttributeWithDefinitions): ComponentChildren { let value = attr.value; switch (attr.def.labelType) { case "number": let formattedValue = value; const numberValue = Number(value); if (attr.def.numberPrecision) { formattedValue = numberValue.toFixed(attr.def.numberPrecision); } return <>{attr.friendlyName}: {formattedValue}; case "date": case "datetime": { const date = new Date(value); const timeFormat = attr.def.labelType !== "date" ? "short" : "none"; return <>{attr.friendlyName}: {formatDateTime(date, "short", timeFormat)}; } case "time": { const date = new Date(`1970-01-01T${value}Z`); return <>{attr.friendlyName}: {formatDateTime(date, "none", "short")}; } case "boolean": return <> {attr.friendlyName}; case "text": default: return <>{attr.friendlyName}: {value}; } } function getAttributesWithDefinitions(note: FNote, attributesToIgnore: string[] = []): AttributeWithDefinitions[] { const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes(); const result: AttributeWithDefinitions[] = []; for (const attr of promotedDefinitionAttributes) { const def = attr.getDefinition(); const [ type, name ] = attr.name.split(":", 2); const value = note.getLabelValue(name); const friendlyName = def?.promotedAlias || name; if (!value) continue; if (attributesToIgnore.includes(name)) continue; result.push({ def, name, type, value, friendlyName }); } return result; }