diff --git a/package-lock.json b/package-lock.json index 5b20a16..88ae919 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "liberal-sydney-sparql-ui", - "version": "0.1.0", + "version": "0.9.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2117,16 +2117,6 @@ } } }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, "@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -2224,11 +2214,6 @@ } } }, - "@sheerun/mutationobserver-shim": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", - "integrity": "sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw==" - }, "@sinonjs/commons": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", @@ -2363,135 +2348,6 @@ "loader-utils": "^2.0.0" } }, - "@testing-library/dom": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-6.16.0.tgz", - "integrity": "sha512-lBD88ssxqEfz0wFL6MeUyyWZfV/2cjEZZV3YRpb2IoJRej/4f1jB0TzqIOznTpfR1r34CNesrubxwIlAQ8zgPA==", - "requires": { - "@babel/runtime": "^7.8.4", - "@sheerun/mutationobserver-shim": "^0.3.2", - "@types/testing-library__dom": "^6.12.1", - "aria-query": "^4.0.2", - "dom-accessibility-api": "^0.3.0", - "pretty-format": "^25.1.0", - "wait-for-expect": "^3.0.2" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "@types/yargs": { - "version": "15.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz", - "integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/jest-dom": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-4.2.4.tgz", - "integrity": "sha512-j31Bn0rQo12fhCWOUWy9fl7wtqkp7In/YP2p5ZFyRuiiB9Qs3g+hS4gAmDWONbAHcRmVooNJ5eOHQDCOmUFXHg==", - "requires": { - "@babel/runtime": "^7.5.1", - "chalk": "^2.4.1", - "css": "^2.2.3", - "css.escape": "^1.5.1", - "jest-diff": "^24.0.0", - "jest-matcher-utils": "^24.0.0", - "lodash": "^4.17.11", - "pretty-format": "^24.0.0", - "redent": "^3.0.0" - } - }, - "@testing-library/react": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-9.5.0.tgz", - "integrity": "sha512-di1b+D0p+rfeboHO5W7gTVeZDIK5+maEgstrZbWZSSvxDyfDRkkyBE1AJR5Psd6doNldluXlCWqXriUfqu/9Qg==", - "requires": { - "@babel/runtime": "^7.8.4", - "@testing-library/dom": "^6.15.0", - "@types/testing-library__react": "^9.1.2" - } - }, - "@testing-library/user-event": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-7.2.1.tgz", - "integrity": "sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA==" - }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", @@ -2583,15 +2439,6 @@ "@types/istanbul-lib-coverage": "*" } }, - "@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", - "requires": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, "@types/json-schema": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", @@ -2627,33 +2474,11 @@ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.2.tgz", "integrity": "sha512-i99hy7Ki19EqVOl77WplDrvgNugHnsSjECVR/wUrzw2TJXz1zlUfT2ngGckR6xN7yFYaijsMAqPkOLx9HgUqHg==" }, - "@types/prop-types": { - "version": "15.7.3", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" - }, "@types/q": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" }, - "@types/react": { - "version": "16.9.53", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.53.tgz", - "integrity": "sha512-4nW60Sd4L7+WMXH1D6jCdVftuW7j4Za6zdp6tJ33Rqv0nk1ZAmQKML9ZLD4H0dehA3FZxXR/GM8gXplf82oNGw==", - "requires": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-dom": { - "version": "16.9.8", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz", - "integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==", - "requires": { - "@types/react": "*" - } - }, "@types/resolve": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", @@ -2677,99 +2502,6 @@ "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.6.tgz", "integrity": "sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==" }, - "@types/testing-library__dom": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.14.0.tgz", - "integrity": "sha512-sMl7OSv0AvMOqn1UJ6j1unPMIHRXen0Ita1ujnMX912rrOcawe4f7wu0Zt9GIQhBhJvH2BaibqFgQ3lP+Pj2hA==", - "requires": { - "pretty-format": "^24.3.0" - } - }, - "@types/testing-library__react": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@types/testing-library__react/-/testing-library__react-9.1.3.tgz", - "integrity": "sha512-iCdNPKU3IsYwRK9JieSYAiX0+aYDXOGAmrC/3/M7AqqSDKnWWVv07X+Zk1uFSL7cMTUYzv4lQRfohucEocn5/w==", - "requires": { - "@types/react-dom": "*", - "@types/testing-library__dom": "*", - "pretty-format": "^25.1.0" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "@types/yargs": { - "version": "15.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz", - "integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "@types/uglify-js": { "version": "3.13.0", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.0.tgz", @@ -2822,14 +2554,6 @@ } } }, - "@types/yargs": { - "version": "13.0.11", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.11.tgz", - "integrity": "sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ==", - "requires": { - "@types/yargs-parser": "*" - } - }, "@types/yargs-parser": { "version": "15.0.0", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", @@ -5041,11 +4765,6 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" }, - "css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=" - }, "cssdb": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", @@ -5212,11 +4931,6 @@ } } }, - "csstype": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", - "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==" - }, "cyclist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", @@ -5473,11 +5187,6 @@ } } }, - "diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==" - }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -5533,11 +5242,6 @@ "esutils": "^2.0.2" } }, - "dom-accessibility-api": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.3.0.tgz", - "integrity": "sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA==" - }, "dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -7694,9 +7398,9 @@ "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==" }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "hpack.js": { "version": "2.1.6", @@ -9164,17 +8868,6 @@ } } }, - "jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - } - }, "jest-docblock": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", @@ -9462,11 +9155,6 @@ } } }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==" - }, "jest-haste-map": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", @@ -9810,17 +9498,6 @@ } } }, - "jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - } - }, "jest-message-util": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", @@ -11332,9 +11009,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash._reinterpolate": { "version": "3.0.0", @@ -11585,11 +11262,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" - }, "mini-css-extract-plugin": { "version": "0.11.3", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz", @@ -11787,11 +11459,6 @@ "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", "optional": true }, - "nanoid": { - "version": "3.1.21", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.21.tgz", - "integrity": "sha512-A6oZraK4DJkAOICstsGH98dvycPr/4GGDH7ZWKmMdd3vGcOurZ6JmWFUt0DA5bzrrn2FrUjmv6mFNWvv8jpppA==" - }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -13540,13 +13207,20 @@ }, "dependencies": { "postcss": { - "version": "8.2.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.8.tgz", - "integrity": "sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw==", + "version": "8.2.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz", + "integrity": "sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==", "requires": { "colorette": "^1.2.2", - "nanoid": "^3.1.20", + "nanoid": "^3.1.23", "source-map": "^0.6.1" + }, + "dependencies": { + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" + } } }, "source-map": { @@ -13652,24 +13326,6 @@ "renderkid": "^2.0.4" } }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", - "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - } - } - }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -14203,15 +13859,6 @@ "minimatch": "3.0.4" } }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -15737,14 +15384,6 @@ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "requires": { - "min-indent": "^1.0.0" - } - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -16647,11 +16286,6 @@ "xml-name-validator": "^3.0.0" } }, - "wait-for-expect": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-3.0.2.tgz", - "integrity": "sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag==" - }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", @@ -17141,8 +16775,7 @@ }, "ssri": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "resolved": "", "requires": { "figgy-pudding": "^3.5.1" } diff --git a/package.json b/package.json index 23a0a7a..ac96890 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "liberal-sydney-sparql-ui", - "version": "0.9.3", + "version": "0.9.4", "private": true, "homepage": "https://lmb.cdhr.anu.edu.au/explorer", "dependencies": { diff --git a/src/App.js b/src/App.js index b86ec1d..059d22d 100644 --- a/src/App.js +++ b/src/App.js @@ -11,7 +11,7 @@ class App extends React.Component { constructor(props) { super(props); this.state = { - selected: {type: '', id: '', content: '', meta: ''}, + selected: {type: '', id: '', content: '', isOptional: false, meta: ''}, transferredSuggestion: {exists: false}, lastReferencedUnknown: -1, lastReferencedUnknownAwaitingClass: false, @@ -30,10 +30,11 @@ class App extends React.Component { * node (specified by it's variant), edge or datatype. * @param {number} id - id of the given changed object. * @param {string} content - the changed input of the object. + * @param {boolean} isOptional - is the object SPARQL OPTIONAL? * @param {Object} meta - metadata of the selected item. */ - handleSelectedItemChange = (type, id, content, meta) => { - this.setState({selected: {type: type, id: id, content: content, meta: meta}}); + handleSelectedItemChange = (type, id, content, isOptional, meta) => { + this.setState({selected: {type: type, id: id, content: content, isOptional: isOptional, meta: meta}}); } /** @@ -77,16 +78,17 @@ class App extends React.Component { * @param {string} type - the initial state of the created Node, either being a placeholder ('nodeUnf') or a * fully formed node ('nodeUnknown'/'nodeKnown') * @param {string} content - content that the node starts with + * @param {boolean} isOptional - is the node SPARQL OPTIONAL? * @param {string} [iri] - optional iri for nodes that have type 'nodeUri' * @returns {number} - id of node just created. */ - createNode = (x, y, type, content, iri) => { + createNode = (x, y, type, content, isOptional, iri) => { const { nodeCounter } = this.state; const variant = Node.variants[type](false); const newNode = { x: x - variant.width / 2, y: y - variant.height / 2, midX: x, midY: y, - id: nodeCounter + 1, type: type, content: content, isOptional: false, amalgam: null + id: nodeCounter + 1, type: type, content: content, isOptional: isOptional, amalgam: null }; if (iri) newNode.iri = iri; @@ -100,7 +102,7 @@ class App extends React.Component { })); if (type !== 'nodeUnf') { - this.handleSelectedItemChange(type, nodeCounter + 1, content, null); + this.handleSelectedItemChange(type, nodeCounter + 1, content, isOptional, null); } return nodeCounter + 1; @@ -136,7 +138,9 @@ class App extends React.Component { tempEdge: {completing: true, x: subjectPos.midX + 1, y: subjectPos.midY + 1} })); - this.handleSelectedItemChange(content === '?' ? 'edgeUnknown' : 'edgeKnown', edgeCounter + 1, content, null); + this.handleSelectedItemChange( + content === '?' ? 'edgeUnknown' : 'edgeKnown', edgeCounter + 1, content, false,null + ); } /** @@ -283,18 +287,40 @@ class App extends React.Component { } } else { // it is an edge and we find the connected node and recursively delete const deletedEdge = graph.edges.find(edge => edge.id === id); - const deletedEdgeNode = graph.nodes.find(node => node.id === deletedEdge.object.id); - if (deletedEdgeNode) { - this.deleteItemCascade(deletedEdgeNode.id, deletedEdgeNode.type, false); + const deletedEdgeObjNode = graph.nodes.find(node => node.id === deletedEdge.object.id); + + if (deletedEdgeObjNode) { + this.deleteItemCascade(deletedEdgeObjNode.id, deletedEdgeObjNode.type, false); } this.deleteEdge(id); } if (isFirst) { // on most shallow recursion, set selected item to incoming item or empty. + if (type.startsWith('edge')) { + this.checkOptionalityOnSubjectNodeOfDeletedEdge(id, graph); + } this.findSuitableSelectedItemChange(id, type, graph); } } + /** + * if there are no more OPTIONAL outgoing edges from the subject node of the deleted edge, set the node to not being + * optional + * @param {number} id - id of the deleted edge + * @param {Object} graph - snapshot of the graph before deletion + */ + checkOptionalityOnSubjectNodeOfDeletedEdge = (id, graph) => { + const deletedEdge = graph.edges.find(edge => edge.id === id); + if (!deletedEdge.isOptional) return; + + const subjNodeOfDeletedEdge = graph.nodes.find(node => node.id === deletedEdge.subject.id); + const subjNodeEdges = graph.edges.filter(edge => edge.subject.id === subjNodeOfDeletedEdge.id && edge.id !== id); + + if (!subjNodeEdges.some(edge => edge.isOptional)) { + this.changeNodeState(subjNodeOfDeletedEdge.id, {isOptional: false}); + } + } + /** * Finds the incoming node or edge off the deleted on, setting it as the selected item * @param id - id of the deleted item @@ -306,15 +332,18 @@ class App extends React.Component { if (type.startsWith('node')) { const selectedEdge = graph.edges.find(edge => edge.object.id === id); if (selectedEdge) { - this.handleSelectedItemChange(selectedEdge.type, selectedEdge.id, selectedEdge.content, null); - } else { - this.handleSelectedItemChange('', -1, '', ''); + this.handleSelectedItemChange( + selectedEdge.type, selectedEdge.id, selectedEdge.content, selectedEdge.isOptional, null + ); + } else { // have no selected item + this.handleSelectedItemChange('', -1, '', false,''); } } else { const deletedEdge = graph.edges.find(edge => edge.id === id); const selectedNode = graph.nodes.find(node => node.id === deletedEdge.subject.id); this.handleSelectedItemChange( - selectedNode.type, selectedNode.id, selectedNode.content, {amalgam: selectedNode.amalgam} + selectedNode.type, selectedNode.id, selectedNode.content, selectedNode.isOptional, + {amalgam: selectedNode.amalgam} ); } } @@ -351,7 +380,7 @@ class App extends React.Component { onSelectedItemChange={this.handleSelectedItemChange} acknowledgeTransferredSuggestion={this.handleAcknowledgedSuggestion}/> this.deleteItemCascade(id, type, true)} onSelectedItemChange={this.handleSelectedItemChange} onTransferSuggestionToCanvas={this.handleTransferSuggestionToCanvas} diff --git a/src/MenuBar.css b/src/MenuBar.css index 0d205ac..9fbabad 100644 --- a/src/MenuBar.css +++ b/src/MenuBar.css @@ -1,5 +1,4 @@ .navbar-default { - background-color: #3e3f3a; border-color: #3e3f3a; } .navbar-fixed-top { @@ -7,15 +6,12 @@ width: 100%; border-radius: 0; position: fixed; - overflow: hidden; right: 0; left: 0; z-index: 2000; } .navbar { - border-radius: 4px; position: fixed; - height: 50px; margin-bottom: 20px; border: 1px solid transparent; } @@ -51,4 +47,40 @@ font-family: 'Roboto Condensed', sans-serif; margin: 0; cursor: pointer; +} + +#help-page-container { + clear: both; + padding: 0 30px; +} + +#help-page-container p { + font-family: 'Roboto Condensed', sans-serif; +} + +.centered-table { + margin: 0 auto; + border-collapse: collapse; +} + +.centered-table td { + border-left: solid 1px black; + text-align: center; + padding: 0 10px; +} + +.big { + font-size: larger; +} + +.help-header { + font-size: 32px; +} + +.text { + font-family: 'Roboto Condensed', sans-serif; +} + +.img-outline { + border: solid 1px black; } \ No newline at end of file diff --git a/src/MenuBar.js b/src/MenuBar.js index 23a665a..ce47946 100644 --- a/src/MenuBar.js +++ b/src/MenuBar.js @@ -1,12 +1,52 @@ -import React from "react"; +import React, {useState} from "react"; +import {motion} from "framer-motion"; +import uri from './img/uri.png'; +import unknown from './img/unknown.png'; +import selectedunknown from './img/selectedunknown.png'; +import literal from './img/literal.png'; +import property from './img/property.png'; +import ex0 from './img/ex0.png'; +import ex1 from './img/ex1.png'; +import ex2 from './img/ex2.png'; +import sidebar from './img/sidebar.png'; +import unknowntype from './img/unknowntype.png'; +import showresults from './img/showresults.png'; +import optional from './img/optional.png'; import './MenuBar.css'; -export default class MenuBar extends React.Component { - /** - * - * @param {number} i - index of example to load - */ - loadExample = (i) => { +export default function MenuBar(props) { + const [isHelpPage, setIsHelpPage] = useState(false); + const menuVariants = { + menubar: { + height: '50px', + borderRadius: '4px', + overflowY: 'hidden', + backgroundColor: '#3e3f3a' + }, + helpbar: { + height: '90%', + borderRadius: '15px', + overflowY: 'auto', + backgroundColor: '#d6d6d6' + } + }; + + return ( + +
+

LMB SPARQL Explorer

+
+ {!isHelpPage && props.loadExampleIntoCanvas(example)} + toggleHelpPage={() => setIsHelpPage(!isHelpPage)} />} + {isHelpPage && setIsHelpPage(!isHelpPage)} />} +
+ ); +} + +function NavBar(props) { + const loadExample = (i) => { fetch('examples.json', { headers: { 'Content-Type': 'application/json', @@ -14,33 +54,112 @@ export default class MenuBar extends React.Component { } }).then( response => response.json().then( - json => this.props.loadExampleIntoCanvas(json.examples[i]), + json => props.loadExampleIntoCanvas(json.examples[i]), error => console.warn("JSON is malformed", error)), error => console.warn("Couldn't get the local .json examples:", error) ); }; - render() { - return ( -
-
-

LMB SPARQL Explorer

-
- -
- ); - } + return ( + + ); +} + +function HelpPage(props) { + + return ( +
props.toggleHelpPage()}> +

Intro and Basics

+

The LMB SPARQL Explorer is a UI that aids the creation of valid, complete SPARQL Queries that does not require prior knowledge of the syntax of SPARQL.

+

It aids the creation of SPARQL queries based on a given ontology and its associated data, through inferring logical connections between the ontology and the instance-level data, through a graph-like structure.

+

In order to create some valid queries, there are some basic, fundamental building blocks that the LMB SPARQL Explorer uses, namely:

+ + + + + + + + + + + + + + + + + + + + + + +
+ Unknown Example + + Selected Unknown Example + + Uri Example + + Literal Example + + Property Example +
UnknownSelected UnknownURILiteralProperty
+

Think of it as a wildcard - some 'thing' that is matched against everything in the dataset

+
+

This is similar, except we want this 'thing' (that we'll call 'person') to be in our results

+
+

A URI is a concrete concept, something specific in the data - like the concept of a Person, a Book, an Address...

+
+

This is a primitive type - like a string of characters ("Hello!"), numbers (12, 42.0) or others as defined in the ontology

+
+

This connects each of the other nodes together in a relationship. Like the URI, this is also a concrete concept

+
+
+

Using just these, we can create simple examples, for example:

+ example of a simple type query +

This would read: "Give me all the things (lets call each of them ?person) that are a type of Person." We can extend upon this example, too (note the automatic simplification for the type of the Unknown):

+ example of a simple query +

If you were to read this in natural language, it would say something like: "Give me all the things (called '?person') which are a Person, who has the name Smith."

+

We can extend the examples further, too, using more relationships:

+ example of a more complex query +

This would be something like: "Give me the titles (called ?t) from Books that are authored by a Person with some first and last name."

+

You might be wondering, how do you know which connections/concepts are valid and will give a result? The LMB SPARQL Explorer infers this from the ontology (the 'blueprint' for the data), meaning you can only make valid queries based on the loaded dataset!

+
+

Using This Tool With an Example Workflow

+

When you have loaded your ontology and instance-level data into the database, you are initially presented with a list of basic concepts that are in the ontology. For example, in the Lord Mayor's Balls ontology, you will see:

+ {'The +

To begin your query, you should begin with a '?' node for an unknown that you may want to find more about. You simply drag a concept from the sidebar and it will automatically be added to the canvas. The suggestion bar will then change, based on the newly-selected 'Unknown' node.

+

You'll then be asked to add a 'type' relationship to this first Unknown, to allow the narrowing down of possible relationships this Unknown can have - there is an example of this above:

+ unknown node with type annotation +

After that, you will be given suggestions based on that type - you then continue to add relationships and nodes until you've finished your query. Examples of queries can be checked at the menubar.

+
+

Other Features

+

Selecting Unknowns for use in Results

+

You can select Unknowns by either clicking on them and giving them a name (don't forget the '?' at the front!) or clicking on them and looking at the clicked item in the sidebar. It'll have a button, like so:

+ show results button +
+

Making Things Optional

+

If you click on a relationship, you'll find a different option on the selected item: to make the relationship (and it's subject and object) optional. This allows you to make queries that have conditions that don't have to match, but if they do will be returned in the results. For example:

+ optional example +

This means "Give me the first name of all the things that are people, and the first names of their children, if they exist."

+
+ ); } \ No newline at end of file diff --git a/src/canvas/Canvas.js b/src/canvas/Canvas.js index eb4f59d..caf66cb 100644 --- a/src/canvas/Canvas.js +++ b/src/canvas/Canvas.js @@ -49,7 +49,9 @@ export default class Canvas extends React.Component { this.props.changeNodeState(amalgamId, amalgamChange); this.props.deleteNode(edgeToDelete.object.id); this.props.deleteEdge(edgeToDelete.id); - this.props.onSelectedItemChange(nodeAmalgam.type, nodeAmalgam.id, nodeAmalgam.content, amalgamChange); + this.props.onSelectedItemChange( + nodeAmalgam.type, nodeAmalgam.id, nodeAmalgam.content, nodeAmalgam.isOptional, amalgamChange + ); } /** @@ -86,10 +88,10 @@ export default class Canvas extends React.Component { this.props.changeNodeState(currentUnfNode.id, {content: prefixedNodeLabel, iri: suggestion.iri, type: type}); this.props.updateEdgeIntersections(selectedEdge, currentUnfNode); - this.props.onSelectedItemChange(type, currentUnfNode.id, prefixedNodeLabel, null); + this.props.onSelectedItemChange(type, currentUnfNode.id, prefixedNodeLabel, currentUnfNode.isOptional, null); } else { // it must be a base class and we would need to create a new one! - const newNodeId = this.props.createNode(50, 100, type, suggestion.label, suggestion.iri); - this.props.onSelectedItemChange(type, newNodeId, suggestion.label, null); + const newNodeId = this.props.createNode(50, 100, type, suggestion.label, false, suggestion.iri); + this.props.onSelectedItemChange(type, newNodeId, suggestion.label, false, null); } } @@ -103,15 +105,17 @@ export default class Canvas extends React.Component { const selectedEdge = edges.find(edge => edge.id === this.props.selected.id); - if (selectedEdge){ + if (selectedEdge){ // there is already an existing node connected to the edge - modify that one const currentUnfNode = nodes.find(node => node.id === selectedEdge.object.id); this.props.changeNodeState(currentUnfNode.id, {content: suggestion.label, type: 'nodeUnknown'}); this.props.updateEdgeIntersections(selectedEdge, currentUnfNode); - this.props.onSelectedItemChange('nodeUnknown', currentUnfNode.id, suggestion.label, null); - } else { - const newNodeId = this.props.createNode(50, 100, 'nodeUnknown', suggestion.label, null); - this.props.onSelectedItemChange('nodeUnknown', newNodeId, suggestion.label); + this.props.onSelectedItemChange( + 'nodeUnknown', currentUnfNode.id, suggestion.label, currentUnfNode.isOptional, null + ); + } else { // create a new nodeUnknown node + const newNodeId = this.props.createNode(50, 100, 'nodeUnknown', suggestion.label, false, null); + this.props.onSelectedItemChange('nodeUnknown', newNodeId, suggestion.label, false); } } @@ -130,18 +134,19 @@ export default class Canvas extends React.Component { else if (suggestion.name === 'int' || suggestion.name === 'integer') content = '0'; this.props.changeNodeState(currentUnfNode.id, {content: content, type: type}); - this.props.onSelectedItemChange(type, currentUnfNode.id, content, null); + this.props.onSelectedItemChange(type, currentUnfNode.id, content, currentUnfNode.isOptional, null); } /** - * Propagates a change on canvas to the root - eventually the sidebar + * Propagates a change on canvas to the root - eventually the Apps graph-state * @param {string} type - the type of the object modified: either a node, edge or datatype * @param {number} id - the canvas id of the modified object * @param {string} content - the content that was changed + * @param {boolean} isOptional - whether the object is SPARQL OPTIONAL * @param {Object} meta - metadata about the given change */ - handleElementChange = (type, id, content, meta) => { - this.props.onSelectedItemChange(type, id, content, meta); + handleElementChange = (type, id, content, isOptional, meta) => { + this.props.onSelectedItemChange(type, id, content, isOptional, meta); } /** @@ -154,7 +159,7 @@ export default class Canvas extends React.Component { if (event.defaultPrevented) return; if (tempEdge.completing){ // we'll complete the edge with a new, unfinished Node as object - const newNodeId = this.props.createNode(event.clientX, event.clientY, 'nodeUnf', "", null); + const newNodeId = this.props.createNode(event.clientX, event.clientY, 'nodeUnf', "", false, null); const variant = Node.variants['nodeUnf'](false); const newNodePos = { x: event.clientX - variant.width / 2, y: event.clientY - variant.height / 2, diff --git a/src/canvas/Edge.js b/src/canvas/Edge.js index cc613f5..9147f82 100644 --- a/src/canvas/Edge.js +++ b/src/canvas/Edge.js @@ -22,9 +22,9 @@ export default class Edge extends React.Component { static labelWidth = 175; handleEntryExit = (e) => { - const { id, type } = this.props; + const { id, type, isOptional } = this.props; - this.props.onSelectedItemChange(type, id, e.target.value, null); + this.props.onSelectedItemChange(type, id, e.target.value, isOptional, null); } /** diff --git a/src/canvas/Node.js b/src/canvas/Node.js index d3d89c4..3339c9e 100644 --- a/src/canvas/Node.js +++ b/src/canvas/Node.js @@ -6,57 +6,60 @@ import "./Canvas.css"; export default class Node extends React.Component { static variants = { nodeUnknown: isOpt => ({ - fill: isOpt ? '#1e90ff' : '#0000fe', + fill: '#0000fe', rx: 50, ry: 50, height: 100, width: 100, strokeWidth: isOpt ? 5 : 0, strokeDasharray: 3, - stroke: '#0000fe' + stroke: '#1e90ff' }), nodeSelectedUnknown: isOpt => ({ - fill: isOpt ? '#59adff' : '#1e90ff', + fill: '#1e90ff', rx: 50, ry: 50, height: 100, width: 100, strokeWidth: isOpt ? 5 : 0, strokeDasharray: 3, - stroke: '#0000fe' + stroke: '#59adff'//'#0000fe' }), nodeUri: isOpt => ({ - fill: isOpt ? '#4e4e4e' : '#bebebe', + fill: '#bebebe', rx: 50, ry: 50, height: 100, width: 100, strokeWidth: isOpt ? 5 : 0, strokeDasharray: 3, - stroke: '#0000fe' + stroke: '#4e4e4e' }), nodeLiteral: isOpt => ({ - fill: isOpt ? '#bebebe' : '#4e4e4e', + fill: '#4e4e4e', rx: 0, ry: 0, height: 100, width: 200, strokeWidth: isOpt ? 5 : 0, strokeDasharray: 3, - stroke: '#0000fe' + stroke: '#bebebe' }), nodeAmalgam: isOpt => ({ - fill: isOpt ? '#bebebe' : '#444444', + fill: '#444444', height: 100, width: 100, rx: 10, - ry: 10 + ry: 10, + strokeWidth: isOpt ? 5 : 0, + strokeDasharray: 3, + stroke: '#bebebe' }), nodeUnf: isOpt => ({ - fill: isOpt ? '#1e90ff' : '#0000fe', - strokeWidth: 0, + fill: '#0000fe', + strokeWidth: isOpt ? 3: 0, strokeDasharray: 3, - stroke: '#0000fe', + stroke: '#1e90ff', width: 40, height: 40, rx: 70, @@ -76,10 +79,10 @@ export default class Node extends React.Component { * @param e - event that triggered the function */ handleEntryExit = (e) => { - const { id, type, content, amalgam } = this.props; + const { id, type, content, isOptional, amalgam } = this.props; e.preventDefault(); - this.props.onSelectedItemChange(type, id, content, {amalgam: amalgam}); + this.props.onSelectedItemChange(type, id, content, isOptional, {amalgam: amalgam}); } diff --git a/src/img/ex0.png b/src/img/ex0.png new file mode 100644 index 0000000..a7632ea Binary files /dev/null and b/src/img/ex0.png differ diff --git a/src/img/ex1.png b/src/img/ex1.png new file mode 100644 index 0000000..68ea78c Binary files /dev/null and b/src/img/ex1.png differ diff --git a/src/img/ex2.png b/src/img/ex2.png new file mode 100644 index 0000000..fe688b7 Binary files /dev/null and b/src/img/ex2.png differ diff --git a/src/img/literal.png b/src/img/literal.png new file mode 100644 index 0000000..a81ff2a Binary files /dev/null and b/src/img/literal.png differ diff --git a/src/img/optional.png b/src/img/optional.png new file mode 100644 index 0000000..17bfdd4 Binary files /dev/null and b/src/img/optional.png differ diff --git a/src/img/property.png b/src/img/property.png new file mode 100644 index 0000000..27e12ee Binary files /dev/null and b/src/img/property.png differ diff --git a/src/img/selectedunknown.png b/src/img/selectedunknown.png new file mode 100644 index 0000000..cd42089 Binary files /dev/null and b/src/img/selectedunknown.png differ diff --git a/src/img/showresults.png b/src/img/showresults.png new file mode 100644 index 0000000..2b1f694 Binary files /dev/null and b/src/img/showresults.png differ diff --git a/src/img/sidebar.png b/src/img/sidebar.png new file mode 100644 index 0000000..5a90760 Binary files /dev/null and b/src/img/sidebar.png differ diff --git a/src/img/unknown.png b/src/img/unknown.png new file mode 100644 index 0000000..fb1121a Binary files /dev/null and b/src/img/unknown.png differ diff --git a/src/img/unknowntype.png b/src/img/unknowntype.png new file mode 100644 index 0000000..d6e0a3c Binary files /dev/null and b/src/img/unknowntype.png differ diff --git a/src/img/uri.png b/src/img/uri.png new file mode 100644 index 0000000..aaeba30 Binary files /dev/null and b/src/img/uri.png differ diff --git a/src/sidebar/ItemViewerComponents.js b/src/sidebar/ItemViewerComponents.js index baa3c20..b903d7b 100644 --- a/src/sidebar/ItemViewerComponents.js +++ b/src/sidebar/ItemViewerComponents.js @@ -98,36 +98,48 @@ export function ItemLiteralType(props) { ); } +const buttonVariants = { + Yes: {backgroundColor: '#b3b3b3'}, + No: {backgroundColor: '#9c9c9c'} +}; + export function BoundUnknownCheckbox(props) { const { type } = props; const toggleBound = () => props.onBoundChange(type === 'nodeUnknown' ? 'nodeSelectedUnknown' : 'nodeUnknown'); - const variants = { - yes: {backgroundColor: '#b3b3b3'}, - no: {backgroundColor: '#9c9c9c'} - }; - const animation = type === 'nodeSelectedUnknown' ? 'yes' : 'no'; + const isSelected = type === 'nodeSelectedUnknown' ? 'Yes' : 'No'; return ( <>

Show in results?

- toggleBound()}> -

{type === 'nodeSelectedUnknown' ? 'Yes' : 'No'}

+

{isSelected}

); } export function DeleteItemButton(props) { - const { id } = props; - const deleteItem = (id) => props.deleteItemCascade(id); - return ( <>

Delete Node

-
deleteItem(id)}> +
props.deleteItemCascade()}>

Delete

); +} + +export function OptionalTripleButton(props) { + const optionality = props.isOptional ? 'Yes' : 'No'; + + return ( + <> +

Make Optional

+ props.toggleOptionalTriple()}> +

{optionality}

+
+ + ); } \ No newline at end of file diff --git a/src/sidebar/QueryExecutor.js b/src/sidebar/QueryExecutor.js index 4cd8a1f..1dd44a3 100644 --- a/src/sidebar/QueryExecutor.js +++ b/src/sidebar/QueryExecutor.js @@ -144,7 +144,11 @@ export default class ExecuteQuerySection extends React.Component { const object = nodes.find(node => edge.object.id === node.id); const objectFrag = this.getNodeFrag(object, unknownNodes); - whereClauseString += ` ${subjectFrag} ${edgeFrag} ${objectFrag} .\n`; + if (edge.isOptional){ + whereClauseString += ` OPTIONAL { ${subjectFrag} ${edgeFrag} ${objectFrag} . }\n`; + } else { + whereClauseString += ` ${subjectFrag} ${edgeFrag} ${objectFrag} .\n`; + } } whereClauseString += '}\n'; @@ -191,9 +195,9 @@ function QueryResultsViewer(props) { const toggleViewer = () => setIsOpen(!isOpen); return ( - toggleViewer()} + -

Results Viewer

+

toggleViewer()}>Results Viewer

