").addClass("input-group").append($input));
const $actionCell = $("
");
const $multiplicityCell = $(" | ")
.addClass("multiplicity")
.attr("nowrap", true);
$tr
.append($labelCell)
.append($inputCell)
.append($actionCell)
.append($multiplicityCell);
if (valueAttr.type === 'label') {
if (definition.labelType === 'text') {
$input.prop("type", "text");
// no need to await for this, can be done asynchronously
server.get('attributes/values/' + encodeURIComponent(valueAttr.name)).then(attributeValues => {
if (attributeValues.length === 0) {
return;
}
attributeValues = attributeValues.map(attribute => { return { value: attribute }; });
$input.autocomplete({
appendTo: document.querySelector('body'),
hint: false,
autoselect: false,
openOnFocus: true,
minLength: 0,
tabAutocomplete: false
}, [{
displayKey: 'value',
source: function (term, cb) {
term = term.toLowerCase();
const filtered = attributeValues.filter(attr => attr.value.toLowerCase().includes(term));
cb(filtered);
}
}]);
});
}
else if (definition.labelType === 'number') {
$input.prop("type", "number");
let step = 1;
for (let i = 0; i < (definition.numberPrecision || 0) && i < 10; i++) {
step /= 10;
}
$input.prop("step", step);
}
else if (definition.labelType === 'boolean') {
$input.prop("type", "checkbox");
if (valueAttr.value === "true") {
$input.prop("checked", "checked");
}
}
else if (definition.labelType === 'date') {
$input.prop("type", "date");
}
else if (definition.labelType === 'url') {
$input.prop("placeholder", "http://website...");
const $openButton = $("")
.addClass("input-group-text open-external-link-button bx bx-trending-up")
.prop("title", "Open external link")
.on('click', () => window.open($input.val(), '_blank'));
$input.after($(" ")
.addClass("input-group-append")
.append($openButton));
}
else {
ws.logError("Unknown labelType=" + definitionAttr.labelType);
}
}
else if (valueAttr.type === 'relation') {
if (valueAttr.value) {
$input.val(await treeService.getNoteTitle(valueAttr.value));
}
// no need to wait for this
noteAutocompleteService.initNoteAutocomplete($input);
$input.on('autocomplete:selected', (event, suggestion, dataset) => {
this.promotedAttributeChanged(event);
});
$input.setSelectedPath(valueAttr.value);
}
else {
ws.logError("Unknown attribute type=" + valueAttr.type);
return;
}
if (definition.multiplicityType === "multivalue") {
const addButton = $("")
.addClass("bx bx-plus pointer")
.prop("title", "Add new attribute")
.on('click', async () => {
const $new = await this.createPromotedAttributeRow(definitionAttr, {
attributeId: "",
type: valueAttr.type,
name: definitionAttr.name,
value: ""
});
$tr.after($new);
$new.find('input').trigger('focus');
});
const removeButton = $("")
.addClass("bx bx-trash pointer")
.prop("title", "Remove this attribute")
.on('click', async () => {
if (valueAttr.attributeId) {
await server.remove("notes/" + this.tabContext.note.noteId + "/attributes/" + valueAttr.attributeId);
}
$tr.remove();
});
$multiplicityCell.append(addButton).append(" ").append(removeButton);
}
return $tr;
}
async promotedAttributeChanged(event) {
const $attr = $(event.target);
let value;
if ($attr.prop("type") === "checkbox") {
value = $attr.is(':checked') ? "true" : "false";
}
else if ($attr.prop("attribute-type") === "relation") {
const selectedPath = $attr.getSelectedPath();
value = selectedPath ? treeService.getNoteIdFromNotePath(selectedPath) : "";
}
else {
value = $attr.val();
}
const result = await server.put(`notes/${this.tabContext.note.noteId}/attribute`, {
attributeId: $attr.prop("attribute-id"),
type: $attr.prop("attribute-type"),
name: $attr.prop("attribute-name"),
value: value
});
$attr.prop("attribute-id", result.attributeId);
// FIXME
// animate only if it's not being animated already, this is important especially for e.g. number inputs
// which can be changed many times in a second by clicking on higher/lower buttons.
// if (this.$savedIndicator.queue().length === 0) {
// this.$savedIndicator.fadeOut();
// this.$savedIndicator.fadeIn();
// }
}
} |