mirror of https://github.com/keeweb/keeweb.git
fix #58: hide empty fields
This commit is contained in:
parent
079d0f53c8
commit
ebf8baac94
|
@ -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() {
|
||||||
|
|
|
@ -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. ' +
|
||||||
|
|
|
@ -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;
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 & {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="details__field details__field--editable">
|
||||||
|
<div class="details__field-label">{{res 'detMore'}}…</div>
|
||||||
|
<div class="details__field-value">
|
||||||
|
<div class="details__field-value-add-label">{{res 'detClickToAddField'}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue