Reactify the Tag Selector

This commit is contained in:
Tom Najdek 2017-05-23 00:10:03 +01:00 committed by Adomas Venčkauskas
parent 506ed313da
commit 897e74c7f1
33 changed files with 1396 additions and 1597 deletions

View file

@ -18,6 +18,7 @@
"syntax-jsx",
"transform-react-jsx",
"transform-react-display-name",
"transform-class-properties",
[
"transform-es2015-modules-commonjs",
{

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,455 @@
/* global Zotero: false */
'use strict';
(function() {
const React = require('react');
const ReactDom = require('react-dom');
const TagSelector = require('react-ui/components/tag-selector.js');
const noop = Promise.resolve();
const defaults = {
tags: [],
searchString: '',
shouldFocus: false,
onSelection: noop,
viewOnly: false
};
const { Cc, Ci } = require('chrome');
ZoteroPane.React.TagSelector = class TagSelectorContainer extends React.Component {
constructor(props) {
super(props);
this.opts = Object.assign({}, defaults, props);
this.selectedTags = new Set();
this.updateScope = Promise.resolve;
this.filterToScope = true;
this.showAutomatic = true;
this._notifierID = Zotero.Notifier.registerObserver(
this,
['collection-item', 'item', 'item-tag', 'tag', 'setting'],
'tagSelector'
);
this._scope = null;
this._allTags = null;
this.state = null;
this.update();
}
componentWillReceiveProps(nextProps) {
this.opts = Object.assign({}, this.opts, nextProps);
this.update();
}
async notify(event, type, ids, extraData) {
if (type === 'setting') {
if (ids.some(val => val.split('/')[1] == 'tagColors')) {
await this.refresh(true);
}
return;
}
// Ignore anything other than deletes in duplicates view
if (this.collectionTreeRow.isDuplicates()) {
switch (event) {
case 'delete':
case 'trash':
break;
default:
return;
}
}
// Ignore item events other than 'trash'
if (type == 'item' && event != 'trash') {
return;
}
// If a selected tag no longer exists, deselect it
if (event == 'delete' || event == 'trash' || event == 'modify') {
for (let tag of this.selectedTags) {
if (tag == extraData[ids[0]].old.tag) {
this.selectedTags.delete(tag);
break;
}
}
await this.refresh(true);
}
if (event == 'add') {
if (type == 'item-tag') {
let tagObjs = ids
// Get tag name and type
.map(x => extraData[x])
// Ignore tag adds for items not in the current library, if there is one
.filter(x => {
if (!this._libraryID) {
return true;
}
return x.libraryID == this._libraryID;
});
if (tagObjs.length) {
this.insertSorted(tagObjs);
}
}
}
// Otherwise, just update the tag selector
return this.updateScope();
}
async refresh(fetch) {
let t = new Date;
if(this._allTags === null) {
fetch = true;
}
if (fetch) {
Zotero.debug('Reloading tags selector');
} else {
Zotero.debug('Refreshing tags selector');
}
// If new data, rebuild boxes
if (fetch) {
let tagColors = Zotero.Tags.getColors(this.libraryID);
this._allTags = await Zotero.Tags.getAll(this.libraryID, this.showAutomatic ? [0, 1] : [0]);
// .tap(() => Zotero.Promise.check(this.mode));
// Add colored tags that aren't already real tags
let regularTags = new Set(this._allTags.map(tag => tag.tag));
let coloredTags = Array.from(tagColors.keys());
coloredTags.filter(ct => !regularTags.has(ct)).forEach(x =>
this._allTags.push(Zotero.Tags.cleanData({ tag: x }))
);
// Sort by name
this._allTags.sort(function (a, b) {
return Zotero.getLocaleCollation().compareString(1, a.tag, b.tag);
});
}
this.update();
Zotero.debug('Loaded tag selector in ' + (new Date - t) + ' ms');
// var event = new Event('refresh');
// this.dispatchEvent(event);
}
update() {
var tags;
let flatScopeTags = Array.isArray(this._scope) ? this._scope.map(tag => tag.tag) : [];
if('libraryID' in this) {
var tagColors = Zotero.Tags.getColors(this.libraryID);
}
if(this.filterToScope && Array.isArray(this._scope)) {
tags = this._allTags.filter(tag =>
flatScopeTags.includes(tag.tag) || (tagColors && tagColors.has(tag.tag))
);
} else {
tags = this._allTags ? this._allTags.slice(0) : [];
}
if(this.opts.searchString) {
tags = tags.filter(tag => !!tag.tag.match(new RegExp(this.opts.searchString, 'i')));
}
this.opts.tags = tags.map(t => {
let name = t.tag;
let selected = this.selectedTags.has(name);
let color = tagColors && tagColors.has(name) ? tagColors.get(name).color : '';
let disabled = !flatScopeTags.includes(name);
return { name, selected, color, disabled };
});
this.state ? this.setState(this.opts) : this.state = Object.assign({}, this.opts);
}
render() {
return <TagSelector
tags={ this.state.tags }
searchString={ this.state.searchString }
shouldFocus={ this.state.shouldFocus }
onSelect={ this.state.viewOnly ? () => {} : this.onTagSelectedHandler.bind(this) }
onTagContext={ this.onTagContextHandler.bind(this) }
onSearch={ this.onSearchHandler.bind(this) }
onSettings={ this.onTagSelectorViewSettingsHandler.bind(this) }
/>;
}
setMode(mode) {
this.setState({viewOnly: mode == 'view'});
}
focusTextbox() {
this.opts.shouldFocus = true;
this.update();
}
toggle() {
this._isCollapsed = !this._isCollapsed;
}
unregister() {
ReactDom.unmountComponentAtNode(this.domEl);
if (this._notifierID) {
Zotero.Notifier.unregisterObserver(this._notifierID);
}
}
uninit() {
this.selectedTags = new Set();
this.opts.searchString = '';
this.update();
}
onTagContextHandler(tag, ev) {
let tagContextMenu = document.getElementById('tag-menu');
ev.preventDefault();
tagContextMenu.openPopup(ev.target, 'end_before', 0, 0, true);
this.contextTag = tag;
}
onTagSelectorViewSettingsHandler(ev) {
let settingsContextMenu = document.getElementById('tag-selector-view-settings-menu');
ev.preventDefault();
settingsContextMenu.openPopup(ev.target, 'end_before', 0, 0, true);
}
onTagSelectedHandler(tag) {
if(this.selectedTags.has(tag)) {
this.selectedTags.delete(tag);
} else {
this.selectedTags.add(tag);
}
if('onSelection' in this.opts && typeof(this.opts.onSelection) === 'function') {
this.opts.onSelection(this.selectedTags).then(this.refresh.bind(this));
}
}
onSearchHandler(searchString) {
this.opts.searchString = searchString;
this.update();
}
getTagSelection() {
return this.selectedTags;
}
clearTagSelection() {
this.selectedTags = new Set();
return this.selectedTags;
}
async openColorPickerWindow() {
var io = {
libraryID: this.libraryID,
name: this.contextTag.name
};
var tagColors = Zotero.Tags.getColors(this.libraryID);
if (tagColors.size >= Zotero.Tags.MAX_COLORED_TAGS && !tagColors.has(io.name)) {
var ps = Cc['@mozilla.org/embedcomp/prompt-service;1']
.getService(Ci.nsIPromptService);
ps.alert(null, '', Zotero.getString('pane.tagSelector.maxColoredTags', Zotero.Tags.MAX_COLORED_TAGS));
return;
}
io.tagColors = tagColors;
window.openDialog(
'chrome://zotero/content/tagColorChooser.xul',
'zotero-tagSelector-colorChooser',
'chrome,modal,centerscreen', io
);
// Dialog cancel
if (typeof io.color == 'undefined') {
return;
}
await Zotero.Tags.setColor(this.libraryID, io.name, io.color, io.position);
this.refresh();
}
async openRenamePrompt() {
var promptService = Cc['@mozilla.org/embedcomp/prompt-service;1']
.getService(Ci.nsIPromptService);
var newName = { value: this.contextTag.name };
var result = promptService.prompt(window,
Zotero.getString('pane.tagSelector.rename.title'),
Zotero.getString('pane.tagSelector.rename.message'),
newName, '', {});
if (!result || !newName.value || this.contextTag.name == newName.value) {
return;
}
if (this.selectedTags.has(this.contextTag.name)) {
var wasSelected = true;
this.selectedTags.delete(this.contextTag.name);
}
if (Zotero.Tags.getID(this.contextTag.name)) {
await Zotero.Tags.rename(this.libraryID, this.contextTag.name, newName.value);
}
// Colored tags don't need to exist, so in that case
// just rename the color setting
else {
let color = Zotero.Tags.getColor(this.libraryID, this.contextTag.name);
if (!color) {
throw new Error("Can't rename missing tag");
}
await Zotero.Tags.setColor(this.libraryID, this.contextTag.name, false);
await Zotero.Tags.setColor(this.libraryID, newName, color);
}
if(wasSelected) {
this.selectedTags.add(newName.value);
}
this.updateScope();
}
async openDeletePrompt() {
var promptService = Cc['@mozilla.org/embedcomp/prompt-service;1']
.getService(Ci.nsIPromptService);
var confirmed = promptService.confirm(window,
Zotero.getString('pane.tagSelector.delete.title'),
Zotero.getString('pane.tagSelector.delete.message'));
if (!confirmed) {
return;
}
var tagID = Zotero.Tags.getID(this.contextTag.name);
if (tagID) {
await Zotero.Tags.removeFromLibrary(this.libraryID, tagID);
}
// If only a tag color setting, remove that
else {
await Zotero.Tags.setColor(this.libraryID, this.contextTag.name, false);
}
this.updateScope();
}
toggleFilterToScope(newValue) {
this.filterToScope = typeof(newValue) === 'undefined' ? !this.filterToScope : newValue;
this.refresh();
}
toggleShowAutomatic(newValue) {
this.showAutomatic = typeof(newValue) === 'undefined' ? !this.showAutomatic : newValue;
this.refresh(true);
}
deselectAll() {
this.selectedTags = new Set();
if('onSelection' in this.opts && typeof(this.opts.onSelection) === 'function') {
this.opts.onSelection(this.selectedTags).then(this.refresh.bind(this));
}
}
set scope(newScope) {
try {
this._scope = Array.from(newScope);
} catch(e) {
this._scope = null;
}
this.refresh();
}
get label() {
let count = this.selectedTags.size;
let mod = count === 1 ? 'singular' : count === 0 ? 'none' : 'plural';
return Zotero.getString('pane.tagSelector.numSelected.' + mod, [count]);
}
onResize() {
const COLLECTIONS_HEIGHT = 32; // minimum height of the collections pane and toolbar
//Zotero.debug('Updating tag selector size');
var zoteroPane = document.getElementById('zotero-pane-stack');
var splitter = document.getElementById('zotero-tags-splitter');
var tagSelector = document.getElementById('zotero-tag-selector');
// Nothing should be bigger than appcontent's height
var max = document.getElementById('appcontent').boxObject.height
- splitter.boxObject.height;
// Shrink tag selector to appcontent's height
var maxTS = max - COLLECTIONS_HEIGHT;
var tsHeight = parseInt(tagSelector.getAttribute("height"));
if (tsHeight > maxTS) {
//Zotero.debug("Limiting tag selector height to appcontent");
tagSelector.setAttribute('height', maxTS);
}
tagSelector.style.height = tsHeight + 'px';
var height = tagSelector.getBoundingClientRect().height;
/*Zotero.debug("tagSelector.boxObject.height: " + tagSelector.boxObject.height);
Zotero.debug("tagSelector.getAttribute('height'): " + tagSelector.getAttribute('height'));
Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height);
Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height'));*/
// Don't let the Z-pane jump back down to its previous height
// (if shrinking or hiding the tag selector let it clear the min-height)
if (zoteroPane.getAttribute('height') < zoteroPane.boxObject.height) {
//Zotero.debug("Setting Zotero pane height attribute to " + zoteroPane.boxObject.height);
zoteroPane.setAttribute('height', zoteroPane.boxObject.height);
}
if (tagSelector.getAttribute('collapsed') == 'true') {
// 32px is the default Z pane min-height in overlay.css
height = 32;
}
else {
// tS.boxObject.height doesn't exist at startup, so get from attribute
if (!height) {
height = parseInt(tagSelector.getAttribute('height'));
}
// 121px seems to be enough room for the toolbar and collections
// tree at minimum height
height = height + COLLECTIONS_HEIGHT;
}
//Zotero.debug('Setting Zotero pane minheight to ' + height);
zoteroPane.setAttribute('minheight', height);
if (this.isShowing() && !this.isFullScreen()) {
zoteroPane.setAttribute('savedHeight', zoteroPane.boxObject.height);
}
// Fix bug whereby resizing the Z pane downward after resizing
// the tag selector up and then down sometimes caused the Z pane to
// stay at a fixed size and get pushed below the bottom
tagSelector.height++;
tagSelector.height--;
}
static init(domEl, opts) {
var ref;
console.log(domEl.style);
ReactDom.render(<TagSelectorContainer ref={c => ref = c } {...opts} />, domEl);
ref.domEl = domEl;
new MutationObserver(ref.onResize).observe(domEl, {attributes: true, attributeFilter: ['height']});
return ref;
}
}
})();

View file

@ -0,0 +1,58 @@
<?xml version="1.0"?>
<!--
***** BEGIN LICENSE BLOCK *****
Copyright © 2017 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 overlay [
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> %globalDTD;
<!ENTITY % zoteroDTD SYSTEM "chrome://zotero/locale/zotero.dtd"> %zoteroDTD;
]>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<menupopup id="tag-menu">
<menuitem label="&zotero.tagSelector.assignColor;"
oncommand="ZoteroPane_Local.tagSelector.openColorPickerWindow(); event.stopPropagation();"/>
<menuitem label="&zotero.tagSelector.renameTag;"
oncommand="ZoteroPane_Local.tagSelector.openRenamePrompt(); event.stopPropagation();"/>
<menuitem label="&zotero.tagSelector.deleteTag;"
oncommand="ZoteroPane_Local.tagSelector.openDeletePrompt(); event.stopPropagation();"/>
</menupopup>
<menupopup id="tag-selector-view-settings-menu"
onpopupshowing="
document.getElementById('show-automatic').setAttribute('checked', ZoteroPane_Local.tagSelector.showAutomatic);
document.getElementById('display-all-tags').setAttribute('checked', !ZoteroPane_Local.tagSelector.filterToScope);
document.getElementById('num-selected').label = ZoteroPane_Local.tagSelector.label">
<menuitem id="num-selected" disabled="true"/>
<menuitem id="deselect-all" label="&zotero.tagSelector.clearAll;"
oncommand="ZoteroPane_Local.tagSelector.deselectAll(); event.stopPropagation();"/>
<menuseparator/>
<menuitem id="show-automatic" label="&zotero.tagSelector.showAutomatic;" type="checkbox"
oncommand="ZoteroPane_Local.tagSelector.toggleShowAutomatic(); event.stopPropagation();"/>
<menuitem
id="display-all-tags"
label="&zotero.tagSelector.displayAllInLibrary;"
type="checkbox"
oncommand="ZoteroPane_Local.tagSelector.toggleFilterToScope(); event.stopPropagation();"
/>
</menupopup>
</overlay>

View file

@ -0,0 +1,40 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2018 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 *****
*/
'use strict';
ZoteroPane.React = {
init() {
var tagSelector = document.getElementById('zotero-tag-selector');
ZoteroPane_Local.tagSelector = ZoteroPane.React.TagSelector.init(tagSelector, {
onSelection: ZoteroPane_Local.updateTagFilter.bind(ZoteroPane_Local)
});
},
destroy() {
ZoteroPane_Local.tagSelector.unregister();
}
}

View file

@ -0,0 +1,35 @@
<?xml version="1.0"?>
<!--
***** BEGIN LICENSE BLOCK *****
Copyright © 2018 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 *****
-->
<?xml-stylesheet href="chrome://zotero/skin/zotero-react-client.css"?>
<?xul-overlay href="chrome://zotero/content/reactUI/tagSelector.xul"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="chrome://zotero/content/include.js"></script>
<script src="zoteroPane.js"></script>
<script src="containers/tag-selector.js"></script>
</overlay>

View file

@ -287,4 +287,7 @@
<stack id="zotero-pane-stack" fullscreenmode="true" flex="1"/>
</vbox>
</hbox>
<menupopup id="tag-menu"/>
<menupopup id="tag-selector-view-settings-menu"/>
</window>

View file

@ -73,6 +73,7 @@ Zotero.Notifier = new function(){
if (priority) {
msg += " with priority " + priority;
}
Zotero.debug(msg);
_observers[hash] = {
ref: ref,
types: types,

View file

@ -58,8 +58,6 @@ var ZoteroPane = new function()
this.document = document;
const COLLECTIONS_HEIGHT = 32; // minimum height of the collections pane and toolbar
var self = this,
_loaded = false, _madeVisible = false,
titlebarcolorState, titleState, observerService,
@ -171,11 +169,6 @@ var ZoteroPane = new function()
itemsTree.addEventListener("mousedown", ZoteroPane_Local.onTreeMouseDown, true);
itemsTree.addEventListener("click", ZoteroPane_Local.onTreeClick, true);
var tagSelector = document.getElementById('zotero-tag-selector');
tagSelector.onchange = function () {
return ZoteroPane_Local.updateTagFilter();
};
Zotero.Keys.windowInit(document);
if (Zotero.restoreFromServer) {
@ -240,6 +233,7 @@ var ZoteroPane = new function()
}
catch (e) {}
}
ZoteroPane.React.init();
if (Zotero.openPane) {
Zotero.openPane = false;
@ -351,8 +345,7 @@ var ZoteroPane = new function()
this.serializePersist();
}
var tagSelector = document.getElementById('zotero-tag-selector');
tagSelector.unregister();
ZoteroPane_Local.React.destroy();
if(this.collectionsView) this.collectionsView.unregister();
if(this.itemsView) this.itemsView.unregister();
@ -1106,85 +1099,17 @@ var ZoteroPane = new function()
// and focus filter textbox
if (showing) {
yield this.setTagScope();
tagSelector.focusTextbox();
ZoteroPane_Local.tagSelector.focusTextbox();
}
// If hiding, clear selection
else {
tagSelector.uninit();
ZoteroPane_Local.tagSelector.uninit();
}
});
this.updateTagSelectorSize = function () {
//Zotero.debug('Updating tag selector size');
var zoteroPane = document.getElementById('zotero-pane-stack');
var splitter = document.getElementById('zotero-tags-splitter');
var tagSelector = document.getElementById('zotero-tag-selector');
// Nothing should be bigger than appcontent's height
var max = document.getElementById('appcontent').boxObject.height
- splitter.boxObject.height;
// Shrink tag selector to appcontent's height
var maxTS = max - COLLECTIONS_HEIGHT;
if (parseInt(tagSelector.getAttribute("height")) > maxTS) {
//Zotero.debug("Limiting tag selector height to appcontent");
tagSelector.setAttribute('height', maxTS);
}
var height = tagSelector.boxObject.height;
/*Zotero.debug("tagSelector.boxObject.height: " + tagSelector.boxObject.height);
Zotero.debug("tagSelector.getAttribute('height'): " + tagSelector.getAttribute('height'));
Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height);
Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height'));*/
// Don't let the Z-pane jump back down to its previous height
// (if shrinking or hiding the tag selector let it clear the min-height)
if (zoteroPane.getAttribute('height') < zoteroPane.boxObject.height) {
//Zotero.debug("Setting Zotero pane height attribute to " + zoteroPane.boxObject.height);
zoteroPane.setAttribute('height', zoteroPane.boxObject.height);
}
if (tagSelector.getAttribute('collapsed') == 'true') {
// 32px is the default Z pane min-height in overlay.css
height = 32;
}
else {
// tS.boxObject.height doesn't exist at startup, so get from attribute
if (!height) {
height = parseInt(tagSelector.getAttribute('height'));
}
// 121px seems to be enough room for the toolbar and collections
// tree at minimum height
height = height + COLLECTIONS_HEIGHT;
}
//Zotero.debug('Setting Zotero pane minheight to ' + height);
zoteroPane.setAttribute('minheight', height);
if (this.isShowing() && !this.isFullScreen()) {
zoteroPane.setAttribute('savedHeight', zoteroPane.boxObject.height);
}
// Fix bug whereby resizing the Z pane downward after resizing
// the tag selector up and then down sometimes caused the Z pane to
// stay at a fixed size and get pushed below the bottom
tagSelector.height++;
tagSelector.height--;
}
function getTagSelection() {
var tagSelector = document.getElementById('zotero-tag-selector');
return tagSelector.selection ? tagSelector.selection : new Set();
}
this.clearTagSelection = function () {
document.getElementById('zotero-tag-selector').deselectAll();
}
@ -1193,7 +1118,7 @@ var ZoteroPane = new function()
*/
this.updateTagFilter = Zotero.Promise.coroutine(function* () {
if (this.itemsView) {
yield this.itemsView.setFilter('tags', getTagSelection());
yield this.itemsView.setFilter('tags', ZoteroPane_Local.tagSelector.getTagSelection());
}
});
@ -1212,23 +1137,23 @@ var ZoteroPane = new function()
*
* Passed to the items tree to trigger on changes
*/
this.setTagScope = Zotero.Promise.coroutine(function* () {
var collectionTreeRow = this.getCollectionTreeRow();
this.setTagScope = async function () {
var collectionTreeRow = self.getCollectionTreeRow();
var tagSelector = document.getElementById('zotero-tag-selector');
if (this.tagSelectorShown()) {
if (self.tagSelectorShown()) {
Zotero.debug('Updating tag selector with current tags');
if (collectionTreeRow.editable) {
tagSelector.mode = 'edit';
ZoteroPane_Local.tagSelector.setMode('edit');
}
else {
tagSelector.mode = 'view';
ZoteroPane_Local.tagSelector.setMode('view');
}
tagSelector.collectionTreeRow = collectionTreeRow;
tagSelector.updateScope = () => this.setTagScope();
tagSelector.libraryID = collectionTreeRow.ref.libraryID;
tagSelector.scope = yield collectionTreeRow.getChildTags();
ZoteroPane_Local.tagSelector.collectionTreeRow = collectionTreeRow;
ZoteroPane_Local.tagSelector.updateScope = self.setTagScope;
ZoteroPane_Local.tagSelector.libraryID = collectionTreeRow.ref.libraryID;
ZoteroPane_Local.tagSelector.scope = await collectionTreeRow.getChildTags();
}
});
};
this.onCollectionSelected = function () {
@ -1280,7 +1205,7 @@ var ZoteroPane = new function()
}*/
collectionTreeRow.setSearch('');
collectionTreeRow.setTags(getTagSelection());
collectionTreeRow.setTags(ZoteroPane_Local.tagSelector.getTagSelection());
this._updateToolbarIconsForRow(collectionTreeRow);

View file

@ -27,6 +27,7 @@
<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero-platform/content/overlay.css" type="text/css"?>
<?xul-overlay href="chrome://zotero/content/reactUI/zoteroPane.xul"?>
<!DOCTYPE overlay [
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> %globalDTD;
@ -35,7 +36,8 @@
]>
<overlay id="zotero"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<script src="include.js"/>
<script src="zoteroPane.js" type="application/javascript;version=1.8"/>
@ -304,7 +306,7 @@
ondragover="return ZoteroPane_Local.collectionsView.onDragOver(event)"
ondrop="return ZoteroPane_Local.collectionsView.onDrop(event)"/>
</tree>
<splitter id="zotero-tags-splitter" onmouseup="ZoteroPane_Local.updateTagSelectorSize()" collapse="after"
<splitter id="zotero-tags-splitter" collapse="after"
zotero-persist="state">
<grippy oncommand="ZoteroPane_Local.toggleTagSelector()"/>
</splitter>
@ -314,7 +316,7 @@
TODO: deal with this some other way?
-->
<zoterotagselector id="zotero-tag-selector" zotero-persist="height,collapsed,showAutomatic,filterToScope"/>
<html:div id="zotero-tag-selector" zotero-persist="height,collapsed,showAutomatic,filterToScope"/>
</vbox>
<splitter id="zotero-collections-splitter" resizebefore="closest" resizeafter="closest" collapse="before"

View file

@ -1,6 +1,5 @@
/* Font sizes */
*[zoteroFontSize=medium] #zotero-tb-search, *[zoteroFontSize=large] #zotero-tb-search,
*[zoteroFontSize=medium] zoterotagselector textbox, *[zoteroFontSize=large] zoterotagselector textbox
{
font-size: 1em !important;
}
@ -81,11 +80,6 @@ zoterosearch
-moz-binding: url('chrome://zotero/content/bindings/zoterosearch.xml#search-box');
}
zoterotagselector
{
-moz-binding: url('chrome://zotero/content/bindings/tagselector.xml#tag-selector');
}
zoterosearchcondition
{

326
package-lock.json generated
View file

@ -409,6 +409,29 @@
"esutils": "^2.0.0"
}
},
"babel-helper-function-name": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
"integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
"dev": true,
"requires": {
"babel-helper-get-function-arity": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1",
"babel-traverse": "^6.24.1",
"babel-types": "^6.24.1"
}
},
"babel-helper-get-function-arity": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
"integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
"dev": true,
"requires": {
"babel-runtime": "^6.22.0",
"babel-types": "^6.24.1"
}
},
"babel-helpers": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
@ -436,7 +459,7 @@
},
"babel-plugin-syntax-class-properties": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
"resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
"integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=",
"dev": true
},
@ -476,6 +499,18 @@
"integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=",
"dev": true
},
"babel-plugin-transform-class-properties": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz",
"integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=",
"dev": true,
"requires": {
"babel-helper-function-name": "^6.24.1",
"babel-plugin-syntax-class-properties": "^6.8.0",
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1"
}
},
"babel-plugin-transform-es2015-modules-commonjs": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz",
@ -841,11 +876,6 @@
"hoek": "2.x.x"
}
},
"bootstrap-sass": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/bootstrap-sass/-/bootstrap-sass-3.3.7.tgz",
"integrity": "sha1-ZZbHq0D2Y3OTMjqwvIDQZPxjBJg="
},
"brace-expansion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
@ -1159,6 +1189,11 @@
"safe-buffer": "^5.0.1"
}
},
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@ -1350,9 +1385,9 @@
}
},
"create-react-class": {
"version": "15.6.0",
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz",
"integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=",
"version": "15.6.3",
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz",
"integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==",
"requires": {
"fbjs": "^0.8.9",
"loose-envify": "^1.3.1",
@ -1444,11 +1479,6 @@
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
},
"deep-diff": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.4.tgz",
"integrity": "sha1-qsXDmVIjar5fA3ojSQYLoBsArkg="
},
"deep-eql": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
@ -1458,11 +1488,6 @@
"type-detect": "^4.0.0"
}
},
"deep-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
},
"define-properties": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
@ -1647,11 +1672,6 @@
"is-symbol": "^1.0.1"
}
},
"es6-promise": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
"integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@ -1768,9 +1788,9 @@
"dev": true
},
"fbjs": {
"version": "0.8.12",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.12.tgz",
"integrity": "sha1-ELXZL3bUVXX9Y6IX1OoCvqL47QQ=",
"version": "0.8.17",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
@ -1778,7 +1798,14 @@
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.9"
"ua-parser-js": "^0.7.18"
},
"dependencies": {
"ua-parser-js": {
"version": "0.7.19",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz",
"integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ=="
}
}
},
"filename-regex": {
@ -1810,19 +1837,6 @@
"pinkie-promise": "^2.0.0"
}
},
"floatthead": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/floatthead/-/floatthead-1.4.0.tgz",
"integrity": "sha1-0s/94CBGWe1TcDQFDOluq/5z6tM="
},
"flux-standard-action": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/flux-standard-action/-/flux-standard-action-0.6.1.tgz",
"integrity": "sha1-bzQhG5SDTqHDzDD056+tPQ+/caI=",
"requires": {
"lodash.isplainobject": "^3.2.0"
}
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@ -2672,17 +2686,6 @@
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
"dev": true
},
"history": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/history/-/history-2.1.2.tgz",
"integrity": "sha1-SqLeiXoOSGfkU5hDvm7Nsphr/ew=",
"requires": {
"deep-equal": "^1.0.0",
"invariant": "^2.0.0",
"query-string": "^3.0.0",
"warning": "^2.0.0"
}
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@ -2700,11 +2703,6 @@
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
"dev": true
},
"hoist-non-react-statics": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
"integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs="
},
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@ -2821,6 +2819,7 @@
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
"integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
"dev": true,
"requires": {
"loose-envify": "^1.0.0"
}
@ -3047,11 +3046,6 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
},
"jquery": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz",
"integrity": "sha1-LInWiJterFIqfuoywUUhVZxsvwI="
},
"js-base64": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.5.tgz",
@ -3223,25 +3217,6 @@
"astw": "^2.0.0"
}
},
"libzotero": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/libzotero/-/libzotero-0.1.13.tgz",
"integrity": "sha1-QQhFtsHa9jmSZwIfnLw0t8SBuzI=",
"requires": {
"es6-promise": "^3.1.2",
"node-fetch": "^1.6.0",
"spark-md5": "^2.0.2",
"striptags": "^2.1.1",
"whatwg-fetch": "^1.0.0"
},
"dependencies": {
"whatwg-fetch": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-1.1.1.tgz",
"integrity": "sha1-rDydOfMgxtzlM5lp0FTvQ90zMxk="
}
}
},
"load-json-file": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
@ -3258,12 +3233,8 @@
"lodash": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
},
"lodash-es": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.4.tgz",
"integrity": "sha1-3MHXVS4VCgZABzupyzHXDwMpUOc="
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
"dev": true
},
"lodash._baseassign": {
"version": "3.2.0",
@ -3287,11 +3258,6 @@
"integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=",
"dev": true
},
"lodash._basefor": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz",
"integrity": "sha1-dVC06SGO8J+tJDQ7YSAhx5tMIMI="
},
"lodash._getnative": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
@ -3336,22 +3302,14 @@
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo="
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
"dev": true
},
"lodash.isarray": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U="
},
"lodash.isplainobject": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz",
"integrity": "sha1-moI4rhayAEMpYM1zRlEtASP79MU=",
"requires": {
"lodash._basefor": "^3.0.0",
"lodash.isarguments": "^3.0.0",
"lodash.keysin": "^3.0.0"
}
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
"dev": true
},
"lodash.keys": {
"version": "3.1.2",
@ -3364,15 +3322,6 @@
"lodash.isarray": "^3.0.0"
}
},
"lodash.keysin": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/lodash.keysin/-/lodash.keysin-3.0.8.tgz",
"integrity": "sha1-IsRJPrvtsUJ5YqVLRFssinZ/tH8=",
"requires": {
"lodash.isarguments": "^3.0.0",
"lodash.isarray": "^3.0.0"
}
},
"lodash.memoize": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
@ -4093,12 +4042,12 @@
}
},
"prop-types": {
"version": "15.5.10",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz",
"integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=",
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
"integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
"requires": {
"fbjs": "^0.8.9",
"loose-envify": "^1.3.1"
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
}
},
"pseudomap": {
@ -4132,14 +4081,6 @@
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
"dev": true
},
"query-string": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-3.0.3.tgz",
"integrity": "sha1-ri4UtNBQcdTpuetIc8NbDc1C5jg=",
"requires": {
"strict-uri-encode": "^1.0.0"
}
},
"querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@ -4235,41 +4176,6 @@
"prop-types": "^15.5.10"
}
},
"react-redux": {
"version": "4.4.8",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-4.4.8.tgz",
"integrity": "sha1-57wd0QDotk6WrIIS2xEyObni4I8=",
"requires": {
"create-react-class": "^15.5.1",
"hoist-non-react-statics": "^1.0.3",
"invariant": "^2.0.0",
"lodash": "^4.2.0",
"loose-envify": "^1.1.0",
"prop-types": "^15.5.4"
}
},
"react-router": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-2.8.1.tgz",
"integrity": "sha1-c+lJH2zrMW0Pd5gpCBhj43juTtc=",
"requires": {
"history": "^2.1.2",
"hoist-non-react-statics": "^1.2.0",
"invariant": "^2.2.1",
"loose-envify": "^1.2.0",
"warning": "^3.0.0"
},
"dependencies": {
"warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
"read-only-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
@ -4337,46 +4243,6 @@
"strip-indent": "^1.0.1"
}
},
"redux": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/redux/-/redux-3.7.0.tgz",
"integrity": "sha512-GHjaOkEQtQnnuLoYPFkRKHIqs1i1tdTlisu/xUHfk2juzCobSy4STxs4Lz5bPkc07Owb6BeGKx/r76c9IVTkOw==",
"requires": {
"lodash": "^4.2.1",
"lodash-es": "^4.2.1",
"loose-envify": "^1.1.0",
"symbol-observable": "^1.0.3"
}
},
"redux-logger": {
"version": "2.10.2",
"resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-2.10.2.tgz",
"integrity": "sha1-PFpfCm8yV3wd6t9mVfJX+CxsOTc=",
"requires": {
"deep-diff": "0.3.4"
}
},
"redux-promise": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/redux-promise/-/redux-promise-0.5.3.tgz",
"integrity": "sha1-6X5snTvzdurLebq+bZBtogES1tg=",
"requires": {
"flux-standard-action": "^0.6.1"
}
},
"redux-router": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/redux-router/-/redux-router-2.1.2.tgz",
"integrity": "sha1-A0ckutCPQbr3VNtNIuLYWK22TxE=",
"requires": {
"deep-equal": "^1.0.1"
}
},
"redux-thunk": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.2.0.tgz",
"integrity": "sha1-5hWhbha0ehmlFXZhM9Hj6Zt4UuU="
},
"regenerator-runtime": {
"version": "0.10.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
@ -4664,11 +4530,6 @@
"source-map": "^0.5.6"
}
},
"spark-md5": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-2.0.2.tgz",
"integrity": "sha1-N7djhHdjrn56zvLKUjPQHmSaeLc="
},
"spdx-correct": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
@ -4777,11 +4638,6 @@
"readable-stream": "^2.0.2"
}
},
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
@ -4843,11 +4699,6 @@
"get-stdin": "^4.0.1"
}
},
"striptags": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/striptags/-/striptags-2.2.1.tgz",
"integrity": "sha1-TEULcI1BuL85zyTEn/I0/Gqr/TI="
},
"subarg": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
@ -4871,11 +4722,6 @@
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
"dev": true
},
"symbol-observable": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz",
"integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0="
},
"syntax-error": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.3.0.tgz",
@ -5021,7 +4867,8 @@
"ua-parser-js": {
"version": "0.7.12",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.12.tgz",
"integrity": "sha1-BMgamb3V3FImPqKdJMa/jUgYpLs="
"integrity": "sha1-BMgamb3V3FImPqKdJMa/jUgYpLs=",
"dev": true
},
"umd": {
"version": "3.0.1",
@ -5110,19 +4957,6 @@
"indexof": "0.0.1"
}
},
"w3c-xmlhttprequest": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/w3c-xmlhttprequest/-/w3c-xmlhttprequest-2.1.2.tgz",
"integrity": "sha1-8LyJN1YoKOLCubQDZkhqE1H+QaA="
},
"warning": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-2.1.0.tgz",
"integrity": "sha1-ISINnGOvx3qMkhEeARr3Bc4MaQE=",
"requires": {
"loose-envify": "^1.0.0"
}
},
"whatwg-fetch": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
@ -5231,28 +5065,6 @@
"dev": true
}
}
},
"zotero-web-library": {
"version": "0.9.4-alpha",
"resolved": "https://registry.npmjs.org/zotero-web-library/-/zotero-web-library-0.9.4-alpha.tgz",
"integrity": "sha1-AbLMXXqmsDEbhNZeLsc6llBdqS4=",
"requires": {
"bootstrap-sass": "^3.3.6",
"floatthead": "1.4.0",
"history": "<3.0.0",
"jquery": "^2.2.4",
"libzotero": "^0.1.8",
"react": "^15.3.2",
"react-dom": "^15.3.2",
"react-redux": "^4.4.5",
"react-router": "<3.0.0",
"redux": "^3.6.0",
"redux-logger": "^2.6.1",
"redux-promise": "^0.5.3",
"redux-router": "^2.1.2",
"redux-thunk": "^2.1.0",
"w3c-xmlhttprequest": "^2.1.0"
}
}
}
}

