2025-11-22 22:10:51 +02:00
|
|
|
import { useEffect, useState } from "preact/hooks";
|
|
|
|
|
import "./PromotedAttributes.css";
|
|
|
|
|
import { useNoteContext } from "./react/hooks";
|
|
|
|
|
import { Attribute } from "../services/attribute_parser";
|
2025-11-22 22:25:32 +02:00
|
|
|
import FAttribute from "../entities/fattribute";
|
2025-11-23 10:22:58 +02:00
|
|
|
import clsx from "clsx";
|
|
|
|
|
import { t } from "../services/i18n";
|
|
|
|
|
import { DefinitionObject } from "../services/promoted_attribute_definition_parser";
|
2025-11-22 22:25:32 +02:00
|
|
|
|
|
|
|
|
interface Cell {
|
|
|
|
|
definitionAttr: FAttribute;
|
2025-11-23 10:22:58 +02:00
|
|
|
definition: DefinitionObject;
|
2025-11-22 22:25:32 +02:00
|
|
|
valueAttr: Attribute;
|
|
|
|
|
valueName: string;
|
|
|
|
|
}
|
2025-11-22 22:10:51 +02:00
|
|
|
|
2025-11-22 21:58:52 +02:00
|
|
|
export default function PromotedAttributes() {
|
2025-11-22 22:10:51 +02:00
|
|
|
const { note } = useNoteContext();
|
2025-11-22 22:25:32 +02:00
|
|
|
const [ cells, setCells ] = useState<Cell[]>();
|
2025-11-22 22:10:51 +02:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!note) return;
|
|
|
|
|
const promotedDefAttrs = note.getPromotedDefinitionAttributes();
|
|
|
|
|
const ownedAttributes = note.getOwnedAttributes();
|
|
|
|
|
// attrs are not resorted if position changes after the initial load
|
|
|
|
|
// promoted attrs are sorted primarily by order of definitions, but with multi-valued promoted attrs
|
|
|
|
|
// the order of attributes is important as well
|
|
|
|
|
ownedAttributes.sort((a, b) => a.position - b.position);
|
|
|
|
|
|
2025-11-22 22:25:32 +02:00
|
|
|
const cells: Cell[] = [];
|
2025-11-22 22:10:51 +02:00
|
|
|
for (const definitionAttr of promotedDefAttrs) {
|
|
|
|
|
const valueType = definitionAttr.name.startsWith("label:") ? "label" : "relation";
|
|
|
|
|
const valueName = definitionAttr.name.substr(valueType.length + 1);
|
|
|
|
|
|
|
|
|
|
let valueAttrs = ownedAttributes.filter((el) => el.name === valueName && el.type === valueType) as Attribute[];
|
|
|
|
|
|
|
|
|
|
if (valueAttrs.length === 0) {
|
|
|
|
|
valueAttrs.push({
|
|
|
|
|
attributeId: "",
|
|
|
|
|
type: valueType,
|
|
|
|
|
name: valueName,
|
|
|
|
|
value: ""
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (definitionAttr.getDefinition().multiplicity === "single") {
|
|
|
|
|
valueAttrs = valueAttrs.slice(0, 1);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 22:25:32 +02:00
|
|
|
for (const valueAttr of valueAttrs) {
|
2025-11-23 10:22:58 +02:00
|
|
|
const definition = definitionAttr.getDefinition();
|
|
|
|
|
cells.push({ definitionAttr, definition, valueAttr, valueName });
|
2025-11-22 22:25:32 +02:00
|
|
|
}
|
2025-11-22 22:10:51 +02:00
|
|
|
}
|
|
|
|
|
setCells(cells);
|
|
|
|
|
}, [ note ]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="promoted-attributes-widget">
|
|
|
|
|
<div className="promoted-attributes-container">
|
2025-11-22 22:25:32 +02:00
|
|
|
{cells?.map(cell => <PromotedAttributeCell cell={cell} />)}
|
2025-11-22 22:10:51 +02:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2025-11-22 21:58:52 +02:00
|
|
|
}
|
2025-11-22 22:25:32 +02:00
|
|
|
|
|
|
|
|
function PromotedAttributeCell({ cell }: { cell: Cell }) {
|
2025-11-23 10:22:58 +02:00
|
|
|
const { valueName, valueAttr, definition, definitionAttr } = cell;
|
2025-11-22 22:25:32 +02:00
|
|
|
const inputId = `value-${valueAttr.attributeId}`;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="promoted-attribute-cell">
|
|
|
|
|
<label for={inputId}>{definition.promotedAlias ?? valueName}</label>
|
2025-11-23 10:22:58 +02:00
|
|
|
<div className="input-group">
|
|
|
|
|
<input
|
|
|
|
|
tabIndex={200 + definitionAttr.position}
|
|
|
|
|
id={inputId}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<ActionCell cell={cell} />
|
|
|
|
|
<MultiplicityCell cell={cell} />
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ActionCell({ cell }: { cell: Cell }) {
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
|
2025-11-22 22:25:32 +02:00
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
2025-11-23 10:22:58 +02:00
|
|
|
|
|
|
|
|
function MultiplicityCell({ cell }: { cell: Cell }) {
|
|
|
|
|
return (cell.definition.multiplicity === "multi" &&
|
|
|
|
|
<td className="multiplicity">
|
|
|
|
|
<PromotedActionButton icon="bx bx-plus" title={t("promoted_attributes.add_new_attribute")} />{' '}
|
|
|
|
|
<PromotedActionButton icon="bx bx-trash" title={t("promoted_attributes.remove_this_attribute")} />
|
|
|
|
|
</td>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function PromotedActionButton({ icon, title }: {
|
|
|
|
|
icon: string,
|
|
|
|
|
title: string })
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
<span
|
|
|
|
|
className={clsx("tn-tool-button pointer", icon)}
|
|
|
|
|
title={title}
|
|
|
|
|
/>
|
|
|
|
|
)
|
|
|
|
|
}
|