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:
Martynas Bagdonas 2022-05-26 18:16:40 +03:00
parent 4a1812e5ba
commit db0ac723fa
24 changed files with 634 additions and 2306 deletions

View file

@ -1,8 +0,0 @@
row > label:first-child
{
color: #7f7f7f;
}
textbox[type="styled"] {
border-style: none;
}

View file

@ -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>

View file

@ -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

View file

@ -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) => {

View file

@ -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);
}

View 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);
}

View file

@ -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>

View file

@ -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>

View file

@ -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);
}

View file

@ -1,7 +0,0 @@
textbox {
margin: 0;
}
row > label {
white-space: pre;
}

4
scss/_noteEditor.scss Normal file
View file

@ -0,0 +1,4 @@
@import "components/noteEditor";
@import "components/clicky";
@import "components/tagsBox";
@import "components/autosuggest";

2
scss/_relatedBox.scss Normal file
View file

@ -0,0 +1,2 @@
@import "components/relatedBox";
@import "components/clicky";

View 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
}
}
}

View 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;
}
}
}

View file

@ -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
View file

@ -0,0 +1 @@
@import "noteEditor";

View file

@ -0,0 +1 @@
@import "noteEditor";

1
scss/noteEditor-win.scss Normal file
View file

@ -0,0 +1 @@
@import "noteEditor";

1
scss/relatedBox-mac.scss Normal file
View file

@ -0,0 +1 @@
@import "relatedBox";

View file

@ -0,0 +1 @@
@import "relatedBox";

1
scss/relatedBox-win.scss Normal file
View file

@ -0,0 +1 @@
@import "relatedBox";