fx-compat: Note editor and links box fixes:
- Add links-box component (inside noteEditor.js). - Add related-box component and fix related pane. - Use tagsBox.jsx instead of tagsbox.xml in note editor links box popup. - Remove CSS styles and bindings for noteeditor, relatedbox and tagsbox.
This commit is contained in:
parent
4a1812e5ba
commit
db0ac723fa
24 changed files with 634 additions and 2306 deletions
|
@ -1,8 +0,0 @@
|
|||
row > label:first-child
|
||||
{
|
||||
color: #7f7f7f;
|
||||
}
|
||||
|
||||
textbox[type="styled"] {
|
||||
border-style: none;
|
||||
}
|
|
@ -1,603 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2009 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
-->
|
||||
|
||||
<bindings xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<binding id="note-editor">
|
||||
<resources>
|
||||
<stylesheet src="chrome://zotero/skin/bindings/noteeditor.css"/>
|
||||
<stylesheet src="chrome://zotero-platform/content/noteeditor.css"/>
|
||||
</resources>
|
||||
|
||||
<implementation>
|
||||
<!--
|
||||
Public properties
|
||||
-->
|
||||
<field name="editable">false</field>
|
||||
<field name="displayTags">false</field>
|
||||
<field name="displayRelated">false</field>
|
||||
<field name="displayButton">false</field>
|
||||
<field name="hideLinksContainer"/>
|
||||
|
||||
<field name="buttonCaption"/>
|
||||
<field name="parentClickHandler"/>
|
||||
<field name="keyDownHandler"/>
|
||||
<field name="commandHandler"/>
|
||||
<field name="clickHandler"/>
|
||||
<field name="navigateHandler"/>
|
||||
|
||||
<field name="returnHandler"/>
|
||||
|
||||
<constructor><![CDATA[
|
||||
this._destroyed = false;
|
||||
this._noteEditorID = Zotero.Utilities.randomString();
|
||||
this._iframe = document.getAnonymousElementByAttribute(this, 'anonid', 'editor-view');
|
||||
this._iframe.addEventListener('DOMContentLoaded', (e) => {
|
||||
// For iframes without chrome priviledges, for unknown reasons,
|
||||
// dataTransfer.getData() returns empty value for `drop` event
|
||||
// when dragging something from the outside of Zotero
|
||||
this._iframe.contentWindow.addEventListener('drop', (event) => {
|
||||
this._iframe.contentWindow.wrappedJSObject.droppedData = Components.utils.cloneInto({
|
||||
'text/plain': event.dataTransfer.getData('text/plain'),
|
||||
'text/html': event.dataTransfer.getData('text/html'),
|
||||
'zotero/annotation': event.dataTransfer.getData('zotero/annotation'),
|
||||
'zotero/item': event.dataTransfer.getData('zotero/item')
|
||||
}, this._iframe.contentWindow);
|
||||
}, true);
|
||||
this._initialized = true;
|
||||
});
|
||||
|
||||
window.fillTooltip = (tooltip) => {
|
||||
let node = window.document.tooltipNode.closest('*[title]');
|
||||
if (!node || !node.getAttribute('title')) {
|
||||
return false;
|
||||
}
|
||||
tooltip.setAttribute('label', node.getAttribute('title'));
|
||||
return true;
|
||||
}
|
||||
|
||||
this.saveSync = () => {
|
||||
if (this._editorInstance) {
|
||||
this._editorInstance.saveSync();
|
||||
}
|
||||
}
|
||||
|
||||
this.getCurrentInstance = () => {
|
||||
return this._editorInstance;
|
||||
}
|
||||
|
||||
this.initEditor = async (state, reloaded) => {
|
||||
if (this._editorInstance) {
|
||||
this._editorInstance.uninit();
|
||||
}
|
||||
|
||||
// Automatically upgrade editable v1 note before it's loaded
|
||||
// TODO: Remove this at some point
|
||||
if (this.editable) {
|
||||
await Zotero.Notes.upgradeSchemaV1(this._item);
|
||||
}
|
||||
|
||||
this._editorInstance = new Zotero.EditorInstance();
|
||||
await this._editorInstance.init({
|
||||
state,
|
||||
item: this._item,
|
||||
reloaded,
|
||||
iframeWindow: document.getAnonymousElementByAttribute(this, 'anonid', 'editor-view').contentWindow,
|
||||
popup: document.getAnonymousElementByAttribute(this, 'anonid', 'editor-menu'),
|
||||
onNavigate: this._navigateHandler,
|
||||
viewMode: this.viewMode,
|
||||
readOnly: !this.editable,
|
||||
disableUI: this.mode == 'merge',
|
||||
onReturn: this._returnHandler,
|
||||
placeholder: this.placeholder
|
||||
});
|
||||
if (this._onInitCallback) {
|
||||
this._onInitCallback();
|
||||
}
|
||||
}
|
||||
|
||||
this.onInit = (callback) => {
|
||||
if (this._editorInstance) {
|
||||
return callback();
|
||||
}
|
||||
this._onInitCallback = callback;
|
||||
}
|
||||
|
||||
this.notify = async (event, type, ids, extraData) => {
|
||||
if (this._editorInstance) {
|
||||
await this._editorInstance.notify(event, type, ids, extraData);
|
||||
}
|
||||
|
||||
if (!this.item) return;
|
||||
// Try to use the state from the item save event
|
||||
let id = this.item.id;
|
||||
if (ids.includes(id)) {
|
||||
if (event == 'delete') {
|
||||
if (this._returnHandler) {
|
||||
this._returnHandler();
|
||||
}
|
||||
}
|
||||
else {
|
||||
let state = extraData && extraData[id] && extraData[id].state;
|
||||
if (state) {
|
||||
if (extraData[id].noteEditorID !== this._editorInstance.instanceID) {
|
||||
this.initEditor(state, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
let curValue = this.item.note;
|
||||
if (curValue !== this._lastHtmlValue) {
|
||||
this.initEditor(null, true);
|
||||
}
|
||||
}
|
||||
this._lastHtmlValue = this.item.note;
|
||||
}
|
||||
}
|
||||
|
||||
this._id('links-container').hidden = !(this.displayTags && this.displayRelated) || this._hideLinksContainer;
|
||||
this._id('links-box').refresh();
|
||||
}
|
||||
|
||||
this._notifierID = Zotero.Notifier.registerObserver(this, ['item', 'file'], 'noteeditor');
|
||||
]]></constructor>
|
||||
|
||||
<property name="editorInstance" onget="return this._editorInstance"/>
|
||||
|
||||
<!-- Modes are predefined settings groups for particular tasks -->
|
||||
<field name="_mode">"view"</field>
|
||||
<property name="mode" onget="return this._mode;">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
// Duplicate default property settings here
|
||||
this.editable = false;
|
||||
this.displayTags = false;
|
||||
this.displayRelated = false;
|
||||
this.displayButton = false;
|
||||
|
||||
switch (val) {
|
||||
case 'view':
|
||||
case 'merge':
|
||||
this.editable = false;
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
this.editable = true;
|
||||
this.parentClickHandler = this.selectParent;
|
||||
this.keyDownHandler = this.handleKeyDown;
|
||||
this.commandHandler = this.save;
|
||||
this.displayTags = true;
|
||||
this.displayRelated = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ("Invalid mode '" + val + "' in noteeditor.xml");
|
||||
}
|
||||
|
||||
this._mode = val;
|
||||
document.getAnonymousNodes(this)[0].setAttribute('mode', val);
|
||||
this._id('links-box').mode = val;
|
||||
this._id('links-container').hidden = !(this.displayTags && this.displayRelated) || this._hideLinksContainer;
|
||||
this._id('links-box').refresh();
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<field name="returnHandler"/>
|
||||
<property name="returnHandler" onget="return this._returnHandler;">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
this._returnHandler = val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<field name="_parentItem"/>
|
||||
<property name="parentItem" onget="return this._parentItem;">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
this._parentItem = this._id('links-box').parentItem = val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<field name="_item"/>
|
||||
<property name="item" onget="return this._item;">
|
||||
<setter><![CDATA[
|
||||
// The binding can be immediately destroyed
|
||||
// (which i.e. happens in merge dialog)
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._item && this._item.id && this._item.id === val.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._editorInstance) {
|
||||
this._editorInstance.uninit();
|
||||
this._editorInstance = null;
|
||||
}
|
||||
|
||||
this._lastHtmlValue = val.note;
|
||||
this._item = val;
|
||||
|
||||
// var parentKey = this._item.parentKey;
|
||||
// if (parentKey) {
|
||||
// this.parentItem = Zotero.Items.getByLibraryAndKey(this._item.libraryID, parentKey);
|
||||
// }
|
||||
|
||||
this._id('links-box').item = this._item;
|
||||
|
||||
(async () => {
|
||||
// `item` field can be set before the constructor is called
|
||||
// or noteeditor is attached to dom (which happens in the
|
||||
// merge dialog i.e.), therefore we wait for the initialization
|
||||
let n = 0;
|
||||
while (!this._initialized && !this._destroyed) {
|
||||
if (n >= 1000) {
|
||||
throw new Error('Waiting for noteeditor initialization failed');
|
||||
}
|
||||
await Zotero.Promise.delay(10);
|
||||
n++;
|
||||
}
|
||||
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.initEditor();
|
||||
this._id('links-box').item = this._item;
|
||||
})();
|
||||
]]></setter>
|
||||
</property>
|
||||
|
||||
<property name="linksOnTop">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
// if (val) {
|
||||
// var container = this._id('links-container');
|
||||
// var parent = container.parentNode;
|
||||
// var sib = container.nextSibling;
|
||||
// while (parent.firstChild !== container) {
|
||||
// parent.insertBefore(parent.removeChild(parent.firstChild), sib);
|
||||
// }
|
||||
// }
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<property name="navigateHandler">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
if (this._editorInstance) {
|
||||
this._editorInstance.onNavigate = val;
|
||||
}
|
||||
this._navigateHandler = val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<property name="hideLinksContainer">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
this._hideLinksContainer = val;
|
||||
this._id('links-container').hidden = val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<field name="collection"/>
|
||||
|
||||
<destructor>
|
||||
<![CDATA[
|
||||
Zotero.Notifier.unregisterObserver(this._notifierID);
|
||||
if (this._editorInstance) {
|
||||
this._editorInstance.uninit();
|
||||
}
|
||||
this._destroyed = true;
|
||||
this._initialized = false;
|
||||
this._editorInstance = null;
|
||||
]]>
|
||||
</destructor>
|
||||
|
||||
<method name="save">
|
||||
<body><![CDATA[
|
||||
return (async () => {
|
||||
|
||||
})();
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="focus">
|
||||
<body>
|
||||
<![CDATA[
|
||||
(async () => {
|
||||
let n = 0;
|
||||
while (!this._editorInstance && n++ < 100) {
|
||||
await Zotero.Promise.delay(10);
|
||||
}
|
||||
await this._editorInstance._initPromise;
|
||||
this._iframe.focus();
|
||||
this._editorInstance.focus();
|
||||
})();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="focusFirst">
|
||||
<body>
|
||||
<![CDATA[
|
||||
(async () => {
|
||||
let n = 0;
|
||||
while (!this._editorInstance && n++ < 100) {
|
||||
await Zotero.Promise.delay(10);
|
||||
}
|
||||
await this._editorInstance._initPromise;
|
||||
this._iframe.focus();
|
||||
try {
|
||||
this._editorInstance._iframeWindow.document.querySelector('.toolbar-button-return').focus();
|
||||
} catch(e) {
|
||||
}
|
||||
})();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="_id">
|
||||
<parameter name="id"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
return document.getAnonymousNodes(this)[0].getElementsByAttribute('id', id)[0];
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<content>
|
||||
<xul:vbox xbl:inherits="flex" style="display: flex;flex-direction: column;flex-grow: 1;">
|
||||
<!-- Notice: Update query selector for `iframe[anonid="editor-view"]` in contextPane.js if updating this -->
|
||||
<xul:iframe tooltip="editor-tooltip" anonid="editor-view" flex="1" overflow="auto" style="border: 0;width: 100%;flex-grow: 1;"
|
||||
frameBorder="0" src="resource://zotero/note-editor/editor.html" type="content"/>
|
||||
<xul:hbox id="links-container" hidden="true">
|
||||
<xul:linksbox id="links-box" flex="1" xbl:inherits="notitle"/>
|
||||
</xul:hbox>
|
||||
|
||||
<xul:popupset>
|
||||
<xul:tooltip id="editor-tooltip" onpopupshowing="return fillTooltip(this);"/>
|
||||
<xul:menupopup anonid="editor-menu" id="editor-menu" flex="1">
|
||||
</xul:menupopup>
|
||||
</xul:popupset>
|
||||
</xul:vbox>
|
||||
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
|
||||
<binding id="links-box">
|
||||
<implementation>
|
||||
<field name="itemRef"/>
|
||||
<property name="item" onget="return this.itemRef;">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
this.itemRef = val;
|
||||
|
||||
this.id('tags').item = this.item;
|
||||
this.id('related').item = this.item;
|
||||
this.refresh();
|
||||
|
||||
// Hide popup to prevent it being visible out of the context or
|
||||
// in some cases even invisible but still blocking the next click
|
||||
this.id('relatedPopup').addEventListener('click', (event) => {
|
||||
let target = event.originalTarget;
|
||||
if (target.classList.contains('zotero-box-label')) {
|
||||
this.id('relatedPopup').hidePopup();
|
||||
}
|
||||
});
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
<property name="mode">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
this.id('related').mode = val;
|
||||
this.id('tags').mode = val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
<field name="_parentItem"/>
|
||||
<property name="parentItem" onget="return this._parentItem;">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
this._parentItem = val;
|
||||
|
||||
var parentText = this.id('parentText');
|
||||
if (parentText.firstChild) {
|
||||
parentText.removeChild(parentText.firstChild);
|
||||
}
|
||||
|
||||
if (this._parentItem && this.getAttribute('notitle') != '1') {
|
||||
this.id('parent-row').hidden = undefined;
|
||||
this.id('parentLabel').value = Zotero.getString('pane.item.parentItem');
|
||||
parentText.appendChild(document.createTextNode(this._parentItem.getDisplayTitle(true)));
|
||||
}
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
<method name="tagsClick">
|
||||
<body><![CDATA[
|
||||
this.id('tags').reload();
|
||||
var x = this.boxObject.screenX;
|
||||
var y = this.boxObject.screenY;
|
||||
this.id('tagsPopup').openPopupAtScreen(x, y, false);
|
||||
|
||||
// If editable and no existing tags, open new empty row
|
||||
var tagsBox = this.id('tags');
|
||||
if (tagsBox.mode == 'edit' && tagsBox.count == 0) {
|
||||
this.id('tags').newTag();
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="refresh">
|
||||
<body><![CDATA[
|
||||
this.updateTagsSummary();
|
||||
this.updateRelatedSummary();
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="updateTagsSummary">
|
||||
<body><![CDATA[
|
||||
var v = this.id('tags').summary;
|
||||
|
||||
if (!v || v == "") {
|
||||
v = "[" + Zotero.getString('pane.item.noteEditor.clickHere') + "]";
|
||||
}
|
||||
|
||||
this.id('tagsLabel').value = Zotero.getString('itemFields.tags')
|
||||
+ Zotero.getString('punctuation.colon');
|
||||
this.id('tagsClick').value = v;
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="relatedClick">
|
||||
<body><![CDATA[
|
||||
var relatedList = this.item.relatedItems;
|
||||
if (relatedList.length > 0) {
|
||||
var x = this.boxObject.screenX;
|
||||
var y = this.boxObject.screenY;
|
||||
this.id('relatedPopup').openPopupAtScreen(x, y, false);
|
||||
}
|
||||
else {
|
||||
this.id('related').add();
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="updateRelatedSummary">
|
||||
<body><![CDATA[
|
||||
var v = this.id('related').summary;
|
||||
|
||||
if (!v || v == "") {
|
||||
v = "[" + Zotero.getString('pane.item.noteEditor.clickHere') + "]";
|
||||
}
|
||||
|
||||
this.id('relatedLabel').value = Zotero.getString('itemFields.related')
|
||||
+ Zotero.getString('punctuation.colon');
|
||||
this.id('relatedClick').value = v;
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="parentClick">
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (!this.item || !this.item.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.getElementById('zotero-pane')) {
|
||||
var zp = ZoteroPane;
|
||||
}
|
||||
else {
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
|
||||
var lastWin = wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
if (!lastWin) {
|
||||
var lastWin = window.open();
|
||||
}
|
||||
|
||||
if (lastWin.ZoteroOverlay && !lastWin.ZoteroPane.isShowing()) {
|
||||
lastWin.ZoteroOverlay.toggleDisplay(true);
|
||||
}
|
||||
|
||||
var zp = lastWin.ZoteroPane;
|
||||
}
|
||||
|
||||
Zotero.spawn(function* () {
|
||||
var parentID = this.item.parentID;
|
||||
yield zp.clearQuicksearch();
|
||||
zp.selectItem(parentID);
|
||||
}, this);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="id">
|
||||
<parameter name="id"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
return document.getAnonymousNodes(this)[0].getElementsByAttribute('id', id)[0];
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
<content>
|
||||
<xul:vbox xbl:inherits="flex">
|
||||
<xul:grid>
|
||||
<xul:columns>
|
||||
<xul:column/>
|
||||
<xul:column flex="1"/>
|
||||
</xul:columns>
|
||||
<xul:rows>
|
||||
<xul:row id="parent-row" hidden="true">
|
||||
<xul:label id="parentLabel"/>
|
||||
<xul:label id="parentText" class="zotero-clicky" crop="end"
|
||||
onclick="document.getBindingParent(this).parentClick();"/>
|
||||
</xul:row>
|
||||
<xul:row>
|
||||
<xul:label id="relatedLabel"/>
|
||||
<xul:label id="relatedClick" class="zotero-clicky" crop="end"
|
||||
onclick="document.getBindingParent(this).relatedClick();"/>
|
||||
</xul:row>
|
||||
<xul:row>
|
||||
<xul:label id="tagsLabel"/>
|
||||
<xul:label id="tagsClick" class="zotero-clicky" crop="end"
|
||||
onclick="document.getBindingParent(this).tagsClick();"/>
|
||||
</xul:row>
|
||||
</xul:rows>
|
||||
</xul:grid>
|
||||
<xul:popupset>
|
||||
<xul:menupopup id="relatedPopup" width="300" onpopupshowing="this.firstChild.refresh();">
|
||||
<xul:relatedbox id="related" flex="1"/>
|
||||
</xul:menupopup>
|
||||
<!-- The onpopup* stuff is an ugly hack to keep track of when the
|
||||
popup is open (and not the descendent autocomplete popup, which also
|
||||
seems to get triggered by these events for reasons that are less than
|
||||
clear) so that we can manually refresh the popup if it's open after
|
||||
autocomplete is used to prevent it from becoming unresponsive
|
||||
|
||||
Note: Code in tagsbox.xml is dependent on the DOM path between the
|
||||
tagsbox and tagsLabel above, so be sure to update fixPopup() if it changes
|
||||
-->
|
||||
<xul:menupopup id="tagsPopup" ignorekeys="true"
|
||||
onpopupshown="if (!document.commandDispatcher.focusedElement || document.commandDispatcher.focusedElement.tagName=='xul:label'){ /* DEBUG: it would be nice to make this work -- if (this.firstChild.count==0){ this.firstChild.newTag(); } */ this.setAttribute('showing', 'true'); }"
|
||||
onpopuphidden="if (!document.commandDispatcher.focusedElement || document.commandDispatcher.focusedElement.tagName=='xul:label'){ this.setAttribute('showing', 'false'); }">
|
||||
<xul:tagsbox id="tags" flex="1" mode="edit"/>
|
||||
</xul:menupopup>
|
||||
</xul:popupset>
|
||||
</xul:vbox>
|
||||
</content>
|
||||
</binding>
|
||||
</bindings>
|
|
@ -1,351 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2009 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
-->
|
||||
|
||||
<!DOCTYPE bindings SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||
|
||||
<bindings xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<binding id="related-box">
|
||||
<implementation>
|
||||
<!-- Modes are predefined settings groups for particular tasks -->
|
||||
<field name="_mode">"view"</field>
|
||||
<property name="mode" onget="return this._mode;">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
this.clickable = false;
|
||||
this.editable = false;
|
||||
|
||||
switch (val) {
|
||||
case 'view':
|
||||
case 'merge':
|
||||
case 'mergeedit':
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
this.clickable = true;
|
||||
this.editable = true;
|
||||
//this.clickHandler = this.showEditor;
|
||||
//this.blurHandler = this.hideEditor;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ("Invalid mode '" + val + "' in relatedbox.xml");
|
||||
}
|
||||
|
||||
this._mode = val;
|
||||
document.getAnonymousNodes(this)[0].setAttribute('mode', val);
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<field name="itemRef"/>
|
||||
<property name="item" onget="return this.itemRef;">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
this.itemRef = val;
|
||||
this.refresh();
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
<property name="summary">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
var r = "";
|
||||
|
||||
if (this.item) {
|
||||
var keys = this.item.relatedItems;
|
||||
if (keys.length) {
|
||||
for (let key of keys) {
|
||||
let item = Zotero.Items.getByLibraryAndKey(this.item.libraryID, key);
|
||||
if (!item) {
|
||||
Zotero.debug(`Related item ${this.item.libraryID}/${key} not found `
|
||||
+ `for item ${this.item.libraryKey}`, 2);
|
||||
continue;
|
||||
}
|
||||
r = r + item.getDisplayTitle() + ", ";
|
||||
}
|
||||
r = r.substr(0,r.length-2);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'relatedbox');
|
||||
]]>
|
||||
</constructor>
|
||||
|
||||
<destructor>
|
||||
<![CDATA[
|
||||
Zotero.Notifier.unregisterObserver(this._notifierID);
|
||||
]]>
|
||||
</destructor>
|
||||
|
||||
<!-- TODO: Asyncify -->
|
||||
<method name="notify">
|
||||
<parameter name="event"/>
|
||||
<parameter name="type"/>
|
||||
<parameter name="ids"/>
|
||||
<parameter name="extraData"/>
|
||||
<body><![CDATA[
|
||||
if (!this.item || !this.item.id) return;
|
||||
|
||||
// Refresh if this item has been modified
|
||||
if (event == 'modify' && ids.includes(this.item.id)) {
|
||||
this.refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
// Or if any listed items have been modified or deleted
|
||||
if (event == 'modify' || event == 'delete') {
|
||||
let libraryID = this.item.libraryID;
|
||||
let relatedItemIDs = new Set(this.item.relatedItems
|
||||
.map(key => Zotero.Items.getIDFromLibraryAndKey(libraryID, key)));
|
||||
for (let id of ids) {
|
||||
if (relatedItemIDs.has(id)) {
|
||||
this.refresh();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="refresh">
|
||||
<body><![CDATA[
|
||||
var addButton = this.id('addButton');
|
||||
addButton.hidden = !this.editable;
|
||||
|
||||
var rows = this.id('relatedRows');
|
||||
while(rows.hasChildNodes())
|
||||
rows.removeChild(rows.firstChild);
|
||||
|
||||
if (this.item) {
|
||||
var relatedKeys = this.item.relatedItems;
|
||||
for (var i = 0; i < relatedKeys.length; i++) {
|
||||
let key = relatedKeys[i];
|
||||
let relatedItem = Zotero.Items.getByLibraryAndKey(
|
||||
this.item.libraryID, key
|
||||
);
|
||||
if (!relatedItem) {
|
||||
Zotero.debug(`Related item ${this.item.libraryID}/${key} not found `
|
||||
+ `for item ${this.item.libraryKey}`, 2);
|
||||
continue;
|
||||
}
|
||||
let id = relatedItem.id;
|
||||
let icon = document.createElement("image");
|
||||
icon.className = "zotero-box-icon";
|
||||
icon.setAttribute('src', relatedItem.getImageSrc());
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.className = "zotero-box-label";
|
||||
label.setAttribute('value', relatedItem.getDisplayTitle());
|
||||
label.setAttribute('crop','end');
|
||||
label.setAttribute('flex','1');
|
||||
|
||||
var box = document.createElement('box');
|
||||
box.setAttribute('onclick',
|
||||
"document.getBindingParent(this).showItem(" + id + ")");
|
||||
box.setAttribute('class','zotero-clicky');
|
||||
box.setAttribute('flex','1');
|
||||
box.appendChild(icon);
|
||||
box.appendChild(label);
|
||||
|
||||
if (this.editable) {
|
||||
var remove = document.createElement("label");
|
||||
remove.setAttribute('value','-');
|
||||
remove.setAttribute('onclick',
|
||||
"document.getBindingParent(this).remove(" + id + ");");
|
||||
remove.setAttribute('class','zotero-clicky zotero-clicky-minus');
|
||||
}
|
||||
|
||||
var row = document.createElement("row");
|
||||
row.appendChild(box);
|
||||
if (this.editable) {
|
||||
row.appendChild(remove);
|
||||
}
|
||||
rows.appendChild(row);
|
||||
}
|
||||
this.updateCount(rows.childNodes.length);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="add">
|
||||
<body><![CDATA[
|
||||
return Zotero.spawn(function* () {
|
||||
var io = {dataIn: null, dataOut: null, deferred: Zotero.Promise.defer()};
|
||||
|
||||
window.openDialog('chrome://zotero/content/selectItemsDialog.xul', '',
|
||||
'chrome,dialog=no,centerscreen,resizable=yes', io);
|
||||
|
||||
yield io.deferred.promise;
|
||||
|
||||
if (!io.dataOut || !io.dataOut.length) {
|
||||
return;
|
||||
}
|
||||
var relItems = yield Zotero.Items.getAsync(io.dataOut);
|
||||
if (!relItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (relItems[0].libraryID != this.item.libraryID) {
|
||||
// FIXME
|
||||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
.getService(Components.interfaces.nsIPromptService);
|
||||
ps.alert(null, "", "You cannot relate items in different libraries.");
|
||||
return;
|
||||
}
|
||||
yield Zotero.DB.executeTransaction(async function () {
|
||||
for (let relItem of relItems) {
|
||||
if (this.item.addRelatedItem(relItem)) {
|
||||
await this.item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
if (relItem.addRelatedItem(this.item)) {
|
||||
await relItem.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
}, this);
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="remove">
|
||||
<parameter name="id"/>
|
||||
<body><![CDATA[
|
||||
return Zotero.spawn(function* () {
|
||||
var item = yield Zotero.Items.getAsync(id);
|
||||
if (item) {
|
||||
yield Zotero.DB.executeTransaction(async function () {
|
||||
if (this.item.removeRelatedItem(item)) {
|
||||
await this.item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
if (item.removeRelatedItem(this.item)) {
|
||||
await item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
}, this);
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="showItem">
|
||||
<parameter name="id"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if(id)
|
||||
{
|
||||
var p;
|
||||
if(window.ZoteroPane_Local)
|
||||
{
|
||||
p = window.ZoteroPane_Local;
|
||||
}
|
||||
else
|
||||
{
|
||||
var win;
|
||||
|
||||
if(window.opener && window.opener.ZoteroPane)
|
||||
{
|
||||
win = window.opener;
|
||||
}
|
||||
else
|
||||
{
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
win = wm.getMostRecentWindow('navigator:browser');
|
||||
if(!win)
|
||||
return;
|
||||
}
|
||||
|
||||
p = win.ZoteroPane;
|
||||
}
|
||||
|
||||
p.selectItem(id);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="updateCount">
|
||||
<parameter name="count"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (count == null) {
|
||||
var count = this.item.relatedItems.length;
|
||||
}
|
||||
|
||||
var str = 'pane.item.related.count.';
|
||||
switch (count){
|
||||
case 0:
|
||||
str += 'zero';
|
||||
break;
|
||||
case 1:
|
||||
str += 'singular';
|
||||
break;
|
||||
default:
|
||||
str += 'plural';
|
||||
break;
|
||||
}
|
||||
this.id('relatedNum').value = Zotero.getString(str, [count]);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="id">
|
||||
<parameter name="id"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
return document.getAnonymousNodes(this)[0].getElementsByAttribute('id',id)[0];
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
<content>
|
||||
<xul:vbox xbl:inherits="flex" class="zotero-box">
|
||||
<xul:hbox align="center">
|
||||
<xul:label id="relatedNum"/>
|
||||
<xul:button id="addButton" label="&zotero.item.add;"
|
||||
oncommand="this.parentNode.parentNode.parentNode.add();"/>
|
||||
</xul:hbox>
|
||||
<xul:grid flex="1">
|
||||
<xul:columns>
|
||||
<xul:column flex="1"/>
|
||||
<xul:column/>
|
||||
</xul:columns>
|
||||
<xul:rows id="relatedRows"/>
|
||||
</xul:grid>
|
||||
</xul:vbox>
|
||||
</content>
|
||||
</binding>
|
||||
</bindings>
|
File diff suppressed because it is too large
Load diff
|
@ -269,7 +269,7 @@ var ZoteroContextPane = new function () {
|
|||
else {
|
||||
var node = _notesPaneDeck.selectedPanel;
|
||||
if (node.selectedIndex == 0) {
|
||||
node.querySelector('textbox').focus();
|
||||
node.querySelector('search-textbox').focus();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
@ -873,7 +873,7 @@ var ZoteroContextPane = new function () {
|
|||
);
|
||||
// Related panel
|
||||
var panelRelated = document.createXULElement('tabpanel');
|
||||
var relatedBox = document.createXULElement('relatedbox');
|
||||
var relatedBox = new (customElements.get('related-box'));
|
||||
relatedBox.setAttribute('flex', '1');
|
||||
relatedBox.className = 'zotero-editpane-related';
|
||||
panelRelated.addEventListener('click', (event) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2020 Corporation for Digital Scholarship
|
||||
Copyright © 2021 Corporation for Digital Scholarship
|
||||
Vienna, Virginia, USA
|
||||
https://www.zotero.org
|
||||
|
||||
|
@ -30,49 +30,21 @@
|
|||
constructor() {
|
||||
super();
|
||||
|
||||
this.editable = false;
|
||||
this.displayTags = false;
|
||||
this.displayRelated = false;
|
||||
this.displayButton = false;
|
||||
this.hideLinksContainer = false;
|
||||
|
||||
this.buttonCaption = null;
|
||||
this.parentClickHandler = null;
|
||||
this.keyDownHandler = null;
|
||||
this.commandHandler = null;
|
||||
this.clickHandler = null;
|
||||
this.navigateHandler = null;
|
||||
this.returnHandler = null;
|
||||
|
||||
this._notitle = false;
|
||||
this._mode = 'view';
|
||||
this._destroyed = false;
|
||||
this._noteEditorID = Zotero.Utilities.randomString();
|
||||
this._item = null;
|
||||
this._parentItem = null;
|
||||
this._iframe = null;
|
||||
this._initialized = true;
|
||||
this._editorInstance = null;
|
||||
this._item = null;
|
||||
this._parentItem = null;
|
||||
|
||||
this.clickable = false;
|
||||
this.editable = false;
|
||||
this.saveOnEdit = false;
|
||||
this.showTypeMenu = false;
|
||||
this.hideEmptyFields = false;
|
||||
this.clickByRow = false;
|
||||
this.clickByItem = false;
|
||||
|
||||
this.clickHandler = null;
|
||||
this.blurHandler = null;
|
||||
this.eventHandlers = [];
|
||||
|
||||
this._mode = 'view';
|
||||
this._destroyed = false;
|
||||
|
||||
this.content = MozXULElement.parseXULToFragment(`
|
||||
<box flex="1" tooltip="html-tooltip" style="display: flex" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<div id="note-editor" style="display: flex;flex-direction: column;flex-grow: 1;" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<iframe id="editor-view" style="border: 0;width: 100%;flex-grow: 1;" src="resource://zotero/note-editor/editor.html" type="content"/>
|
||||
<div id="links-container">
|
||||
<div id="links-box"/>
|
||||
<links-box id="links-box" style="display: flex" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
|
||||
</div>
|
||||
</div>
|
||||
</box>
|
||||
|
@ -81,10 +53,6 @@
|
|||
<menupopup id="editor-menu"/>
|
||||
</popupset>
|
||||
`, ['chrome://zotero/locale/zotero.dtd']);
|
||||
|
||||
this._destroyed = false;
|
||||
this._noteEditorID = Zotero.Utilities.randomString();
|
||||
this._notifierID = Zotero.Notifier.registerObserver(this, ['item', 'file'], 'noteeditor');
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
|
@ -116,9 +84,10 @@
|
|||
}, true);
|
||||
this._initialized = true;
|
||||
});
|
||||
shadow.appendChild(content);
|
||||
shadow.append(content);
|
||||
|
||||
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'itembox');
|
||||
this.notitle = !!this.getAttribute('notitle');
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@ -132,10 +101,7 @@
|
|||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
// Empty the DOM. We will rebuild if reconnected.
|
||||
while (this.lastChild) {
|
||||
this.removeChild(this.lastChild);
|
||||
}
|
||||
this.replaceChildren();
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
|
@ -147,11 +113,11 @@
|
|||
if (this._editorInstance) {
|
||||
this._editorInstance.saveSync();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getCurrentInstance = () => {
|
||||
return this._editorInstance;
|
||||
}
|
||||
};
|
||||
|
||||
initEditor = async (state, reloaded) => {
|
||||
if (this._editorInstance) {
|
||||
|
@ -160,7 +126,7 @@
|
|||
|
||||
// Automatically upgrade editable v1 note before it's loaded
|
||||
// TODO: Remove this at some point
|
||||
if (this.editable) {
|
||||
if (this._mode == 'edit') {
|
||||
await Zotero.Notes.upgradeSchemaV1(this._item);
|
||||
}
|
||||
|
||||
|
@ -173,35 +139,35 @@
|
|||
popup: this._id('editor-menu'),
|
||||
onNavigate: this._navigateHandler,
|
||||
viewMode: this.viewMode,
|
||||
readOnly: !this.editable,
|
||||
disableUI: this._mode === 'merge',
|
||||
readOnly: this._mode != 'edit',
|
||||
disableUI: this._mode == 'merge',
|
||||
onReturn: this._returnHandler,
|
||||
placeholder: this.placeholder
|
||||
});
|
||||
if (this._onInitCallback) {
|
||||
this._onInitCallback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onInit = (callback) => {
|
||||
if (this._editorInstance) {
|
||||
return callback();
|
||||
}
|
||||
this._onInitCallback = callback;
|
||||
}
|
||||
};
|
||||
|
||||
notify = async (event, type, ids, extraData) => {
|
||||
if (this._editorInstance) {
|
||||
await this._editorInstance.notify(event, type, ids, extraData);
|
||||
}
|
||||
|
||||
if (!this.item) {
|
||||
if (!this._item) {
|
||||
return;
|
||||
}
|
||||
// Try to use the state from the item save event
|
||||
let id = this.item.id;
|
||||
let id = this._item.id;
|
||||
if (ids.includes(id)) {
|
||||
if (event === 'delete') {
|
||||
if (event == 'delete') {
|
||||
if (this._returnHandler) {
|
||||
this._returnHandler();
|
||||
}
|
||||
|
@ -214,17 +180,23 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
let curValue = this.item.note;
|
||||
let curValue = this._item.note;
|
||||
if (curValue !== this._lastHtmlValue) {
|
||||
this.initEditor(null, true);
|
||||
}
|
||||
}
|
||||
this._lastHtmlValue = this.item.note;
|
||||
this._lastHtmlValue = this._item.note;
|
||||
}
|
||||
}
|
||||
|
||||
// this._id('links-container').hidden = !(this.displayTags && this.displayRelated) || this._hideLinksContainer;
|
||||
// this._id('links-box').refresh();
|
||||
if (ids.includes(id) || this._parentItem && ids.includes(this._parentItem.id)) {
|
||||
this._id('links-box').refresh();
|
||||
}
|
||||
};
|
||||
|
||||
set notitle(val) {
|
||||
this._notitle = !!val;
|
||||
this._id('links-box').notitle = val;
|
||||
}
|
||||
|
||||
set navigateHandler(val) {
|
||||
|
@ -238,46 +210,28 @@
|
|||
this._returnHandler = val;
|
||||
}
|
||||
|
||||
//
|
||||
// Public properties
|
||||
//
|
||||
|
||||
// Modes are predefined settings groups for particular tasks
|
||||
get mode() {
|
||||
return this._mode;
|
||||
}
|
||||
|
||||
set mode(val) {
|
||||
// Duplicate default property settings here
|
||||
this.editable = false;
|
||||
this.displayTags = false;
|
||||
this.displayRelated = false;
|
||||
this.displayButton = false;
|
||||
|
||||
var displayLinks = true;
|
||||
switch (val) {
|
||||
case 'view':
|
||||
case 'merge':
|
||||
this.editable = false;
|
||||
displayLinks = false;
|
||||
case 'view':
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
this.editable = true;
|
||||
this.parentClickHandler = this.selectParent;
|
||||
this.keyDownHandler = this.handleKeyDown;
|
||||
this.commandHandler = this.save;
|
||||
this.displayTags = true;
|
||||
this.displayRelated = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ("Invalid mode '" + val + "' in noteEditor.js");
|
||||
throw new Error(`Invalid mode '${val}'`);
|
||||
}
|
||||
|
||||
this._mode = val;
|
||||
// document.getAnonymousNodes(this)[0].setAttribute('mode', val);
|
||||
// this._id('links-box').mode = val;
|
||||
// this._id('links-container').hidden = !(this.displayTags && this.displayRelated) || this._hideLinksContainer;
|
||||
// this._id('links-box').refresh();
|
||||
this._id('links-container').hidden = !displayLinks;
|
||||
this._id('links-box').mode = val;
|
||||
}
|
||||
|
||||
get item() {
|
||||
|
@ -291,7 +245,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
if (this._item && this._item.id && this._item.id === val.id) {
|
||||
if (this._item && this._item.id && this._item.id == val.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -303,12 +257,12 @@
|
|||
this._lastHtmlValue = val.note;
|
||||
this._item = val;
|
||||
|
||||
// var parentKey = this._item.parentKey;
|
||||
// if (parentKey) {
|
||||
// this.parentItem = Zotero.Items.getByLibraryAndKey(this._item.libraryID, parentKey);
|
||||
// }
|
||||
var parentKey = this._item.parentKey;
|
||||
if (parentKey) {
|
||||
this.parentItem = Zotero.Items.getByLibraryAndKey(this._item.libraryID, parentKey);
|
||||
}
|
||||
|
||||
// this._id('links-box').item = this._item;
|
||||
this._id('links-box').item = this._item;
|
||||
|
||||
(async () => {
|
||||
// `item` field can be set before the constructor is called
|
||||
|
@ -328,7 +282,7 @@
|
|||
}
|
||||
|
||||
this.initEditor();
|
||||
// this._id('links-box').item = this._item;
|
||||
this._id('links-box').item = this._item;
|
||||
})();
|
||||
}
|
||||
|
||||
|
@ -338,7 +292,7 @@
|
|||
|
||||
set parentItem(val) {
|
||||
this._parentItem = val;
|
||||
// this._id('links-box').parentItem = val;
|
||||
this._id('links-box').parentItem = val;
|
||||
}
|
||||
|
||||
async focus() {
|
||||
|
@ -371,3 +325,239 @@
|
|||
}
|
||||
customElements.define("note-editor", NoteEditor);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
let TagsBoxContainer = require('containers/tagsBoxContainer').default;
|
||||
|
||||
class LinksBox extends XULElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._mode = 'view';
|
||||
this._item = null;
|
||||
this._parentItem = null;
|
||||
this._destroyed = false;
|
||||
|
||||
this.content = MozXULElement.parseXULToFragment(`
|
||||
<div id="links-box" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<div class="grid">
|
||||
<div id="parent-label" class="label" hidden="true"/>
|
||||
<div id="parent-value" class="value zotero-clicky" hidden="true"/>
|
||||
|
||||
<div id="related-label" class="label"/>
|
||||
<div id="related-value" class="value zotero-clicky"/>
|
||||
|
||||
<div id="tags-label" class="label"/>
|
||||
<div id="tags-value" class="value zotero-clicky"/>
|
||||
</div>
|
||||
</div>
|
||||
<popupset xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<menupopup id="related-popup" width="300">
|
||||
<related-box id="related"/>
|
||||
</menupopup>
|
||||
<menupopup id="tags-popup" width="300" ignorekeys="true">
|
||||
<div style="display: flex" id="tags-box-container" xmlns="http://www.w3.org/1999/xhtml"/>
|
||||
</menupopup>
|
||||
</popupset>
|
||||
`, ['chrome://zotero/locale/zotero.dtd']);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this._destroyed = false;
|
||||
window.addEventListener("unload", this.destroy);
|
||||
|
||||
var shadow = this.attachShadow({ mode: "open" });
|
||||
var s1 = document.createElement("link");
|
||||
s1.rel = "stylesheet";
|
||||
s1.href = "chrome://zotero-platform/content/noteEditor.css";
|
||||
shadow.append(s1);
|
||||
|
||||
var s2 = document.createElement("link");
|
||||
s2.rel = "stylesheet";
|
||||
s2.href = "chrome://global/skin/global.css";
|
||||
shadow.append(s2);
|
||||
|
||||
shadow.append(document.importNode(this.content, true));
|
||||
|
||||
this._id('parent-value').addEventListener('click', this._parentClickHandler);
|
||||
this._id('related-value').addEventListener('click', this._relatedClickHandler);
|
||||
this._id('tags-value').addEventListener('click', this._tagsClickHandler);
|
||||
|
||||
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'itembox');
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
window.removeEventListener("unload", this.destroy);
|
||||
this._destroyed = true;
|
||||
|
||||
Zotero.Notifier.unregisterObserver(this._notifierID);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.replaceChildren();
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
set item(val) {
|
||||
this._item = val;
|
||||
this._id('related').item = this._item;
|
||||
|
||||
this.refresh();
|
||||
|
||||
// Hide popup to prevent it being visible out of the context or
|
||||
// in some cases even invisible but still blocking the next click
|
||||
this._id('related-popup').addEventListener('click', (event) => {
|
||||
let target = event.originalTarget;
|
||||
if (target.classList.contains('zotero-box-label')) {
|
||||
this._id('related-popup').hidePopup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
set mode(val) {
|
||||
this._mode = val;
|
||||
this._id('related').mode = val;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
set notitle(val) {
|
||||
this._notitle = val;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
set parentItem(val) {
|
||||
this._parentItem = val;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this._updateParentRow();
|
||||
this._updateTagsSummary();
|
||||
this._updateRelatedSummary();
|
||||
|
||||
// TODO: Update tagsBox container state via tagsBoxRef and imperative handle, instead of recreating it
|
||||
let container = this._id('tags-box-container');
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
if (this._item) {
|
||||
var tagsBoxRef = React.createRef();
|
||||
ReactDOM.render(
|
||||
<TagsBoxContainer
|
||||
key={'tagsBox-' + this._item.id}
|
||||
item={this._item}
|
||||
editable={this._mode == 'edit'}
|
||||
ref={tagsBoxRef}
|
||||
/>,
|
||||
container
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_updateParentRow() {
|
||||
let hidden = !this._parentItem || !!this._notitle;
|
||||
this._id('parent-value').hidden = hidden;
|
||||
this._id('parent-label').hidden = hidden;
|
||||
if (!hidden) {
|
||||
this._id('parent-label').replaceChildren(Zotero.getString('pane.item.parentItem'));
|
||||
this._id('parent-value').replaceChildren(document.createTextNode(this._parentItem.getDisplayTitle(true)));
|
||||
}
|
||||
}
|
||||
|
||||
_updateRelatedSummary() {
|
||||
var r = '';
|
||||
if (this._item) {
|
||||
var keys = this._item.relatedItems;
|
||||
if (keys.length) {
|
||||
for (let key of keys) {
|
||||
let item = Zotero.Items.getByLibraryAndKey(this._item.libraryID, key);
|
||||
if (!item) {
|
||||
Zotero.debug(`Related item ${this._item.libraryID}/${key} not found `
|
||||
+ `for item ${this._item.libraryKey}`, 2);
|
||||
continue;
|
||||
}
|
||||
r = r + item.getDisplayTitle() + ", ";
|
||||
}
|
||||
r = r.slice(0, -2);
|
||||
}
|
||||
}
|
||||
|
||||
let v = r;
|
||||
|
||||
if (!v || v == '') {
|
||||
v = "[" + Zotero.getString('pane.item.noteEditor.clickHere') + "]";
|
||||
}
|
||||
|
||||
this._id('related-label').innerText = Zotero.getString('itemFields.related')
|
||||
+ Zotero.getString('punctuation.colon');
|
||||
this._id('related-value').innerText = v;
|
||||
}
|
||||
|
||||
_updateTagsSummary() {
|
||||
var r = '';
|
||||
|
||||
if (this._item) {
|
||||
var tags = this._item.getTags();
|
||||
|
||||
// Sort tags alphabetically
|
||||
var collation = Zotero.getLocaleCollation();
|
||||
tags.sort((a, b) => collation.compareString(1, a.tag, b.tag));
|
||||
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
r = r + tags[i].tag + ", ";
|
||||
}
|
||||
r = r.slice(0, -2);
|
||||
}
|
||||
|
||||
let v = r;
|
||||
|
||||
if (!v || v == '') {
|
||||
v = "[" + Zotero.getString('pane.item.noteEditor.clickHere') + "]";
|
||||
}
|
||||
|
||||
this._id('tags-label').innerText = Zotero.getString('itemFields.tags')
|
||||
+ Zotero.getString('punctuation.colon');
|
||||
this._id('tags-value').innerText = v;
|
||||
}
|
||||
|
||||
_parentClickHandler = () => {
|
||||
if (!this._item || !this._item.id) {
|
||||
return;
|
||||
}
|
||||
var parentID = this._item.parentID;
|
||||
var win = Zotero.getMainWindow();
|
||||
if (win) {
|
||||
win.ZoteroPane.selectItem(parentID);
|
||||
win.Zotero_Tabs.select('zotero-pane');
|
||||
win.focus();
|
||||
}
|
||||
};
|
||||
|
||||
_relatedClickHandler = (event) => {
|
||||
var relatedList = this._item.relatedItems;
|
||||
if (relatedList.length > 0) {
|
||||
this._id('related-popup').openPopup(this, 'topleft topleft', 0, 0, false);
|
||||
}
|
||||
else if (this._mode == 'edit') {
|
||||
this._id('related').add();
|
||||
}
|
||||
};
|
||||
|
||||
_tagsClickHandler = (event) => {
|
||||
this._id('tags-popup').openPopup(this, 'topleft topleft', 0, 0, false);
|
||||
// If editable and no existing tags, open new empty row
|
||||
if (this._mode == 'edit' && !this._item.getTags().length) {
|
||||
setTimeout(() => {
|
||||
this._id('tags-popup').querySelector('.tags-box-header button').click();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_id(id) {
|
||||
return this.shadowRoot.querySelector(`[id=${id}]`);
|
||||
}
|
||||
}
|
||||
customElements.define("links-box", LinksBox);
|
||||
}
|
||||
|
|
265
chrome/content/zotero/elements/relatedBox.js
Normal file
265
chrome/content/zotero/elements/relatedBox.js
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2021 Corporation for Digital Scholarship
|
||||
Vienna, Virginia, USA
|
||||
https://www.zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
{
|
||||
class RelatedBox extends XULElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._mode = 'view';
|
||||
this._item = null;
|
||||
this._destroyed = false;
|
||||
|
||||
this.content = MozXULElement.parseXULToFragment(`
|
||||
<box flex="1" style="display: flex" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<div style="flex-grow: 1" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<div class="header">
|
||||
<label id="related-num"/>
|
||||
<button id="related-add">&zotero.item.add;</button>
|
||||
</div>
|
||||
<div id="related-grid" class="grid"/>
|
||||
</div>
|
||||
</box>
|
||||
`, ['chrome://zotero/locale/zotero.dtd']);
|
||||
|
||||
this._destroyed = false;
|
||||
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'relatedbox');
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this._destroyed = false;
|
||||
window.addEventListener("unload", this.destroy);
|
||||
|
||||
let shadow = this.attachShadow({ mode: "open" });
|
||||
|
||||
let s1 = document.createElement("link");
|
||||
s1.rel = "stylesheet";
|
||||
s1.href = "chrome://zotero-platform/content/relatedBox.css";
|
||||
shadow.append(s1);
|
||||
|
||||
let content = document.importNode(this.content, true);
|
||||
shadow.append(content);
|
||||
|
||||
this._id('related-add').addEventListener('click', this.add);
|
||||
|
||||
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'relatedbox');
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
window.removeEventListener("unload", this.destroy);
|
||||
this._destroyed = true;
|
||||
|
||||
Zotero.Notifier.unregisterObserver(this._notifierID);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.replaceChildren();
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
get mode() {
|
||||
return this._mode;
|
||||
}
|
||||
|
||||
set mode(val) {
|
||||
switch (val) {
|
||||
case 'view':
|
||||
case 'merge':
|
||||
case 'mergeedit':
|
||||
case 'edit':
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid mode '${val}'`);
|
||||
}
|
||||
|
||||
this._mode = val;
|
||||
}
|
||||
|
||||
get item() {
|
||||
return this._item;
|
||||
}
|
||||
|
||||
set item(val) {
|
||||
this._item = val;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
notify(event, type, ids, extraData) {
|
||||
if (!this._item || !this._item.id) return;
|
||||
|
||||
// Refresh if this item has been modified
|
||||
if (event == 'modify' && ids.includes(this._item.id)) {
|
||||
this.refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
// Or if any listed items have been modified or deleted
|
||||
if (event == 'modify' || event == 'delete') {
|
||||
let libraryID = this._item.libraryID;
|
||||
let relatedItemIDs = new Set(this._item.relatedItems.map(key => Zotero.Items.getIDFromLibraryAndKey(libraryID, key)));
|
||||
for (let id of ids) {
|
||||
if (relatedItemIDs.has(id)) {
|
||||
this.refresh();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this._id('related-add').hidden = this._mode == 'edit';
|
||||
|
||||
let grid = this._id('related-grid');
|
||||
grid.replaceChildren();
|
||||
|
||||
if (this._item) {
|
||||
let relatedKeys = this._item.relatedItems;
|
||||
for (let i = 0; i < relatedKeys.length; i++) {
|
||||
let key = relatedKeys[i];
|
||||
let relatedItem = Zotero.Items.getByLibraryAndKey(
|
||||
this._item.libraryID, key
|
||||
);
|
||||
if (!relatedItem) {
|
||||
Zotero.debug(`Related item ${this._item.libraryID}/${key} not found `
|
||||
+ `for item ${this._item.libraryKey}`, 2);
|
||||
continue;
|
||||
}
|
||||
let id = relatedItem.id;
|
||||
let icon = document.createElement("img");
|
||||
icon.src = relatedItem.getImageSrc();
|
||||
|
||||
let label = document.createElement("label");
|
||||
label.append(relatedItem.getDisplayTitle());
|
||||
|
||||
let box = document.createElement('div');
|
||||
box.addEventListener('click', () => this._handleShowItem(id));
|
||||
box.className = 'box zotero-clicky';
|
||||
box.appendChild(icon);
|
||||
box.appendChild(label);
|
||||
|
||||
grid.append(box);
|
||||
|
||||
if (this._mode == 'edit') {
|
||||
let remove = document.createElement("label");
|
||||
remove.addEventListener('click', () => this._handleRemove(id));
|
||||
remove.className = 'zotero-clicky zotero-clicky-minus';
|
||||
remove.append('-');
|
||||
grid.append(remove);
|
||||
}
|
||||
}
|
||||
this._updateCount();
|
||||
}
|
||||
}
|
||||
|
||||
add = async () => {
|
||||
let io = { dataIn: null, dataOut: null, deferred: Zotero.Promise.defer() };
|
||||
window.openDialog('chrome://zotero/content/selectItemsDialog.xhtml', '',
|
||||
'chrome,dialog=no,centerscreen,resizable=yes', io);
|
||||
|
||||
await io.deferred.promise;
|
||||
if (!io.dataOut || !io.dataOut.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let relItems = await Zotero.Items.getAsync(io.dataOut);
|
||||
if (!relItems.length) {
|
||||
return;
|
||||
}
|
||||
if (relItems[0].libraryID != this._item.libraryID) {
|
||||
Zotero.alert.alert(null, "", "You cannot relate items in different libraries.");
|
||||
return;
|
||||
}
|
||||
await Zotero.DB.executeTransaction(async () => {
|
||||
for (let relItem of relItems) {
|
||||
if (this._item.addRelatedItem(relItem)) {
|
||||
await this._item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
if (relItem.addRelatedItem(this._item)) {
|
||||
await relItem.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async _handleRemove(id) {
|
||||
let item = await Zotero.Items.getAsync(id);
|
||||
if (item) {
|
||||
await Zotero.DB.executeTransaction(async () => {
|
||||
if (this._item.removeRelatedItem(item)) {
|
||||
await this._item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
if (item.removeRelatedItem(this._item)) {
|
||||
await item.save({
|
||||
skipDateModifiedUpdate: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_handleShowItem(id) {
|
||||
let win = Zotero.getMainWindow();
|
||||
if (win) {
|
||||
win.ZoteroPane.selectItem(id);
|
||||
win.Zotero_Tabs.select('zotero-pane');
|
||||
win.focus();
|
||||
}
|
||||
}
|
||||
|
||||
_updateCount() {
|
||||
let count = this._item.relatedItems.length;
|
||||
let str = 'pane.item.related.count.';
|
||||
switch (count) {
|
||||
case 0:
|
||||
str += 'zero';
|
||||
break;
|
||||
case 1:
|
||||
str += 'singular';
|
||||
break;
|
||||
default:
|
||||
str += 'plural';
|
||||
break;
|
||||
}
|
||||
this._id('related-num').replaceChildren(Zotero.getString(str, [count]));
|
||||
}
|
||||
|
||||
_id(id) {
|
||||
return this.shadowRoot.querySelector(`[id=${id}]`);
|
||||
}
|
||||
}
|
||||
customElements.define("related-box", RelatedBox);
|
||||
}
|
|
@ -18,8 +18,14 @@
|
|||
<script>
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js", this);
|
||||
Services.scriptloader.loadSubScript("resource://zotero/require.js", this);
|
||||
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/noteEditor.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/relatedBox.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/note.js", this);
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
</script>
|
||||
|
||||
<keyset>
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/menulistItemTypes.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/itemBox.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/noteEditor.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/relatedBox.js", this);
|
||||
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/tabs.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/zoteroPane.js", this);
|
||||
|
@ -1137,7 +1138,7 @@
|
|||
</tabpanel>
|
||||
|
||||
<tabpanel>
|
||||
<relatedbox id="zotero-editpane-related" class="zotero-editpane-related" flex="1"/>
|
||||
<related-box id="zotero-editpane-related" class="zotero-editpane-related" flex="1"/>
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
/* Don't collapse blank note parent labels, since it prevents access to parent */
|
||||
#citeLabel[onclick]:hover
|
||||
{
|
||||
cursor: pointer !important;
|
||||
min-height: 1.25em;
|
||||
}
|
||||
|
||||
#tagsPopup {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
row label
|
||||
{
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
row > label, row > hbox
|
||||
{
|
||||
margin-top: 1px !important;
|
||||
margin-bottom: 1px !important;
|
||||
-moz-box-pack: start;
|
||||
-moz-margin-start: 1px !important;
|
||||
-moz-margin-end: 5px !important;
|
||||
padding: 0 2px 0 2px !important;
|
||||
border-radius: 6px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
row > hbox
|
||||
{
|
||||
-moz-box-align: center;
|
||||
}
|
||||
|
||||
row > label:first-child
|
||||
{
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
-moz-margin-start: 5px !important;
|
||||
-moz-margin-end: 0 !important;
|
||||
width: 62px;
|
||||
text-align: right;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
vbox > hbox:first-child > linksbox {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
vbox > hbox:not(:first-child) > linksbox {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
textbox[type="styled"] {
|
||||
border-width: 0 1px 0 1px;
|
||||
border-style: solid;
|
||||
border-color: rgb(204, 204, 204);
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
textbox {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
row > label {
|
||||
white-space: pre;
|
||||
}
|
4
scss/_noteEditor.scss
Normal file
4
scss/_noteEditor.scss
Normal file
|
@ -0,0 +1,4 @@
|
|||
@import "components/noteEditor";
|
||||
@import "components/clicky";
|
||||
@import "components/tagsBox";
|
||||
@import "components/autosuggest";
|
2
scss/_relatedBox.scss
Normal file
2
scss/_relatedBox.scss
Normal file
|
@ -0,0 +1,2 @@
|
|||
@import "components/relatedBox";
|
||||
@import "components/clicky";
|
26
scss/components/_noteEditor.scss
Normal file
26
scss/components/_noteEditor.scss
Normal file
|
@ -0,0 +1,26 @@
|
|||
#links-box {
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
|
||||
& > * {
|
||||
margin-top: 1px !important;
|
||||
margin-bottom: 1px !important;
|
||||
padding: 0 2px 0 2px !important;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #7f7f7f;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
min-width: 62px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.value {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
}
|
||||
}
|
42
scss/components/_relatedBox.scss
Normal file
42
scss/components/_relatedBox.scss
Normal file
|
@ -0,0 +1,42 @@
|
|||
.header {
|
||||
display: flex;
|
||||
padding-left: 10px;
|
||||
align-items: center;
|
||||
|
||||
label {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
button {
|
||||
min-width: 79px;
|
||||
margin: 5px 6px 3px;
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
color: ButtonText;
|
||||
text-shadow: none;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
|
||||
.box {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
margin-left: 5px;
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-left: 3px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,10 +16,6 @@
|
|||
flex-grow: 1;
|
||||
//width: 330px;
|
||||
|
||||
// This is necessary for XUL layout to prevent children
|
||||
// container to force its height for the parent
|
||||
height: 0;
|
||||
|
||||
.tags-box-header {
|
||||
display: flex;
|
||||
padding-left: 10px;
|
||||
|
|
1
scss/noteEditor-mac.scss
Normal file
1
scss/noteEditor-mac.scss
Normal file
|
@ -0,0 +1 @@
|
|||
@import "noteEditor";
|
1
scss/noteEditor-unix.scss
Normal file
1
scss/noteEditor-unix.scss
Normal file
|
@ -0,0 +1 @@
|
|||
@import "noteEditor";
|
1
scss/noteEditor-win.scss
Normal file
1
scss/noteEditor-win.scss
Normal file
|
@ -0,0 +1 @@
|
|||
@import "noteEditor";
|
1
scss/relatedBox-mac.scss
Normal file
1
scss/relatedBox-mac.scss
Normal file
|
@ -0,0 +1 @@
|
|||
@import "relatedBox";
|
1
scss/relatedBox-unix.scss
Normal file
1
scss/relatedBox-unix.scss
Normal file
|
@ -0,0 +1 @@
|
|||
@import "relatedBox";
|
1
scss/relatedBox-win.scss
Normal file
1
scss/relatedBox-win.scss
Normal file
|
@ -0,0 +1 @@
|
|||
@import "relatedBox";
|
Loading…
Reference in a new issue