View file

@ -16,20 +16,21 @@
"license": "",
"dependencies": {
"bluebird": "^3.5.1",
"classnames": "^2.2.6",
"prop-types": "^15.6.2",
"react": "^15.6.2",
"react-dom": "^15.6.2",
"zotero-web-library": "^0.9.4-alpha"
"react-dom": "^15.6.2"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-plugin-syntax-async-generators": "^6.13.0",
"babel-plugin-syntax-class-properties": "^6.13.0",
"babel-plugin-syntax-decorators": "^6.13.0",
"babel-plugin-syntax-do-expressions": "^6.13.0",
"babel-plugin-syntax-export-extensions": "^6.13.0",
"babel-plugin-syntax-flow": "^6.13.0",
"babel-plugin-syntax-jsx": "^6.13.0",
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"babel-preset-react": "^6.16.0",
"browserify": "^14.5.0",

1
resource/classnames.js Symbolic link
View file

@ -0,0 +1 @@
../node_modules/classnames/index.js

1
resource/prop-types.js Symbolic link
View file

@ -0,0 +1 @@
../node_modules/prop-types/prop-types.js

View file

@ -0,0 +1,155 @@
/* eslint-disable react/no-deprecated */
'use strict';
const React = require('react');
const PropTypes = require('prop-types');
const cx = require('classnames');
const { noop } = () => {};
class Input extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
value: props.value
};
}
cancel(event = null) {
this.props.onCancel(this.hasChanged, event);
this.hasBeenCancelled = true;
this.input.blur();
}
commit(event = null) {
this.props.onCommit(this.state.value, this.hasChanged, event);
this.hasBeenCommitted = true;
}
focus() {
if(this.input != null) {
this.input.focus();
this.props.selectOnFocus && this.input.select();
}
}
componentWillReceiveProps({ value }) {
if (value !== this.props.value) {
this.setState({ value });
}
}
handleChange({ target }) {
this.setState({ value: target.value });
this.props.onChange(target.value);
}
handleBlur(event) {
const shouldCancel = this.props.onBlur(event);
if (this.hasBeenCancelled || this.hasBeenCommitted) { return; }
shouldCancel ? this.cancel(event) : this.commit(event);
}
handleFocus(event) {
this.props.selectOnFocus && event.target.select();
this.props.onFocus(event);
}
handleKeyDown(event) {
switch (event.key) {
case 'Escape':
this.cancel(event);
break;
case 'Enter':
this.commit(event);
break;
default:
return;
}
}
get hasChanged() {
return this.state.value !== this.props.value;
}
render() {
this.hasBeenCancelled = false;
this.hasBeenCommitted = false;
const extraProps = Object.keys(this.props).reduce((aggr, key) => {
if(key.match(/^(aria-|data-).*/)) {
aggr[key] = this.props[key];
}
return aggr;
}, {});
const input = <input
autoFocus={ this.props.autoFocus }
className={ this.props.className }
disabled={ this.props.isDisabled }
form={ this.props.form }
id={ this.props.id }
inputMode={ this.props.inputMode }
max={ this.props.max }
maxLength={ this.props.maxLength }
min={ this.props.min }
minLength={ this.props.minLength }
name={ this.props.name }
onBlur={ this.handleBlur.bind(this) }
onChange={ this.handleChange.bind(this) }
onFocus={ this.handleFocus.bind(this) }
onKeyDown={ this.handleKeyDown.bind(this) }
placeholder={ this.props.placeholder }
readOnly={ this.props.isReadOnly }
ref={ input => this.input = input }
required={ this.props.isRequired }
size={ this.props.size }
spellCheck={ this.props.spellCheck }
step={ this.props.step }
tabIndex={ this.props.tabIndex }
type={ this.props.type }
value={ this.state.value }
{ ...extraProps }
/>;
return input;
}
static defaultProps = {
className: 'form-control',
onBlur: noop,
onCancel: noop,
onChange: noop,
onCommit: noop,
onFocus: noop,
tabIndex: -1,
type: 'text',
value: '',
};
static propTypes = {
autoFocus: PropTypes.bool,
className: PropTypes.string,
form: PropTypes.string,
id: PropTypes.string,
inputMode: PropTypes.string,
isDisabled: PropTypes.bool,
isReadOnly: PropTypes.bool,
isRequired: PropTypes.bool,
max: PropTypes.number,
maxLength: PropTypes.number,
min: PropTypes.number,
minLength: PropTypes.number,
name: PropTypes.string,
onBlur: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onCommit: PropTypes.func.isRequired,
onFocus: PropTypes.func.isRequired,
placeholder: PropTypes.string,
selectOnFocus: PropTypes.bool,
spellCheck: PropTypes.bool,
step: PropTypes.number,
tabIndex: PropTypes.number,
type: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
};
}
module.exports = Input;

