mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
promoted attributes are now part of attr list and also responsive
This commit is contained in:
parent
b60efbbf5a
commit
2217c5a3e0
@ -136,7 +136,6 @@ export default class DesktopMainWindowLayout {
|
|||||||
.child(new NoteActionsWidget().hideInZenMode())
|
.child(new NoteActionsWidget().hideInZenMode())
|
||||||
)
|
)
|
||||||
.child(new TabCachingWidget(() => new AttributeListWidget()))
|
.child(new TabCachingWidget(() => new AttributeListWidget()))
|
||||||
.child(new TabCachingWidget(() => new PromotedAttributesWidget()))
|
|
||||||
.child(new TabCachingWidget(() => new NoteDetailWidget()))
|
.child(new TabCachingWidget(() => new NoteDetailWidget()))
|
||||||
.child(...this.customWidgets.get('center-pane'))
|
.child(...this.customWidgets.get('center-pane'))
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ import AttributeDetailWidget from "./attribute_detail.js";
|
|||||||
import attributeRenderer from "../services/attribute_renderer.js";
|
import attributeRenderer from "../services/attribute_renderer.js";
|
||||||
import AttributeEditorWidget from "./attribute_editor.js";
|
import AttributeEditorWidget from "./attribute_editor.js";
|
||||||
import options from '../services/options.js';
|
import options from '../services/options.js';
|
||||||
|
import PromotedAttributesWidget from "./promoted_attributes.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="attribute-list">
|
<div class="attribute-list">
|
||||||
@ -63,7 +64,18 @@ const TPL = `
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="attr-expander attr-owned-expander">
|
<div class="attr-expander attr-promoted-expander">
|
||||||
|
<hr class="w-100">
|
||||||
|
|
||||||
|
<div class="attr-expander-text">Promoted attributes</div>
|
||||||
|
|
||||||
|
<hr class="w-100">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="all-attr-wrapper">
|
||||||
|
<div class="promoted-attributes-placeholder"></div>
|
||||||
|
|
||||||
|
<div class="attr-expander attr-owned-and-inherited-expander">
|
||||||
<hr class="w-100">
|
<hr class="w-100">
|
||||||
|
|
||||||
<div class="attr-expander-text"></div>
|
<div class="attr-expander-text"></div>
|
||||||
@ -71,7 +83,7 @@ const TPL = `
|
|||||||
<hr class="w-100">
|
<hr class="w-100">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="attr-display">
|
<div class="owned-and-inherited-wrapper">
|
||||||
<div class="attr-editor-placeholder"></div>
|
<div class="attr-editor-placeholder"></div>
|
||||||
|
|
||||||
<hr class="w-100 attr-inherited-empty-expander" style="margin-bottom: 10px;">
|
<hr class="w-100 attr-inherited-empty-expander" style="margin-bottom: 10px;">
|
||||||
@ -86,7 +98,7 @@ const TPL = `
|
|||||||
|
|
||||||
<div class="inherited-attributes"></div>
|
<div class="inherited-attributes"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -94,21 +106,33 @@ export default class AttributeListWidget extends TabAwareWidget {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.promotedAttributesWidget = new PromotedAttributesWidget().setParent(this);
|
||||||
this.attributeDetailWidget = new AttributeDetailWidget().setParent(this);
|
this.attributeDetailWidget = new AttributeDetailWidget().setParent(this);
|
||||||
this.attributeEditorWidget = new AttributeEditorWidget(this.attributeDetailWidget).setParent(this);
|
this.attributeEditorWidget = new AttributeEditorWidget(this.attributeDetailWidget).setParent(this);
|
||||||
|
|
||||||
this.child(this.attributeEditorWidget, this.attributeDetailWidget);
|
this.child(this.promotedAttributesWidget, this.attributeEditorWidget, this.attributeDetailWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
|
|
||||||
this.$attrDisplay = this.$widget.find('.attr-display');
|
this.$promotedExpander = this.$widget.find('.attr-promoted-expander');
|
||||||
this.$attrDisplay.toggle(options.is('attributeListExpanded'));
|
this.$allAttrWrapper = this.$widget.find('.all-attr-wrapper');
|
||||||
|
|
||||||
this.$ownedExpander = this.$widget.find('.attr-owned-expander');
|
this.$promotedExpander.on('click', async () => {
|
||||||
|
if (this.$allAttrWrapper.is(":visible")) {
|
||||||
|
this.$allAttrWrapper.slideUp(200);
|
||||||
|
} else {
|
||||||
|
this.$allAttrWrapper.slideDown(200);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$ownedAndInheritedWrapper = this.$widget.find('.owned-and-inherited-wrapper');
|
||||||
|
this.$ownedAndInheritedWrapper.toggle(options.is('attributeListExpanded'));
|
||||||
|
|
||||||
|
this.$ownedExpander = this.$widget.find('.attr-owned-and-inherited-expander');
|
||||||
this.$ownedExpander.on('click', async () => {
|
this.$ownedExpander.on('click', async () => {
|
||||||
const collapse = this.$attrDisplay.is(":visible");
|
const collapse = this.$ownedAndInheritedWrapper.is(":visible");
|
||||||
|
|
||||||
await options.save('attributeListExpanded', !collapse);
|
await options.save('attributeListExpanded', !collapse);
|
||||||
|
|
||||||
@ -133,6 +157,7 @@ export default class AttributeListWidget extends TabAwareWidget {
|
|||||||
|
|
||||||
this.$inheritedEmptyExpander = this.$widget.find('.attr-inherited-empty-expander');
|
this.$inheritedEmptyExpander = this.$widget.find('.attr-inherited-empty-expander');
|
||||||
|
|
||||||
|
this.$widget.find('.promoted-attributes-placeholder').replaceWith(this.promotedAttributesWidget.render());
|
||||||
this.$widget.find('.attr-editor-placeholder').replaceWith(this.attributeEditorWidget.render());
|
this.$widget.find('.attr-editor-placeholder').replaceWith(this.attributeEditorWidget.render());
|
||||||
this.$widget.append(this.attributeDetailWidget.render());
|
this.$widget.append(this.attributeDetailWidget.render());
|
||||||
}
|
}
|
||||||
@ -199,10 +224,9 @@ export default class AttributeListWidget extends TabAwareWidget {
|
|||||||
*/
|
*/
|
||||||
attributeListCollapsedStateChangedEvent({collapse}) {
|
attributeListCollapsedStateChangedEvent({collapse}) {
|
||||||
if (collapse) {
|
if (collapse) {
|
||||||
this.$attrDisplay.slideUp(200);
|
this.$ownedAndInheritedWrapper.slideUp(200);
|
||||||
}
|
} else {
|
||||||
else {
|
this.$ownedAndInheritedWrapper.slideDown(200);
|
||||||
this.$attrDisplay.slideDown(200);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,34 +5,39 @@ import noteAutocompleteService from "../services/note_autocomplete.js";
|
|||||||
import TabAwareWidget from "./tab_aware_widget.js";
|
import TabAwareWidget from "./tab_aware_widget.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="promoted-attributes-wrapper">
|
<div>
|
||||||
<style>
|
<style>
|
||||||
.promoted-attributes-wrapper {
|
.promoted-attributes-container {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
/* setting the display to block since "table" doesn't support scrolling */
|
display: flex;
|
||||||
display: block;
|
flex-direction: row;
|
||||||
/** flex-basis: content; - use once "content" is implemented by chrome */
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
|
justify-content: space-evenly;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.promoted-attributes td, .promoted-attributes th {
|
.promoted-attribute-cell {
|
||||||
padding: 5px;
|
display: flex;
|
||||||
min-width: 50px; /* otherwise checkboxes can collapse into 0 width (if there are only checkboxes) */
|
align-items: center;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promoted-attribute-cell div.input-group {
|
||||||
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<table class="promoted-attributes"></table>
|
<div class="promoted-attributes-container"></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default class PromotedAttributesWidget extends TabAwareWidget {
|
export default class PromotedAttributesWidget extends TabAwareWidget {
|
||||||
doRender() {
|
doRender() {
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
|
this.$container = this.$widget.find(".promoted-attributes-container");
|
||||||
this.$container = this.$widget.find(".promoted-attributes");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshWithNote(note) {
|
async refreshWithNote(note) {
|
||||||
@ -48,9 +53,9 @@ export default class PromotedAttributesWidget extends TabAwareWidget {
|
|||||||
return def && def.isPromoted;
|
return def && def.isPromoted;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (promoted.length > 0 && !note.hasLabel('hidePromotedAttributes')) {
|
const cells = [];
|
||||||
const $tbody = $("<tbody>");
|
|
||||||
|
|
||||||
|
if (promoted.length > 0 && !note.hasLabel('hidePromotedAttributes')) {
|
||||||
for (const definitionAttr of promoted) {
|
for (const definitionAttr of promoted) {
|
||||||
const definitionType = definitionAttr.name.startsWith('label:') ? 'label' : 'relation';
|
const definitionType = definitionAttr.name.startsWith('label:') ? 'label' : 'relation';
|
||||||
const valueName = definitionAttr.name.substr(definitionType.length + 1);
|
const valueName = definitionAttr.name.substr(definitionType.length + 1);
|
||||||
@ -71,15 +76,15 @@ export default class PromotedAttributesWidget extends TabAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const valueAttr of valueAttrs) {
|
for (const valueAttr of valueAttrs) {
|
||||||
const $tr = await this.createPromotedAttributeRow(definitionAttr, valueAttr, valueName);
|
const $cell = await this.createPromotedAttributeCell(definitionAttr, valueAttr, valueName);
|
||||||
|
|
||||||
$tbody.append($tr);
|
cells.push($cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we replace the whole content in one step so there can't be any race conditions
|
// we replace the whole content in one step so there can't be any race conditions
|
||||||
// (previously we saw promoted attributes doubling)
|
// (previously we saw promoted attributes doubling)
|
||||||
this.$container.empty().append($tbody);
|
this.$container.empty().append(...cells);
|
||||||
this.toggleInt(true);
|
this.toggleInt(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -89,10 +94,9 @@ export default class PromotedAttributesWidget extends TabAwareWidget {
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPromotedAttributeRow(definitionAttr, valueAttr, valueName) {
|
async createPromotedAttributeCell(definitionAttr, valueAttr, valueName) {
|
||||||
const definition = definitionAttr.getDefinition();
|
const definition = definitionAttr.getDefinition();
|
||||||
const $tr = $("<tr>");
|
|
||||||
const $labelCell = $("<th>").append(valueName);
|
|
||||||
const $input = $("<input>")
|
const $input = $("<input>")
|
||||||
.prop("tabindex", 200 + definitionAttr.position)
|
.prop("tabindex", 200 + definitionAttr.position)
|
||||||
.prop("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
|
.prop("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
|
||||||
@ -103,16 +107,14 @@ export default class PromotedAttributesWidget extends TabAwareWidget {
|
|||||||
.addClass("promoted-attribute-input")
|
.addClass("promoted-attribute-input")
|
||||||
.on('change', event => this.promotedAttributeChanged(event));
|
.on('change', event => this.promotedAttributeChanged(event));
|
||||||
|
|
||||||
const $inputCell = $("<td>").append($("<div>").addClass("input-group").append($input));
|
const $actionCell = $("<div>");
|
||||||
|
|
||||||
const $actionCell = $("<td>");
|
|
||||||
const $multiplicityCell = $("<td>")
|
const $multiplicityCell = $("<td>")
|
||||||
.addClass("multiplicity")
|
.addClass("multiplicity")
|
||||||
.attr("nowrap", true);
|
.attr("nowrap", true);
|
||||||
|
|
||||||
$tr
|
const $wrapper = $('<div class="promoted-attribute-cell">')
|
||||||
.append($labelCell)
|
.append($("<strong>").text(valueName))
|
||||||
.append($inputCell)
|
.append($("<div>").addClass("input-group").append($input))
|
||||||
.append($actionCell)
|
.append($actionCell)
|
||||||
.append($multiplicityCell);
|
.append($multiplicityCell);
|
||||||
|
|
||||||
@ -210,14 +212,14 @@ export default class PromotedAttributesWidget extends TabAwareWidget {
|
|||||||
.addClass("bx bx-plus pointer")
|
.addClass("bx bx-plus pointer")
|
||||||
.prop("title", "Add new attribute")
|
.prop("title", "Add new attribute")
|
||||||
.on('click', async () => {
|
.on('click', async () => {
|
||||||
const $new = await this.createPromotedAttributeRow(definitionAttr, {
|
const $new = await this.createPromotedAttributeCell(definitionAttr, {
|
||||||
attributeId: "",
|
attributeId: "",
|
||||||
type: valueAttr.type,
|
type: valueAttr.type,
|
||||||
name: definitionAttr.name,
|
name: definitionAttr.name,
|
||||||
value: ""
|
value: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
$tr.after($new);
|
$wrapper.after($new);
|
||||||
|
|
||||||
$new.find('input').trigger('focus');
|
$new.find('input').trigger('focus');
|
||||||
});
|
});
|
||||||
@ -230,13 +232,13 @@ export default class PromotedAttributesWidget extends TabAwareWidget {
|
|||||||
await server.remove("notes/" + this.noteId + "/attributes/" + valueAttr.attributeId, this.componentId);
|
await server.remove("notes/" + this.noteId + "/attributes/" + valueAttr.attributeId, this.componentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
$tr.remove();
|
$wrapper.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
$multiplicityCell.append(addButton).append(" ").append(removeButton);
|
$multiplicityCell.append(addButton).append(" ").append(removeButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $tr;
|
return $wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
async promotedAttributeChanged(event) {
|
async promotedAttributeChanged(event) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user