summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUdo Walter2019-01-13 03:05:51 +0100
committerUdo Walter2019-01-13 03:05:51 +0100
commit859c61b0d9ad9e702bda57b0ebacd99511aa3023 (patch)
tree4391f45645c84c319f3ecd0cca652bdfbafdd172
parent[server] eslint fixes (diff)
downloadbas-859c61b0d9ad9e702bda57b0ebacd99511aa3023.tar.gz
bas-859c61b0d9ad9e702bda57b0ebacd99511aa3023.tar.xz
bas-859c61b0d9ad9e702bda57b0ebacd99511aa3023.zip
[webapp] add first version of a virtualized table for better performance
-rw-r--r--webapp/package-lock.json139
-rw-r--r--webapp/package.json21
-rw-r--r--webapp/src/components/DashboardPage.vue6
-rw-r--r--webapp/src/components/DataTable.vue194
-rw-r--r--webapp/src/components/DataTableItem.vue88
-rw-r--r--webapp/src/components/GroupModuleGroupList.vue31
-rw-r--r--webapp/src/main.js4
7 files changed, 393 insertions, 90 deletions
diff --git a/webapp/package-lock.json b/webapp/package-lock.json
index 1989e9f..fe6ce80 100644
--- a/webapp/package-lock.json
+++ b/webapp/package-lock.json
@@ -1829,9 +1829,9 @@
}
},
"chalk": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
- "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
@@ -3081,9 +3081,9 @@
}
},
"engine.io-client": {
- "version": "3.2.1",
- "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
- "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.1.tgz",
+ "integrity": "sha512-q66JBFuQcy7CSlfAz9L3jH+v7DTT3i6ZEadYcVj2pOs8/0uJHLxKX3WBkGTvULJMdz0tUCyJag0aKT/dpXL9BQ==",
"requires": {
"component-emitter": "1.2.1",
"component-inherit": "0.0.3",
@@ -3093,7 +3093,7 @@
"indexof": "0.0.1",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
- "ws": "~3.3.1",
+ "ws": "~6.1.0",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
@@ -3107,13 +3107,11 @@
}
},
"ws": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
- "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.2.tgz",
+ "integrity": "sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw==",
"requires": {
- "async-limiter": "~1.0.0",
- "safe-buffer": "~5.1.0",
- "ultron": "~1.1.0"
+ "async-limiter": "~1.0.0"
}
}
}
@@ -7151,9 +7149,9 @@
"dev": true
},
"portfinder": {
- "version": "1.0.19",
- "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.19.tgz",
- "integrity": "sha512-23aeQKW9KgHe6citUrG3r9HjeX6vls0h713TAa+CwTKZwNIr/pD2ApaxYF4Um3ZZyq4ar+Siv3+fhoHaIwSOSw==",
+ "version": "1.0.20",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz",
+ "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==",
"dev": true,
"requires": {
"async": "^1.5.2",
@@ -9856,12 +9854,28 @@
}
},
"rimraf": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
- "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"requires": {
- "glob": "^7.0.5"
+ "glob": "^7.1.3"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
}
},
"ripemd160": {
@@ -10066,7 +10080,8 @@
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
},
"safe-regex": {
"version": "1.1.0",
@@ -10098,6 +10113,11 @@
"ajv": "^5.0.0"
}
},
+ "scrollparent": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.0.1.tgz",
+ "integrity": "sha1-cV1bnMV3YPsivczDvvtb/gaxoxc="
+ },
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -10400,23 +10420,23 @@
}
},
"socket.io-client": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
- "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz",
+ "integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==",
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "~3.1.0",
- "engine.io-client": "~3.2.0",
+ "engine.io-client": "~3.3.1",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
- "socket.io-parser": "~3.2.0",
+ "socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
@@ -10431,9 +10451,9 @@
}
},
"socket.io-parser": {
- "version": "3.2.0",
- "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
- "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
+ "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
"requires": {
"component-emitter": "1.2.1",
"debug": "~3.1.0",
@@ -11086,11 +11106,6 @@
}
}
},
- "ultron": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
- "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
- },
"union-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
@@ -11430,9 +11445,9 @@
}
},
"vue": {
- "version": "2.5.17",
- "resolved": "https://registry.npmjs.org/vue/-/vue-2.5.17.tgz",
- "integrity": "sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ=="
+ "version": "2.5.22",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-2.5.22.tgz",
+ "integrity": "sha512-pxY3ZHlXNJMFQbkjEgGVMaMMkSV1ONpz+4qB55kZuJzyJOhn6MSy/YZdzhdnumegNzVTL/Dn3Pp4UrVBYt1j/g=="
},
"vue-codemirror": {
"version": "4.0.6",
@@ -11500,10 +11515,20 @@
"vue-template-es2015-compiler": "^1.6.0"
}
},
+ "vue-observe-visibility": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/vue-observe-visibility/-/vue-observe-visibility-0.4.3.tgz",
+ "integrity": "sha512-YyyO3a5OUkgpmC0NEf+xWJR0jVdFWzVbKRDzUumOVMhfr3+jxXEycYNHCM3rEO5lcj3ZNJpDomZEYEx0Wqqh9A=="
+ },
+ "vue-resize": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-0.4.5.tgz",
+ "integrity": "sha512-bhP7MlgJQ8TIkZJXAfDf78uJO+mEI3CaLABLjv0WNzr4CcGRGPIAItyWYnP6LsPA4Oq0WE+suidNs6dgpO4RHg=="
+ },
"vue-router": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.1.tgz",
- "integrity": "sha512-vLLoY452L+JBpALMP5UHum9+7nzR9PeIBCghU9ZtJ1eWm6ieUI8Zb/DI3MYxH32bxkjzYV1LRjNv4qr8d+uX/w=="
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.2.tgz",
+ "integrity": "sha512-opKtsxjp9eOcFWdp6xLQPLmRGgfM932Tl56U9chYTnoWqKxQ8M20N7AkdEbM5beUh6wICoFGYugAX9vQjyJLFg=="
},
"vue-style-loader": {
"version": "3.1.2",
@@ -11516,9 +11541,9 @@
}
},
"vue-template-compiler": {
- "version": "2.5.17",
- "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.5.17.tgz",
- "integrity": "sha512-63uI4syCwtGR5IJvZM0LN5tVsahrelomHtCxvRkZPJ/Tf3ADm1U1wG6KWycK3qCfqR+ygM5vewUvmJ0REAYksg==",
+ "version": "2.5.22",
+ "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.5.22.tgz",
+ "integrity": "sha512-1VTw/NPTUeHNiwhkq6NkFzO7gYLjFCueBN0FX8NEiQIemd5EUMQ5hxrF7O0zCPo5tae+U9S/scETPea+hIz8Eg==",
"dev": true,
"requires": {
"de-indent": "^1.0.2",
@@ -11542,6 +11567,16 @@
"rollup-plugin-node-resolve": "^2.0.0"
}
},
+ "vue-virtual-scroller": {
+ "version": "1.0.0-beta.4",
+ "resolved": "https://registry.npmjs.org/vue-virtual-scroller/-/vue-virtual-scroller-1.0.0-beta.4.tgz",
+ "integrity": "sha512-ZS4iKucfYb5JSH/Zr1LzQv8X+EEHY6eqJuWr7x7P8itKTqVAdNQk9YL/I5IBn1Xw3k2g9z1Sqc3di2O9YpWd2Q==",
+ "requires": {
+ "scrollparent": "^2.0.1",
+ "vue-observe-visibility": "^0.4.3",
+ "vue-resize": "^0.4.5"
+ }
+ },
"vue2-hammer": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/vue2-hammer/-/vue2-hammer-1.0.7.tgz",
@@ -11551,17 +11586,17 @@
}
},
"vuedraggable": {
- "version": "2.16.0",
- "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.16.0.tgz",
- "integrity": "sha512-fr9gcWKXMJlzbbtJcrQs4kU7qdOZqd4SEpAcx+r0nykbW8AygZN0aKVpadEtI53T8A2azhzCdXMvEqrLuKE2fA==",
+ "version": "2.17.0",
+ "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.17.0.tgz",
+ "integrity": "sha512-TAC5tJTSbHSINQCSB59qHnuzT0Ad+E3IgvSWuA1e9UaebD8DxKaY1tCdvL3XvuLoaM3wc1dhpP/NbjpdxYsrng==",
"requires": {
"sortablejs": "^1.7.0"
}
},
"vuetify": {
- "version": "1.3.7",
- "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-1.3.7.tgz",
- "integrity": "sha512-EXKQggY+SPTBlmzmcFcNI6h/xdZLv6vr9VBcos8+kUumftIG7HDqqtFdRVNCco4EPrQgiG5sWDm5raM07NDvBg=="
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-1.4.1.tgz",
+ "integrity": "sha512-rf86frKvK3c6fdzXkbLdChamifnyhLRwLEVvXKua8zfVSrFZ4HXFK4T8zN68Pg/4vxlp+5DFxF1IQ4rq4tJ03A=="
},
"vuex": {
"version": "3.0.1",
@@ -11999,9 +12034,9 @@
}
},
"webpack-merge": {
- "version": "4.1.4",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.4.tgz",
- "integrity": "sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.1.tgz",
+ "integrity": "sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==",
"dev": true,
"requires": {
"lodash": "^4.17.5"
diff --git a/webapp/package.json b/webapp/package.json
index d27666a..82df089 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -12,15 +12,16 @@
},
"dependencies": {
"axios": "^0.18.0",
- "socket.io-client": "^2.1.1",
- "vue": "^2.5.17",
+ "socket.io-client": "^2.2.0",
+ "vue": "^2.5.22",
"vue-codemirror": "^4.0.6",
"vue-i18n": "^7.8.1",
- "vue-router": "^3.0.1",
+ "vue-router": "^3.0.2",
"vue-touch": "^2.0.0-beta.4",
+ "vue-virtual-scroller": "^1.0.0-beta.4",
"vue2-hammer": "^1.0.7",
- "vuedraggable": "^2.16.0",
- "vuetify": "^1.3.7",
+ "vuedraggable": "^2.17.0",
+ "vuetify": "^1.4.1",
"vuex": "^3.0.1"
},
"devDependencies": {
@@ -35,7 +36,7 @@
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
- "chalk": "^2.0.1",
+ "chalk": "^2.4.2",
"copy-webpack-plugin": "^4.6.0",
"css-loader": "^0.28.0",
"eslint": "^4.15.0",
@@ -54,22 +55,22 @@
"node-notifier": "^5.3.0",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
- "portfinder": "^1.0.19",
+ "portfinder": "^1.0.20",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.1.6",
"postcss-url": "^7.2.1",
- "rimraf": "^2.6.0",
+ "rimraf": "^2.6.3",
"semver": "^5.6.0",
"shelljs": "^0.7.6",
"uglifyjs-webpack-plugin": "^1.3.0",
"url-loader": "^1.1.2",
"vue-loader": "^13.7.3",
"vue-style-loader": "^3.0.1",
- "vue-template-compiler": "^2.5.17",
+ "vue-template-compiler": "^2.5.22",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.13.1",
"webpack-dev-server": "^2.11.3",
- "webpack-merge": "^4.1.4"
+ "webpack-merge": "^4.2.1"
},
"engines": {
"node": ">= 6.0.0",
diff --git a/webapp/src/components/DashboardPage.vue b/webapp/src/components/DashboardPage.vue
index 53d19be..f408ebb 100644
--- a/webapp/src/components/DashboardPage.vue
+++ b/webapp/src/components/DashboardPage.vue
@@ -16,7 +16,7 @@
</i18n>
<template>
- <v-app :dark="settings.dark" class="non-selectable">
+ <v-app :dark="settings.dark">
<v-touch
@panstart="drawerDragStart"
@panmove="drawerDragMove"
@@ -26,7 +26,7 @@
class="drawer-handle-closed"
></v-touch>
<v-navigation-drawer
- class="drawer"
+ class="drawer non-selectable"
:style="drawerDraggingStyle"
persistent
:mini-variant="desktop && drawerMini"
@@ -59,7 +59,7 @@
</v-touch>
</v-navigation-drawer>
- <v-toolbar dark app :clipped-left="settings.clipped">
+ <v-toolbar dark app :clipped-left="settings.clipped" class="non-selectable">
<v-toolbar-side-icon class="drawer-icon" @click.stop="toggleDrawer"></v-toolbar-side-icon>
<div class="logo"><img class="non-draggable hidden-md-and-down" src="@/assets/logo.svg" /></div>
<v-spacer></v-spacer>
diff --git a/webapp/src/components/DataTable.vue b/webapp/src/components/DataTable.vue
new file mode 100644
index 0000000..721f137
--- /dev/null
+++ b/webapp/src/components/DataTable.vue
@@ -0,0 +1,194 @@
+<i18n>
+{
+ "en": {
+ "search": "Search",
+ "all": "All",
+ "pageText": "{0}-{1} of {2}",
+ "pageTextZero": "0 of 0",
+ "rowsPerPageText": "Rows per page:"
+ },
+ "de": {
+ "search": "Suche",
+ "all": "Alle",
+ "pageText": "{0}-{1} von {2}",
+ "pageTextZero": "0 von 0",
+ "rowsPerPageText": "Reihen pro page:"
+ }
+}
+</i18n>
+
+<template>
+ <div>
+ <v-layout wrap align-center class="actions-container">
+ <v-flex md3 sm5 xs12 order-md1 order-sm1 order-xs1 class="text-xs-left">
+ <v-text-field
+ class="search-field"
+ :placeholder="$t('search')"
+ v-model="search"
+ hide-details
+ prepend-inner-icon="search"
+ ></v-text-field>
+ </v-flex>
+ <v-flex md4 sm12 xs12 offset-md1 offset-sm0 offset-xs0 order-md2 order-sm3 order-xs3
+ class="text-md-center text-xs-right caption font-weight-thin">
+ {{ $t('rowsPerPageText') }}
+ <v-select
+ v-model="rowCount"
+ :items="[ 5, 10, 20, 50, 100, { text: $t('all'), value: '-1' }]"
+ color="primary"
+ hide-details
+ :menu-props="{
+ offsetY: ''
+ }"
+ ></v-select>
+ </v-flex>
+ </v-layout>
+ <v-divider></v-divider>
+
+ <RecycleScroller
+ class="scroller"
+ :style="scrollerStyle"
+ :items="filterdRows"
+ :item-height="48"
+ :page-mode="rowCount <= 0"
+ >
+ <div slot-scope="{ item }" class="table-row"
+ :style="item.selected && { backgroundColor: $vuetify.theme.primary + '11' }"
+ @click="selectItem(item)"
+ >
+ <div>
+ <v-icon style="cursor: pointer">{{ item.selected ? 'check_box' : 'check_box_outline_blank' }}</v-icon>
+ </div>
+ <div v-for="header in headers" :key="header.key" :style="{ width: header.width || headerWidthPercent }">
+ {{ item.data[header.key] }}
+ </div>
+ </div>
+ </RecycleScroller>
+
+ </div>
+</template>
+
+<script>
+import DataTableItem from '@/components/DataTableItem'
+
+export default {
+ name: 'DataTable',
+ components: { DataTableItem },
+ props: {
+ headers: {
+ type: Array
+ },
+ items: {
+ type: Array,
+ required: true
+ },
+ value: {
+ type: Array,
+ default: () => []
+ },
+ minWidth: {
+ type: Number,
+ default: 600
+ }
+ },
+ data () {
+ return {
+ search: '',
+ lastSelectId: null,
+ shiftKey: false,
+ rowCount: 10
+ }
+ },
+ computed: {
+ scrollerStyle () {
+ const style = { '--min-table-width': this.minWidth + 'px' }
+ if (this.rowCount > 0) style.height = Math.min(this.rowCount, this.filterdRows.length) * 48 + 'px'
+ return style
+ },
+ headerWidthPercent () { return 100/this.headers.length + '%' },
+ dataKeys () { return this.headers.map(x => x.key) },
+ rows () {
+ return this.items.map(item => ({ data: item, selected: false, id: item.id }))
+ },
+ filterdRows () {
+ const search = String(this.search).toLowerCase().trim()
+ return search === '' ? this.rows : this.rows.filter(row => {
+ return Object.keys(row.data).some(key => {
+ return this.dataKeys.includes(key) && String(row.data[key]).toLowerCase().includes(search)
+ })
+ })
+ }
+ },
+ watch: {
+ value (v) {
+
+ },
+ items () {
+ this.$emit('input', [])
+ }
+ },
+ methods: {
+ setShiftState (event) {
+ this.shiftKey = event.shiftKey
+ },
+ selectItem (item) {
+ item.selected = !item.selected
+ var tmp = this.value
+ if (item.selected) {
+ tmp.push(item.data)
+ } else {
+ tmp.splice(tmp.indexOf(item.data), 1)
+ }
+ this.$emit('input', tmp)
+ }
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+
+.scroller-wrapper {
+ overflow-x: auto;
+}
+
+.scroller {
+ overflow-x: auto;
+}
+
+.scroller >>> .vue-recycle-scroller__item-wrapper {
+ min-width: var(--min-table-width);
+}
+
+.table-row {
+ height: 48px;
+ display: flex;
+ align-items: center;
+}
+
+.table-row > div {
+ padding: 0 24px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.theme--dark .table-row {
+ border-bottom: 1px solid hsla(0,0%,100%,.12);
+}
+
+.theme--light .table-row {
+ border-bottom: 1px solid rgba(0,0,0,.12);
+}
+
+
+.theme--dark .scroller >>> .hover > .table-row {
+ background-color: #616161;
+}
+
+.theme--light .scroller >>> .hover > .table-row {
+ background-color: #eee;
+}
+
+</style>
+
diff --git a/webapp/src/components/DataTableItem.vue b/webapp/src/components/DataTableItem.vue
new file mode 100644
index 0000000..5210efa
--- /dev/null
+++ b/webapp/src/components/DataTableItem.vue
@@ -0,0 +1,88 @@
+<i18n>
+{
+ "en": {
+ },
+ "de": {
+ }
+}
+</i18n>
+
+<template>
+ <tr
+ :style="row.selected && { backgroundColor: $vuetify.theme.primary + '11' }"
+ @click="selectRow(row)"
+ @dblclick="$emit('dbclick', row.item)"
+ >
+ <td class="narrow-td">
+ <v-icon style="cursor: pointer">{{ row.selected ? 'check_box' : 'check_box_outline_blank' }}</v-icon>
+ </td>
+ <td v-for="header in headers" :key="header.key">
+ {{ row.item[header.key] }}
+ </td>
+ </tr>
+</template>
+
+<script>
+
+export default {
+ name: 'DataTableItem',
+ props: {
+ row: {
+ type: Object,
+ required: true
+ },
+ headers: {
+ type: Array,
+ required: true
+ }
+ },
+ data () {
+ return {
+ }
+ },
+ methods: {
+ selectRow (row) {
+ row.selected = !row.selected
+ this.$emit('select', row)
+ }
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+.narrow-td {
+ width: 10px;
+}
+
+tr {
+ height: 48px;
+ box-sizing: border-box;
+}
+
+td {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.theme--dark td {
+ border-bottom: 1px solid hsla(0,0%,100%,.12);
+}
+
+.theme--light td {
+ border-bottom: 1px solid rgba(0,0,0,.12);
+}
+
+.theme--dark tr:hover {
+ background: #616161;
+}
+
+.theme--light tr:hover {
+ background: #eee;
+}
+
+td {
+ padding: 0 24px;
+}
+</style>
diff --git a/webapp/src/components/GroupModuleGroupList.vue b/webapp/src/components/GroupModuleGroupList.vue
index 896eeaa..88add11 100644
--- a/webapp/src/components/GroupModuleGroupList.vue
+++ b/webapp/src/components/GroupModuleGroupList.vue
@@ -24,25 +24,7 @@
<template>
<div>
<v-card>
- <component-search-table v-model="selected" :headers="headers" :items="groups" select-all :loading="loading">
- <template slot="items" slot-scope="row">
- <tr :style="row.color" @click="row.data.selected = !row.data.selected" @dblclick="loadGroup(row.data.item)">
- <td class="narrow-td">
- <v-checkbox
- color="primary"
- v-model="row.data.selected"
- hide-details
- ></v-checkbox>
- </td>
- <td class="narrow-td">{{ row.data.item.id }}</td>
- <td>{{ row.data.item.name }}</td>
- <td>{{ row.data.item.description }}</td>
- <td class="narrow-td">
- <v-btn class="next-arrow" icon @click.stop="loadGroup(row.data.item)"><v-icon>keyboard_arrow_right</v-icon></v-btn>
- </td>
- </tr>
- </template>
- </component-search-table>
+ <data-table v-model="selected" :headers="headers" :items="groups" :loading="loading" />
</v-card>
<div v-if="tabIndex === 0" class="text-xs-right">
<v-btn flat color="error" @click="deleteSelected" :disabled="selected.length === 0">
@@ -60,14 +42,14 @@
</template>
<script>
-import ComponentSearchTable from '@/components/ComponentSearchTable'
+import DataTable from '@/components/DataTable'
import { mapMutations } from 'vuex'
export default {
name: 'GroupModuleGroupList',
props: ['tabIndex', 'groupId', 'groups'],
components: {
- ComponentSearchTable
+ DataTable
},
data () {
return {
@@ -77,10 +59,9 @@ export default {
computed: {
headers () {
return [
- { text: this.$t('id'), value: 'id' },
- { text: this.$t('name'), value: 'name' },
- { text: this.$t('description'), value: 'description' },
- { sortable: false }
+ { text: this.$t('id'), key: 'id', width: '100px' },
+ { text: this.$t('name'), key: 'name' },
+ { text: this.$t('description'), key: 'description' }
]
},
loading () {
diff --git a/webapp/src/main.js b/webapp/src/main.js
index d7ee839..bba65c5 100644
--- a/webapp/src/main.js
+++ b/webapp/src/main.js
@@ -1,6 +1,8 @@
import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'
+import VueVirtualScroller from 'vue-virtual-scroller'
+import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import VueCodemirror from 'vue-codemirror'
import 'codemirror/mode/shell/shell.js'
import 'codemirror/lib/codemirror.css'
@@ -43,6 +45,8 @@ Vue.use(Vuetify, {
}
})
+Vue.use(VueVirtualScroller)
+
Vue.use(VueCodemirror, {
options: {
theme: 'monokai',