import { t } from "../../services/i18n.js"; import server from "../../services/server.js"; import ws from "../../services/ws.js"; import treeService from "../../services/tree.js"; import noteAutocompleteService from "../../services/note_autocomplete.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js"; import attributeService from "../../services/attributes.js"; import options from "../../services/options.js"; import utils from "../../services/utils.js"; const TPL = ` `; /** * This widget is quite special because it's used in the desktop ribbon, but in mobile outside of ribbon. * This works without many issues (apart from autocomplete), but it should be kept in mind when changing things * and testing. */ export default class PromotedAttributesWidget extends NoteContextAwareWidget { get name() { return "promotedAttributes"; } get toggleCommand() { return "toggleRibbonTabPromotedAttributes"; } doRender() { this.$widget = $(TPL); this.contentSized(); this.$container = this.$widget.find(".promoted-attributes-container"); } getTitle(note) { const promotedDefAttrs = note.getPromotedDefinitionAttributes(); if (promotedDefAttrs.length === 0) { return { show: false }; } return { show: true, activate: options.is("promotedAttributesOpenInRibbon"), title: t("promoted_attributes.promoted_attributes"), icon: "bx bx-table" }; } async refreshWithNote(note) { this.$container.empty(); 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); if (promotedDefAttrs.length === 0) { this.toggleInt(false); return; } const $cells = []; 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); if (valueAttrs.length === 0) { valueAttrs.push({ attributeId: "", type: valueType, name: valueName, value: "" }); } if (definitionAttr.getDefinition().multiplicity === "single") { valueAttrs = valueAttrs.slice(0, 1); } for (const valueAttr of valueAttrs) { const $cell = await this.createPromotedAttributeCell(definitionAttr, valueAttr, valueName); $cells.push($cell); } } // we replace the whole content in one step, so there can't be any race conditions // (previously we saw promoted attributes doubling) this.$container.empty().append(...$cells); this.toggleInt(true); } async createPromotedAttributeCell(definitionAttr, valueAttr, valueName) { const definition = definitionAttr.getDefinition(); const id = `value-${valueAttr.attributeId}`; const $input = $("") .prop("tabindex", 200 + definitionAttr.position) .prop("id", id) .attr("data-attribute-id", valueAttr.noteId === this.noteId ? valueAttr.attributeId : "") // if not owned, we'll force creation of a new attribute instead of updating the inherited one .attr("data-attribute-type", valueAttr.type) .attr("data-attribute-name", valueAttr.name) .prop("value", valueAttr.value) .prop("placeholder", t("promoted_attributes.unset-field-placeholder")) .addClass("form-control") .addClass("promoted-attribute-input") .on("change", (event) => this.promotedAttributeChanged(event)); const $actionCell = $("
"); const $multiplicityCell = $("").addClass("multiplicity").attr("nowrap", true); const $wrapper = $('