mirror of
https://github.com/zadam/trilium.git
synced 2024-11-11 01:23:57 +08:00
attributes dialog converted to algolia autocomplete, closes #203
This commit is contained in:
parent
90dbe637ed
commit
13f9007bd6
5 changed files with 92 additions and 75 deletions
|
@ -61,7 +61,8 @@ function AttributesModel() {
|
|||
|
||||
for (const attr of ownedAttributes) {
|
||||
attr.labelValue = attr.type === 'label' ? attr.value : '';
|
||||
attr.relationValue = attr.type === 'relation' ? (await treeUtils.getNoteTitle(attr.value) + " (" + attr.value + ")") : '';
|
||||
attr.relationValue = attr.type === 'relation' ? (await treeUtils.getNoteTitle(attr.value)) : '';
|
||||
attr.selectedPath = attr.type === 'relation' ? attr.value : '';
|
||||
attr.labelDefinition = (attr.type === 'label-definition' && attr.value) ? attr.value : {
|
||||
labelType: "text",
|
||||
multiplicityType: "singlevalue",
|
||||
|
@ -94,12 +95,6 @@ function AttributesModel() {
|
|||
|
||||
// attribute might not be rendered immediatelly so could not focus
|
||||
setTimeout(() => $(".attribute-type-select:last").focus(), 100);
|
||||
|
||||
$ownedAttributesBody.sortable({
|
||||
handle: '.handle',
|
||||
containment: $ownedAttributesBody,
|
||||
update: this.updateAttributePositions
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteAttribute = function(data, event) {
|
||||
|
@ -149,7 +144,7 @@ function AttributesModel() {
|
|||
attr.value = attr.labelValue;
|
||||
}
|
||||
else if (attr.type === 'relation') {
|
||||
attr.value = treeUtils.getNoteIdFromNotePath(linkService.getNotePathFromLabel(attr.relationValue)) || "";
|
||||
attr.value = treeUtils.getNoteIdFromNotePath(attr.selectedPath);
|
||||
}
|
||||
else if (attr.type === 'label-definition') {
|
||||
attr.value = attr.labelDefinition;
|
||||
|
@ -236,64 +231,69 @@ async function showDialog() {
|
|||
}
|
||||
|
||||
$dialog.on('focus', '.attribute-name', function (e) {
|
||||
if (!$(this).hasClass("ui-autocomplete-input")) {
|
||||
if (!$(this).hasClass("aa-input")) {
|
||||
$(this).autocomplete({
|
||||
source: async (request, response) => {
|
||||
appendTo: document.querySelector('body'),
|
||||
hint: false,
|
||||
autoselect: true,
|
||||
openOnFocus: true,
|
||||
minLength: 0
|
||||
}, [{
|
||||
displayKey: 'name',
|
||||
source: async (term, cb) => {
|
||||
const attribute = attributesModel.getTargetAttribute(this);
|
||||
const type = (attribute().type === 'relation' || attribute().type === 'relation-definition') ? 'relation' : 'label';
|
||||
const names = await server.get('attributes/names/?type=' + type + '&query=' + encodeURIComponent(request.term));
|
||||
const names = await server.get('attributes/names/?type=' + type + '&query=' + encodeURIComponent(term));
|
||||
const result = names.map(name => {
|
||||
return {
|
||||
label: name,
|
||||
value: name
|
||||
}
|
||||
return {name};
|
||||
});
|
||||
|
||||
if (result.length > 0) {
|
||||
response(result);
|
||||
if (result.length === 0) {
|
||||
result.push({name: "No results"})
|
||||
}
|
||||
else {
|
||||
response([{
|
||||
label: "No results",
|
||||
value: "No results"
|
||||
}]);
|
||||
}
|
||||
},
|
||||
minLength: 0
|
||||
});
|
||||
|
||||
cb(result);
|
||||
}
|
||||
}]);
|
||||
}
|
||||
|
||||
$(this).autocomplete("search", $(this).val());
|
||||
$(this).autocomplete("open");
|
||||
});
|
||||
|
||||
$dialog.on('focus', '.label-value', async function (e) {
|
||||
if (!$(this).hasClass("ui-autocomplete-input")) {
|
||||
if (!$(this).hasClass("aa-input")) {
|
||||
const attributeName = $(this).parent().parent().find('.attribute-name').val();
|
||||
|
||||
if (attributeName.trim() === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const attributeValues = await server.get('attributes/values/' + encodeURIComponent(attributeName));
|
||||
const attributeValues = (await server.get('attributes/values/' + encodeURIComponent(attributeName)))
|
||||
.map(attribute => { return { value: attribute }; });
|
||||
|
||||
if (attributeValues.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(this).autocomplete({
|
||||
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
||||
// because we have overriden filter() function in autocomplete.js
|
||||
source: attributeValues.map(attribute => {
|
||||
return {
|
||||
attribute: attribute,
|
||||
value: attribute
|
||||
}
|
||||
}),
|
||||
appendTo: document.querySelector('body'),
|
||||
hint: false,
|
||||
autoselect: true,
|
||||
openOnFocus: true,
|
||||
minLength: 0
|
||||
});
|
||||
}, [{
|
||||
displayKey: 'value',
|
||||
source: function (term, cb) {
|
||||
term = term.toLowerCase();
|
||||
|
||||
const filtered = attributeValues.filter(attr => attr.value.toLowerCase().includes(term));
|
||||
|
||||
cb(filtered);
|
||||
}
|
||||
}]);
|
||||
}
|
||||
|
||||
$(this).autocomplete("search", $(this).val());
|
||||
$(this).autocomplete("open");
|
||||
});
|
||||
|
||||
export default {
|
||||
|
|
|
@ -63,6 +63,10 @@ function initNoteAutocomplete($el) {
|
|||
ko.bindingHandlers.noteAutocomplete = {
|
||||
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
|
||||
initNoteAutocomplete($(element));
|
||||
|
||||
$(element).on('autocomplete:selected', function(event, suggestion, dataset) {
|
||||
bindingContext.$data.selectedPath = suggestion.path;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -326,19 +326,24 @@ async function showAttributes() {
|
|||
return;
|
||||
}
|
||||
|
||||
$input.autocomplete({
|
||||
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
||||
// because we have overriden filter() function in autocomplete.js
|
||||
source: attributeValues.map(attribute => {
|
||||
return {
|
||||
attribute: attribute,
|
||||
value: attribute
|
||||
}
|
||||
}),
|
||||
minLength: 0
|
||||
});
|
||||
attributeValues = attributeValues.map(attribute => { return { value: attribute }; });
|
||||
|
||||
$input.focus(() => $input.autocomplete("search", ""));
|
||||
$input.autocomplete({
|
||||
appendTo: document.querySelector('body'),
|
||||
hint: false,
|
||||
autoselect: true,
|
||||
openOnFocus: true,
|
||||
minLength: 0
|
||||
}, [{
|
||||
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') {
|
||||
|
@ -376,16 +381,22 @@ async function showAttributes() {
|
|||
}
|
||||
else if (valueAttr.type === 'relation') {
|
||||
if (valueAttr.value) {
|
||||
$input.val((await treeUtils.getNoteTitle(valueAttr.value) + " (" + valueAttr.value + ")"));
|
||||
$input.val(await treeUtils.getNoteTitle(valueAttr.value));
|
||||
}
|
||||
|
||||
// no need to wait for this
|
||||
noteAutocompleteService.initNoteAutocomplete($input);
|
||||
|
||||
$input.on('autocomplete:selected', function(event, suggestion, dataset) {
|
||||
promotedAttributeChanged(event);
|
||||
});
|
||||
|
||||
$input.prop("data-selected-path", valueAttr.value);
|
||||
|
||||
// ideally we'd use link instead of button which would allow tooltip preview, but
|
||||
// we can't guarantee updating the link in the a element
|
||||
const $openButton = $("<button>").addClass("btn btn-sm").text("Open").click(() => {
|
||||
const notePath = linkService.getNotePathFromLabel($input.val());
|
||||
const notePath = $input.prop("data-selected-path");
|
||||
|
||||
treeService.activateNote(notePath);
|
||||
});
|
||||
|
@ -473,9 +484,14 @@ async function showAttributes() {
|
|||
$attributeListInner.append(utils.formatLabel(attribute) + " ");
|
||||
}
|
||||
else if (attribute.type === 'relation') {
|
||||
$attributeListInner.append('@' + attribute.name + "=");
|
||||
$attributeListInner.append(await linkService.createNoteLink(attribute.value));
|
||||
$attributeListInner.append(" ");
|
||||
if (attribute.value) {
|
||||
$attributeListInner.append('@' + attribute.name + "=");
|
||||
$attributeListInner.append(await linkService.createNoteLink(attribute.value));
|
||||
$attributeListInner.append(" ");
|
||||
}
|
||||
else {
|
||||
messagingService.logError(`Relation ${attribute.attributeId} has empty target`);
|
||||
}
|
||||
}
|
||||
else if (attribute.type === 'label-definition' || attribute.type === 'relation-definition') {
|
||||
$attributeListInner.append(attribute.name + " definition ");
|
||||
|
@ -501,8 +517,10 @@ async function promotedAttributeChanged(event) {
|
|||
value = $attr.is(':checked') ? "true" : "false";
|
||||
}
|
||||
else if ($attr.prop("attribute-type") === "relation") {
|
||||
if ($attr.val()) {
|
||||
value = treeUtils.getNoteIdFromNotePath(linkService.getNotePathFromLabel($attr.val()));
|
||||
const selectedPath = $attr.prop("data-selected-path");
|
||||
|
||||
if (selectedPath) {
|
||||
value = treeUtils.getNoteIdFromNotePath(selectedPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -523,7 +523,7 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th
|
|||
|
||||
/* if modal height overflows, then only modal body scrolls */
|
||||
.modal-body {
|
||||
max-height: calc(100vh - 120px);
|
||||
max-height: calc(100vh - 200px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,13 +7,12 @@
|
|||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form data-bind="submit: save">
|
||||
<form data-bind="submit: save">
|
||||
<div class="modal-body">
|
||||
<div style="height: 97%; overflow: auto">
|
||||
<table id="owned-attributes-table" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Type</th>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
|
@ -22,10 +21,6 @@
|
|||
</thead>
|
||||
<tbody data-bind="foreach: ownedAttributes">
|
||||
<tr data-bind="if: !isDeleted">
|
||||
<td class="handle">
|
||||
<span class="glyphicon glyphicon-resize-vertical"></span>
|
||||
<input type="hidden" name="position" data-bind="value: position"/>
|
||||
</td>
|
||||
<td>
|
||||
<select class="form-control attribute-type-select" style="width: auto;"
|
||||
data-bind="options: $parent.availableTypes, optionsText: 'text', optionsValue: 'value', value: type, event: { change: $parent.typeChanged }"></select>
|
||||
|
@ -122,16 +117,16 @@
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer" style="display: flex; justify-content: space-between;">
|
||||
<button class="btn btn-primary btn-large" style="width: 200px;" id="save-attributes-button" type="submit">
|
||||
Save changes <kbd>enter</kbd></button>
|
||||
</div>
|
||||
<div class="modal-footer" style="display: flex; justify-content: space-between;">
|
||||
<button class="btn btn-primary btn-large" style="width: 200px;" id="save-attributes-button" type="submit">
|
||||
Save changes <kbd>enter</kbd></button>
|
||||
|
||||
<button class="btn btn-sm" type="button" data-help-page="Attributes">
|
||||
<i class="glyphicon glyphicon-info-sign"></i> Help
|
||||
</button>
|
||||
</div>
|
||||
<button class="btn btn-sm" type="button" data-help-page="Attributes">
|
||||
<i class="glyphicon glyphicon-info-sign"></i> Help
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue