diff --git a/frontend/vre/field/field.model.js b/frontend/vre/field/field.model.js index b432ccb0..20bd3591 100644 --- a/frontend/vre/field/field.model.js +++ b/frontend/vre/field/field.model.js @@ -1,6 +1,6 @@ import Backbone from 'backbone'; import { canonicalSort } from '../utils/generic-functions'; -import {properties} from "../utils/record-ontology"; +import {fieldList, properties} from "../utils/record-ontology"; import {getStringLiteral} from "../utils/jsonld.model"; // A single field of a single record. @@ -53,7 +53,7 @@ export var FlatFields = Backbone.Collection.extend({ }, toFlat: function(record) { const content = record.toJSON(); - const fieldNames = Object.keys(content).filter((name) => content[name]["@type"] === 'edpoprec:Field'); + const fieldNames = Object.keys(content).filter((name) => fieldList.includes(content[name]["@type"])); const fields = fieldNames.map((name) => ({key: name, value: content[name]})); return fields; }, diff --git a/frontend/vre/index.html b/frontend/vre/index.html index 9ecc262a..51dc206c 100644 --- a/frontend/vre/index.html +++ b/frontend/vre/index.html @@ -17,7 +17,7 @@ -
+
diff --git a/frontend/vre/record/record.list.managing.view.js b/frontend/vre/record/record.list.managing.view.js index 87c89508..f79657f1 100644 --- a/frontend/vre/record/record.list.managing.view.js +++ b/frontend/vre/record/record.list.managing.view.js @@ -20,6 +20,7 @@ export var RecordListManagingView = CompositeView.extend({ this.vreCollectionsSelect.submitForm(event, selection); }, 'click .more-records': 'loadMore', + 'click .500-more-records': 'load500More', 'click .download-xlsx': 'downloadXLSX', 'click .download-csv': 'downloadCSV', }, @@ -39,7 +40,11 @@ export var RecordListManagingView = CompositeView.extend({ }, loadMore: function(event) { - this.collection.trigger('moreRequested', event); + this.collection.trigger('moreRequested', event, 50); + }, + + load500More: function(event) { + this.collection.trigger('moreRequested', event, 500); }, downloadXLSX: function() { diff --git a/frontend/vre/record/record.list.managing.view.mustache b/frontend/vre/record/record.list.managing.view.mustache index f7f4e68f..e8a0224c 100644 --- a/frontend/vre/record/record.list.managing.view.mustache +++ b/frontend/vre/record/record.list.managing.view.mustache @@ -1,6 +1,7 @@

\ No newline at end of file diff --git a/frontend/vre/record/record.list.view.js b/frontend/vre/record/record.list.view.js index 7d45c2be..ac898a1a 100644 --- a/frontend/vre/record/record.list.view.js +++ b/frontend/vre/record/record.list.view.js @@ -3,6 +3,31 @@ import {properties} from "../utils/record-ontology"; import {getStringLiteral} from "../utils/jsonld.model"; import {vreChannel} from "../radio"; import Tabulator from "tabulator"; +import {columnChooseMenu} from "../utils/tabulator-utils"; + +const columnProperties = { + 'edpoprec:title': { + visible: true, + widthGrow: 5, + formatter: 'textarea', + }, + 'edpoprec:placeOfPublication': { + visible: true, + }, + 'edpoprec:dating': { + visible: true, + widthGrow: 0.5, + }, + 'edpoprec:publisherOrPrinter': { + visible: true, + }, + 'edpoprec:contributor': { + visible: true, + }, + 'edpoprec:activity': { + visible: true, + }, +}; export var RecordListView = Backbone.View.extend({ id: "record-list", @@ -25,13 +50,18 @@ export var RecordListView = Backbone.View.extend({ autoColumns: true, autoColumnsDefinitions: (definitions) => { for (let definition of definitions) { - if (definition.field === "model") { - definition.visible = false; - } + // All columns invisible by default + definition.visible = false; const property = properties.get(definition.field); if (property) { definition.title = getStringLiteral(property.get("skos:prefLabel")); } + definition.headerFilter = true; + definition.headerContextMenu = columnChooseMenu; + const hardcodedProperties = columnProperties[definition.field]; + if (hardcodedProperties) { + Object.assign(definition, hardcodedProperties); + } } return definitions; }, @@ -49,6 +79,7 @@ export var RecordListView = Backbone.View.extend({ cell.getRow().toggleSelect(); }, }, + headerFilterLiveFilterDelay: 0, }); this.table.on("rowClick", (e, row) => { const model = row.getData().model; diff --git a/frontend/vre/search/search.view.js b/frontend/vre/search/search.view.js index 5f1c10c7..e806a558 100644 --- a/frontend/vre/search/search.view.js +++ b/frontend/vre/search/search.view.js @@ -30,7 +30,7 @@ export var SearchView = CompositeView.extend({ this.$('form button').first().text('Search'); return this; }, - submitSearch: function(startRecord) { + submitSearch: function(startRecord, number=50) { this.showPending(); var searchTerm = this.$('input').val(); var searchPromise = this.collection.query({ @@ -38,6 +38,7 @@ export var SearchView = CompositeView.extend({ source: this.model.id, query: searchTerm, start: startRecord, + end: startRecord + number, // Backend correctly handles overflows here }, error: _.bind(this.alertError, this), remove: startRecord === 0, // Remove current records if search starts at zero @@ -63,11 +64,11 @@ export var SearchView = CompositeView.extend({ this.feedback(); }, this)); }, - nextSearch: function(event) { + nextSearch: function(event, number) { event.preventDefault(); $('#more-records').hide(); var startRecord = this.collection.length; - this.submitSearch(startRecord).then(this.feedback.bind(this)); + this.submitSearch(startRecord, number).then(this.feedback.bind(this)); }, feedback: function() { if (this.collection.length >= this.collection.totalResults) { diff --git a/frontend/vre/utils/record-ontology.js b/frontend/vre/utils/record-ontology.js index 73fdfbbc..0342cb16 100644 --- a/frontend/vre/utils/record-ontology.js +++ b/frontend/vre/utils/record-ontology.js @@ -14,3 +14,16 @@ export var PropertyList = JsonLdNestedCollection.extend({ export var properties = new PropertyList(); properties.fetch(); + +/** + * A list of all field types according to the record ontology. + * This is hardcoded for now because there is no trivial way to infer this + * by reasoning from the ontology JSON-LD file. + * @type {string[]} + */ +export const fieldList = [ + 'edpoprec:Field', + 'edpoprec:DatingField', + 'edpoprec:LanguageField', + 'edpoprec:LocationField', +]; diff --git a/frontend/vre/utils/tabulator-utils.js b/frontend/vre/utils/tabulator-utils.js new file mode 100644 index 00000000..7ab6200c --- /dev/null +++ b/frontend/vre/utils/tabulator-utils.js @@ -0,0 +1,59 @@ +/** + * A Tabulator menu to hide and show the available columns. + * Adapted from: https://tabulator.info/examples/6.2#menu + */ +export const columnChooseMenu = function(){ + const menu = []; + const columns = this.getColumns(); + menu.push({ + label: "Show/hide columns", + disabled: true, + }, { + separator: true, + }); + + for (let column of columns) { + const definition = column.getDefinition(); + if (definition.field === "model" || !definition.title) { + /* Do not add the 'model' column (for internal use only) and + do not add columns that do not have a title */ + continue; + } + // create checkbox element using font awesome icons + const icon = document.createElement("i"); + icon.classList.add("glyphicon"); + icon.classList.add(column.isVisible() ? "glyphicon-check" : "glyphicon-unchecked"); + + // build label + let label = document.createElement("span"); + let title = document.createElement("span"); + + title.textContent = " " + definition.title; + + label.appendChild(icon); + label.appendChild(title); + + // create menu item + menu.push({ + label: label, + action: function(e){ + // prevent menu closing + e.stopPropagation(); + + // toggle current column visibility + column.toggle(); + + // change menu item icon + if (column.isVisible()) { + icon.classList.remove("glyphicon-unchecked"); + icon.classList.add("glyphicon-check"); + } else { + icon.classList.remove("glyphicon-check"); + icon.classList.add("glyphicon-unchecked"); + } + } + }); + } + + return menu; +}; \ No newline at end of file