View file

@ -0,0 +1,53 @@
'use strict';
const React = require('react');
const PropTypes = require('prop-types');
const TagList = require('./tag-selector/tag-list');
const Input = require('./form/input');
class TagSelector extends React.Component {
render() {
return (
<div className="tag-selector">
<TagList { ...this.props } />
<div className="tag-selector-filter-container">
<Input
type="search"
value={ this.props.searchString }
onChange={ this.props.onSearch }
className="tag-selector-filter"
size="1"
/>
<button className="tag-selector-actions" onClick={ ev => this.props.onSettings(ev) } />
</div>
</div>
);
}
}
TagSelector.propTypes = {
tags: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
selected: PropTypes.bool,
color: PropTypes.string,
disabled: PropTypes.bool
})),
searchString: PropTypes.string,
shouldFocus: PropTypes.bool,
onSelect: PropTypes.func,
onTagContext: PropTypes.func,
onSearch: PropTypes.func,
onSettings: PropTypes.func,
};
TagSelector.defaultProps = {
tags: [],
searchString: '',
shouldFocus: false,
onSelect: () => Promise.resolve(),
onTagContext: () => Promise.resolve(),
onSearch: () => Promise.resolve(),
onSettings: () => Promise.resolve()
};
module.exports = TagSelector;

View file

@ -0,0 +1,55 @@
const React = require('react');
const PropTypes = require('prop-types');
const cx = require('classnames');
class TagList extends React.PureComponent {
renderTag(index) {
const { tags } = this.props;
const tag = index < tags.length ?
tags[index] : {
tag: "",
};
const className = cx('tag-selector-item', {
selected: tag.selected,
colored: tag.color,
});
let props = {
className,
onClick: ev => this.props.onSelect(tag.name, ev),
onContextMenu: ev => this.props.onTagContext(tag, ev),
};
if(tag.color) {
props['style'] = {
color: tag.color,
};
}
return (
<li key={ index } { ...props }>
{ tag.name }
</li>
);
}
render() {
const totalTagCount = this.props.tags.length;
return (
<div
className="tag-selector-container"
ref={ ref => { this.container = ref } }>
<ul className="tag-selector-list">
{
[...Array(totalTagCount).keys()].map(index => this.renderTag(index))
}
</ul>
</div>
)
}
}
module.exports = TagList;

View file

@ -1,5 +1,7 @@
'use strict';
var Zotero;
var EXPORTED_SYMBOLS = ['require'];
var require = (function() {
@ -57,20 +59,41 @@ var require = (function() {
dump(msg + "\n\n");
};
function getZotero() {
if (typeof Zotero === 'undefined') {
try {
Zotero = Components.classes["@zotero.org/Zotero;1"]
.getService(Components.interfaces.nsISupports).wrappedJSObject;
} catch (e) {}
}
return Zotero || {};
}
var cons;
if (typeof console !== 'undefined') {
cons = console;
}
if (!cons) {
cons = {};
for (let key of ['log', 'warn', 'error']) {
cons[key] = text => {getZotero(); typeof Zotero !== 'undefined' && false && Zotero.debug(`console.${key}: ${text}`)};
}
}
let globals = {
document: typeof document !== 'undefined' && document || {},
console: cons,
navigator: typeof navigator !== 'undefined' && navigator || {},
window,
setTimeout: window.setTimeout,
clearTimeout: window.clearTimeout,
};
Object.defineProperty(globals, 'Zotero', { get: getZotero });
var loader = Loader({
id: 'zotero/require',
paths: {
'': 'resource://zotero/',
},
globals: {
document: typeof document !== 'undefined' && document || {},
console: typeof console !== 'undefined' && console || {},
navigator: typeof navigator !== 'undefined' && navigator || {},
window,
setTimeout: window.setTimeout,
clearTimeout: window.clearTimeout,
Zotero: typeof Zotero !== 'undefined' && Zotero || {}
}
globals
});
return Require(loader, requirer);

View file

@ -1 +0,0 @@
../node_modules/zotero-web-library/lib

View file

@ -12,7 +12,7 @@ const cluster = require('cluster');
async function babelWorker(ev) {
const t1 = Date.now();
const sourcefile = ev.file;
const outfile = path.join('build', sourcefile);
const outfile = path.join('build', sourcefile.replace('.jsx', '.js'));
const postError = (error) => {
process.send({
sourcefile,
@ -28,7 +28,8 @@ async function babelWorker(ev) {
let contents = await fs.readFile(sourcefile, 'utf8');
if (sourcefile === 'resource/react-dom.js') {
// patch react
transformed = contents.replace(/ownerDocument\.createElement\((.*?)\)/gi, 'ownerDocument.createElementNS(DOMNamespaces.html, $1)');
transformed = contents.replace(/ownerDocument\.createElement\((.*?)\)/gi, 'ownerDocument.createElementNS(DOMNamespaces.html, $1)')
.replace("isInputEventSupported = false", 'isInputEventSupported = true');
} else if ('ignore' in options && options.ignore.some(ignoreGlob => multimatch(sourcefile, ignoreGlob).length)) {
transformed = contents;
isSkipped = true;

View file

@ -6,7 +6,7 @@ const getJS = require('./js');
const getSass = require('./sass');
const getSymlinks = require('./symlinks');
const { formatDirsForMatcher, getSignatures, writeSignatures, cleanUp, onSuccess, onError} = require('./utils');
const { dirs, symlinkDirs, copyDirs, symlinkFiles, jsFiles, ignoreMask } = require('./config');
const { dirs, symlinkDirs, copyDirs, symlinkFiles, jsFiles, scssFiles, ignoreMask } = require('./config');
if (require.main === module) {
(async () => {
@ -23,7 +23,7 @@ if (require.main === module) {
getBrowserify(signatures),
getCopy(copyDirs.map(d => `${d}/**`), { ignore: ignoreMask }, signatures),
getJS(jsFiles, { ignore: ignoreMask }, signatures),
getSass('scss/*.scss', { root: 'scss', ignore: ignoreMask }, signatures),
getSass(scssFiles, { ignore: ignoreMask }, signatures),
getSymlinks(symlinks, { nodir: true, ignore: ignoreMask }, signatures),
getSymlinks(symlinkDirs, { ignore: ignoreMask }, signatures),
cleanUp(signatures)

View file

@ -49,13 +49,20 @@ const browserifyConfigs = [
];
// exclude mask used for js, copy, symlink and sass tasks
const ignoreMask = ['**/#*'];
const ignoreMask = ['**/#*', '**/_*.scss'];
const jsFiles = [
`{${dirs.join(',')}}/**/*.js`,
`!{${symlinkDirs.concat(copyDirs).join(',')}}/**/*.js`
`{${dirs.join(',')}}/**/*.jsx`,
`!{${symlinkDirs.concat(copyDirs).join(',')}}/**/*.js`,
`!{${symlinkDirs.concat(copyDirs).join(',')}}/**/*.jsx`
];
const scssFiles = [
'scss/**/*.scss',
'chrome/skin/default/zotero/**/*.scss'
];
module.exports = {
dirs, symlinkDirs, copyDirs, symlinkFiles, browserifyConfigs, jsFiles, ignoreMask
dirs, symlinkDirs, copyDirs, symlinkFiles, browserifyConfigs, jsFiles, scssFiles, ignoreMask
};

View file

@ -11,7 +11,7 @@ const sassRender = universalify.fromCallback(sass.render);
const ROOT = path.resolve(__dirname, '..');
async function getSass(source, options, signatures) {
async function getSass(source, options, signatures={}) {
const t1 = Date.now();
const files = await globby(source, Object.assign({ cwd: ROOT }, options ));
const totalCount = files.length;
@ -20,7 +20,9 @@ async function getSass(source, options, signatures) {
while ((f = files.pop()) != null) {
let newFileSignature = await getFileSignature(f);
const dest = path.join.apply(this, ['build', 'chrome', 'skin', 'default', 'zotero', 'components', getPathRelativeTo(f, 'scss')]);
let destFile = getPathRelativeTo(f, 'scss');
destFile = path.join(path.dirname(destFile), path.basename(destFile, '.scss') + '.css');
const dest = path.join.apply(this, ['build', 'chrome', 'skin', 'default', 'zotero', destFile]);
if (f in signatures) {
if (compareSignatures(newFileSignature, signatures[f])) {
@ -34,10 +36,14 @@ async function getSass(source, options, signatures) {
}
try {
const sass = await sassRender({
file: f
file: f,
outFile: dest,
sourceMap: true,
outputStyle: 'compressed'
});
await fs.outputFile(dest, sass);
await fs.outputFile(dest, sass.css);
await fs.outputFile(`${dest}.map`, sass.map);
onProgress(f, dest, 'sass');
signatures[f] = newFileSignature;
count++;

View file

@ -1,7 +1,7 @@
const path = require('path');
const chokidar = require('chokidar');
const multimatch = require('multimatch');
const { dirs, jsFiles, ignoreMask, copyDirs, symlinkFiles } = require('./config');
const { dirs, jsFiles, scssFiles, ignoreMask, copyDirs, symlinkFiles } = require('./config');
const { onSuccess, onError, getSignatures, writeSignatures, cleanUp, formatDirsForMatcher } = require('./utils');
const getJS = require('./js');
const getSass = require('./sass');
@ -49,8 +49,12 @@ function getWatch() {
try {
if (multimatch(path, jsFiles).length && !multimatch(path, ignoreMask).length) {
onSuccess(await getJS(path, { ignore: ignoreMask }, signatures));
} else if (multimatch(path, 'scss/*.scss').length) {
} else if (multimatch(path, scssFiles).length) {
if (multimatch(path, '**/_*.scss').length) {
onSuccess(await getSass(scssFiles, { ignore: ignoreMask }));
} else {
onSuccess(await getSass(path, {}, signatures));
}
} else if (multimatch(path, copyDirs.map(d => `${d}/**`)).length) {
onSuccess(await getCopy(path, {}, signatures));
} else if (multimatch(path, symlinks).length) {

View file

@ -0,0 +1,4 @@
//
// Functions
// --------------------------------------------------

View file

@ -0,0 +1,4 @@
//
// Mixins
// --------------------------------------------------

View file

@ -0,0 +1,3 @@
//
// Placeholders
// --------------------------------------------------

View file

@ -0,0 +1,4 @@
//
// Utilities
// --------------------------------------------------

View file

@ -0,0 +1,77 @@
//
// Variables
// --------------------------------------------------
// Dimensions
// --------------------------------------------------
$space-min: 4px;
$space-xs: 8px;
$space-sm: 12px;
$space-md: 16px;
$space-lg: 24px;
$space-xl: 32px;
$space-xxl: 48px;
$space-max: 64px;
$space-thumb: 42px;
// Typography
// --------------------------------------------------
$font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
$font-family-base: $font-family-sans-serif;
$font-size-base: 13px;
$font-size-h1: 20px;
$font-size-h2: 16px;
$line-height-base: 1.539;
$line-height-computed: ceil($font-size-base * $line-height-base);
// todo: line-height-small
$line-height-large: 26px;
$line-height-large-touch: $space-thumb;
$link-hover-decoration: underline;
$headings-font-family: inherit;
$headings-font-weight: 400;
$headings-line-height: 1.2;
// Components
// --------------------------------------------------
$padding-x-sm: $space-sm;
$padding-x-md: $space-md;
$default-padding-x: $space-sm;
$default-padding-x-touch: $space-md;
$border-radius: 4px;
$border-radius-sm: 3px;
$border-radius-lg: 6px;
$border-width: 1px;
$separator-width: 1px;
// Z-index master list
// --------------------------------------------------
$z-index-mobile-nav: 0;
$z-index-navbar-bg: 10;
$z-index-main: 10;
$z-index-level: 10;
$z-index-level-active: 20;
$z-index-navbar: 20;
$z-index-menu: 30;
$z-index-modal: 40;
$z-index-drag-layer: 50;
$z-index-loading-cover: 60;

View file

@ -0,0 +1,86 @@
//
// Tag selector
// --------------------------------------------------
.library .tag-selector {
height: 160px;
}
.tag-selector {
display: flex;
flex: 1 0 100%;
flex-direction: column;
overflow: hidden;
background-color: $tag-selector-bg;
}
.tag-selector-container {
flex: 1 1 auto;
justify-content: space-between;
overflow: auto;
height: auto;
}
.tag-selector-filter-container {
height: auto;
flex: 0 0 1em;
display: flex;
flex-direction: row;
padding: 0.25em;
}
.tag-selector-list {
list-style: none;
display: inline-block;
margin: 0;
padding: 0;
}
.tag-selector-filter {
flex: 1 0 40px;
min-width: 40px;
}
.tag-selector-actions {
flex: 0 0 32px;
}
.tag-selector-item {
border-radius: 1em;
border: 1px solid transparent;
cursor: pointer;
display: inline-block;
padding: .05em .5em;
margin: 0;
&.selected {
background-color: $secondary;
border: 1px solid $secondary;
}
&.colored {
font-weight: bold;
}
&.disabled {
opacity: .6;
cursor: auto;
pointer-events: none;
}
&:not(.disabled):hover {
background-color: lighten($secondary, 15%);
border: 1px solid $secondary;
}
}
#zotero-tag-selector {
min-height: 100px;
width: 100%;
display: flex;
}
#zotero-tag-selector[collapsed=true] {
visibility: collapse;
}

179
scss/themes/_light.scss Normal file
View file

@ -0,0 +1,179 @@
//
// Light theme
// --------------------------------------------------
// Colors
// --------------------------------------------------
$red: #cc2936;
$blue: #2e4de5; // 230° 80 90
$asphalt: #7a8799;
$asphalt-light: #dadee3;
$primary: $red;
$secondary: $blue;
$blue-dark: #333c66;
$blue-darkest: #3d4466;
$transparent: transparent;
// Grays
// --------------------------------------------------
$shade-0: #fff;
$shade-1: #f6f6f6;
$shade-2: #e6e6e6;
$shade-3: #ddd;
$shade-4: #ccc;
$shade-5: #bbb;
$shade-6: #777;
$shade-7: #555;
$shade-8: #444;
$shade-9: #222;
$shade-10: #000;
// Scaffolding
// --------------------------------------------------
$body-bg: $shade-0;
$text-color: $shade-9;
$headings-color: $shade-8;
$link-color: $blue;
$link-hover-color: $blue;
$focus-color: $secondary;
// Typography
// --------------------------------------------------
$text-selection-color: inherit;
$text-selection-bg: rgba($secondary, 0.2);
// Separator
$separator-color: $shade-3;
// Partials
// --------------------------------------------------
// Main
$main-bg: $shade-0;
// Sidebar
$sidebar-bg: $shade-1;
// Components
// --------------------------------------------------
// Icon
$icon-color: $secondary;
$icon-active-color: $shade-9;
// Collection tree
$collection-tree-headings-color: $shade-6;
$collection-tree-link-color: $shade-8;
$collection-tree-active-link-color: $shade-9;
$collection-tree-active-icon-color: $shade-7;
$collection-tree-active-link-bg: $shade-2;
$collection-tree-focus-color: $asphalt;
$collection-tree-icon-color: $shade-6;
$touch-collection-tree-active-link-color: $shade-0;
$touch-collection-tree-active-link-bg: $blue;
$touch-collection-tree-border: $shade-2;
$touch-collection-tree-separator: $body-bg;
$collection-tree-dnd-target-link-color: $shade-0;
$collection-tree-dnd-target-bg-color: $blue;
$collection-tree-dnd-target-icon-color: $shade-0;
// Items
$items-bg: $shade-0;
$items-odd-bg: $shade-1;
$item-list-head-color: $shade-8;
$item-list-head-border: $shade-2;
// Item list
$item-odd-bg: $shade-1;
$item-active-color: $shade-0;
$item-active-bg: $secondary;
// Item details
$item-details-bg: $shade-1;
$info-view-color: $shade-5;
// Panel
$panel-bg: $shade-1;
$panel-header-color: $shade-8;
$panel-header-shadow: rgba($shade-10, 0.08);
// Tabs
$tabs-border-color: $shade-3;
$tab-active-color: $secondary;
$tab-inactive-color: $headings-color;
// Metadata list
$key-color: $shade-6;
$editable-hover-bg: rgba($shade-10, 0.04);
$handle-color-touch: $shade-4;
$handle-color-mouse: $shade-6;
$metadata-heading-color: $shade-5;
$metadata-separator-color: $shade-3;
// Button
$btn-primary-color: $shade-0;
$btn-primary-bg: $asphalt;
$btn-secondary-color: $secondary;
$btn-default-color: $text-color;
$btn-default-bg: $shade-0;
$btn-default-active-color: rgba($btn-default-color, 0.5);
$btn-icon-bg: $transparent;
$btn-icon-focus-color: $shade-0;
$btn-icon-focus-bg: $asphalt;
$btn-icon-focus-active-color: rgba($shade-0, 0.5);
$btn-icon-active-color: $shade-7;
$btn-icon-active-bg: $shade-2;
$twisty-color: $shade-6;
$twisty-focus-color: $asphalt;
$twisty-selected-color: $shade-7;
$twisty-dnd-target-color: $shade-0;
// Forms
$input-color: $text-color;
$input-bg: $body-bg;
$input-border-color: $shade-3;
$input-focus-color: $secondary;
$placeholder-color: $shade-5;
// Editable
$editable-color: $text-color;
$editable-bg: $shade-0;
$editable-border-color: $secondary;
// Menu (select, dropdown, )
$menu-bg: $body-bg;
$menu-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08), 0 3px 8px rgba(0, 0, 0, 0.2);
$menu-item-hover-bg: $shade-1;
$menu-item-selected-bg: $shade-2;
$menu-devider: $shade-2;
$menu-no-results-color: $shade-5;
// Drag layer
$creator-drag-preview-shadow: rgba($shade-10, 0.2);
// Modal
$modal-backdrop-bg: rgba($shade-10, 0.4);
$modal-content-bg: $shade-1;
$modal-header-bg: $shade-0;
$modal-footer-border-width: 1px;
$modal-footer-border-color: $shade-2;
$modal-icon-spin-color: $shade-0;
// Tag selector
$tag-selector-bg: $shade-0;

View file

@ -0,0 +1,23 @@
//
// Zotero React Client
// --------------------------------------------------
// Abstracts
// --------------------------------------------------
@import "abstracts/variables";
@import "abstracts/functions";
@import "abstracts/mixins";
@import "abstracts/placeholders";
@import "abstracts/utilities";
// Theme
// --------------------------------------------------
@import "themes/light";
// Components
// --------------------------------------------------
@import "components/tag-selector";