fix #58: hide empty fields

This commit is contained in:
Antelle 2016-03-05 11:35:22 +03:00
parent 079d0f53c8
commit ebf8baac94
9 changed files with 154 additions and 37 deletions

View File

@ -21,7 +21,8 @@ var AppSettingsModel = Backbone.Model.extend({
lockOnMinimize: true, lockOnMinimize: true,
lockOnCopy: false, lockOnCopy: false,
helpTipCopyShown: false, helpTipCopyShown: false,
skipOpenLocalWarn: false skipOpenLocalWarn: false,
hideEmptyFields: false
}, },
initialize: function() { initialize: function() {

View File

@ -163,7 +163,6 @@ var Locale = {
detUpdated: 'Updated', detUpdated: 'Updated',
detHistory: 'History', detHistory: 'History',
detNetField: 'New Field', detNetField: 'New Field',
detAddField: 'add field',
detAttachments: 'Attachments', detAttachments: 'Attachments',
detDelFromTrash: 'Delete from trash?', detDelFromTrash: 'Delete from trash?',
detDelFromTrashBody: 'You will not be able to put it back.', detDelFromTrashBody: 'You will not be able to put it back.',
@ -171,6 +170,12 @@ var Locale = {
detFieldCopied: 'Copied', detFieldCopied: 'Copied',
detFieldCopiedTime: 'Copied for {} seconds', detFieldCopiedTime: 'Copied for {} seconds',
detCopyHint: 'You can copy field value with click on its title', detCopyHint: 'You can copy field value with click on its title',
detMore: 'more',
detClickToAddField: 'click to add a new field',
detMenuAddNewField: 'Add new field',
detMenuShowEmpty: 'Show empty fields',
detMenuHideEmpty: 'Hide empty fields',
detMenuAddField: 'Add {}',
appSecWarn: 'Not Secure!', appSecWarn: 'Not Secure!',
appSecWarnBody1: 'You have loaded this app with insecure connection. ' + appSecWarnBody1: 'You have loaded this app with insecure connection. ' +

View File

@ -0,0 +1,28 @@
'use strict';
var Backbone = require('backbone');
var DetailsAddFieldView = Backbone.View.extend({
template: require('templates/details/details-add-field.hbs'),
events: {
'click .details__field-label': 'fieldLabelClick',
'click .details__field-value': 'fieldValueClick'
},
render: function () {
this.renderTemplate();
this.labelEl = this.$el.find('.details__field-label');
return this;
},
fieldLabelClick: function() {
this.trigger('more-click');
},
fieldValueClick: function() {
this.trigger('add-field');
}
});
module.exports = DetailsAddFieldView;

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
var Backbone = require('backbone'), var Backbone = require('backbone'),
kdbxweb = require('kdbxweb'),
GroupModel = require('../../models/group-model'), GroupModel = require('../../models/group-model'),
AppSettingsModel = require('../../models/app-settings-model'), AppSettingsModel = require('../../models/app-settings-model'),
Scrollable = require('../../mixins/scrollable'), Scrollable = require('../../mixins/scrollable'),
@ -15,6 +16,8 @@ var Backbone = require('backbone'),
IconSelectView = require('../icon-select-view'), IconSelectView = require('../icon-select-view'),
DetailsHistoryView = require('./details-history-view'), DetailsHistoryView = require('./details-history-view'),
DetailsAttachmentView = require('./details-attachment-view'), DetailsAttachmentView = require('./details-attachment-view'),
DetailsAddFieldView = require('./details-add-field-view'),
DropdownView = require('../../views/dropdown-view'),
Keys = require('../../const/keys'), Keys = require('../../const/keys'),
KeyHandler = require('../../comp/key-handler'), KeyHandler = require('../../comp/key-handler'),
Alerts = require('../../comp/alerts'), Alerts = require('../../comp/alerts'),
@ -23,8 +26,7 @@ var Backbone = require('backbone'),
Locale = require('../../util/locale'), Locale = require('../../util/locale'),
Tip = require('../../util/tip'), Tip = require('../../util/tip'),
Timeouts = require('../../const/timeouts'), Timeouts = require('../../const/timeouts'),
FileSaver = require('filesaver'), FileSaver = require('filesaver');
kdbxweb = require('kdbxweb');
var DetailsView = Backbone.View.extend({ var DetailsView = Backbone.View.extend({
template: require('templates/details/details.hbs'), template: require('templates/details/details.hbs'),
@ -36,7 +38,6 @@ var DetailsView = Backbone.View.extend({
passEditView: null, passEditView: null,
userEditView: null, userEditView: null,
urlEditView: null, urlEditView: null,
addNewFieldView: null,
fieldCopyTip: null, fieldCopyTip: null,
events: { events: {
@ -152,19 +153,8 @@ var DetailsView = Backbone.View.extend({
this.fieldViews.push(new FieldViewCustom({ model: { name: '$' + field, title: field, this.fieldViews.push(new FieldViewCustom({ model: { name: '$' + field, title: field,
value: function() { return model.fields[field]; } } })); value: function() { return model.fields[field]; } } }));
}, this); }, this);
var newFieldTitle = Locale.detNetField;
if (model.fields[newFieldTitle]) { var hideEmptyFields = AppSettingsModel.instance.get('hideEmptyFields');
for (var i = 1; ; i++) {
var newFieldTitleVariant = newFieldTitle + i;
if (!model.fields[newFieldTitleVariant]) {
newFieldTitle = newFieldTitleVariant;
break;
}
}
}
this.addNewFieldView = new FieldViewCustom({ model: { name: '$', title: Locale.detAddField, newField: newFieldTitle,
value: function() { return ''; } } });
this.fieldViews.push(this.addNewFieldView);
var fieldsMainEl = this.$el.find('.details__body-fields'); var fieldsMainEl = this.$el.find('.details__body-fields');
var fieldsAsideEl = this.$el.find('.details__body-aside'); var fieldsAsideEl = this.$el.find('.details__body-aside');
@ -172,7 +162,92 @@ var DetailsView = Backbone.View.extend({
fieldView.setElement(fieldView.readonly ? fieldsAsideEl : fieldsMainEl).render(); fieldView.setElement(fieldView.readonly ? fieldsAsideEl : fieldsMainEl).render();
fieldView.on('change', this.fieldChanged.bind(this)); fieldView.on('change', this.fieldChanged.bind(this));
fieldView.on('copy', this.fieldCopied.bind(this)); fieldView.on('copy', this.fieldCopied.bind(this));
if (hideEmptyFields && !fieldView.model.value()) {
fieldView.hide();
}
}, this); }, this);
this.moreView = new DetailsAddFieldView();
this.moreView.setElement(fieldsMainEl).render();
this.moreView.on('add-field', this.addNewField.bind(this));
this.moreView.on('more-click', this.toggleMoreOptions.bind(this));
},
addNewField: function() {
this.moreView.remove();
this.moreView = null;
var newFieldTitle = Locale.detNetField;
if (this.model.fields[newFieldTitle]) {
for (var i = 1; ; i++) {
var newFieldTitleVariant = newFieldTitle + i;
if (!this.model.fields[newFieldTitleVariant]) {
newFieldTitle = newFieldTitleVariant;
break;
}
}
}
var fieldView = new FieldViewCustom({ model: { name: '$' + newFieldTitle, title: newFieldTitle, newField: newFieldTitle,
value: function() { return ''; } } });
fieldView.on('change', this.fieldChanged.bind(this));
fieldView.setElement(this.$el.find('.details__body-fields')).render();
fieldView.edit();
this.fieldViews.push(fieldView);
},
toggleMoreOptions: function() {
if (this.views.dropdownView) {
this.views.dropdownView.remove();
this.views.dropdownView = null;
} else {
this.setTimeout(function() {
var dropdownView = new DropdownView();
this.listenTo(dropdownView, 'cancel', this.toggleMoreOptions);
this.listenTo(dropdownView, 'select', this.moreOptionsSelect);
var hideEmptyFields = AppSettingsModel.instance.get('hideEmptyFields');
var moreOptions = [];
if (hideEmptyFields) {
this.fieldViews.forEach(function(fieldView) {
if (!fieldView.model.value()) {
moreOptions.push({value: 'add:' + fieldView.model.name, icon: 'pencil',
text: Locale.detMenuAddField.replace('{}', fieldView.model.title)});
}
}, this);
moreOptions.push({value: 'add-new', icon: 'plus', text: Locale.detMenuAddNewField});
moreOptions.push({value: 'toggle-empty', icon: 'eye', text: Locale.detMenuShowEmpty});
} else {
moreOptions.push({value: 'add-new', icon: 'plus', text: Locale.detMenuAddNewField});
moreOptions.push({value: 'toggle-empty', icon: 'eye-slash', text: Locale.detMenuHideEmpty});
}
var rect = this.moreView.labelEl[0].getBoundingClientRect();
dropdownView.render({
position: {top: rect.bottom, right: rect.right},
options: moreOptions
});
this.views.dropdownView = dropdownView;
});
}
},
moreOptionsSelect: function(e) {
this.views.dropdownView.remove();
this.views.dropdownView = null;
switch (e.item) {
case 'add-new':
this.addNewField();
break;
case 'toggle-empty':
var hideEmptyFields = AppSettingsModel.instance.get('hideEmptyFields');
AppSettingsModel.instance.set('hideEmptyFields', !hideEmptyFields);
this.render();
break;
default:
if (e.item.lastIndexOf('add:', 0) === 0) {
var fieldName = e.item.substr(4);
var fieldView = _.find(this.fieldViews, function(f) { return f.model.name === fieldName; });
fieldView.show();
fieldView.edit();
}
}
}, },
getUserNameCompletions: function(part) { getUserNameCompletions: function(part) {
@ -289,7 +364,7 @@ var DetailsView = Backbone.View.extend({
copyKeyPress: function(editView) { copyKeyPress: function(editView) {
if (!window.getSelection().toString()) { if (!window.getSelection().toString()) {
var fieldValue = editView.value; var fieldValue = editView.value;
var fieldText = fieldValue.isProtected ? fieldValue.getText() : fieldValue; var fieldText = fieldValue && fieldValue.isProtected ? fieldValue.getText() : fieldValue;
if (!fieldText) { if (!fieldText) {
return; return;
} }
@ -325,8 +400,8 @@ var DetailsView = Backbone.View.extend({
} }
AppSettingsModel.instance.set('helpTipCopyShown', true); AppSettingsModel.instance.set('helpTipCopyShown', true);
this.helpTipCopyShown = true; this.helpTipCopyShown = true;
var newFieldLabel = this.addNewFieldView.labelEl; var label = this.moreView.labelEl;
var tip = new Tip(newFieldLabel, { title: Locale.detCopyHint, placement: 'right' }); var tip = new Tip(label, { title: Locale.detCopyHint, placement: 'right' });
tip.show(); tip.show();
setTimeout(function() { tip.hide(); }, Timeouts.AutoHideHint); setTimeout(function() { tip.hide(); }, Timeouts.AutoHideHint);
}, },
@ -335,7 +410,7 @@ var DetailsView = Backbone.View.extend({
if (e.field) { if (e.field) {
if (e.field[0] === '$') { if (e.field[0] === '$') {
var fieldName = e.field.substr(1); var fieldName = e.field.substr(1);
if (e.newField && e.newField !== fieldName) { if (e.newField) {
if (fieldName) { if (fieldName) {
this.model.setField(fieldName, undefined); this.model.setField(fieldName, undefined);
} }
@ -370,6 +445,9 @@ var DetailsView = Backbone.View.extend({
fieldView.update(); fieldView.update();
} }
}, this); }, this);
} else if (e.newField) {
this.render();
return;
} }
if (e.tab) { if (e.tab) {
this.focusNextField(e.tab); this.focusNextField(e.tab);
@ -538,7 +616,7 @@ var DetailsView = Backbone.View.extend({
var fieldView = this.fieldViews[i]; var fieldView = this.fieldViews[i];
if (fieldView.model.name === config.field) { if (fieldView.model.name === config.field) {
found = true; found = true;
} else if (found && !fieldView.readonly) { } else if (found && !fieldView.readonly && !fieldView.isHidden()) {
nextFieldView = fieldView; nextFieldView = fieldView;
break; break;
} }

View File

@ -19,7 +19,8 @@ var DropdownView = Backbone.View.extend({
this.renderTemplate(config); this.renderTemplate(config);
this.$el.appendTo(document.body); this.$el.appendTo(document.body);
var ownRect = this.$el[0].getBoundingClientRect(); var ownRect = this.$el[0].getBoundingClientRect();
this.$el.css({ top: config.position.top, left: config.position.right - ownRect.right + ownRect.left }); var left = config.position.left || (config.position.right - ownRect.right + ownRect.left);
this.$el.css({ top: config.position.top, left: left });
return this; return this;
}, },

View File

@ -4,7 +4,6 @@ var Backbone = require('backbone'),
FieldViewText = require('./field-view-text'), FieldViewText = require('./field-view-text'),
FieldView = require('./field-view'), FieldView = require('./field-view'),
Keys = require('../../const/keys'), Keys = require('../../const/keys'),
Locale = require('../../util/locale'),
kdbxweb = require('kdbxweb'); kdbxweb = require('kdbxweb');
var FieldViewCustom = FieldViewText.extend({ var FieldViewCustom = FieldViewText.extend({
@ -18,10 +17,6 @@ var FieldViewCustom = FieldViewText.extend({
startEdit: function() { startEdit: function() {
FieldViewText.prototype.startEdit.call(this); FieldViewText.prototype.startEdit.call(this);
if (this.model.newField && this.model.title === Locale.detAddField) {
this.model.title = this.model.newField;
this.$el.find('.details__field-label').text(this.model.newField);
}
this.$el.addClass('details__field--can-edit-title'); this.$el.addClass('details__field--can-edit-title');
if (this.isProtected === undefined) { if (this.isProtected === undefined) {
this.isProtected = this.value instanceof kdbxweb.ProtectedValue; this.isProtected = this.value instanceof kdbxweb.ProtectedValue;
@ -50,10 +45,6 @@ var FieldViewCustom = FieldViewText.extend({
} }
} }
FieldView.prototype.endEdit.call(this, newVal, extra); FieldView.prototype.endEdit.call(this, newVal, extra);
if (!newVal && this.model.newField) {
this.model.title = Locale.detAddField;
this.$el.find('.details__field-label').text(this.model.title);
}
if (this.model.titleChanged) { if (this.model.titleChanged) {
delete this.model.titleChanged; delete this.model.titleChanged;
} }
@ -88,11 +79,10 @@ var FieldViewCustom = FieldViewText.extend({
fieldLabelClick: function(e) { fieldLabelClick: function(e) {
e.stopImmediatePropagation(); e.stopImmediatePropagation();
if (this.editing) { if (this.model.newField) {
this.startEditTitle();
} else if (this.model.newField) {
this.edit();
this.startEditTitle(true); this.startEditTitle(true);
} else if (this.editing) {
this.startEditTitle();
} else { } else {
FieldViewText.prototype.fieldLabelClick.call(this, e); FieldViewText.prototype.fieldLabelClick.call(this, e);
} }

View File

@ -196,6 +196,9 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
margin-right: 20px; margin-right: 20px;
&-add-label {
color: transparent;
}
.details__field--editable & { .details__field--editable & {
border-radius: $base-border-radius; border-radius: $base-border-radius;
&:hover { &:hover {
@ -204,6 +207,10 @@
border: 1px solid light-border-color(); border: 1px solid light-border-color();
box-shadow: 0 0 3px form-box-shadow-color(); box-shadow: 0 0 3px form-box-shadow-color();
} }
.details__field-value-add-label {
@include th { color: muted-color(); }
transition: color $base-duration $base-timing;
}
} }
} }
.details__field--multiline & { .details__field--multiline & {

View File

@ -0,0 +1,6 @@
<div class="details__field details__field--editable">
<div class="details__field-label">{{res 'detMore'}}&hellip;</div>
<div class="details__field-value">
<div class="details__field-value-add-label">{{res 'detClickToAddField'}}</div>
</div>
</div>

View File

@ -17,6 +17,7 @@ Storage providers, usability improvements
`+` build for 32-bit linux `+` build for 32-bit linux
`+` ability to import xml `+` ability to import xml
`+` warning for kdb files `+` warning for kdb files
`+` hide empty fields
`-` fix #88: capslock indicator `-` fix #88: capslock indicator
`-` fix file settings input behavior `-` fix file settings input behavior