keeweb/app/scripts/views/list-view.js

337 lines
11 KiB
JavaScript
Raw Normal View History

2020-03-14 20:29:55 +01:00
import { View, DefaultTemplateOptions } from 'framework/views/view';
2019-09-16 22:57:56 +02:00
import { Events } from 'framework/events';
2019-11-03 11:24:33 +01:00
import { SearchResultCollection } from 'collections/search-result-collection';
2019-09-15 14:16:32 +02:00
import { DragDropInfo } from 'comp/app/drag-drop-info';
import { Alerts } from 'comp/ui/alerts';
import { AppSettingsModel } from 'models/app-settings-model';
import { EntryPresenter } from 'presenters/entry-presenter';
import { StringFormat } from 'util/formatting/string-format';
import { Locale } from 'util/locale';
2019-09-16 20:42:33 +02:00
import { Resizable } from 'framework/views/resizable';
import { Scrollable } from 'framework/views/scrollable';
2019-09-15 14:16:32 +02:00
import { DropdownView } from 'views/dropdown-view';
import { ListSearchView } from 'views/list-search-view';
2019-09-18 20:42:17 +02:00
import throttle from 'lodash/throttle';
2019-09-16 17:43:57 +02:00
import template from 'templates/list.hbs';
import emptyTemplate from 'templates/list-empty.hbs';
2017-01-31 07:50:28 +01:00
2019-09-16 17:43:57 +02:00
class ListView extends View {
parent = '.app__list';
2015-10-17 23:49:24 +02:00
2019-09-16 17:43:57 +02:00
template = template;
emptyTemplate = emptyTemplate;
events = {
2019-09-16 19:26:31 +02:00
'click': 'click',
2016-06-10 21:08:28 +02:00
'click .list__table-options': 'tableOptionsClick',
2015-11-08 09:59:46 +01:00
'dragstart .list__item': 'itemDragStart'
2019-09-16 17:43:57 +02:00
};
2015-10-17 23:49:24 +02:00
2019-09-16 17:43:57 +02:00
minWidth = 200;
minHeight = 200;
maxWidth = 500;
maxHeight = 500;
2015-10-17 23:49:24 +02:00
2019-09-16 17:43:57 +02:00
itemsEl = null;
2015-10-17 23:49:24 +02:00
2019-09-16 17:43:57 +02:00
tableColumns = [
2016-06-11 16:18:11 +02:00
{ val: 'title', name: 'title', enabled: true },
{ val: 'user', name: 'user', enabled: true },
{ val: 'url', name: 'website', enabled: true },
{ val: 'tags', name: 'tags', enabled: true },
{ val: 'notes', name: 'notes', enabled: true },
{ val: 'groupName', name: 'group', enabled: false },
{ val: 'fileName', name: 'file', enabled: false }
2019-09-16 17:43:57 +02:00
];
2019-09-16 17:58:44 +02:00
constructor(model, options) {
super(model, options);
2016-06-11 16:18:11 +02:00
2015-10-17 23:49:24 +02:00
this.initScroll();
2019-09-16 17:43:57 +02:00
this.views.search = new ListSearchView(this.model);
2015-10-17 23:49:24 +02:00
this.listenTo(this.views.search, 'select-prev', this.selectPrev);
this.listenTo(this.views.search, 'select-next', this.selectNext);
this.listenTo(this.views.search, 'create-entry', this.createEntry);
2015-10-31 20:09:32 +01:00
this.listenTo(this.views.search, 'create-group', this.createGroup);
2017-05-02 21:22:08 +02:00
this.listenTo(this.views.search, 'create-template', this.createTemplate);
2015-10-17 23:49:24 +02:00
this.listenTo(this, 'show', this.viewShown);
this.listenTo(this, 'hide', this.viewHidden);
2015-11-11 19:58:29 +01:00
this.listenTo(this, 'view-resize', this.viewResized);
2019-09-16 22:57:56 +02:00
this.listenTo(Events, 'filter', this.filterChanged);
this.listenTo(Events, 'entry-updated', this.entryUpdated);
this.listenTo(Events, 'set-locale', this.render);
2015-10-17 23:49:24 +02:00
2015-11-21 15:55:42 +01:00
this.listenTo(this.model.settings, 'change:tableView', this.setTableView);
2016-07-27 00:01:32 +02:00
this.readTableColumnsEnabled();
2019-11-03 11:24:33 +01:00
this.items = new SearchResultCollection();
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-08-18 10:17:09 +02:00
render() {
2015-10-17 23:49:24 +02:00
if (!this.itemsEl) {
2019-09-16 17:43:57 +02:00
super.render();
2015-10-17 23:49:24 +02:00
this.itemsEl = this.$el.find('.list__items>.scroller');
2019-09-16 17:43:57 +02:00
this.views.search.render();
2015-11-21 15:55:42 +01:00
this.setTableView();
2015-10-17 23:49:24 +02:00
2016-01-17 21:19:42 +01:00
this.createScroll({
2015-10-17 23:49:24 +02:00
root: this.$el.find('.list__items')[0],
scroller: this.$el.find('.scroller')[0],
2016-01-17 21:19:42 +01:00
bar: this.$el.find('.scroller__bar')[0]
2015-10-17 23:49:24 +02:00
});
}
if (this.items.length) {
2017-01-31 07:50:28 +01:00
const itemTemplate = this.getItemTemplate();
const itemsTemplate = this.getItemsTemplate();
2019-09-17 19:50:42 +02:00
const noColor = AppSettingsModel.colorfulIcons ? '' : 'grayscale';
2019-08-18 08:05:38 +02:00
const presenter = new EntryPresenter(
this.getDescField(),
noColor,
this.model.activeEntryId
);
2017-01-31 07:50:28 +01:00
const columns = {};
2020-06-01 16:53:51 +02:00
this.tableColumns.forEach((col) => {
2016-06-11 16:18:11 +02:00
if (col.enabled) {
columns[col.val] = true;
}
});
presenter.columns = columns;
2017-01-31 07:50:28 +01:00
let itemsHtml = '';
2020-06-01 16:53:51 +02:00
this.items.forEach((item) => {
2015-10-17 23:49:24 +02:00
presenter.present(item);
2020-03-14 20:29:55 +01:00
itemsHtml += itemTemplate(presenter, DefaultTemplateOptions);
2015-10-17 23:49:24 +02:00
}, this);
2020-03-14 20:29:55 +01:00
const html = itemsTemplate(
2020-04-23 19:24:39 +02:00
{ itemsHtml, columns: this.tableColumns },
2020-03-14 20:29:55 +01:00
DefaultTemplateOptions
);
2015-12-12 16:43:43 +01:00
this.itemsEl.html(html);
2015-10-17 23:49:24 +02:00
} else {
2020-03-14 20:29:55 +01:00
this.itemsEl.html(this.emptyTemplate({}, DefaultTemplateOptions));
2015-10-17 23:49:24 +02:00
}
this.pageResized();
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-08-18 10:17:09 +02:00
getItemsTemplate() {
2019-09-17 19:50:42 +02:00
if (this.model.settings.tableView) {
2015-12-16 22:50:45 +01:00
return require('templates/list-table.hbs');
2015-11-21 15:55:42 +01:00
} else {
return this.renderPlainItems;
}
2019-09-16 17:43:57 +02:00
}
2015-11-21 15:55:42 +01:00
2020-04-23 19:24:39 +02:00
renderPlainItems(data) {
return data.itemsHtml;
2019-09-16 17:43:57 +02:00
}
2015-11-21 15:55:42 +01:00
2019-08-18 10:17:09 +02:00
getItemTemplate() {
2019-09-17 19:50:42 +02:00
if (this.model.settings.tableView) {
2015-12-16 22:50:45 +01:00
return require('templates/list-item-table.hbs');
2015-11-21 15:55:42 +01:00
} else {
2015-12-16 22:50:45 +01:00
return require('templates/list-item-short.hbs');
2015-11-21 15:55:42 +01:00
}
2019-09-16 17:43:57 +02:00
}
2015-11-21 15:55:42 +01:00
2019-08-18 10:17:09 +02:00
getDescField() {
2015-10-17 23:49:24 +02:00
return this.model.sort.replace('-', '');
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-09-16 19:26:31 +02:00
click(e) {
const listItemEl = e.target.closest('.list__item');
if (!listItemEl) {
return;
}
const id = listItemEl.id;
2017-01-31 07:50:28 +01:00
const item = this.items.get(id);
2015-10-17 23:49:24 +02:00
if (!item.active) {
this.selectItem(item);
}
2019-09-16 22:57:56 +02:00
Events.emit('toggle-details', true);
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-08-18 10:17:09 +02:00
selectPrev() {
2017-01-31 07:50:28 +01:00
const ix = this.items.indexOf(this.items.get(this.model.activeEntryId));
2015-10-17 23:49:24 +02:00
if (ix > 0) {
2019-09-19 18:28:55 +02:00
this.selectItem(this.items[ix - 1]);
2015-10-17 23:49:24 +02:00
}
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-08-18 10:17:09 +02:00
selectNext() {
2017-01-31 07:50:28 +01:00
const ix = this.items.indexOf(this.items.get(this.model.activeEntryId));
2015-10-17 23:49:24 +02:00
if (ix < this.items.length - 1) {
2019-09-19 18:28:55 +02:00
this.selectItem(this.items[ix + 1]);
2015-10-17 23:49:24 +02:00
}
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-08-18 10:17:09 +02:00
createEntry(arg) {
2017-05-02 21:22:08 +02:00
const newEntry = this.model.createNewEntry(arg);
2015-10-17 23:49:24 +02:00
this.items.unshift(newEntry);
this.render();
this.selectItem(newEntry);
2019-10-26 19:53:20 +02:00
Events.emit('toggle-details', true);
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-08-18 10:17:09 +02:00
createGroup() {
2017-01-31 07:50:28 +01:00
const newGroup = this.model.createNewGroup();
2019-09-16 22:57:56 +02:00
Events.emit('edit-group', newGroup);
2019-09-16 17:43:57 +02:00
}
2015-10-31 20:09:32 +01:00
2019-08-18 10:17:09 +02:00
createTemplate() {
2020-04-23 19:55:52 +02:00
if (!this.model.settings.templateHelpShown_) {
2017-05-03 20:44:16 +02:00
Alerts.yesno({
icon: 'sticky-note-o',
header: Locale.listAddTemplateHeader,
2019-08-16 23:05:39 +02:00
body:
2020-04-23 19:55:52 +02:00
Locale.listAddTemplateBody1.replace('{}', '"+"') +
'\n' +
2017-05-03 20:44:16 +02:00
Locale.listAddTemplateBody2.replace('{}', 'Templates'),
buttons: [Alerts.buttons.ok, Alerts.buttons.cancel],
success: () => {
2019-09-17 19:50:42 +02:00
this.model.settings.templateHelpShown = true;
2017-05-03 20:44:16 +02:00
this.createTemplate();
}
});
return;
}
const templateEntry = this.model.createNewTemplateEntry();
this.items.unshift(templateEntry);
this.render();
this.selectItem(templateEntry);
2019-09-16 17:43:57 +02:00
}
2017-05-02 21:22:08 +02:00
2019-08-18 10:17:09 +02:00
selectItem(item) {
this.model.activeEntryId = item.id;
2019-09-16 22:57:56 +02:00
Events.emit('entry-selected', item);
2015-10-17 23:49:24 +02:00
this.itemsEl.find('.list__item--active').removeClass('list__item--active');
2017-01-31 07:50:28 +01:00
const itemEl = document.getElementById(item.id);
2015-10-17 23:49:24 +02:00
itemEl.classList.add('list__item--active');
2017-01-31 07:50:28 +01:00
const listEl = this.itemsEl[0];
const itemRect = itemEl.getBoundingClientRect();
const listRect = listEl.getBoundingClientRect();
2015-10-17 23:49:24 +02:00
if (itemRect.top < listRect.top) {
listEl.scrollTop += itemRect.top - listRect.top;
} else if (itemRect.bottom > listRect.bottom) {
listEl.scrollTop += itemRect.bottom - listRect.bottom;
}
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-08-18 10:17:09 +02:00
viewShown() {
2015-10-17 23:49:24 +02:00
this.views.search.show();
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-08-18 10:17:09 +02:00
viewHidden() {
2015-10-17 23:49:24 +02:00
this.views.search.hide();
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-08-18 10:17:09 +02:00
setTableView() {
2019-09-17 19:50:42 +02:00
const isTable = this.model.settings.tableView;
2015-11-21 15:55:42 +01:00
this.dragView.setCoord(isTable ? 'y' : 'x');
this.setDefaultSize();
2019-09-16 17:43:57 +02:00
}
2015-11-21 15:55:42 +01:00
2019-08-18 10:17:09 +02:00
setDefaultSize() {
2019-09-17 19:50:42 +02:00
this.setSize(this.model.settings.listViewWidth);
2019-09-16 17:43:57 +02:00
}
2015-11-21 15:55:42 +01:00
2019-08-18 10:17:09 +02:00
setSize(size) {
this.$el.css({ width: 'auto', height: 'auto' });
2015-11-21 15:55:42 +01:00
if (size) {
this.$el.css('flex', '0 0 ' + size + 'px');
} else {
2019-09-20 23:25:43 +02:00
this.$el.css('flex', '');
2015-11-21 15:55:42 +01:00
}
2019-09-16 17:43:57 +02:00
}
2015-11-21 15:55:42 +01:00
2019-08-18 10:17:09 +02:00
viewResized(size) {
2015-11-21 15:55:42 +01:00
this.setSize(size);
this.throttleSetViewSizeSetting(size);
2019-09-16 17:43:57 +02:00
}
2015-11-21 15:55:42 +01:00
2020-06-01 16:53:51 +02:00
throttleSetViewSizeSetting = throttle((size) => {
2019-09-17 19:50:42 +02:00
AppSettingsModel.listViewWidth = size;
2019-09-16 17:43:57 +02:00
}, 1000);
2015-11-11 19:58:29 +01:00
2019-08-18 10:17:09 +02:00
filterChanged(filter) {
2015-10-17 23:49:24 +02:00
this.items = filter.entries;
this.render();
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-08-18 10:17:09 +02:00
entryUpdated() {
2017-01-31 07:50:28 +01:00
const scrollTop = this.itemsEl[0].scrollTop;
2015-10-17 23:49:24 +02:00
this.render();
this.itemsEl[0].scrollTop = scrollTop;
2019-09-16 17:43:57 +02:00
}
2015-11-08 09:59:46 +01:00
2019-08-18 10:17:09 +02:00
itemDragStart(e) {
2015-11-08 09:59:46 +01:00
e.stopPropagation();
2020-06-01 16:53:51 +02:00
const id = $(e.target).closest('.list__item').attr('id');
2019-09-16 21:49:21 +02:00
e.dataTransfer.setData('text/entry', id);
e.dataTransfer.effectAllowed = 'move';
2015-11-08 09:59:46 +01:00
DragDropInfo.dragObject = this.items.get(id);
2019-09-16 17:43:57 +02:00
}
2016-06-10 21:08:28 +02:00
2019-08-18 10:17:09 +02:00
tableOptionsClick(e) {
2016-06-11 16:18:11 +02:00
e.stopImmediatePropagation();
if (this.views.optionsDropdown) {
this.hideOptionsDropdown();
return;
}
2017-01-31 07:50:28 +01:00
const view = new DropdownView();
2016-06-11 16:18:11 +02:00
this.listenTo(view, 'cancel', this.hideOptionsDropdown);
this.listenTo(view, 'select', this.optionsDropdownSelect);
2017-01-31 07:50:28 +01:00
const targetElRect = this.$el.find('.list__table-options')[0].getBoundingClientRect();
2020-06-01 16:53:51 +02:00
const options = this.tableColumns.map((col) => ({
2016-07-17 13:30:38 +02:00
value: col.val,
icon: col.enabled ? 'check-square-o' : 'square-o',
2019-09-15 08:11:11 +02:00
text: StringFormat.capFirst(Locale[col.name])
2016-07-17 13:30:38 +02:00
}));
2016-06-11 16:18:11 +02:00
view.render({
position: {
top: targetElRect.bottom,
left: targetElRect.left
},
2019-08-18 10:17:09 +02:00
options
2016-06-11 16:18:11 +02:00
});
this.views.optionsDropdown = view;
2019-09-16 17:43:57 +02:00
}
2016-06-11 16:18:11 +02:00
2019-08-18 10:17:09 +02:00
hideOptionsDropdown() {
2016-06-11 16:18:11 +02:00
if (this.views.optionsDropdown) {
this.views.optionsDropdown.remove();
delete this.views.optionsDropdown;
}
2019-09-16 17:43:57 +02:00
}
2016-06-11 16:18:11 +02:00
2019-08-18 10:17:09 +02:00
optionsDropdownSelect(e) {
2020-06-01 16:53:51 +02:00
const col = this.tableColumns.find((c) => c.val === e.item);
2016-06-11 16:18:11 +02:00
col.enabled = !col.enabled;
e.el.find('i:first').toggleClass('fa-check-square-o fa-square-o');
this.render();
2016-07-27 00:01:32 +02:00
this.saveTableColumnsEnabled();
2019-09-16 17:43:57 +02:00
}
2016-07-27 00:01:32 +02:00
readTableColumnsEnabled() {
2019-09-17 19:50:42 +02:00
const tableViewColumns = AppSettingsModel.tableViewColumns;
2016-07-27 00:01:32 +02:00
if (tableViewColumns && tableViewColumns.length) {
2020-06-01 16:53:51 +02:00
this.tableColumns.forEach((col) => {
2016-07-27 00:01:32 +02:00
col.enabled = tableViewColumns.indexOf(col.name) >= 0;
});
}
2019-09-16 17:43:57 +02:00
}
2016-07-27 00:01:32 +02:00
saveTableColumnsEnabled() {
2019-08-18 08:05:38 +02:00
const tableViewColumns = this.tableColumns
2020-06-01 16:53:51 +02:00
.filter((column) => column.enabled)
.map((column) => column.name);
2019-09-17 19:50:42 +02:00
AppSettingsModel.tableViewColumns = tableViewColumns;
2015-10-17 23:49:24 +02:00
}
2019-09-16 17:43:57 +02:00
}
2015-10-17 23:49:24 +02:00
2019-09-16 17:43:57 +02:00
Object.assign(ListView.prototype, Resizable);
Object.assign(ListView.prototype, Scrollable);
2015-10-17 23:49:24 +02:00
2019-09-15 14:16:32 +02:00
export { ListView };