Reactify the Tag Selector
This commit is contained in:
parent
506ed313da
commit
897e74c7f1
33 changed files with 1396 additions and 1597 deletions
1
.babelrc
1
.babelrc
|
@ -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
455
chrome/content/zotero/reactUI/containers/tag-selector.jsx
Normal file
455
chrome/content/zotero/reactUI/containers/tag-selector.jsx
Normal 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;
|
||||
}
|
||||
}
|
||||
})();
|
58
chrome/content/zotero/reactUI/tagSelector.xul
Normal file
58
chrome/content/zotero/reactUI/tagSelector.xul
Normal 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>
|
40
chrome/content/zotero/reactUI/zoteroPane.js
Normal file
40
chrome/content/zotero/reactUI/zoteroPane.js
Normal 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();
|
||||
}
|
||||
}
|
||||
|
35
chrome/content/zotero/reactUI/zoteroPane.xul
Normal file
35
chrome/content/zotero/reactUI/zoteroPane.xul
Normal 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>
|
|
@ -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>
|
||||
|
|
|
@ -73,6 +73,7 @@ Zotero.Notifier = new function(){
|
|||
if (priority) {
|
||||
msg += " with priority " + priority;
|
||||
}
|
||||
Zotero.debug(msg);
|
||||
_observers[hash] = {
|
||||
ref: ref,
|
||||
types: types,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
326
package-lock.json
generated
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
1
resource/classnames.js
Symbolic link
|
@ -0,0 +1 @@
|
|||
../node_modules/classnames/index.js
|
1
resource/prop-types.js
Symbolic link
1
resource/prop-types.js
Symbolic link
|
@ -0,0 +1 @@
|
|||
../node_modules/prop-types/prop-types.js
|
155
resource/react-ui/components/form/input.jsx
Normal file
155
resource/react-ui/components/form/input.jsx
Normal 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;
|
53
resource/react-ui/components/tag-selector.jsx
Normal file
53
resource/react-ui/components/tag-selector.jsx
Normal 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;
|
55
resource/react-ui/components/tag-selector/tag-list.jsx
Normal file
55
resource/react-ui/components/tag-selector/tag-list.jsx
Normal 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;
|
|
@ -1,5 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
var Zotero;
|
||||
|
||||
var EXPORTED_SYMBOLS = ['require'];
|
||||
|
||||
var require = (function() {
|
||||
|
@ -56,21 +58,42 @@ var require = (function() {
|
|||
window.debug = function (msg) {
|
||||
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);
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../node_modules/zotero-web-library/lib
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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) {
|
||||
onSuccess(await getSass(path, {}, signatures));
|
||||
} 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) {
|
||||
|
|
4
scss/abstracts/_functions.scss
Normal file
4
scss/abstracts/_functions.scss
Normal file
|
@ -0,0 +1,4 @@
|
|||
//
|
||||
// Functions
|
||||
// --------------------------------------------------
|
||||
|
4
scss/abstracts/_mixins.scss
Normal file
4
scss/abstracts/_mixins.scss
Normal file
|
@ -0,0 +1,4 @@
|
|||
//
|
||||
// Mixins
|
||||
// --------------------------------------------------
|
||||
|
3
scss/abstracts/_placeholders.scss
Normal file
3
scss/abstracts/_placeholders.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
//
|
||||
// Placeholders
|
||||
// --------------------------------------------------
|
4
scss/abstracts/_utilities.scss
Normal file
4
scss/abstracts/_utilities.scss
Normal file
|
@ -0,0 +1,4 @@
|
|||
//
|
||||
// Utilities
|
||||
// --------------------------------------------------
|
||||
|
77
scss/abstracts/_variables.scss
Normal file
77
scss/abstracts/_variables.scss
Normal 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;
|
||||
|
86
scss/components/_tag-selector.scss
Normal file
86
scss/components/_tag-selector.scss
Normal 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
179
scss/themes/_light.scss
Normal 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;
|
23
scss/zotero-react-client.scss
Normal file
23
scss/zotero-react-client.scss
Normal 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";
|
Loading…
Reference in a new issue