{props.query}

); diff --git a/src/sidebar/SelectedItemViewer.js b/src/sidebar/SelectedItemViewer.js index e90ea61..31f7ec3 100644 --- a/src/sidebar/SelectedItemViewer.js +++ b/src/sidebar/SelectedItemViewer.js @@ -9,7 +9,8 @@ import { ItemInferredProps, ItemLiteralType, BoundUnknownCheckbox, - DeleteItemButton + DeleteItemButton, + OptionalTripleButton } from "./ItemViewerComponents"; export default class SelectedItemViewer extends React.Component { @@ -46,14 +47,14 @@ export default class SelectedItemViewer extends React.Component { * @param {string} newType - the updated type */ notifySelectedStateChange = (newType) => { - const { id, content, meta } = this.props; + const { id, content, isOptional, meta } = this.props; this.props.changeNodeState(id, {type: newType}); - this.props.onSelectedItemChange(newType, id, content, meta); + this.props.onSelectedItemChange(newType, id, content, isOptional, meta); } render() { - const { id, type, content, basePrefix, info, infoLoaded, meta } = this.props; + const { id, type, content, isOptional, meta, basePrefix, info, infoLoaded } = this.props; const { expandedPrefix, expandedPrefixLoaded } = this.state; if (type === "nodeUri" || type === "edgeKnown") { @@ -71,8 +72,10 @@ export default class SelectedItemViewer extends React.Component { ); } else { return (); + deleteItemCascade={this.props.deleteItemCascade} + setOptionalTriple={this.props.setOptionalTriple} />); } } else if (type === "nodeUnknown" || type === "nodeSelectedUnknown") { return ( @@ -104,7 +107,7 @@ function SelectedUriNodeViewer(props) { return (
- props.deleteItemCascade(id, type)}/> + props.deleteItemCascade(id, type)}/> {infoLoaded && selectedUriInfo && @@ -119,7 +122,7 @@ function SelectedUnknownNodeViewer(props) { return (
- props.deleteItemCascade(id, type)}/> + props.deleteItemCascade(id, type)}/> props.onBoundChange(type)} /> {meta && @@ -136,7 +139,7 @@ function SelectedLiteralNodeViewer(props) { return (
- props.deleteItemCascade(id, type)}/> + props.deleteItemCascade(id, type)}/>
); @@ -148,20 +151,22 @@ function SelectedUnknownEdgeViewer(props) { return (
- props.deleteItemCascade(id, type)}/> + props.deleteItemCascade(id, type)}/>
); } function SelectedKnownEdgeViewer(props) { - const { id, type, prefix, name, info, infoLoaded } = props; + const { id, type, isOptional, prefix, name, info, infoLoaded } = props; const selectedUriInfo = info[prefix + '#' + name] ? info[prefix + '#' + name].comment : false; return (
- props.deleteItemCascade(id, type)}/> + props.deleteItemCascade(id, type)}/> + props.setOptionalTriple(id, !isOptional)} /> {infoLoaded && selectedUriInfo && diff --git a/src/sidebar/SideBar.js b/src/sidebar/SideBar.js index 6b2a88b..842c7ff 100644 --- a/src/sidebar/SideBar.js +++ b/src/sidebar/SideBar.js @@ -62,18 +62,37 @@ export default class SideBar extends React.Component { ); } + /** + * Sets the triple with the given edgeId to be SPARQL OPTIONAL or not in the graph. + * @param {number} edgeId - the id of the edge of the triple whose OPTIONALity will be modified. + * @param {boolean} updatedIsOptional - the new isOptional value + */ + setOptionalTriple = (edgeId, updatedIsOptional) => { + const { graph } = this.props; + + const edge = graph.edges.find(edge => edge.id === edgeId); + + console.log(`setting optional: subject ${edge.subject.id} edge ${edgeId} object ${edge.object.id} to ${updatedIsOptional}`); + + this.props.changeEdgeState(edgeId, {isOptional: updatedIsOptional}); + this.props.changeNodeState(edge.subject.id, {isOptional: updatedIsOptional}); + this.props.changeNodeState(edge.object.id, {isOptional: updatedIsOptional}); + this.props.onSelectedItemChange(edge.type, edge.id, edge.content, updatedIsOptional, edge.meta); + } + render(){ const { graph, canvasStateSnapshot } = this.props; - const { content, type, id, meta } = this.props.selected; + const { content, type, id, isOptional, meta } = this.props.selected; const { info, infoLoaded, basePrefix, basePrefixLoaded, error } = this.state; return (
{error &&

{error.toString()}

} -