diff --git a/package-lock.json b/package-lock.json index 64f5dc9aa47ec1f0c4da483dc60da8090a363d67..4cdadf08d8250d4d85ce4715d992d6cb73b24b8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1338,16 +1338,6 @@ "regenerator-runtime": "^0.13.4" } }, - "@babel/runtime-corejs3": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.9.tgz", - "integrity": "sha512-p6WSr71+5u/VBf1KDS/Y4dK3ZwbV+DD6wQO3X2EbUVluEOiyXUk09DzcwSaUH4WomYXrEPC+i2rqzuthhZhOJw==", - "optional": true, - "requires": { - "core-js-pure": "^3.0.0", - "regenerator-runtime": "^0.13.4" - } - }, "@babel/template": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", @@ -1431,14 +1421,6 @@ "resolved": "https://registry.npmjs.org/@date-io/core/-/core-1.3.13.tgz", "integrity": "sha512-AlEKV7TxjeK+jxWVKcCFrfYAk8spX9aCyiToFIiLPtfQbsjmRGLIhb5VZgptQcJdHtLXo7+m0DuurwFgUToQuA==" }, - "@date-io/date-fns": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@date-io/date-fns/-/date-fns-1.1.0.tgz", - "integrity": "sha512-FMRhYWfoGiIXdN4xWAArpkdEbqsg2Fr+6Yda7Np2eVWCNx6gSMYsHIM51IIcI+3762ajYbhoEYjHYXVFNZIk1g==", - "requires": { - "@date-io/core": "^1.1.0" - } - }, "@date-io/moment": { "version": "1.3.13", "resolved": "https://registry.npmjs.org/@date-io/moment/-/moment-1.3.13.tgz", @@ -6158,12 +6140,6 @@ "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", "dev": true }, - "@types/raf": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.0.tgz", - "integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==", - "optional": true - }, "@types/reach__router": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.7.tgz", @@ -7168,7 +7144,8 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true }, "autoprefixer": { "version": "9.8.6", @@ -7845,12 +7822,6 @@ } } }, - "base64-arraybuffer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz", - "integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==", - "optional": true - }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -8272,11 +8243,6 @@ "node-int64": "^0.4.0" } }, - "btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" - }, "buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", @@ -8471,20 +8437,6 @@ "integrity": "sha512-8aE+sqBqtXz4G8g35Eg/XEaFr2N7rd/VQ6eABGBmNtcB8cN6qNJhMi6oSFy4UWWZgqgL3filHT8Nha4meu3tsw==", "dev": true }, - "canvg": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.7.tgz", - "integrity": "sha512-4sq6iL5Q4VOXS3PL1BapiXIZItpxYyANVzsAKpTPS5oq4u3SKbGfUcbZh2gdLCQ3jWpG/y5wRkMlBBAJhXeiZA==", - "optional": true, - "requires": { - "@babel/runtime-corejs3": "^7.9.6", - "@types/raf": "^3.4.0", - "raf": "^3.4.1", - "rgbcolor": "^1.0.1", - "stackblur-canvas": "^2.0.0", - "svg-pathdata": "^5.0.5" - } - }, "capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -9533,7 +9485,8 @@ "core-js-pure": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.9.1.tgz", - "integrity": "sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A==" + "integrity": "sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A==", + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -9822,14 +9775,6 @@ "randomfill": "^1.0.3" } }, - "css-box-model": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", - "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", - "requires": { - "tiny-invariant": "^1.0.6" - } - }, "css-in-js-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", @@ -9839,15 +9784,6 @@ "isobject": "^3.0.1" } }, - "css-line-break": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-1.1.1.tgz", - "integrity": "sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==", - "optional": true, - "requires": { - "base64-arraybuffer": "^0.2.0" - } - }, "css-loader": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.1.1.tgz", @@ -10213,11 +10149,6 @@ "node-addon-api": "^1.7.1" } }, - "debounce": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz", - "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -10669,12 +10600,6 @@ "domelementtype": "1" } }, - "dompurify": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.6.tgz", - "integrity": "sha512-7b7ZArhhH0SP6W2R9cqK6RjaU82FZ2UPM7RO8qN1b1wyvC/NY1FNWcX1Pu00fFOAnzEORtwXe4bPaClg6pUybQ==", - "optional": true - }, "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", @@ -12407,11 +12332,6 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "optional": true }, - "filefy": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/filefy/-/filefy-0.1.10.tgz", - "integrity": "sha512-VgoRVOOY1WkTpWH+KBy8zcU1G7uQTVsXqhWEgzryB9A5hg2aqCyZ6aQ/5PSzlqM5+6cnVrX6oYV0XqD3HZSnmQ==" - }, "filesize": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", @@ -13600,15 +13520,6 @@ } } }, - "html2canvas": { - "version": "1.0.0-rc.7", - "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.0.0-rc.7.tgz", - "integrity": "sha512-yvPNZGejB2KOyKleZspjK/NruXVQuowu8NnV2HYG7gW7ytzl+umffbtUI62v2dCHQLDdsK6HIDtyJZ0W3neerA==", - "optional": true, - "requires": { - "css-line-break": "1.1.1" - } - }, "htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -17630,24 +17541,6 @@ "graceful-fs": "^4.1.6" } }, - "jspdf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.1.0.tgz", - "integrity": "sha512-NQygqZEKhSw+nExySJxB72Ge/027YEyIM450Vh/hgay/H9cgZNnkXXOQPRspe9EuCW4sq92zg8hpAXyyBdnaIQ==", - "requires": { - "atob": "^2.1.2", - "btoa": "^1.2.1", - "canvg": "^3.0.6", - "core-js": "^3.6.0", - "dompurify": "^2.0.12", - "html2canvas": "^1.0.0-rc.5" - } - }, - "jspdf-autotable": { - "version": "3.5.9", - "resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.5.9.tgz", - "integrity": "sha512-ZRfiI5P7leJuWmvC0jGVXu227m68C2Jfz1dkDckshmDYDeVFCGxwIBYdCUXJ8Eb2CyFQC2ok82fEWO+xRDovDQ==" - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -18160,59 +18053,6 @@ "integrity": "sha512-O8DMCl32V34RrD+ZHxcAPc2+kYytuDIoQYjY36RVdsLK7uHjgNVvFec4yv0X6LgB4YEZgSvK5QtFi5YVqEpoMA==", "dev": true }, - "material-table": { - "version": "1.69.2", - "resolved": "https://registry.npmjs.org/material-table/-/material-table-1.69.2.tgz", - "integrity": "sha512-OFst2Dzo5EZHk5mTPVR9cILFT9BcDSVVlFii0UOgvWE8ho/sn0euV1B2MfoTLKSX6pMkH0I/f3QTLX0AXhlUzQ==", - "requires": { - "@date-io/date-fns": "1.1.0", - "@material-ui/pickers": "3.2.2", - "classnames": "2.2.6", - "date-fns": "2.0.0-alpha.27", - "debounce": "1.2.0", - "fast-deep-equal": "2.0.1", - "filefy": "0.1.10", - "jspdf": "2.1.0", - "jspdf-autotable": "3.5.9", - "prop-types": "15.6.2", - "react-beautiful-dnd": "13.0.0", - "react-double-scrollbar": "0.0.15" - }, - "dependencies": { - "@material-ui/pickers": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@material-ui/pickers/-/pickers-3.2.2.tgz", - "integrity": "sha512-on/J1yyKeJ4CkLnItpf/jPDKMZVWvHDklkh5FS7wkZ0s1OPoqTsPubLWfA7eND6xREnVRyLFzVTlE3VlWYdQWw==", - "requires": { - "@babel/runtime": "^7.2.0", - "@types/styled-jsx": "^2.2.8", - "clsx": "^1.0.2", - "react-transition-group": "^4.0.0", - "rifm": "^0.7.0", - "tslib": "^1.9.3" - } - }, - "date-fns": { - "version": "2.0.0-alpha.27", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.0.0-alpha.27.tgz", - "integrity": "sha512-cqfVLS+346P/Mpj2RpDrBv0P4p2zZhWWvfY5fuWrXNR/K38HaAGEkeOwb47hIpQP9Jr/TIxjZ2/sNMQwdXuGMg==" - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", - "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" - } - } - } - }, "math.gl": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/math.gl/-/math.gl-3.4.2.tgz", @@ -18297,11 +18137,6 @@ "fs-monkey": "1.0.3" } }, - "memoize-one": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", - "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" - }, "memoizerific": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", @@ -20579,11 +20414,6 @@ "performance-now": "^2.1.0" } }, - "raf-schd": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz", - "integrity": "sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ==" - }, "railroad-diagrams": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", @@ -20674,20 +20504,6 @@ "prop-types": "^15.6.2" } }, - "react-beautiful-dnd": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz", - "integrity": "sha512-87It8sN0ineoC3nBW0SbQuTFXM6bUqM62uJGY4BtTf0yzPl8/3+bHMWkgIe0Z6m8e+gJgjWxefGRVfpE3VcdEg==", - "requires": { - "@babel/runtime": "^7.8.4", - "css-box-model": "^1.2.0", - "memoize-one": "^5.1.1", - "raf-schd": "^4.0.2", - "react-redux": "^7.1.1", - "redux": "^4.0.4", - "use-memo-one": "^1.1.1" - } - }, "react-colorful": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.1.2.tgz", @@ -21010,11 +20826,6 @@ "scheduler": "^0.19.1" } }, - "react-double-scrollbar": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/react-double-scrollbar/-/react-double-scrollbar-0.0.15.tgz", - "integrity": "sha1-6RWrjLO5WYdwdfSUNt6/2wQoj+Q=" - }, "react-draggable": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz", @@ -22106,12 +21917,6 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "rgbcolor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", - "integrity": "sha1-1lBezbMEplldom+ktDMHMGd1lF0=", - "optional": true - }, "rifm": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/rifm/-/rifm-0.7.0.tgz", @@ -23093,12 +22898,6 @@ } } }, - "stackblur-canvas": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz", - "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", - "optional": true - }, "stackframe": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", @@ -23608,12 +23407,6 @@ } } }, - "svg-pathdata": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-5.0.5.tgz", - "integrity": "sha512-TAAvLNSE3fEhyl/Da19JWfMAdhSXTYeviXsLSoDT1UM76ADj5ndwAPX1FKQEgB/gFMPavOy6tOqfalXKUiXrow==", - "optional": true - }, "svg.draggable.js": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", @@ -24712,11 +24505,6 @@ "use-isomorphic-layout-effect": "^1.0.0" } }, - "use-memo-one": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz", - "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==" - }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", diff --git a/package.json b/package.json index c13b69132089188f59f2bdf1092adcae69239463..d961048b0a65b4486171ee034f20aec39cb3b32f 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "leaflet.markercluster": "^1.4.1", "leaflet.zoominfo": "git+https://github.com/SemanticComputing/Leaflet.zoominfo.git", "lodash": "^4.17.20", - "material-table": "^1.69.2", "moment": "^2.24.0", "moment-range": "^4.0.2", "react": "^16.13.0", @@ -69,6 +68,7 @@ "react-router-dom": "^5.2.0", "react-sortable-tree": "2.8.0", "react-sortable-tree-theme-file-explorer": "git+https://github.com/SemanticComputing/react-sortable-tree-theme-file-explorer.git", + "react-virtualized": "^9.22.3", "redux": "^4.0.1", "redux-observable": "^1.0.0", "reselect": "^4.0.0", diff --git a/src/client/actions/index.js b/src/client/actions/index.js index ca81c1badfbdd4d82713a26fd8707d790a75aca6..e9328f7d2506e12bdc77002e2ed22f532dd5d9aa 100644 --- a/src/client/actions/index.js +++ b/src/client/actions/index.js @@ -5,6 +5,7 @@ export const FETCH_RESULTS_FAILED = 'FETCH_RESULTS_FAILED' export const FETCH_RESULT_COUNT = 'FETCH_RESULT_COUNT' export const FETCH_RESULT_COUNT_FAILED = 'FETCH_RESULT_COUNT_FAILED' export const FETCH_FULL_TEXT_RESULTS = 'FETCH_FULL_TEXT_RESULTS' +export const SORT_FULL_TEXT_RESULTS = 'SORT_FULL_TEXT_RESULTS' export const UPDATE_RESULT_COUNT = 'UPDATE_RESULT_COUNT' export const UPDATE_PAGINATED_RESULTS = 'UPDATE_PAGINATED_RESULTS' export const UPDATE_RESULTS = 'UPDATE_RESULTS' @@ -115,6 +116,11 @@ export const fetchFullTextResults = ({ resultClass, query }) => ({ resultClass, query }) +export const sortFullTextResults = ({ resultClass, sortBy }) => ({ + type: SORT_FULL_TEXT_RESULTS, + resultClass, + sortBy +}) export const fetchResultsFailed = (resultClass, error, message) => ({ type: FETCH_RESULTS_FAILED, resultClass, @@ -148,7 +154,7 @@ export const sortResults = (resultClass, sortBy) => ({ resultClass, sortBy }) -export const clearResults = resultClass => ({ +export const clearResults = ({ resultClass }) => ({ type: CLEAR_RESULTS, resultClass }) diff --git a/src/client/components/facet_results/MaterialTableFullTextResults.js b/src/client/components/facet_results/MaterialTableFullTextResults.js deleted file mode 100644 index e0019eb541c9d0ccd0216a2b191d0c9976dcdb9d..0000000000000000000000000000000000000000 --- a/src/client/components/facet_results/MaterialTableFullTextResults.js +++ /dev/null @@ -1,131 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { withStyles } from '@material-ui/core/styles' -import MaterialTable from 'material-table' -import SearchIcon from '@material-ui/icons/Search' -import ClearIcon from '@material-ui/icons/Clear' -import FirstPageIcon from '@material-ui/icons/FirstPage' -import LastPageIcon from '@material-ui/icons/LastPage' -import ChevronRightIcon from '@material-ui/icons/ChevronRight' -import ChevronLeftIcon from '@material-ui/icons/ChevronLeft' -import CircularProgress from '@material-ui/core/CircularProgress' -import ResultTableCell from './ResultTableCell' -import purple from '@material-ui/core/colors/purple' -import Paper from '@material-ui/core/Paper' - -const styles = () => ({ - progressContainer: { - width: '100%', - height: 'calc(100% - 72px)', - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - }, - tableContainer: { - maxWidth: '100%', - height: 'calc(100% - 72px)' - } -}) - -class MaterialTableFullTextResults extends React.Component { - render () { - const results = this.props.data - const resultText = results === 1 ? 'result' : 'results' - if (this.props.fetching) { - return ( - <Paper square className={this.props.classes.progressContainer}> - <CircularProgress style={{ color: purple[500] }} thickness={5} /> - </Paper> - ) - } else { - return ( - <div className={this.props.classes.tableContainer}> - <MaterialTable - columns={[ - { - title: 'Label', - field: 'prefLabel', - render: data => - <ResultTableCell - columnId='prefLabel' - data={data.prefLabel} - valueType='object' - makeLink - externalLink={false} - sortValues - numberedList={false} - minWidth={150} - container='div' - expanded - /> - }, - { - title: 'Type', - field: 'type', - render: data => - <ResultTableCell - columnId='type' - data={data.type} - valueType='object' - makeLink={false} - externalLink={false} - sortValues - numberedList={false} - minWidth={150} - container='div' - expanded - /> - }, - { - title: 'Source', - field: 'source', - render: data => - <ResultTableCell - columnId='source' - data={data.source} - valueType='object' - makeLink - externalLink - sortValues - numberedList={false} - minWidth={150} - container='div' - expanded - /> - } - ]} - data={results} - title={results > 1 - ? `Search term: "${this.props.query}", ${results.length} ${resultText}` - : ''} - icons={{ - Search: SearchIcon, - ResetSearch: ClearIcon, - FirstPage: FirstPageIcon, - LastPage: LastPageIcon, - NextPage: ChevronRightIcon, - PreviousPage: ChevronLeftIcon - }} - options={{ - pageSize: 5, - pageSizeOptions: [5, 10, 15, 20, 25] - }} - style={{ - height: '100%', - overflow: 'auto' - }} - /> - </div> - ) - } - } -} - -MaterialTableFullTextResults.propTypes = { - classes: PropTypes.object.isRequired, - data: PropTypes.array, - query: PropTypes.string, - fetching: PropTypes.bool.isRequired -} - -export default withStyles(styles)(MaterialTableFullTextResults) diff --git a/src/client/components/facet_results/ReactVirtualizedTable.js b/src/client/components/facet_results/ReactVirtualizedTable.js new file mode 100644 index 0000000000000000000000000000000000000000..1591ff7cb149eb7a3985173ed590dadaeb209dfe --- /dev/null +++ b/src/client/components/facet_results/ReactVirtualizedTable.js @@ -0,0 +1,232 @@ +import React from 'react' +import PropTypes from 'prop-types' +import clsx from 'clsx' +import { withStyles } from '@material-ui/core/styles' +import TableCell from '@material-ui/core/TableCell' +import TableSortLabel from '@material-ui/core/TableSortLabel' +import Tooltip from '@material-ui/core/Tooltip' +import ResultTableCell from './ResultTableCell' +import Paper from '@material-ui/core/Paper' +import { AutoSizer, Column, Table } from 'react-virtualized' +import intl from 'react-intl-universal' +import CircularProgress from '@material-ui/core/CircularProgress' +import purple from '@material-ui/core/colors/purple' + +const styles = theme => ({ + flexContainer: { + display: 'flex', + alignItems: 'center', + boxSizing: 'border-box' + }, + table: { + // temporary right-to-left patch, waiting for + // https://github.com/bvaughn/react-virtualized/issues/454 + '& .ReactVirtualized__Table__headerRow': { + flip: false, + paddingRight: theme.direction === 'rtl' ? '0 !important' : undefined + }, + '& .ReactVirtualized__Table__rowColumn': { + marginRight: theme.spacing(3) + } + }, + tableRow: { + borderBottom: '1px solid rgba(224, 224, 224, 1)' + }, + tableRowHover: { + '&:hover': { + backgroundColor: theme.palette.grey[200] + } + }, + tableCell: { + flex: 1 + }, + tableCellWithSort: { + marginRight: 16 + } +}) + +class MuiVirtualizedTable extends React.PureComponent { + static defaultProps = { + headerHeight: 48, + rowHeight: 40 + }; + + componentDidUpdate = prevProps => { + if (prevProps.sortBy !== this.props.sortBy || + prevProps.sortDirection !== this.props.sortDirection) { + this.forceUpdate() + } + } + + getRowClassName = ({ index }) => { + const { classes, onRowClick } = this.props + + return clsx(classes.tableRow, classes.flexContainer, { + [classes.tableRowHover]: index !== -1 && onRowClick != null + }) + }; + + cellRenderer = ({ cellData, columnIndex }) => { + const { columns /* classes, rowHeight, onRowClick */ } = this.props + const { id, valueType, makeLink, externalLink, sortValues, numberedList, minWidth } = columns[columnIndex] + return ( + <ResultTableCell + columnId={id} + data={cellData} + valueType={valueType} + makeLink={makeLink} + externalLink={externalLink} + numberedList={numberedList} + sortValues={sortValues} + minWidth={minWidth} + container='div' + expanded + /> + ) + }; + + headerRenderer = ({ label, columnIndex, dataKey }) => { + const { headerHeight, columns, classes, sortBy, sortDirection } = this.props + return ( + <> + <TableCell + component='div' + className={clsx( + classes.tableCell, + classes.flexContainer)} + variant='head' + style={{ height: headerHeight, textTransform: 'none' }} + align={columns[columnIndex].numeric || false ? 'right' : 'left'} + > + <Tooltip + title={`Sort by ${label}`} + enterDelay={300} + > + <TableSortLabel + active={sortBy === dataKey} + direction={sortDirection} + hideSortIcon + onClick={this.onSortBy(dataKey)} + > + {label} + </TableSortLabel> + </Tooltip> + </TableCell> + </> + ) + } + + onSortBy = sortBy => () => { + this.props.sortFullTextResults({ + resultClass: 'fullText', + sortBy + }) + } + + render () { + const { classes, columns, rowHeight, headerHeight, sortDirection, ...tableProps } = this.props + return ( + <AutoSizer> + {({ height, width }) => ( + <Table + height={height} + width={width} + rowHeight={rowHeight} + gridStyle={{ + direction: 'inherit' + }} + headerHeight={headerHeight} + className={classes.table} + {...tableProps} + rowClassName={this.getRowClassName} + > + {columns.map(({ id, minWidth, ...other }, index) => { + const label = intl.get(`perspectives.fullTextSearch.properties.${id}.label`) + return ( + <Column + key={id} + headerRenderer={(headerProps) => + this.headerRenderer({ + ...headerProps, + label, + columnIndex: index, + dataKey: id + })} + className={classes.flexContainer} + cellRenderer={this.cellRenderer} + dataKey={id} + width={minWidth} + {...other} + /> + ) + })} + </Table> + )} + </AutoSizer> + ) + } +} + +MuiVirtualizedTable.propTypes = { + classes: PropTypes.object.isRequired, + columns: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string.isRequired, + numeric: PropTypes.bool, + minWidth: PropTypes.number + }) + ).isRequired, + headerHeight: PropTypes.number, + onRowClick: PropTypes.func, + rowHeight: PropTypes.number +} + +const VirtualizedTable = withStyles(styles)(MuiVirtualizedTable) + +const rootStyle = { + height: 'calc(100% - 80px)', + fontFamily: 'Roboto' +} + +const tableContainer = { + width: 700, + height: '100%', + marginLeft: 'auto', + marginRight: 'auto' +} + +const progressContainerStyle = { + width: '100%', + height: 'calc(100% - 80px)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' +} + +const ReactVirtualizedTable = props => { + const { results, properties, sortBy, sortDirection, fetching } = props.fullTextSearch + return ( + <Paper square style={rootStyle}> + {fetching + ? ( + <div style={progressContainerStyle}> + <CircularProgress style={{ color: purple[500] }} thickness={5} /> + </div> + ) : ( + <div style={tableContainer}> + <VirtualizedTable + rowCount={results.length} + rowGetter={({ index }) => results[index]} + columns={properties} + fetching={fetching} + sortBy={sortBy} + sortDirection={sortDirection} + sortFullTextResults={props.sortFullTextResults} + /> + </div> + )} + </Paper> + ) +} + +export default ReactVirtualizedTable diff --git a/src/client/components/perspectives/sampo/FullTextSearch.js b/src/client/components/perspectives/sampo/FullTextSearch.js index 94cb82c50c7c432962d28b050c1760dfdcc54e47..b27a44a5caeb9a42f4c01c027c74bfa373b0885b 100644 --- a/src/client/components/perspectives/sampo/FullTextSearch.js +++ b/src/client/components/perspectives/sampo/FullTextSearch.js @@ -2,9 +2,16 @@ import React from 'react' import PropTypes from 'prop-types' import { Route, Redirect } from 'react-router-dom' import PerspectiveTabs from '../../main_layout/PerspectiveTabs' -import MaterialTableFullTextResults from '../../facet_results/MaterialTableFullTextResults' +import ReactVirtualizedTable from '../../facet_results/ReactVirtualizedTable' import CalendarViewDayIcon from '@material-ui/icons/CalendarViewDay' +const rootStyle = { + height: 'calc(100% - 8px)', + marginTop: 8, + marginLeft: 8, + marginRight: 8 +} + /** * A component for displaying full text search results. */ @@ -12,7 +19,7 @@ const FullTextSearch = props => { const { rootUrl } = props const perspectiveUrl = `${rootUrl}/full-text-search` return ( - <> + <div style={rootStyle}> <PerspectiveTabs routeProps={props.routeProps} screenSize={props.screenSize} @@ -31,15 +38,14 @@ const FullTextSearch = props => { path={`${perspectiveUrl}/table`} render={() => { return ( - <MaterialTableFullTextResults - data={props.fullTextSearch.results || []} - query={props.fullTextSearch.query} - fetching={props.fullTextSearch.fetching} + <ReactVirtualizedTable + fullTextSearch={props.fullTextSearch} + sortFullTextResults={props.sortFullTextResults} /> ) }} /> - </> + </div> ) } diff --git a/src/client/containers/SemanticPortal.js b/src/client/containers/SemanticPortal.js index 1bd7ec0b5d5f98a98a9160932c1096fdfc085ac6..50bc1b9354eda0167e93039ce2e623815f183896 100644 --- a/src/client/containers/SemanticPortal.js +++ b/src/client/containers/SemanticPortal.js @@ -42,6 +42,7 @@ import { fetchResults, fetchInstanceAnalysis, fetchFullTextResults, + sortFullTextResults, clearResults, fetchByURI, fetchFacet, @@ -327,6 +328,7 @@ const SemanticPortal = props => { <Grid item xs={12} className={classes.resultsContainer}> <FullTextSearch fullTextSearch={props.fullTextSearch} + sortFullTextResults={props.sortFullTextResults} routeProps={routeProps} screenSize={screenSize} rootUrl={rootUrlWithLang} @@ -654,6 +656,7 @@ const mapDispatchToProps = ({ fetchResults, fetchInstanceAnalysis, fetchFullTextResults, + sortFullTextResults, fetchByURI, fetchFacet, fetchFacetConstrainSelf, diff --git a/src/client/reducers/sampo/fullTextSearch.js b/src/client/reducers/sampo/fullTextSearch.js index c34dd1c3b1bad58c321a01b466896b0a30b0e796..e45cad1864f5878eba44af95a1a539dc2619dc63 100644 --- a/src/client/reducers/sampo/fullTextSearch.js +++ b/src/client/reducers/sampo/fullTextSearch.js @@ -1,12 +1,36 @@ import { FETCH_FULL_TEXT_RESULTS, + SORT_FULL_TEXT_RESULTS, UPDATE_RESULTS, CLEAR_RESULTS } from '../../actions' +import { orderBy } from 'lodash' export const INITIAL_STATE = { query: '', - results: null, + results: [], + sortBy: null, + sortDirection: null, + properties: [ + { + id: 'prefLabel', + valueType: 'object', + makeLink: true, + externalLink: false, + sortValues: false, + numberedList: false, + minWidth: 400 + }, + { + id: 'type', + valueType: 'string', + makeLink: false, + externalLink: false, + sortValues: false, + numberedList: false, + minWidth: 300 + } + ], fetching: false } @@ -27,6 +51,28 @@ const fullTextSearch = (state = INITIAL_STATE, action) => { } case CLEAR_RESULTS: return INITIAL_STATE + case SORT_FULL_TEXT_RESULTS: { + let sortBy + let sortDirection + if (action.sortBy === state.sortBy) { + sortBy = state.sortBy + sortDirection = state.sortDirection === 'asc' ? 'desc' : 'asc' + } + if (action.sortBy !== state.sortBy) { + sortBy = action.sortBy + sortDirection = 'asc' + } + return { + ...state, + sortBy, + sortDirection, + results: orderBy( + state.results, + sortBy === 'prefLabel' ? 'prefLabel.prefLabel' : sortBy, + sortDirection + ) + } + } default: return state } diff --git a/src/client/translations/sampo/localeEN.js b/src/client/translations/sampo/localeEN.js index 807788ecd8cc83e9b8bc99d3bd4a712a9bcc2de6..81cb159d374a8b5c665474498f1a5a1427655d38 100644 --- a/src/client/translations/sampo/localeEN.js +++ b/src/client/translations/sampo/localeEN.js @@ -667,6 +667,22 @@ export default { } } }, + fullTextSearch: { + properties: { + prefLabel: { + label: 'Label', + description: '' + }, + type: { + label: 'Type', + description: '' + }, + source: { + label: 'Source', + description: '' + } + } + }, manuscripts: { label: 'Manuscripts', facetResultsType: 'manuscripts', diff --git a/src/server/sparql/JenaQuery.js b/src/server/sparql/JenaQuery.js index 87eaa5f349b875ed332a524c2d54ae70e7e8afb8..116d0398f26c77cd0d0ebd5da80e0bdc8a62a1e7 100644 --- a/src/server/sparql/JenaQuery.js +++ b/src/server/sparql/JenaQuery.js @@ -1,6 +1,6 @@ import { has } from 'lodash' import { runSelectQuery } from './SparqlApi' -import { jenaQuery } from './SparqlQueriesGeneral' +import { fullTextQuery } from './SparqlQueriesGeneral' import { makeObjectList } from './SparqlObjectMapper' export const queryJenaIndex = async ({ @@ -9,7 +9,7 @@ export const queryJenaIndex = async ({ resultClass, resultFormat }) => { - let q = jenaQuery + let q = fullTextQuery const config = backendSearchConfig[resultClass] let endpoint if (has(config, 'endpoint')) { diff --git a/src/server/sparql/SparqlQueriesGeneral.js b/src/server/sparql/SparqlQueriesGeneral.js index f3e153f3be58e4b4e9c6632b17778b2195d699e3..a1566dbabb99291afb3ada32b36b5c82e53a7495 100644 --- a/src/server/sparql/SparqlQueriesGeneral.js +++ b/src/server/sparql/SparqlQueriesGeneral.js @@ -23,6 +23,16 @@ export const jenaQuery = ` } ` +export const fullTextQuery = ` + SELECT ?id ?prefLabel__id ?prefLabel__prefLabel ?prefLabel__dataProviderUrl + (SAMPLE(?type_) as ?type) + WHERE { + <QUERY> + <RESULT_SET_PROPERTIES> + } + GROUP BY ?id ?prefLabel__id ?prefLabel__prefLabel ?prefLabel__dataProviderUrl +` + export const facetResultSetQuery = ` SELECT * WHERE { diff --git a/src/server/sparql/sampo/sparql_queries/SparqlQueriesFullText.js b/src/server/sparql/sampo/sparql_queries/SparqlQueriesFullText.js index c140a852fb90d58f451a094c58c2e043a30dd973..04e2513bc1f538d9809a926bfff118155951ca16 100644 --- a/src/server/sparql/sampo/sparql_queries/SparqlQueriesFullText.js +++ b/src/server/sparql/sampo/sparql_queries/SparqlQueriesFullText.js @@ -1,20 +1,23 @@ export const fullTextSearchProperties = ` -{ - ?id a ?type__id . - ?type__id rdfs:label|skos:prefLabel ?type__prefLabel_ . - BIND(STR(?type__prefLabel_) AS ?type__prefLabel) # ignore language tags - } - UNION - { - ?id dct:source ?source__id . - ?source__id skos:prefLabel ?source__prefLabel . - ?source__id mmm-schema:data_provider_url ?source__dataProviderUrl . - } - UNION { - ?id mmm-schema:data_provider_url ?source__id . - BIND(?source__id as ?source__dataProviderUrl) - BIND(?source__id as ?source__prefLabel) + VALUES ?type_uri { + frbroo:F4_Manifestation_Singleton + frbroo:F1_Work + frbroo:F2_Expression + crm:E10_Transfer_of_Custody + crm:E12_Production + crm:E7_Activity + crm:E67_Birth + crm:E69_Death + mmm-schema:ManuscriptActivity + crm:E21_Person + crm:E74_Group + crm:E39_Actor + crm:E53_Place + crm:E78_Collection + } + ?id a ?type_uri . + ?type_uri skos:prefLabel|rdfs:label ?type_ . } UNION { @@ -32,7 +35,7 @@ export const fullTextSearchProperties = ` } UNION { - VALUES ?eventClass { crm:E10_Transfer_of_Custody crm:E12_Production crm:E7_Activity crm:E67_Birth crm:E69_Death } + VALUES ?eventClass { crm:E10_Transfer_of_Custody crm:E12_Production crm:E7_Activity crm:E67_Birth crm:E69_Death mmm-schema:ManuscriptActivity } ?id a ?eventClass . OPTIONAL { ?id skos:prefLabel ?prefLabel__id_ } BIND(COALESCE(?prefLabel__id_, ?id) as ?prefLabel__id) @@ -50,7 +53,8 @@ export const fullTextSearchProperties = ` UNION { ?id a crm:E53_Place . - ?id skos:prefLabel ?prefLabel__id . + OPTIONAL { ?id skos:prefLabel ?prefLabel__id_ } + BIND(COALESCE(?prefLabel__id_, ?id) as ?prefLabel__id) BIND(?prefLabel__id as ?prefLabel__prefLabel) BIND(CONCAT("/places/page/", REPLACE(STR(?id), "^.*\\\\/(.+)", "$1")) AS ?prefLabel__dataProviderUrl) }