summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/api/users.js1
-rw-r--r--webapp/src/components/DataTable.vue90
-rw-r--r--webapp/src/main.js18
3 files changed, 77 insertions, 32 deletions
diff --git a/server/api/users.js b/server/api/users.js
index 853740c..d7c290a 100644
--- a/server/api/users.js
+++ b/server/api/users.js
@@ -1,7 +1,6 @@
/* global __appdir */
var path = require('path')
var db = require(path.join(__appdir, 'lib', 'sequelize'))
-var jwt = require('jsonwebtoken')
var express = require('express')
const { decorateApp } = require('@awaitjs/express')
var router = decorateApp(express.Router())
diff --git a/webapp/src/components/DataTable.vue b/webapp/src/components/DataTable.vue
index 86aa789..037f689 100644
--- a/webapp/src/components/DataTable.vue
+++ b/webapp/src/components/DataTable.vue
@@ -8,6 +8,7 @@
"height": "Height",
"regex": "Regular Expressions",
"caseSensitive": "Case Sensitive",
+ "onlyShowSelected": "Only show selected entries.",
"selected": "selected"
},
"de": {
@@ -18,6 +19,7 @@
"height": "Höhe",
"regex": "Regulärer Ausdruck",
"caseSensitive": "Groß-/Kleinschreibung beachten",
+ "onlyShowSelected": "Nur ausgewählte Einträge anzeigen.",
"selected": "ausgewählt"
}
}
@@ -86,7 +88,7 @@
</div>
<div class="text-xs-center">
- {{ filterdRows.length + ' ' + $t('entries') + ' (' + selected.length + ' ' + $t('selected') + ')' }}
+ {{ filteredRows.length + ' ' + $t('entries') + ' (' + selected.length + ' ' + $t('selected') + ')' }}
</div>
<div class="text-xs-right">
@@ -114,7 +116,7 @@
<RecycleScroller
class="scroller non-selectable"
:style="scrollerStyle"
- :items="filterdRows"
+ :items="filteredRows"
:item-height="48"
:page-mode="rowCount <= 0"
@click.native.capture.passive="setShiftState"
@@ -159,7 +161,7 @@
<slot v-else :name="header.key" :item="item.data" />
</div>
</div>
- <div v-if="filterdRows.length === 0" slot="after-container" class="no-result">{{ $t('noResult') }}</div>
+ <div v-if="filteredRows.length === 0" slot="after-container" class="no-result">{{ $t('noResult') }}</div>
</RecycleScroller>
</div>
</template>
@@ -200,7 +202,9 @@ export default {
caseSensitive: false,
onlyShowSelected: false,
lastSelectIndex: null,
- shiftKey: false
+ shiftKey: false,
+ filteredRows: [],
+ filteringLoop: { breakLoop: false }
}
},
computed: {
@@ -209,7 +213,7 @@ export default {
},
scrollerStyle () {
const style = { '--min-table-width': this.minWidth }
- if (this.rowCount > 0) style.height = (Math.max(Math.min(this.rowCount, this.filterdRows.length), 1) * 48 + 58) + 'px'
+ if (this.rowCount > 0) style.height = (Math.max(Math.min(this.rowCount, this.filteredRows.length), 1) * 48 + 58) + 'px'
return style
},
dataKeys () { return this.headers.map(x => x.key) },
@@ -230,22 +234,6 @@ export default {
else if (this.regex && !this.caseSensitive) return (s, str) => s.text.regexCI.test(str)
else if (!this.regex && this.caseSensitive) return (s, str) => str.indexOf(s.text.raw) !== -1
else if (!this.regex && !this.caseSensitive) return (s, str) => str.toUpperCase().indexOf(s.text.upper) !== -1
- },
- filterdRows () {
- if (this.search.every(s => s.text.raw === '') && !this.onlyShowSelected) return this.sortedRows
- const onlySelected = this.onlyShowSelected
- const search = this.search
- const dataKeys = this.dataKeys
- const test = this.filterFunction
- return this.sortedRows.filter(row => (!onlySelected || (onlySelected && row.selected)) && search.every(s => {
- if (s.key === null) {
- return dataKeys.some(key => {
- return test(s, String(row.data[key]))
- })
- } else {
- return test(s, String(row.data[s.key]))
- }
- }))
}
},
watch: {
@@ -266,12 +254,21 @@ export default {
this.selected.push(row.data)
}
})
- this.calcSelectState()
+ if (this.filteredRows) this.calcSelectState()
this.$emit('input', this.selected)
}
}
},
- filterdRows () {
+ sortedRows () {
+ this.filterRows()
+ },
+ search: {
+ deep: true,
+ handler () {
+ this.filterRows()
+ }
+ },
+ filteredRows () {
this.lastSelectIndex = null
this.calcSelectState()
}
@@ -292,14 +289,15 @@ export default {
// Update variables and emit the new value
this.lastSelectIndex = index
this.calcSelectState()
+ this.$emit('click', row.data)
this.$emit('input', this.selected)
},
toggleSelectAll () {
- if (this.filterdRows.length === 0) return
+ if (this.filteredRows.length === 0) return
if (this.selectState <= 1) {
- this.selectRows(0, this.filterdRows.length - 1)
+ this.selectRows(0, this.filteredRows.length - 1)
} else {
- this.deselectRows(0, this.filterdRows.length - 1)
+ this.deselectRows(0, this.filteredRows.length - 1)
}
// Update variables and emit the new value
this.calcSelectState()
@@ -307,8 +305,8 @@ export default {
},
selectRows (start, end) {
var rows
- if (this.onlyShowSelected) rows = this.filterdRows.slice(0)
- else rows = this.filterdRows
+ if (this.onlyShowSelected) rows = this.filteredRows.slice(0)
+ else rows = this.filteredRows
for (let i = Math.min(start, end); i <= Math.max(start, end); i++) {
let row = rows[i]
if (row.selected !== true) {
@@ -319,8 +317,8 @@ export default {
},
deselectRows (start, end) {
var rows
- if (this.onlyShowSelected) rows = this.filterdRows.slice(0)
- else rows = this.filterdRows
+ if (this.onlyShowSelected) rows = this.filteredRows.slice(0)
+ else rows = this.filteredRows
const deselected = {}
for (let i = Math.min(start, end); i <= Math.max(start, end); i++) {
let row = rows[i]
@@ -336,7 +334,7 @@ export default {
this.selected = newSelected
},
calcSelectState () {
- const displayedRows = this.filterdRows
+ const displayedRows = this.filteredRows
var seenTrue = false
var seenFalse = false
for (let i = 0; i < displayedRows.length; i++) {
@@ -384,6 +382,36 @@ export default {
},
setShiftState (event) {
this.shiftKey = event.shiftKey
+ },
+ filterRows () {
+ if (this.search.every(s => s.text.raw === '') && !this.onlyShowSelected) this.filteredRows = this.sortedRows
+ const onlySelected = this.onlyShowSelected
+ const search = this.search
+ const dataKeys = this.dataKeys
+ const test = this.filterFunction
+ const rows = this.sortedRows
+
+ this.filteringLoop.breakLoop = true
+ const newFilteringLoop = { breakLoop: false }
+ this.filteringLoop = newFilteringLoop
+
+ const result = []
+ this.$loop(rows.length, 1000,
+ i => {
+ const row = rows[i]
+ if ((!onlySelected || (onlySelected && row.selected)) && search.every(s => {
+ if (s.key === null) {
+ return dataKeys.some(key => {
+ return test(s, String(row.data[key]))
+ })
+ } else {
+ return test(s, String(row.data[s.key]))
+ }
+ })) result.push(row)
+ },
+ () => newFilteringLoop.breakLoop,
+ () => { this.filteredRows = result }
+ )
}
}
}
diff --git a/webapp/src/main.js b/webapp/src/main.js
index 4eb4636..a87aae8 100644
--- a/webapp/src/main.js
+++ b/webapp/src/main.js
@@ -88,10 +88,28 @@ Vue.prototype.$alert = function (data, emit = true) {
if (emit) socket.emit('notifications newAlert', data)
store.commit('notifications/newAlert', data)
}
+
Vue.prototype.$snackbar = function (data) {
store.commit('notifications/newSnackbar', data)
}
+// Loop that processes work in chunks to allow the UI to refresh inbetween and prevents freezing.
+Vue.prototype.$loop = function (count, chunksize, callback, breakLoop = () => false, finished = () => {}) {
+ var i = 0;
+ (function chunk () {
+ var end = Math.min(i + chunksize, count)
+ for (; i < end; ++i) {
+ callback(i)
+ }
+ let bl = breakLoop()
+ if (i < count && !bl) {
+ setTimeout(chunk, 0)
+ } else if (!bl) {
+ finished()
+ }
+ })()
+}
+
new Vue({
store,
router,