{"_id":"react-auto-edit","_rev":"129-25cf66f8775a5dbfa5845b30eb93ae1e","name":"react-auto-edit","dist-tags":{"latest":"3.0.7","beta":"3.0.7-beta.0"},"versions":{"1.0.0":{"name":"react-auto-edit","version":"1.0.0","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-fk-extension":"^2.0.0"},"peerDependencies":{"react":"^16.0.0","react-dom":"^16.0.0","react-router-dom":"^5.0.0","joi":"^14.0.0","mobx":"^5.0.0","mobx-react":"^5.0.0"},"jest":{"collectCoverageFrom":["src/*.js"]},"dependencies":{"split-string":"^6.1.0"},"gitHead":"69b9822dc3a3c5c04ef584e52284649f625e9a0f","_id":"react-auto-edit@1.0.0","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-oGYzpHGpyDe1ewyP2WGPP/GkArM9oXJ47KZfX7Aj3pESgHOYLINefdtD3w5dQJBvDS8tiDxO3V4BLMl9IAhcfA==","shasum":"a5ebd0199afda1e3b1205b8a80cb8875236ab080","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-1.0.0.tgz","fileCount":64,"unpackedSize":338919,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3HtyCRA9TVsSAnZWagAAvdMP/0UvAAJjDebizVYLkOvP\n5oPBgwlbX5XOh2kFMSOzPeTphPhR8JjdVvUeEbWPyH1iiWHURi06QFkoVRL4\nIAXT96Hc6Kl5MG9kHqlkPPCsjIhJV5RGOfyfK+8mTjfpRxTCAb8Q7lTBgnMO\n1TNEDt+5q8ds2ToSdyVgGtu+0RsUUFSRNX83+rbQV6y6NvLEeBaQkgS+YfzP\n5ydL4FeqNGrbkTJlmtntNH14NwWVUxEzkf03cKI1dDHne6omvqNM9dBrVcgl\ndjEJj7FupS+Y5vlT1P/7kZS+i6lxH7Jd/o5u2xDsPzhXPzOcXtVmf0UmNNwE\noZ5CSEOfdLPE+mAkifF7KzmbN2H3dVJ+tSgDByLYbcMfdGqGBYQT9N1dB4dp\nnMk0P1yQqZ9SQ2T+dyUkV44KlzmQBQD3RsI6ff2/acK818fATV/gwFSjbY0R\n1PhyLSKxTcpsLqtKOYF+wLQJUOwL8kS3CG6X1r/XDihEjs9ywcYU/Ov2JwpH\n74rrau3lsp27qf+7q9M7mWPTt0FHU5qO2NKAMMH8HsFWCrlPOOjQcJe1vC5F\nxBpFhTsXsmFUSbGzY9t/nrWt/FdItrLfXNst1LHToR+PImWYJ2QoESyKoi26\nfUwAX/TMvcBOJCPEyA3RK+QDKAlkXhDLA6Hl8rX1+9FUHo7P6Tk2YIeIf82y\nfy1C\r\n=tqJ1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCsdV+lUWROTA5hu2hmOJfHg+8iAU0QCjoA0fMW5e/cngIhAO3RkmAnWzCkGuW7PEVe2WRzC7WclhexO2j5Qpy0Xe/9"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_1.0.0_1557953393899_0.9972805605868487"},"_hasShrinkwrap":false},"1.0.2":{"name":"react-auto-edit","version":"1.0.2","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-fk-extension":"^2.0.0"},"peerDependencies":{"react":"^16.0.0","react-dom":"^16.0.0","react-router-dom":"^5.0.0","joi":"^14.0.0","mobx":"^5.0.0","mobx-react":"^5.0.0"},"jest":{"collectCoverageFrom":["src/*.js"]},"dependencies":{"split-string":"^6.1.0"},"gitHead":"074234672dd7d34a3ec475457966e9942d2da308","_id":"react-auto-edit@1.0.2","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-XgmQVpuBZZYwe3vwDFdJAfCVSYo8hyQiaMzaGOQF48FbiHA53h5qiXeOwHHAJDQ3NUH7zrBiFfudykdll3TZHw==","shasum":"390aff8f72148d38cab73144a441a5a6599ba1a1","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-1.0.2.tgz","fileCount":64,"unpackedSize":341725,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3eucCRA9TVsSAnZWagAAbh4P/jy/C6S+0VajmZc0YF8g\nzvFiTqcurYF66+bW8oNKmgfASvbnVCubqIY8p3HAHR9Z2n6Ef3CKF+a+JX7V\n2XFB5/xWPlq1GfURdz42yKTSUX/JPHB8Kt5BbLAmMtu45wF/iXUqIHswNYQp\nnGCz2XUv5xHL4HcZV+YOVyAl8ZtDcgcTbRFGOnoBywDRKHEMlPYLkdA8b6lP\ngmDs3u85E4aTO30ECerSJayE34QA21n1G8jBkFOyMJejtsLl8do9/zh4wITF\n2IOfu4bl3PIIX5Y06r7S5jbU4ER+DnqCmAuyicfELIRl0c5Pf61bUrD8sMY8\n0ieg6L9O9Gamzd8i0iDEhCkvtWtO2MG8/zoNMS+Dhi/GWJsNcbhe31BjXAIv\nSwOzQnNT4aE6ilRko3H9O4XVjpqYq+65BNcIKb+O55VFYDer81SDtjgBoIPo\nYS60SeF/44jeFMIYn5suC4YBiHbWCibr58YkY5v3RDrmA9ETh8+7eKw32rvT\ngAN5CBm+XU43bwT2ldDLxTxBB4wB5ioY36VGip1hwysQfxfJ2TGa20eW7/GU\nFjata1H1m86NERfggGOVd4blW/JmViEHfnE5wYizRxqrGHriHMEj3CC5YPsC\nGeLsdxC9ZTfjzrHPDJhNeY3r0862kl2d9fqefTQ8PHX5Qfxn5eGt//UlUNzN\n5PTD\r\n=Ye7O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHfsB7e9Y9N6vId4c5bVx/591F+7YnDyrR1RjP0MHJpEAiEA/aElvrbdo5qrIK504icboAv6ovLEuLXR21BiWFAhC3s="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_1.0.2_1558047643384_0.6032796261642654"},"_hasShrinkwrap":false},"1.0.4":{"name":"react-auto-edit","version":"1.0.4","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-fk-extension":"^2.1.3"},"peerDependencies":{"react":"^16.0.0","react-dom":"^16.0.0","react-router-dom":"^5.0.0","joi":"^14.0.0","joi-fk-extension":"^2.1.3","mobx":"^5.0.0","mobx-react":"^5.0.0"},"jest":{"collectCoverageFrom":["src/*.js"]},"dependencies":{"split-string":"^6.1.0"},"gitHead":"0223d54046d84e76a4719ea88369c6dfba59514e","_id":"react-auto-edit@1.0.4","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-BWRK2vqcaKtV9Bz+U3gl8kDbYzlOHSY6HdoSVIqc5IekRbwz5P+UascesMJFyV6DBb3npRtCJOLmWFrzwjerpg==","shasum":"efc7481741d6add667732b590413c4bc0a24c0f2","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-1.0.4.tgz","fileCount":48,"unpackedSize":170184,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc43STCRA9TVsSAnZWagAA9VgQAJGKzQNSdsVjpNSa45D5\nXfErzypeD105XY3ln/lv623AITJCYRfCMPUc7ypUuDw1gVTEolci1saOsqow\nODeDbWN5YcT7Lo3ED/2RgQYIy+fU2Pbs8oMp5RfjA3P+Uwc/53G/9V+vXvS7\nF6rHgFPiEKEqvKq6XgB558APU/wBoq+iwLTaNmA/Npa6ePgobPNLnP6MOpNm\n5VF2kKYbgef6TtTf00M6+251HV4uQzWNWlRIMQOo6Gbg/SRewR3orR+DCo4t\n5Ml/sEcXugWPlHIv0ZyhvbRg+WZcTsx9Z7wB0kFDBbTL5fzoGZwYRsro1r+1\nXqj201OFOstQSkMrT1H9KXdC/+VcF96v3aPH7u0Zpsvm+vZS7VeAFOKRnjcL\nSxjdGZSE6mfKhvI/545Xk2XTGLLLAFAWKXFL8WHIjsts78lSpuwShk3ELQhL\nfM8+I1+VUiY5CluXixoYayUQYj8wVPR7KqwYRSex9s2nSsybmmhlHl4qOXhu\nh+szj/2Xx75FJ3wiT6qIQR8trfjw0AdJreWfPJOxSWx9ZLH5wYdSy1rl/QCH\nll/+5/KPqjsRn5ncYQ+b1GEGoXwNNPhkEZoCBlu9JlFy5R39KmPbNZIFEFuX\nnd62si7P86Lt3jHAWfO56cOWNAtVHkrcgm6xmOwRuaFErDHZ51MbxA4BFA5r\nl1Uo\r\n=beC3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDU0lQWwmDMSSWOx2RpBnZ7Pe4tqyRPVL+3ldjkNj0qbAIhAIqmP5dsB01anuS9uZMUjbY1xZ/E4N5I4Nniz44jdJVB"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_1.0.4_1558410386756_0.6759381357581704"},"_hasShrinkwrap":false},"1.1.0":{"name":"react-auto-edit","version":"1.1.0","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-fk-extension":"^2.1.4","mobx":"^5.9.4"},"peerDependencies":{"react":"^16.0.0","react-dom":"^16.0.0","react-router-dom":"^5.0.0","joi":"^14.0.0","joi-fk-extension":"^2.1.3","react-select":"^2.4.3","mobx":"^5.0.0","mobx-react":"^5.0.0"},"jest":{"collectCoverageFrom":["src/*.js"]},"dependencies":{"split-string":"^6.1.0"},"gitHead":"c86dd22ac080c87b538b06d389e226e799595b7b","_id":"react-auto-edit@1.1.0","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-zZOolPUtw6uBoeioNMMfk6uw3XHtRgG2PC7ognS01KqWSx3skgoBnhEO3ylyHqkF90qwzzXb958veYJ5eEk/gg==","shasum":"7b5eef0ed352ec476d640eb7cd9a98a5547d3709","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-1.1.0.tgz","fileCount":48,"unpackedSize":158452,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc5xOoCRA9TVsSAnZWagAAQ0MQAKIuwBpMMludCBLjOVXn\nPwEZ/mbC7kx2QI89n4ttKIpnBT/qzzhQWc9fFt0wVoqDao2SYb7v9b7e/MLJ\nIDv21XOQG8mczWs844LvnaCSjmNqFlXyrqnGOfeeE1jsQeiWH8LMe8aEL0OR\ntgEuDb6oK2oSFT4779VghvUt9AVl7RwIeeFvPxShXyb8mQLKiGiruDOr8V7m\nb7AIo3V6gfiE0Cfh622ua3E+KLXEj+9uwvQT2wPRcv2LHeKjBU8Qz6XbUFey\nltUHRu9QtqckppBovOep0yHvN6IV9fxz1K03YsBiRp4kmDwE+rcozc6+s8JE\nP18aP/vyEbozDDiUMSYexWuXfBRq5g0LmMlaynqOwGHfDZUiwYkRhh1AEeFb\nCz/ALotK636KcTsHjrd8J6EWXC4o/2e2lNUT5PCtHPUGWqKaovXao20KDyCb\ny+daJirOBNsoFJsPwj7UDSvm+N+sOd1f+sMrts0oYCWd1DwIyAfNC1kj0P3m\n6bwVRNrOcP33TdbWRnVROKfonmujeKBGw0B0xwXZ+x0Tl/6I5h2y6V164SqE\noy0PBDI6tGf+4XPkp5N/HwItnFytOkV0Fvoh3SKqk5mfGMseF53XsUuqdQ40\nTaJ459iFeKY5svbZnQcnYX4o1Q+eRFJKEI/86KRF8rl1HwXib8/hyVsY9nKX\n6hiH\r\n=k0ll\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHKkulGFqSq3DSXAPZg7R8pBowQ+iCAsTAUq+1S1uRScAiEAqa16R30oB9tRozyusH2HiJfRU069FroKHcw6tGhunfs="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_1.1.0_1558647720010_0.15258993774359375"},"_hasShrinkwrap":false},"1.0.5":{"name":"react-auto-edit","version":"1.0.5","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-fk-extension":"^2.1.4","mobx":"^5.9.4"},"peerDependencies":{"react":"^16.0.0","react-dom":"^16.0.0","react-router-dom":"^5.0.0","joi":"^14.0.0","joi-fk-extension":"^2.1.3","react-select":"^2.4.3","mobx":"^5.0.0","mobx-react":"^5.0.0"},"jest":{"collectCoverageFrom":["src/*.js"]},"dependencies":{"split-string":"^6.1.0"},"gitHead":"f905fdac63f1faa034cdc56b7d51c57cd8127cac","_id":"react-auto-edit@1.0.5","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-o5kFyRZ2o7kIHHbFBHg7+2NvMHWqsoBwV10Iemz39rV1QB/bg1cCaU9fvtYUBZeDZtpuyDWiA4PcLDGAlNcvag==","shasum":"2e87a2d5590f4250e6e1059910535db2134c571d","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-1.0.5.tgz","fileCount":47,"unpackedSize":90544,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7G7gCRA9TVsSAnZWagAAXgcP/3nvnzC1w2NPnB1tGhwO\nZFQDZLfSF1o7LICho98dl+KWMGnp3UnzXDj6ByzbS8LG1bO+g7f0ce3xtBf6\nTxBjxUn36lbAIJDDw7dOlIa48O+5Ld5VbHmWZojE9Bvz48BpDOQFeKXacm4K\nFs6p1GUDb8OItrZXlVtuM8/3kRpy1168mRbXyuCxSBndrdikgLV8KrBsWFju\nQEK5FWKVFVISfzJLGnN3AApYevYGv6ANYnCId/8/luc6sYvSUMla4XsuzO3u\nSHUxd1u4dyUGTTHPVp3PijBA1o53UKHpMMw8zt/zD8YcrILph7rbAheqaWqN\nIWW202B1tf1YwAc5/2ZTnTb8Mf1GWxsq4LhEhkW4Ashs98dGOwijdnjZHMxo\nq8vCzVCefXaVHPUyxotvTMpc29zT4or9xvLSSQbP4zHiVb+GRi+H1TcgVQx6\n4W+ThnI9piAG3htjwAisfWiH9c11oT8ddnF/TO9+fTFT5CPGiD5jLjcvj1pG\nCgUYsyKMotUcDC9vH34XZXN6NL9jnS9zJYQNSv4h37p1lbL60Rgg0AVCltrU\nHgUoNqQTwuM4ZDoK+e3vckDELLq6pgdZbGF/lTAU2Bha+r5jHAdFs7DxryZ9\nw0FGVK/DFdulDPrh0YMclob9SeEjQhra/dv1AxDcoAqvaL3G0yDFoO9N0Rj6\n2U75\r\n=2rnL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFBol2mT7yp28//mSVsct5Mg1TdhATi7Qk4SBflvxtZbAiEAi1drLKRtJQTExXGK48+clkqINty2SYFCW4/IbFfdk+I="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_1.0.5_1558998751350_0.6377172623977987"},"_hasShrinkwrap":false},"1.1.1":{"name":"react-auto-edit","version":"1.1.1","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-fk-extension":"^2.1.4","mobx":"^5.9.4"},"peerDependencies":{"react":"^16.0.0","react-dom":"^16.0.0","react-router-dom":"^5.0.0","joi":"^14.0.0","joi-fk-extension":"^2.1.3","react-select":"^2.4.3","mobx":"^5.0.0","mobx-react":"^5.0.0"},"jest":{"collectCoverageFrom":["src/*.js"]},"dependencies":{"split-string":"^6.1.0"},"gitHead":"f9534e62de6e0961955129564c667a63c8061702","_id":"react-auto-edit@1.1.1","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-YkMV2fUX9dEtvNqrsOrfZMvUhmN25KZEw159+/E2IXyCYBxB8Ts7miFFSqXZIIGXJlWzGN9IZrBfjGuI4KPakg==","shasum":"4fc96c3e2ecd96643ae17f4e0edfaa8c458c008c","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-1.1.1.tgz","fileCount":47,"unpackedSize":90544,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7HAsCRA9TVsSAnZWagAAgFsQAKDQEJ8beoyFfovqr5XV\nXKiO59sKSywd4HCNXWVZYjxEL0pehVDKthKT46ssjdWuOxrEKvLBKA8b9/mw\ny3v7nId4FRtotlDBTZ2mF5BZsCNPANP8T/XCmqsXjbrc7DswAkcB5cBGQLZ5\n85nQBnPEhflS5aLmXNh3j1paY3nqf4j4d6lPgtPlK/IOzPGRqD45dwhLhw7P\nQMsvTvHASQ9fP+wIe+G/JadYeLuM1zMBA+FwsDL4Lw8RC0wdU/gGiXYNKfZv\nQqdeTFFKxiqMsCx2P7D4ZN+zEvUmMKfElu7TPZZNPp4eReWS61domav87vr2\n+90UTw8oViwseFxi61VIxKOeMaYK9FqwsdlFjedjS2JofjYwMt3j/BBxnnSN\n6vVAdoRN/XauoHeptalU1I8l8guJsY1JP+oAB6r3rKNFp1Czr8gdkKg/yt94\nkOQGbB73foZ7YzI6ACQTT5gnAUabnKAEFKjztEtMJJRpbMfwRw7tIskgLa/A\nhIeYHBKoeFflWejzPQ0knf8NvzprW+zsHw40lWh5Ry5vcmhUN/pNXCnEwEeD\nEsleCuTLain2KL29kc8UlYE6VI3R2/yWtEB+MeLi6GTgCI7C+a+0okC1X5Ui\nabhTWHfNvd1Z78NfgtmF3IWdajY11Nc5lmfaAEfsU8CD0Sfga1Ws0dz1aLDi\n4inG\r\n=fgpC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF4Dr6tVBEj74PRRw3U9Y+Vbrzi2x2zyA59de2RcELpDAiBVLdW1kdIwSvcSASIyQ5evAWw73eylZ3Oo8QZ83cwcEQ=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_1.1.1_1558999083460_0.20735136211499827"},"_hasShrinkwrap":false},"1.1.2":{"name":"react-auto-edit","version":"1.1.2","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-fk-extension":"^2.1.4","mobx":"^5.9.4"},"peerDependencies":{"react":"^16.0.0","react-dom":"^16.0.0","react-router-dom":"^5.0.0","joi":"^14.0.0","joi-fk-extension":"^2.1.3","react-select":"^2.4.3","mobx":"^5.0.0","mobx-react":"^5.0.0"},"jest":{"collectCoverageFrom":["src/*.js"]},"dependencies":{"split-string":"^6.1.0"},"gitHead":"f04c466da02d51b0f874c932ca0de80fb502d8ce","_id":"react-auto-edit@1.1.2","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-EHwy5xrdYW8EYDnd2iiFpfDy8vZNO4BaANhey63d+77fFVIMj9sI8vx6AILwsMRbDGc8ur6yyXf/7PudodFiFA==","shasum":"339b6e6908bf032b92446b51dfa135ceed363e7a","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-1.1.2.tgz","fileCount":47,"unpackedSize":90435,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7002CRA9TVsSAnZWagAAI/4P/0bEccVXmsurBXbh1K5u\n3pZRQjBT4w52abAtWxzxUNy3QMo0ckoZdFeDnnJV5eyamGEZzeJPpJlc9oUb\nCkn0moSwUvrhV7uACLlJkYBzg4xGDcgf/OegUXlK7XZqyW3O5fkADo7b4VSR\nuG+b872O4MFIWmj2P1nibvaDzV9NL4vfH83DQIjx7ecOe22deA9n2NvZjrnR\nyYEtv48Jp5CI1t6Iq+lWxgBEyzYsOChwIIq7frsatRzpbDHrlsLLfZhhPaub\n3mZLBEFI/Bsnmj1U6JQBzeJjG2H2q/HEG9ZZGNWzVv5oJPkoRZsryHx0zAgn\nLI8m0jt0WZwPzgsLmI50ZigL3bgohQZ0eoA+HMWkRi+LXFQn3PwEz2JB+Cve\nJ7VqPC+6AhQXhEFmmxBHmub4Zn4OXXExQMITQlbY4z+xSv0inwmEFba9M+lO\ngzbUN6qe7oBZcONomEkSMTrxtga/OTbZbxVVfJGeKrFCtmlVyupjBvVcsCW5\nHULNvUjMy/H69FTQjhGIf7JfmdJdMK90kRJJ1TYnqyG250NobeWEcyOf5pj0\nV8zNHdOVEdhM+W/2XROwHmmB06MGISMHanxfPpKpRADU70Y9PEcBe11Zuklg\nBgZDJG/O5HvRzCQ1XpmX8sl6f072B2oqsDiDntEKCgMpSOK2nG1uIfdnfPYf\nVxKY\r\n=bYRa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCY24TCWvs2gjKRJ8DK2+eH60V7XRxfjnypecFrh1dZoAIhAKppQgu7ifSE6p/7Jy6OeunMyHh7WmHKHJRPt5Z9z/GA"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_1.1.2_1559186741579_0.5743117580825583"},"_hasShrinkwrap":false},"1.1.3":{"name":"react-auto-edit","version":"1.1.3","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-fk-extension":"^2.1.4","mobx":"^5.9.4"},"peerDependencies":{"react":"^16.0.0","react-dom":"^16.0.0","react-router-dom":"^5.0.0","joi":"^14.0.0","joi-fk-extension":"^2.1.3","react-select":"^2.4.3","mobx":"^5.0.0","mobx-react":"^5.0.0"},"jest":{"collectCoverageFrom":["src/*.js"]},"dependencies":{"split-string":"^6.1.0"},"gitHead":"c181172b58003f91b0f74d75ec50f1d07d855cde","_id":"react-auto-edit@1.1.3","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-6cVFyIBB6YkVOplyFG95sBH6DNvk6pGFJdRdstxQ5dJCs8h6m2t2wPSg1m+usd/+WKXTDWrV7zGNGqcx9a9DlQ==","shasum":"5ac43fa325daace1280e31929ce4fac60147b323","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-1.1.3.tgz","fileCount":47,"unpackedSize":90714,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdEDhPCRA9TVsSAnZWagAANbkP/A7FfM83vdwL6HDJpij5\neG7t/xoUKOwYCvFh1oYohrDOSwr4KT0AxbO8zZTjgignkdYdouHs9h0Bo+ux\nINU2j5nseGMEt4Z8b43RgO4EGHgUIbbC2SVgn5IgrKa+2EkJNdqGtErzl8ls\nIBA7yXfMNJWrYnfZGw9edk1xdWax783irIob/HghZ5YtY7duRwSuZSxsowgD\nJZiCV/c6TVhk3qkfozY3ERpSvuRjUII1FprtwOKvCSK7PCuSKnfVg+BA4vLR\nnH5PCD0q5BygSh7ON5v/Y0MES9J7yWOQADThG14udVOCwm3cVXpA9MF3fo2j\nxXqN7S4KkcnoN2S4NJz+RhgjxC04RS9vyklv0HNnFNQuC4chHBZ6AMT3trUD\ngZHngSAiYB125p4H+wxoqofaebDgAh9O6x4eW1WxXxCUDf2I3DEuWihOZTRj\nxxyki4fJ86m44K7TOaDtduvOUBA2yiEf0L57FFvuJPcKLGW4K3zlIGzGueLv\n559rg9SN42Sp0axntQRbgp5wKBFCn4+aR3xyvy+mOAco2rEeFCU8WG7wjv9f\nGNucZv/Q3i3n3VN+qKcTwBmMAmJYmOUU4XHylzzrGalr84OV3M2WV7rA+X/x\nIZ3J0qpaBGHvKuwzZ9Ojj9cUtstl//3NpDTXFXBdJTGLMU6BhqgiG8jVDodf\nlMLB\r\n=8cN7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICwBXSw7KL3cHsZs22KtuXP5VqmHfWShRobL6sPBRanIAiEArPKzy36yu+rgZIn758cB6B9KaCcGwKer0Uc2IU3ffgA="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_1.1.3_1561344079256_0.35958358576910543"},"_hasShrinkwrap":false},"2.0.0":{"name":"react-auto-edit","version":"2.0.0","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"keywords":[],"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","mobx":"^5.9.0","mobx-react":"^5.4.3","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","joi-key-extensions":"^1.0.1","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react":"^16.8.3","react-aria-menubutton":"^6.1.0","react-dom":"^16.8.3","react-hotkeys":"^2.0.0","react-router-dom":"^4.3.1","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"_id":"react-auto-edit@2.0.0","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-HdfCfZGJPgyGNg5bVAok7aWzM+Zwtw5jme8gNRd3W7LxUelaCLv/AHREokzjfRQRrV6w0tjmvXZ8Y2h38uKU7g==","shasum":"bea508e096b2dffe40825233a104a64f0c3d7111","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.0.tgz","fileCount":67,"unpackedSize":175286,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdS5WYCRA9TVsSAnZWagAAD74P/0AEWkL9SiUZGH/Y7ypo\nl9zfSf/rTgTaeV5zZnBRvD3i17J3SZ3dtyTDq7bgmiPLFfvYxavaGv31g+8l\nZQ6lCGoEexPmuA0f7L0wlLVtiPy0GyfuUuzQmvT3qYjOJxVpfVXlZTu1pfvY\nklFKmH8YkmXP6i/Yz+TB/sY4DF1lnUGT4L47s51G38K3KceMDU1IDlLu3l6B\nPZa6C4w4mZhEHKA4fSA42w+si6YxQ0ZOiWrDv7xmPpgoQUzmK1C2Lulqhuhk\n2m2jzo2Sus5G8qe7OQMqEAfffvhfyLo2Omruc6A8dzEs9jVuPYlU1mtWkNCv\nmuqMBM4xDSIyEUgL+QAP9P0aPy+WCcXjEKFX1POXtzl4Qii3ebpQs5QaOjYF\n7nLQ5Gi5zrSoCp7cQxYgK/FPQFtj3lzThs51WjLuxxHnUL3OfheqraYzpWF7\nWEB6FDjYww1ECb5lFZfypbtUcq6XYT5m3TsgeV/au2o4e4gw9GRr+h6njrQ2\n0P94K3qbdqj4qOT24U7No/ymyEtXKVcyXvvEAgxLhbxLRQIx7+DOZetX64QW\nFQB13rC58+O2ggUU+EeM8PAMf7cEat6mmWQv9DRQNH+51KCRxq+zq9DsYrQ7\nAbwIHQt9Gy6RUhLoeGKYVW9ycu0bcpiSOGlwbAnaPEbFYRLLNU3Nn5t1m/h/\n1BbD\r\n=aoGg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDj1GoU4jgV4vQGhajR7xenZdJDQLJ7xkGATTqCbpwjKgIgaMHAa1qxr2SqqPVU+Q/+y28uyUF5BF3QOXGTYJpu+4Y="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.0_1565234584093_0.7806037652237088"},"_hasShrinkwrap":false},"2.0.1":{"name":"react-auto-edit","version":"2.0.1","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"keywords":[],"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","mobx":"^5.9.0","mobx-react":"^5.4.3","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","joi-key-extensions":"^1.0.1","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react":"^16.8.3","react-aria-menubutton":"^6.1.0","react-dom":"^16.8.3","react-hotkeys":"^2.0.0","react-router-dom":"^4.3.1","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"gitHead":"97a6c3449b8bf141346a0b37474776f446c31966","_id":"react-auto-edit@2.0.1","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-agHGAcBxwXq7lkit0dPpCnr1XXv/GoDG8fPaIs2waiBhB63VYDI9aJ4c+t93nXSkXbW/7LizzytKAinLoauUXQ==","shasum":"73b29cbbd5a3db7dd576aea9b0c9e3ac0a61a723","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.1.tgz","fileCount":70,"unpackedSize":181800,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdUkGCCRA9TVsSAnZWagAAwBYQAI5ZlNmPX5RKtfXKl/Dd\nMxy21Ml+zUVtZ2xE53AP1oQD/7DJGdgJH0HBF4lSNJtcCMjvn57vDGljGR3a\nr9kVUnBDA9upzXuHhdyUJxDOlpEkzIAZJdMUWMRnL0Q0YvOVXHH7QRCfA2mk\nNYtlv17i8bfT9bxLYPtS4OR34C30lVLgkhQJI8SsBJEciLIKq3nHeGZlo40v\n6ldbtbWO46WTi9z/j81ejKFuN1+pZo/x70J9ER0Gx/kOleNAEhGNDZeiSU/x\n/SrEJFjB0dS4YZOyjv4f2FXGOzk9rH2fyHnRiyLHFry358Dm848O7oA/DnrF\ncY+FnunWhSSoSBitAR60JYDp0rgh6/mPLpl+KeYQ59X+WtR7izQSUeqY5vo/\nkiXmqA3Ox04f5hinyXAgL5Y5vFRheUc6rkHi7DkzNhVejiwz0BnhG4Tphsfx\np7xC+05VuxLBKabuez0EiLt+XhROPs13UAqPIM2BXbqcL9jEp3WOzJ7+J5mc\n3B34JtAkwLVK+ihuGwXko2ZuIiLEzNEQV1GF9F9GgrEpWddY4HvOrkFFSCCl\nClO0edfY9HF0sWA2PQUo9LdOmMMFdetfmsyGBjI87bb4jOsd5Y90W0/pSg8J\nkmfEODuStytxT4FUIgdr9X/vP64GoZljg1pu/XPuMVef5Ed3DHXZXE4tIeX8\nSKpF\r\n=lCFq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDFqm5w7yhnNM+oQ0F+KvgDUp9I/xFQatz95eBQHGDM+AiAKciDaVfuDVhRW2/CEBF2zkR/sOBlsk8xK2f6Rd823hA=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.1_1565671808784_0.5752136253078781"},"_hasShrinkwrap":false},"2.0.2":{"name":"react-auto-edit","version":"2.0.2","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"keywords":[],"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","mobx":"^5.9.0","mobx-react":"^5.4.3","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","joi-key-extensions":"^1.0.1","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react":"^16.8.3","react-aria-menubutton":"^6.1.0","react-dom":"^16.8.3","react-hotkeys":"^2.0.0","react-router-dom":"^4.3.1","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"gitHead":"ef8b3502e44fac587567b85cdaab678109fef0f6","_id":"react-auto-edit@2.0.2","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-bUX/KT9z0xJhZgBrxJe60r/9GXLreMtQOicgTkzmXhjCz4fezCA0U78XRWyWvIuiV+ShzLBXitfSVwcnWp4NPw==","shasum":"04a5a3309cef18880926b7f916a1d091999f5a8b","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.2.tgz","fileCount":70,"unpackedSize":181740,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdUzBWCRA9TVsSAnZWagAACbIP/25tgecgh2AK9Umm6nPo\nl5X0cWjAPXLluEnHRcd+cFSbfKsh7xDdVqtUErW8Fg6SFauCKo3JbayFiLHr\n0RE0/c8upqi+hUDV28NYx5W6K3gfpB+Liu4VKVaslh1aB3UJhHdZOmrgrhmT\ntzBVsB74NWGqSqBU3gBMc3RJ4FhG6JJJio2DyTSTIMm50LQ/k78U6EmZ02BE\nLrL7FCSn01LS+XTxkr9mT1yTh6Kv3MeaAgy5F2Vsdh99GXj+hmyeyDjH0IEW\n2Hu0KmubWbGXMhZjD42WX+rwDXnUmjqA16d6hWYQ9SWEMyjBDsUQ0ZJrlwnf\nBywXoUxvjxRJF3nCNw8Ab09ZOJp27Xgctbr2RYVfp8/aCby8pHyZmWZ1nFrp\nKLxNbMRt87gwaCaU0WdDPPCizYBmXr6dYi8JnxPAY+VOskQK9FT4S8WlhF/U\nVUcT8xlpaNhFn5ovKlykWu0YJTm1PC7nnjwn4IvvTF5ou/qDwvrytRvaTou9\nxrzbxRorshVkYMXiLarHPZIDHkHUXM+YINw/iOC4doW2PHcLre3irhOkQgXy\n+1zMW1XNEQ0EIsz2j2qB7FytQEeShsGFALyyx6dK18rqYdMfW+vykgBm2N7F\nE9jM5PTJ8LV1hVhCmrLBCvF6NpJnKaJghztZJ05iusOMGbvhv3cDym9FLBmq\nqngg\r\n=f7Fz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCXf3TE9V2k9dVoLGEco0AnKMkN816GEzY4Uh8IjskGMQIgUvjzo66u4UyCQ5AKlkoKGhlKIT3QpRPJpiHDcoLhY3M="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.2_1565732950044_0.8590543034566305"},"_hasShrinkwrap":false},"2.0.3":{"name":"react-auto-edit","version":"2.0.3","description":"","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"keywords":[],"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","mobx":"^5.9.0","mobx-react":"^5.4.3","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","joi-key-extensions":"^1.0.1","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react":"^16.8.3","react-aria-menubutton":"^6.1.0","react-dom":"^16.8.3","react-hotkeys":"^2.0.0","react-router-dom":"^4.3.1","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"gitHead":"ef8b3502e44fac587567b85cdaab678109fef0f6","_id":"react-auto-edit@2.0.3","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-o/qMWgLztGQc0GGnrbGAScflRJyQ30fm1f+Eg50qF5BfrlefVGg12sFZwWBrekuTclxWJNXm277Jxgztna3psQ==","shasum":"ea18f80249cb288088e750d45f27bbd7de861f74","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.3.tgz","fileCount":70,"unpackedSize":181535,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdUzPaCRA9TVsSAnZWagAA4bAQAKPPUNOmP14ja/xFLw90\nnVWsJ3ENTi0GS9QCK383OuGcPxZyS1kTdtW9A1+N/m/Gf4b6C8RZNDPmy55t\nO41Xg/X8wbmnkiSC2mAoj8yeLt/WxSd+0RK24RF64FbMkg9T5nM2Kc0hKKfx\nMtwlkYPcSXi/2yxvos4LtYSFqYkOYeWSCXneLkWnqEifHbZUMUit8Hh/5pPi\n8VUzDHq934S6HGXckylMNxKg/yNHUG7vsi1eFnTyh7I38H4SJ3dpVFCbMGH3\nolaUi5Bn00IKPnWUboygE+63BDxWp5NomV5kg/ER77laCTP3idnx3lKRs96x\nIImc5vXueEe04ktdvyw//jXsMkrpq23ufYDxiFE5C7t22OnjtvhCDzN+VCSs\ndcGXN0CsO9Q9I5+KLDVQVzrmd1qhwU6j3+lLZaM84E45r5crehMc4jjt7mur\n1/iZL25tTNsMbJwY+xavV+qc7tALoDkBwZqikuFoXms124UeuluSFcNS6Eh0\nUQiRxc3eVndbEhpvNIHW3eQy1raT5x1EQlTlraemicRwqN0x6XnBGilRkCks\nyjEIUfK6FiOtFnEXJv+mMOuHvQIAAge14UeZv+vGN6Go623nxcWlQC0wUB4V\nTSJvhFyFxZTd6KriARLX+dwAjiF80e5NzxS4rmm0ua0HMJRztVfeZ5cnQ2pu\n2By9\r\n=P6Iz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDoCKOoM7oMEQ5RqZSenRgt4SUYu5PMwoiTx+lBLYhQVAIgfVTkL3gphORN0/3Z67qmOsCGn3ye01POsvpUlDaLDAo="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.3_1565733850287_0.20269145331802063"},"_hasShrinkwrap":false},"2.0.5":{"name":"react-auto-edit","version":"2.0.5","description":"npx create-react-app myapp cd myapp npm install --save joi joi-key-extensions react-auto-edit react-router-dom@4.3.1","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^5.0.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"938370c567cd18bc781846c5a0596094f117376a","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.5","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-Q6z5zbfkPf9xPAB/5i/uEfTfx9X74VFydWchhvGCL6jgT4qorLy+x/C+oSX5AsK8EzKNZ9YF9MAN27y4ciFRbQ==","shasum":"ea8a9eabefd9a26d05c516e98fcd3ad9a50ee7f0","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.5.tgz","fileCount":72,"unpackedSize":192569,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdVI8cCRA9TVsSAnZWagAAcdgQAKPrFGreYRB4cuzbu9Ew\nKLV6roSnfQ1HqYQu5bZHkgib8Ef4fRbWidruRLbN3xtAsZcmbR/8IIqi59VD\n4kkIVfBnRpb2AixsiPWmHdpMRyfolmeijLb3G37ZuYpZpMeAykLc+jo8PYOX\nAGwxgs2EYhgYKtTujxYictR3aSb6zaPWVCCfFyUubrjYwjpS8bm49uDkMpcm\nR0AEoSwOXlXm7D82YCjivN9Thk5xUms2QOAn8hjwiBidn0MU60/oRal5oyMo\nRGbgBY4NDwN/WwC7l1YYtdKbfJIKkMQuSCLg9REVV0a05ME6MoPpwLaI8UPA\nAFqmqhZ/dAeGAMIM6QEp70Q1sY2W6q5rm74gQ3jdkr7/lFTCg5BLH/McssSD\nCjZhp8VCozraw7J13xtjXb2msBqU4/9Su2SnWKAxsvIC+mudbcLHUbqPAnLG\nb5/ZfwuECYA88i+hqGXseG6FjFNcQBBUYtavI5OOO4HVReTCLqbM9mX5K1wC\nfr27cYsQ5vM12rrvFCiA9VRY0HPxSegwdRkQzPnHhQmGhH8TX8pA6mnjogeB\nFFj9Z4ZA0QBM+nyl9Xi/jnifveD78tgGVPQsjl6SklkC65oFWJyoDRLoiZ+r\n98oT6fdQE7dHJtq3qshEyIsw+9fyHY81D3LOjdp47x/newkC/m6yMZhqh5Wi\nmBag\r\n=J3Cq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDxAG85XlTKqQFAgUtS8VAtwlfzWhH+BbTXr6BUnWfX4AIhAJfianOubB7aDWN0AJOyFO/bW68pqJQMB/mWl7PkBjVr"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.5_1565822747226_0.2544403644518891"},"_hasShrinkwrap":false},"2.0.6":{"name":"react-auto-edit","version":"2.0.6","description":"React Auto Edit is a library of components for producing low-code schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^5.0.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"938370c567cd18bc781846c5a0596094f117376a","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.6","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-MUsL3tDxMrvffGE23gSprA/1LWVnFgAS++c/UwUkevFP65Y1kuu8HnB+pDvzI5IOwiIZQlcKdedpaNePu7z6TA==","shasum":"1ca393d1964b69d60c902c29c466193f79495e98","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.6.tgz","fileCount":72,"unpackedSize":193077,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdVJYlCRA9TVsSAnZWagAAdMkQAIT/Sp7ZqNfRTRpoUTKm\nVN6eCjDoJ7lmh6TCvBIyNtjqMRIY7HF9Eo75ySz0CjnG+TisriUAEAkNFEzm\nrZFzXfBs9u9wgTuhWfEfWwjT0XQSudHkKoZIgH6YCN9Of1uEOsPl399u6dZJ\n1c69aEqXs4IkFpTUN1i+DCadppT5KdRas/tJsu0hLZbWnRind/QQ3r27gNZ4\nn8f5R/sp7KT5W5NhgBMO553tBGEn3Yu6FBQm+5Gj5X4chVokWDu68N/ZHt2n\n5KOB4qmCQmd6C5xE7pqoaUgpN6+d1It9XGeoUKRJo2+kYAMtMHuuT3/yxiFr\nBS7tuQ+B5Kr7y7ONNdHea2xcR7zNHONFbCRyAeZmDYjEU/mx/dKyZBxhUepV\nS/cYdI/F9YyOrTlPEUhuJdi3uGQqXoXq4G3nNmiH5S44FOPj34T1K/WwrT3c\nHHpnWvAgohbBeQZryKk0sjtYbtiJFk4MBs8mBCaeBQwvlXnAOYhspw0W1q0c\nd9ZsZ7Ye5e/3dDhHEw92xQfdDES3+fTiQs1ghuHwYTbMSYpkpwyxIu7fJ64O\ntgOIOrSMbfpQu4p76MkcG6TReQ9IyAHzl9uUWpV8BBkD40ftGNpI2xb4EOrl\ng74GRwvnzMvO0fVsVk2s8cR5JKJI1MyVKI55N0/lt+yexRISO9Q5RjeV06p6\nlj4O\r\n=bKjY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC0vD0m2woCiQ9CMAFBT90WlGRxdt+noMD99j30KvYnmwIhALSePHhbGhTqXdDfDMHc9FJ/wMwRGlKOanundSqwjJIl"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.6_1565824549360_0.6998836353866646"},"_hasShrinkwrap":false},"2.0.8":{"name":"react-auto-edit","version":"2.0.8","description":"React Auto Edit is a library of components for producing low-code schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"81184c14cfc801f0aa6950cc4139f10ae8044146","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.8","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-2s/GkyB8Mxe2wUgFdWq1xNO3t1Hv9erD9Rz17eMBpXv6M9D0IDx7iGrq8GUkkNvyPc7qtmupVFRBznoMSQAOLQ==","shasum":"1756ed15d228a9f97de1b102feda2a88dc2fff97","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.8.tgz","fileCount":72,"unpackedSize":196892,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdVMcRCRA9TVsSAnZWagAAqfYP/R3oec+7d3F5EmeNS76C\nazOk3nRCDLoCuhJ/W4o4HlhxOuByD2N0+sRXFZBPHng2qbgALuFPtU8D/UOH\nq7cqIBggheSM/bP366Z7HJVz9up8nnXDyGMG6z8JhkM187Ra136FvsnxFGEp\nZF7Qi02dV5WHC8Bhnwk2e4RAReyxLqU7MxsSFGVjB5yWo92VdOwF/H5gJy1Y\nkwobfaIz3CTlm+flEw06yYjm4WcWpxfuB4OLgtC7/Gw3eoVNwIK6EsuyglWN\nCLFVA5U/77BeLvZDGBjuRZ6ZD0JDz/bQMDMXHt56eq7uMst2CtzLu3lXVG41\nZ6cfFDgk+1rET7pg2c/4O5OoAxFWwAzRHSUmSIwiPWXVtXh2oP44Ib8F/KBj\nf7iJ8pk36+cWw52GNPe62qGMq57gKhz4v4i3oRD5RW1ZeYZzum33Key4PwlD\nbNOltrdeQvZjV9Zrx3Wx3clGuOTUywlomxHgw7srLXl1IfcCZCqLgE2i3z7B\nSYXXc8IYQARkb4M/TMuttWEtHxEsDuYK4OtTsybzQ2CpSNx/lCx7s19bH4C1\nZmBUKL9EDTJyGKP0ix9kDGmLigs+k+hWjPXm3XnCAgugjH46zA2Y6csVRjt+\naC24bDOQl4XPphUqWZ8BeJwXtxpJxJ1/mMZ9W+zyXaRRQS876XgsPZLKASeN\nzUDM\r\n=gwRT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD5Rv/fLvwWysKbp481O7iH5KC/JSBFczbuJVBuGa7zlgIhAOab5/YSimqb1uiymJQ4Y0MzqMtFAqbGM9uFeoTYjN1h"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.8_1565837073297_0.5343142047432397"},"_hasShrinkwrap":false},"2.0.9":{"name":"react-auto-edit","version":"2.0.9","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"655e6a4da700755dd97c01471d65d623ba16db1c","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.9","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-b9lNpCgMBMur2yRLGEvzHu5S3r7spQ1TjJDi+gcEfl7KoOfqcgem2j9oi1VEP+nLRi/96KUpEgac0XWe13ALWg==","shasum":"bb8d60ad89fc91d247c963db4ac4e367486d9b58","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.9.tgz","fileCount":72,"unpackedSize":207166,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdVObUCRA9TVsSAnZWagAAVwcP/iI++XzDnHZFSwRtLorE\n5I88uSe8H4bZmiKEZugzEcVRZB3vt8KHFBgO3wR7sokjm0nAOyaKilQxSODQ\nLITlK4zrUsQ3vbTXohryzzfmSLjT9GqpS/1+FsY1UMSPwSxrIc8jmk5hGkD3\nlUcLntpoWRuA+a6f1TSI1EbFPSiRuBYT+iLLki4aaWzAD9GOtG5Y+WY/zkJy\nxi5MUsYEvEF/Lkq453xUhqZHv8jSF/rrC6dUOak9X19E+cVGNQkB4fwTLpNc\nRN14qd6hZIpOdLQh/mK6XoGG+c03RW2q70QlbgDtMTlrdyZyFB/O74p+0qPC\nYMr6C0IkhfR8KTWnNPZKSkp4rH9Ne0ojwlEXOgGrX5GccEht+kl8YdEkqZ5e\nhFtwRsmYe1DfnpCO+tN8RRX5Kp1b6uItkYlwt2+x5+nylNXJpBSxQpN4PhcA\n9yyKJAMyNUFipjT/rn4GJPNeq/9cN9CGUPIZG18zb3tzYXL2cKZo1gBWsrdb\nYrhHVRLTfiLLbsm7EttM1JTUbXG6IDsqkI4G8jDthzOePqnmUCnBd7TxPhOm\nFkzwYOtggS7bPxZg4J6Mrdsa3VP6ENsMjYxto+PMLXFLHBbfNAvTxVq2sR46\n+jLGIZrSYsSdvIPUg79GTfRkn8CMsmwJQzjaT9RALRA4E8ofe0Q+mYXiRahv\nFscg\r\n=GDGz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEQoumuAg4PxddGWfGlAPJ+4Dm83dD2Waxzl3SsWwNvrAiAxfHWKo/kUs1a79cm4WVtschglP8pWVB7pauazbMz6Kw=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.9_1565845201457_0.7737477927153407"},"_hasShrinkwrap":false},"2.0.10":{"name":"react-auto-edit","version":"2.0.10","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"7ff710162304e1bbd6c90b6a9cd31287bb4c026a","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.10","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-HyyTcYNzQjL01/f/TLSoxG0zyy7cIGJzFkt86pIG5BFmulLHglg/+GFtfjiqPX+/Mi2J/HDqbm1+ZlH4fj5rrA==","shasum":"3dc1dfa887d013910ae130c6bccbbce5db2182dd","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.10.tgz","fileCount":72,"unpackedSize":208959,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdW3A4CRA9TVsSAnZWagAA5IcP/12l5q4DhzwkCDQG6u90\naqaq4Ftx1drtr3TvOsMRlclhiTsBc912Qsbs1lcuzbKJyGxu9yetuB0w6BNR\njWjRibEr8mgYcpUss2xU6ncIPixRd3ZHretAMPfBlESP39NMeUO/xSiVBQZs\npUJYF1mXtFgBvsxJuNvcxTLwNgf6W0CG2yAsUiij+swhYZZJoSz/aCN17LqP\nKT5wB9G2y3h/qLF8pp3gGF/hg1suQ7nGu6WOK4P/HW4iKLzefSYnBOtC3M46\nQIcatmNcR9WA3jLxS8OQ0rhGzeDNTds+MxPN3+XeLkP8ECQwiUpBB+nE+YJz\naWAiSpzTnq2oDsTpclU60Lo6bmNUTGnd1ViNKAUnYngKUxn/jnBv7PZg932r\nkpm1RHnXsYpEaHwz/8+yeGUVlrS+eZsfeVFl8QIjzjk5MeFZU9HulETq+T9j\nAtWbrlDPqqR+fWvvawbCsdH8li9gT2HPMCRE6HVmnM8gv3yBG/tDwLE0eYeu\n2rKB82XqnzhLgOgwTvTyWDUv5E0zOTSVBzHILctp1qQZbgf00Me2LGdVPorO\njVbnGvdZ//b8vsRrhJ6hAYqyYI4nyfNzxHt0AWS4E1JB8zWX3C6/u7Y4MKK5\nfyXeBROKkY9IzIOnKzCI91wEV4VyIziphPvNVQUwf5zxYMwE0FZx/NjVlPF7\nYGVD\r\n=ybVu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEqNr7mjp8BcQCmnlKqoCPqtT9WsojVSRsAL6o5rvmhHAiEA2xuxcl1y2meI/4lKVBa2wzxS2YbKDRd6JvJv+FbcuEE="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.10_1566273592215_0.9159859249801485"},"_hasShrinkwrap":false},"2.0.11":{"name":"react-auto-edit","version":"2.0.11","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"a7ae6c1086dac81f2bab77f9f0d4384022294b63","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.11","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-fCI7/ggPaVB07SA48gkAKqcGKe5Mh8t2GFEMhAb5pHDS36x9nwMoDhD1hLsF+2bZVtV4foI9P8Y8i2nSSC0hOQ==","shasum":"925f0fc174653837d8ca7450966b56db9e724ba4","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.11.tgz","fileCount":72,"unpackedSize":209803,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdXwaCCRA9TVsSAnZWagAAzxcP/0VcQkfGzz3CiOrGTKh3\nkTpbKcAwV9+8sL/B3DSlUUPSX23g/98A+YJLS08k6ZfQz90RWdYMNBN/uoXu\nCZly6VxywUY4AKycI0K6mfVTJBFXeei0Mh1r0rJyp0JM56hHMvM9lxJbIl3y\nsgBUqglc5bngP5pfzNGFcnCOVWQqpJsqDED6NAlOe7c0I9DkKg3cNPGcH4dC\n/shjgi+aXe++ACRt4kAOPk20rYADJScFkADkgIN1s37FcXzaMhqhxVhuMXRU\ngvJlFBIVwqicahHVaLOwJJknfexkgA8QmkpP9lTbXpCpfjJmgvMAQydSVLMW\nfg5cN9BwPgJc6oIl57FxVcFrBwRUSn+jqg65DVceGzTG/tBBRX9IfYl2xZtp\nuBj789V0P9vfF/xCsPaGnZ9cTgVFQfvXVjmM9d1rrG0y7QZNqVcajy78kFaA\n9m8LlzeqdTyAuOUzCwM4f5N39yhK8Qo0M/byuajy3D+GnLKccnt5/gdH7sK8\nQacX9qRGv7o73Bx3xQjD7NNTaKbBc9M//evt1dFJ8RgwYQIgbPaYfn2el+g3\nW7B2RS/wQZVSTwPmvYkci1QK395JMdq6WtxOiRjPWUsbhkJhZUvwKIxqwH2D\n0x5rwyq0Z8z7zeHIqfPem2Wwq4MLY8EYTBn6DsVZ6h3yeV38lzVK1SnnHn5W\n91J7\r\n=xsLn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUr+SLSSiskgFk6Cb2SddNpiUj385rG3CO2ScJ3/QmSAIhAKR8eg2l+/98Um6ICWhcsk7lVlYOP2t2WC5Eu642fl+2"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.11_1566508673104_0.5387739340723356"},"_hasShrinkwrap":false},"2.0.12":{"name":"react-auto-edit","version":"2.0.12","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"4306da3930470164cc21594d99a85eec5ef081b8","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.12","_npmVersion":"6.5.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"dist":{"integrity":"sha512-DoP4NrQii5HhLhLhyBA0bFFykzqWCKO/lfeYoE1bMcmJ1LLmCxS/Hm0coDwp4oUc61j+awVpUx4p6tWijymSSw==","shasum":"91b715ee442ddd2909844932f097d300cb7e1396","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.12.tgz","fileCount":74,"unpackedSize":217477,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdYvoGCRA9TVsSAnZWagAAoAgP/0l4DwYnuwMhc5i5PtMS\nEMDwWlBOo52pHNxXO0tlObeoWVJcmeC/AL8lNGTomu/2zG88YA0hH5uNN4oi\njwIPbtNVfO82Y4CBYX1jEiUMPpBSxgl0/+Aoo173nZxVsIEn4tf9zY58x5Na\nqstFsac57pfymbFqJxIo0XdR0hYMTgVbRFlIzY/pkqvht8p0YvCYiQoIHGeq\nmf7lC/TpZ/bXV3p53EGGDZUgK4VS5icI6kSsfK7sOrtkMoOrkXvMySQnoPw+\na3td1/VBt5YgVaqqxwBiz5eOd1lL9yyseqkuUJCz8vg+ymDwjXQs+ph4Hjru\n5hPagU/Tw8v2zx14Ah64peJNyjPLC7VVZKqUnABdCtgTMfZepCCT726Jn2w4\ngJ8IHGwqQXrSkv6dZn71A+3TpfMyxXe/BEJ31+p9gMCj+Q1dW5JncwKCBgPV\no+bWp0IDWBT1EEclN58kDg+3sCbSzTfVI+lkuKDr9bLF4SP3mzD75rpl7onB\nHSDBBk52exZE7NHyNJRHgn5Wb43lmco+YUSS9/5D8iugfFTqhpWun/wTd9l/\ngjbFlHg+E+1/C2LgkQH7cP8lG0dxxEcjwhLkf1VwFdCWi+aYTA2WNMDQgZbO\nnHm8e+oKMJK3bRrf4xyitUBv8PsKwNOUEOgOmE2BuILs8pY4QCod8MGl9g1M\nJDuu\r\n=pW9U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCF6EFq5nGkG8DAR2UCy8rLHvjjBrNjt8z3fdDEcemucgIhAKKX2TWM+lr38un15ZxQLx0bkkmn3FYGzkgayhRPePoY"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.12_1566767621484_0.6999621369333799"},"_hasShrinkwrap":false},"2.0.13-beta.0":{"name":"react-auto-edit","version":"2.0.13-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"6614e12f77e98f5a9eb721930fc12dc75b15ceb8","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.13-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-JiKGtcUf4RylTyDqZPg4LC5E0FfQmyg486/2zllO+CIpGUYUp8bVOkPgdUy8mmAISU23cRaTwVGdD28z7JkvMQ==","shasum":"a30cbafa2aa35ef3e0b9b41430f7013acc690853","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.13-beta.0.tgz","fileCount":75,"unpackedSize":219984,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbd1gCRA9TVsSAnZWagAA9v0P/j0pH17gaV2NBcWlrlVS\n8YdE3KBifAShGqqutRywXjPtH87Jl/8pIfaALlBKb0H+ca6dkIGv9hIZxzEx\n+47f+aKHOWCzoXfYS7izJeq/GlkiluGjTnPI8GIcGGZHyY+Q1VlAYgvjJUJQ\nJRT2Ly1lsqxH6zl+ldCwJhYeQfWpk2IMPnIbGWNw2jCYHVbWHmosWajuF3rf\nMDAbcurNuue6DnriEXtszorh9iTP14i32kSqZMHfASilGY/3g+XhobCynRKG\neAb/IJmTLsvkGHXho608b/HXHxZGZywO2tihFJCbB7EIW67vHRrEZzpT4+6U\nAmbXF0amaVvkW06FAY+Vud6cPlIjPVdoWgUfmRaw8IlBGwuHu0R69pIxdK7N\n9ezrNJHjRKwm3/9GquDM9+whIzggB7PEN1GlKm/J0AzR37CWGsiIPxbdXdvn\nDYfQA6de1YLsontP66amwG5Mx4xa7FmxPa+rxtMnyaEpp25SS9Cxd6czdu1X\n4JMI2Po9N06LLsHH9Bt4nr+ZsKQXWKLIgEQnz9uZZLcAHVRRHgQLJmNT+YR7\nwBX4VDGzYy2eW3wTgIktdRFAB+/lmCiknRFi+kV0RfUcCLKLOPTlkx5X4byh\nUXh3Alhsd7Wk/hx9BcYNxcvz0aIMK2vIgg+XV5tw6T0+w/xGaKVUWHY0xLxg\n44lL\r\n=N/Ut\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAN99WbQR0fHLGflYGJyXIXTmUdtJw6+9FATZGygQDpqAiBg5UqxPMqOdijW4jmQHqzhKgAtee9cst1Lckk0rfCRvA=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.13-beta.0_1567481183756_0.5570764264959638"},"_hasShrinkwrap":false},"2.0.14-beta.0":{"name":"react-auto-edit","version":"2.0.14-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"6db6313d301a122be09ed48e0d1e5a76d80b45fc","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.14-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-jQS1fUgzpLw5vjJ24cgguGaYbaSY1OxOsbtSR2xiSbFrxgOeqPubwdm6TDEy4G1chajqXxqT/gIqrU4NvyekQg==","shasum":"25062daeb5de620084caa9577a1f9181d451477c","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.14-beta.0.tgz","fileCount":75,"unpackedSize":221560,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbuTHCRA9TVsSAnZWagAA2XYQAJgFVlpnrwh7hoxAS+g5\nEU019bXQfV8IZgMuu/GyFIMdj971xWVs/jNOl3/4F6IfLI6rtOzChOtVO2xV\nYei1usdctX3s3QZvfdb6Fp3nk8Y7NquJn+F3TSKTsuyafzuh1hQDcXr3rIv9\nv1/anqq77YQi+vepjOOfpA7jsKYf83HruZYyjgSWbv1BX8D4VbhZ8hAXK/Gw\n2twCl3x2jSMUMU476BmLAam87GgIuCAC+K8qoh4MUVyP3tKXMMENGWMrZDvY\ngEvKC92Ph5RIROEULka/VpzXaC/q3/rdX1zIeX8P7BzUuVaXnsaKlXNpToCn\nPvAmQ77+1dKWu3fa8SdBH6HN0lqKx3avW/6IEV14ThXbLBVAUmUR/XfR1rOp\nk9Kvbh3ydfMEv0bKqdl+v+uHZX7F13tbrktGID9az1/f7TgDG4LD9CBkBNMU\nCHBRPpuugyGa3Yg/k6vxCzqVtCmo9/jhcoNjsiDn9Xf35VLEwBzkgkMcN4Jv\nEkY2Y6IZfa6/1xtJY4lEiTumCrDmU0KvedOowLTfFzXMTIyHYUekuiewsqTX\n0S3qwnRd+YDXVMljDvFAhDtYozf4q5NzLcBO/TmVIhKu0v+Z4D71RoUixrHl\npQ6i8+PWqhLD8gIK9LhN1DbNZt50PmjBuyrMYSzweIT4lzJItcuSBcRP8VPI\neAWJ\r\n=cEc3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDXbTg27iRfIAkHQeDwwr1jZeVxPN6Vb4zztRGPUkNrHQIgbTseJlZzESVULLHJUOIS6oEQUnT3oGxxoP1xWTU5n1w="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.14-beta.0_1567548614814_0.4765541695625637"},"_hasShrinkwrap":false},"2.0.14-beta.1":{"name":"react-auto-edit","version":"2.0.14-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"9f5d27157dadd88af20153c4f559104f21043f60","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.14-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-2aVFO/H2yJ+OB9AuNK7uBsug9pyH4MQBpTNgeE3zetkFff0dTsqUyZZQR8YMYcdSzUuCKWbnsYplY0IXoLGakA==","shasum":"c09f4ce5676cca65b7f753b5600cf43e7d01ed21","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.14-beta.1.tgz","fileCount":75,"unpackedSize":221560,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbuwCCRA9TVsSAnZWagAA32gP/AoG3QIyBsxcsjko16Qp\nGYpBg4iY/wxjUJ/uUIXrVuPiYxLrT4Q7xrRpQR6wpCXWEpVr96rAsvPQb/x9\nS4j5UXQnGPI4ngxkUZWygnj0uiYTSMoy7ttDVtUNAhGCvTSc7PS208cJwMFH\nqxotsjMrWxYkUxiskqmgdf1AGJg0/B0j0t2Wnv4AlvbmgR2iBHHfDnm+/+mu\nVApb06z5+BzYfI7coRJC8ujHSafe+WYTw4Mhevf2WWzJt7Bu831DHJs2B+uZ\nr8tVtsJnx5sEspOuln+CQ0CLplAZWpAxlMx8OnyfUswd8r7tUazI3VBNQk9x\neCdEV6L0jQ1rNNgz1IrtTDIXg2W8ZZemM/dxixkqsaKMDjvjwSueT7s74Uld\nETLJHKzUgUwFRbyEscAN5K4LDaIQbFgZXGbvGPxFAh00t6lIQrmS0ho9mMbB\niWoM3EZUPLystdUO3VulASVy5y5YqmrNIDQWjYRqHsa+Ce9BA4KsbJxaFJ4F\n24ttQEH4OPOK2ka6thgslAHcYYc3JGxmauf/Xa8YTUAyIQ/1lz8m7dSqoW6S\nhIeZi6msNEBBYWy5HIDw7sTByDoWhEVceLMDxrGSKPYJnM1e+QRj2B25QN+L\nTogeTqvin09R0IPNcOq3TS7lhoibRQZi3aXbT6rtDcgZwxKX4DR0i3MVU5yH\nve0m\r\n=GeAX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDpkqs8yofy9loZKiyt210TTdJ2c0E8FJBsyWnn+oJQcAIgevC3E1789t7k+yV0Nx34WJiSgdhc/51wK7tf2QEmztI="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.14-beta.1_1567550465466_0.6474993409576368"},"_hasShrinkwrap":false},"2.0.14-beta.2":{"name":"react-auto-edit","version":"2.0.14-beta.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"daa2f44a309d92598b1c2bf4823e1136429a483c","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.14-beta.2","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-SSlvipyXRTJQGx6dTaL8gF4TnWFAIC+ll+8LK6weVpp1mC56SPFL3+fA7RPyehA0F7eMqr9xulzAbnfqFtCUMw==","shasum":"8d739fba403486dac75107ee195751e69e962d1a","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.14-beta.2.tgz","fileCount":75,"unpackedSize":221560,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbu4JCRA9TVsSAnZWagAAGZcP+wbEkoCZsHKttm3TQj3u\nfSd9k7ZPhU3CunUV6goOXKEWtpprGDNVaJ0/S7gNrbxopHsgpUmsk8VW54BI\nZA9uHll6f6kpUDiSpzCSLfW7qv58BhnefdZlaNWbtKTg1R8WUtWCw+Z1dbxG\ntvvDSZFaBH/z2pU/BFZrijsRbhWzTnGn4D0HDaO7V8qMOtmkDmHGYtWR8wik\nezs0FhDRhbARYMqjTYXW6L6FpGztLA50qvJoY1WAh9HZ3/IEY8JYS16N1w9v\numQsnW9xsJW3Ptv9jBvM7lCm2y8499W2szP7PJ5+rjWWpVGNYDQi8ipAzrdt\npFTaZ/PWYjF2DEnbw+DNNJURLpIv6r7KVEhYIYBl7ZaQnl9lgdsYXkOMcQBm\nJnIfFdsZqEfZso6Tey0moyHzjIGd6vFmg71WmP1M/VOqu6UlVUeXjiRsUtj3\natce/O/ssuIVkHqen1FTa2KXa83mJXsHjz48bqNLcf6wbEWw0HyaqLBiawyl\nN1gI+MwBx1abnhpDNt3q0qi7CMkCCRpUjxgJFZFpX7qtWvaJgveYyd5m0hpM\nMbuxwbRL96Dxwh9mFIq0WNQLP7g5hIje2Z4L8d8G/aFiTSvurHgZ6tl907jW\nx6+VMpvej/Xe5u2phMIBqSsi0HVlOdwK76CLFmsJQWe0HT5PJ4EMZWDgsEEJ\nzMEi\r\n=beuG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTeWMgw7feiN5ASiA7M4MaXgfNdzmAEAguV9Q87nEZfQIgGtpRW8KChJrzKIVfZqxuvVfOUuIDQsrSC4N4eVwUG78="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.14-beta.2_1567550984532_0.676107967723645"},"_hasShrinkwrap":false},"2.0.14-beta.3":{"name":"react-auto-edit","version":"2.0.14-beta.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"e2e8b75426ef22ed9bb1352874b1ca7652e078c0","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.14-beta.3","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-VD5xuWkkI9ancqvch7SsXKREYY1WtVSRrPBpUSLmAtp1FyaeD1oV8kP81J5uAvJrgRWsltmopo7AUHmGFrV+7Q==","shasum":"ec97ebfd14e45bdaf7dadaf981fd0ad59401f32d","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.14-beta.3.tgz","fileCount":75,"unpackedSize":221552,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbu+yCRA9TVsSAnZWagAAj24P/Ao9wF5g4bDkuxkPvR0c\nMucohFaNc9B+dg92m4DKA0R114Txejpaun+K4xzC45oB02qZTtUOMmb+XkNk\n1NUxCqwxFwVtUTNQaELbTBaXz3oSAOqbQO4HHeQwYUBLDi6bmSnuE1QoyUP5\nyXRuf9igp9aiCFLS4ivo6HcytuoaTIBUVyJ8sID8/kf3IHngauqgpIdj/fkK\naMVhE16hdYt1rzwE3BRm5REeNwPKcoCdcjW/R3CS8hSF4lYoC9rgCZuhHzJb\nircY+yZ1TSdxCw/3G9fNQg33WXJgjTV0y9cWTcN1BNSAWWfhB9cksB2jLyDZ\nLv8x5GGQhYVFyPna5GvgOJmQvYZn5TyrUfwxfwV0k+Z/Gouc8Z2BHZToxU8d\ns3BrAderYNWaAO+9Q5ESNP2a2EWEG1B3qy51t34pRcQH5YH8c69SENiD2BAN\n1NS5gO9uOBrP9rP2Akq3xyWObzAXJlDDMhYH3ajwibD59g08R1oZmyCQYLpx\nuvarRv7ehp8ow/hGsVAf2BpuiGcsvKZVo6a2IsZ+ml+QEmmRIgcoJiCsU5h4\nDJZt1DPG/17WsU21w5MMZodYQpodpudWQR4+2gzRMSaZ9WiyeRoAj4O1mbxy\ngzoBc7exxvgrV6YkmpkIs1oHeqHcdzNTrDcSVI2+OoHclIji/ee3R2IT62i5\ncPQh\r\n=uHZF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCV1NLbZljLog9MgnLwu1bsOq7AowWp8h2zd74YTIUwQwIhANdB0gqfEGjrbKvpAF8om2R+8omo5bOq7lc503raBj5O"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.14-beta.3_1567551409952_0.7864046626485983"},"_hasShrinkwrap":false},"2.0.14-beta.4":{"name":"react-auto-edit","version":"2.0.14-beta.4","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"6b1f73538660701384257cb7c862c29cba35be92","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.14-beta.4","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-tw+MMUFOR0xhxPkOUZWsoPdydUaGGmi0Gurel4d6aJWYGw59Q8X9k2g7zc3W3g99eVLwY/S7BpZRRm7X/V4S+Q==","shasum":"2e7428bcc67cbfdb0add756ed6b3e8bb30616909","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.14-beta.4.tgz","fileCount":75,"unpackedSize":221552,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbu/6CRA9TVsSAnZWagAAlIoP/1ZYC9h93FefdjA5IMH/\nhE9FcapVsBfj2CuJP+v7D8UHirnZxJXhlRTXCrA/dIRfw0KfBRIdgGb0iTXM\nQAM8EmzWRadk/dZHI+cUghCvsuGYyqG9WAMSygIt2XrVZNFgJ5PUwaogoJdZ\nsXmN31L4uo+MG5kLx4j1tcOhQmJiLDnm55f+HiLdA+SEqLKFbEBXZvgvFAtZ\nNG27kRO5GztRBhYbmLX0Ck8dYfi4Ua1+u7k5yei60PEXkqSxD3IginNQgDZC\nkKHbUJn4DAJ4/Nw8agYtEDNveK1EEUyaGSvygMEWXzM9YYpT/AMUjNR2m81O\nQ57gV4vDBrlGe0FfkRh7qJJwH3O7OlUVtwqQ4BhZZPZcgK/4tEas4EoLZjfh\nCOSCtdhNbOte+3XTUawULFAbuZ8uNdLFhoX4Fo7tVCJB8Y/uy3HNoIVGPPAx\nJ9WBftDWjEgw+VsTRQ2pi4cSdXkdmQ6CaCnABTfFBlXkiat+Xkqr2Ld55ny3\npqbj7+DJfFOAwfXT3+K2uPjFGT5/RyFCm44Zc610+VccFj9nZL4ZpT7ngLdF\nzJJ9LNXceNzpbFq0sZIUbtgrcQBYqCRDdTyHZe35yYiUUGYBLx+RMmAE/9zP\n2g9YuF5Ibpvojp1amFGtYKcPEQloD1HrVmYNrsss7hXejWZySajSmcMQuVjx\nkqKE\r\n=Qz0g\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICohWvLGCa5kWc23iIwtbL9M3Z1RzMmVqfTmmIScXR5bAiEAvc5O/FGCbl8O8NmjMGD74PBcp1IafPURZw+S9Rky/jg="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.14-beta.4_1567551481871_0.15114819032743343"},"_hasShrinkwrap":false},"2.0.13":{"name":"react-auto-edit","version":"2.0.13","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"23fc89d036fe93c95f491ddfbd053c5aebaaa066","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.13","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-ieddmL5nCAUho4x+YOl8Mv6s3qA8pKQOc4WYhY7Usp8oArZ6tiJyBCpfkjSv5wQxDS03qgA/rEUBWPWf1mOYrw==","shasum":"17485ec36fcaf06d8299961f486f9e325638eb27","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.13.tgz","fileCount":75,"unpackedSize":221545,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbvB0CRA9TVsSAnZWagAAF64P/0fZdkB4nqf9rOQd3TTh\na+sX+nKdq5Hm7isu+1qPf9OTIFr1+eXa8lXyxnP3srJz1UNW7k9R9CpSn98i\nNWTXZ6iGLLKHpPr4OlbRmB54EySAmK6MWXnOT6je28Md/Ix+6E1ue6f9cGyM\nJHHnkG8r2sguUeh/kZ+3MaV3Ft27cQpfuU+C5G/uUCCgDtzaUS+8OnmuD8o1\nh1TcbPWzKMaSLRvjUSOOBH8r1Prw2LXLnSB1xCjFfeQBwMZl5S6EGUSn44N1\n9QkZlH6uYKU6tHbpEyn2rvSMJQKMl1o+fPRbsJuoGnVV3g2F8Fz+QiGZjeck\nBaA8/oaPoUJPmC9WjxR2VsOrIcBWWzj3udrZRijjVtUFrgeeRO8IS8HSyy4o\nkYjRDsjqyQzCmeUbeGDD2an1eZH6eAtTXlgnqWkr1Va9y6Jf7/PQl06OEAjZ\n6It6gC5gJD5doSp+RUnrMUelJu4cXNszo8c+1Wl+Ry7BgM4P8zVHt9nfqPHE\n9t5+bxOTbKzoXGdKx+MLfkBajiEyf5E513K0d8wrF/lun9LDYV8Xx8pUra7I\nULfm1EeKdtdmue/ArmYqjp88nf53yN8oEoJSLABthOhTD7Lkws+44r4X7LdT\nxiDXnlr1QUZIRxl52dVLlBmmgrEG3yZWtU5Xgfoi7zkDkIsTu7FCQCXhI1VO\nrsXm\r\n=g5xQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDUAeXUuf2c0At6xfPfPudZZOXr0l0SnfZ5SfbPKjblfQIgQkEOY2ZSpr6zx6TQkA8TaPFUEGpDJe/Gh6Bf3SY/OZw="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.13_1567551603950_0.16755386693414187"},"_hasShrinkwrap":false},"2.0.13-beta.1":{"name":"react-auto-edit","version":"2.0.13-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"3ddd38b6967708f1016b85b398d63de226c76b2a","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.13-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-L+yBB4kjg0DOdfw0fmr7qgBRFDsli29qv7g1xMeGNnEfNo8Fmi5KIgY3rmw7h1rwORTmXh50J6fFC0KQb0pR9Q==","shasum":"1fdfca6168bd91ae8c51e8feeda887d70bf7a48c","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.13-beta.1.tgz","fileCount":77,"unpackedSize":222439,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbzakCRA9TVsSAnZWagAAVXoP/iDtWH4o0Ec3yWmdX1Ly\n9WQBMWKSCD5CtXNsr4ohhO1QhQylHWRffyzymm+ml+XEO1+lqwgJ2rXq2zDV\nkPamkyoHvVr/B2PBB8IOEcPZwXzUBwJKvNPLT5g2iDOCjU98kPuj7GTZPv3Q\nADNCbsiKDZYNpAUtg/YRhpAoi73rMgSGMikTbyDrDsiVuIeGqTIA85HiglV/\nSV9HOmWkI9ALUSd8NmKiq7zu6eW0D3xbfB/3HZsEid6QtRrBOh4R7zYZ4fW0\n5BxFwiTyb+c9hjAJX2heNfrBDE3fl4xk0LKbhEmqKZp8QKKhKhtYRfZVabdq\nTzHwUvgO8Exhe8wfqcUYJ5fWAuQr2GXScXvs2FTHFKraFUXFkAPIghYqQCSE\nF3IVyMVEzWx6URr/O30QD3mw2RIUeSTEnnHjc16xjGvz28bV4sm4s0VKe19T\nk6edbd1T5/1tVvSDAtG86Km/hj72YzGtk72oEanG6nFswwsmnv+jVm6G2syb\n63+tiGxe0Tik0HR02OCI5uJcwk/aXhxTjVppFlXi6+gYefs1wbTGfM7azzul\n9AgE8YMmq+K7KIzQMfoAqtURiKFwXmh3D6HNTSBMIt13LyLG2TQSVHt2vQbB\nMoiFTatzoT4FiZLi4OrbodBSdpJeRR4Bey2orJjkkO5bBEng2edQin1uPkyE\niFLv\r\n=LGRr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDBnb7IdL/gV2rj0Chr5T2FZlAP2msOHuzmz34XQhMD/AiAJ+Wh/2/ohcEZiWmi39sEXbzACjfccXuwOh8t1qFVqkA=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.13-beta.1_1567569571921_0.8080137099641074"},"_hasShrinkwrap":false},"2.0.14-beta.5":{"name":"react-auto-edit","version":"2.0.14-beta.5","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"f8e915c470a44a67237a356bf33ea82490b7d258","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ image: true }) - an input type 'file' field will be shown, allowing image files (up to 5mb) to be selected and uploaded.\n  * meta({ maxFileSize: 1024}) - specify a file size limit (in bytes) for an image field, otherwise a default limit of 5mb will be applied\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.14-beta.5","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-emo5huHM9tcsRTiF6R9AyuTdKGMmIEaySLxa7/8pfI253OTcBlaYzVr6hUVdPtVRscsM8OW4r8G3gxzRvGVLrA==","shasum":"f9a8581a4391fb3b0e09c21f52f0158c3b71f4a0","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.14-beta.5.tgz","fileCount":77,"unpackedSize":224473,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdgZbrCRA9TVsSAnZWagAAjKIP/0FL73sDc+iwepeW9JYL\nnFqbox6/aCOrKIA2dy4ZwythV0WRlvzacv44u4MKABXYd1RCs51AVRHQ27Dn\nH2ihE32x6/barqx34h2JfTG8QO6RQ/T9dyS9e3AnvJhSghHH/UwQn4UH03mI\nbh+/lNbfd8rHxAWffnTY6iRwi7+SJw5uKUajAO/79msj+DZm9nIpWiAaE+CN\nNXVkhE/zwqe5T4opY4/oBlKYylZN0Ov9UdtLaVpdWWHwX7RW4W94VMdN8Uhp\ncJ9og+AvG9xztwEEWn2JWZQ3jl+apnZBHEijQra6jB3tvFAQVCrKfO3gM+pG\nBOsAw2lxtdGHgNZ4QPkxRNamUtun7ryf9OPRNOEllrB4Tat+I6jstVcdMdIe\nQZe7xnYg1t3+y58zuhUZh9RUVBHv8+KHdbl7ag0NBlMMnt7XCtt5mXHoGFk7\n6zcd4qPIA6daATX4r3dNXytFNCgcbRZDPxSEC8SfraaPMcr46zdk3RoXSvwR\ndb3hCf4Jl4xrmvUmvv9RkjL66d9AyXU63hj2yYENS0SumCsSr1Pam5Il+1x3\nFr5bHvLToJkoDyjQsM4MN0+tjZUAHMrQVYlUbgLSOP+r0SyVn1A9atjwzfbs\nvrg2QdEhaOd3IbAMGMRa/gQlqL2RmUJam3B9m3vjLtog4qlWmb0JfRzzzJhl\nlsNI\r\n=sgwg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIQCoHqNdgzNWF0qB70y8RTNtPuo85rOMsaKBk9O27yOOmQIfY7G1gnJI5/nrS93JQ1TbqRTNqRiWMzIqlBnKalBJhA=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.14-beta.5_1568773866659_0.6439565968399199"},"_hasShrinkwrap":false},"2.0.14-beta.6":{"name":"react-auto-edit","version":"2.0.14-beta.6","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"151942cd7116b37a6d112cf21a202504553341ae","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.14-beta.6","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-xdhxOav4M9l4KBWZTdskL8N4pzACg7YeF3BuqiHC0uFGXgmB70G4yzqhnslm06LcmxMO9PRnpd0KXQr8LacGrw==","shasum":"a0f96d59fa2cec333df2d626051e2b09fa324f33","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.14-beta.6.tgz","fileCount":77,"unpackedSize":222756,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdiSYECRA9TVsSAnZWagAAmEwQAKSs/EZGi0Z3D6fLTuur\nmTOg4N0NuD1LsLPQcj7mU1ww6k+7GpN4gbyOo0QpSo3b8FdIWeiLH5vXd3OO\nSLf4WVFU8WHMen04y41ZEJbDSg45FaU+xNs6Y4WOqSsEpnhOOaFEl4r3g6pJ\nubFbvNB6G+l93rOLyG7qYpUjt1SPx1ghEpMxYYL/kHwzFOO//wRyK17V3IlS\nQ9YHcQTSbzmihXRh2L4TRqOuvCmHvZHLPg2Xqmuoe2Rw34Hn67uhpjgHmK1X\nX2GS5lF7xWGqNHeVChjWrpHuOhFb6DmY23m8lu1M/KhYVMNfrNnW69zs6E2G\nGTIbnVt3u5ePhUSWmCSpRLf5fSwLnrbu6ZLOIwk/9KCti2TCjyZVrD3eRkx8\nBt7Xa7tvxrU+3QGYFnUbGMt1ZlPJNVo0i0zIDCeEQM2S8duhVUL6RL3IYEbt\nm9aRi5B2/nf4K77cOQKp8iA0qk7fj4CO9fsd/Gy4E4GVkrdmHgBy0Ccs8IpL\n8P1KfVJz3Oh5xQrfdipRXQ+B4Qt03SCZ8/hJdMvLGBa4uJbEkV9MUmpCMKlK\nYh94C1P0oO0V5UeUHowyZxyZI+3PK5macKf3DKKoED4TJ3QxLyGDd1LWD0R1\n/MoAybBfhYxiZj56+xYHHkCq1q5E5dPXKIl6YJ/Rsn6XGXpH7wxVoxRzNvmk\nFtER\r\n=T44J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHr0J8BAb7S/kfqbfyivwVUSewSHuli1/j7fnL0w70bVAiBwdEFyBE4VgD0O1RbJaj+svYZdu1oM2CWoPxl2aqvjEQ=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.14-beta.6_1569269251596_0.7136711161851699"},"_hasShrinkwrap":false},"2.0.14-beta.7":{"name":"react-auto-edit","version":"2.0.14-beta.7","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"c74b181b722a2109c486f9aa39511260a23f32c4","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.14-beta.7","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-oMLg89Gcw0Tv6rBnFUX22bJ5FIkS5r37cXuTn6NOzKmUxycfdxipos9GAY6+GInsIUTRxNSeUbg98R/lYMESOg==","shasum":"fac4d4baaec530edc3872cb4c39c5106175673a9","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.14-beta.7.tgz","fileCount":77,"unpackedSize":222762,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdiTSACRA9TVsSAnZWagAAmxMP/1PYfk8ZeiWUJAhzGLIz\n+uf3i8qVZVPgXPGjVXi+xt6KOiavVy+9Id3vhhwGHac7FE2/JNZOaxbNRNzE\nKfzms+9wD5KgzUtu78uNoi3JBI+f97RST287c/qgjRQNSnRyxf2xk4BHVfKq\n7vGlwqxwYKBcvGNC/I24IhwKK+hNLY2baNNNMsQ5mJF76JtAzzddWirJj66R\nrtDwuw3LbvqR6CNih8x5ixu0PrAJbxzv2EdrXVfoIy3fMFSvCXYQDeHfNkDg\nBn9XSDFlxNXWz8U95ScxfPbUTM8w2k2+NjHT6NJGqox2TgDN/8TTEpRzytRs\ndbRR2BjmUkHRQDkdru/DIKxNRJMS09OUXnSv6d0sl8edby5/d8/i+MyXd33K\nQemctmSWTJJcrj2JTrjRfNP1oJe+utiI58/BU4hUf/pyNLlOq+2ePu4yfQJd\n7v906TBuP/bU+hWGAx6R84obpiXAPfp4YiVl5ynxq9v2iCDQWfxWEDhRznQp\nbhbXWg4tB2SIiEhzEqrC8So8LMeMazGfu47A8u6Pd6d4d110O/7ddeJh/VWe\nWfZ8q22tU1Vy5LARp70o3BLh5T2U0Bi+W+zDrtTtYLKGAGHEZqoCbvXaHVy7\n/BMiPB8yzoCzCAMqpuaFfkTD/4mog7GRv1BMPSUUoQHT+qeo/1kOQ5IOI8tx\nWoH1\r\n=zP3D\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD+1WgNYl0Hgly0ZjL8/GZdjqKmnBbqpgXXOLGFAjpPZgIhAIrRvfPIUuX5E78pRxLDxi85B0pL2+t+zOiAdJk+swCN"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.14-beta.7_1569272959505_0.4238672337500182"},"_hasShrinkwrap":false},"2.0.14":{"name":"react-auto-edit","version":"2.0.14","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.4","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"2c99e228452e4e59da5c2975dd74fae1b91d874e","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.0.14","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-Br6PSBeEbCTl+F3Q777boU+ZGSNG8wRt1g+/8X5uTZ9DRlsunxrf3GXm+hAzphZkZDAC9UQ5qfZLIEDuscXNkQ==","shasum":"292b261c8ddece16c963c6d410ab4f88b8c27564","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.0.14.tgz","fileCount":77,"unpackedSize":222755,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdiTfKCRA9TVsSAnZWagAAlpcP/39Lf+0hvSFJm1r0XmKC\nS/BNpbxB8zdVWSRn9ByPoW9ti6qLhKtDnuxP40ht291UzQHjXbHgC2pPzQMG\nWub1n4ceTIlCfkKm+vrDcxy7v8pkzq/957rJm2WjpoxZ5Mh6tG0yopljV5DR\nzNikOk4Vz34m6p7Fev+/EefoKyujYTOiJeVYZE8ZqJ/B/SQRgEigL5q5b4do\nr1pKCOVbMUdBYDhAbyYC/s7U3b22DOlpAE+/Xv5Ux1WIuHII1J9ABn7xwphU\ncBGE4cUvVY3M5H1n2n7Ou0W8CFk0wjL7kWWVlX8ch/BI6iJvwdv0Ex6Bv8Gf\nqeAw6cJ02R0cc230AWoSkzu4XMlqGVgsjmYEkkUSfqH/bq5rkN4O1zIxOKjf\n2IdENUcPQHS6KhdM7RV8+2YYrWItjkI1mk+u7K1LenDgwErWFQ/xudK2dZlP\nQnXWK8FyUHpVUJDyKi99QQVUOJTfnEeRPsZjzRxH40eOjIjMKVdKRYkJuS6C\n9aRpsyl8Po+KnxuJykv+Mcdx0ic4gPRU7l89Z8/ZxeE4ey89dWErebVntlsP\nA4f8TOwC4WFOqMcYgQS8RDCkHaGxCqH/SFg5ODg5dB9q2Rqc2IOXGY84rBZT\nSyyyaA+0NWlZQX9olh9UY7blY03D2zfE/AG2/iLqArlwAAmarZDl7oDE0dL+\nXRQu\r\n=upVf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBwPDOustTK312H+x6Lf2c3L84gkiejdvJWDFsbrC8S4AiAkJ+yEAB8ieZEZ2AIBBFTV8B0VWNEA5MwdXgN0E45LNQ=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.0.14_1569273801824_0.2606660571914916"},"_hasShrinkwrap":false},"2.1.0-beta.0":{"name":"react-auto-edit","version":"2.1.0-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"c4280cc3a40282f38d7eb97a013ca545da853019","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.1.0-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-2CXy/VDT3lSLG3vouG3xGBlEPmvoFJrz30n63je5ckoNP34SKF80FVFaqEN4GQob6rS1rZi137SyD1psCG1tjw==","shasum":"c488267eb457b4d5815db88b53d60d997abf416f","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.1.0-beta.0.tgz","fileCount":78,"unpackedSize":240588,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdiqtfCRA9TVsSAnZWagAADn0P/jBzE3wGcEc029lXaIxa\nOijPGw8BF9LdppsQgPbZAImsgDWyDvP1fk2GKE8kZY4knGIOlUQTEHVHXUP3\n0QG+akD4Dic9uYGe7FC/rJCFojUXGjZ4DccGVZxEUfS2hZsNyY/KtfYuaIvB\nDWqOsvUSD4YB03dLGUPTDsDqxTKNs6yGw3QS0fa3KoFBzss/k0aVECpPjBCs\n/NYDrkXN7WaMVg5XCvm0HXm26nn4EOQhoiOS5QYGxbectiTA8Ie9RJqUWAnp\n4jsRN0crDTVQTDDdIn94SAdZoiTnBJF4mxlSpzYP+KJef5JfCR3TDGUiz+Ov\njaUI0Fg/bd53U925ZtPjTto/qNeIsdcEFnQCtMMbVI7SxvABzIvWItTcML1h\n9d8UDL3amDEdaGyxC4XjsJ6vwKz4WEeo6JsG+IzJiaqA88uLkh0zpv4bAc7P\nt7jqyuWyZhijihB7vGrKvvi1ETJkHuuJL5hjOiC5RJegY4YPJ7B58SUNhz2U\nft6RtnerBNczRWtBRD5rl7UsSOSr+Z7ncW3u6ty/TzJaUMNCVsDjOBk0uPKt\nID1eoCVp24KeDjJz8s8smLMPCuhel8NckOy6k/Exx58eet6YRrSRf2uK2yZ0\nuCRimQq9YL3jpReVqV/iLuDXBFDilUOJxyPI3paLBX0srll/nXRIh0ixSL+9\nT0DC\r\n=Yagf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEMAsrkU7Rj+cakInc9fEtfvSmyEcA3VbP3ViGP/TJZHAiEAgsL97UHcuFFyoVKom6NhyDAzAepE50eR1yeLgO6lZW0="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.1.0-beta.0_1569368926639_0.8475410960318992"},"_hasShrinkwrap":false},"2.1.0-beta.1":{"name":"react-auto-edit","version":"2.1.0-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"41c58f3d1a80d007ac1051a4893a26637c36159b","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.1.0-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-9Z4+uZ0pKT+ofZqSlqvLNz9o3y3LpgjevTW4lHp48VJrWNzOXSBJ+hNqYiGxGeNJg4i+J0uk6akNZRhYnAyUjg==","shasum":"adcbc32edf140f11625e2c39623c29dcce3ddd27","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.1.0-beta.1.tgz","fileCount":78,"unpackedSize":240588,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdiq5uCRA9TVsSAnZWagAAi0UP/0HyZTMmTFcLxZLpdIJf\nn7UchkXtb898eSs2d+LvuLZnjvD740z6iF8qtwbsyIU4HoGYG9baBXgC9mF4\nrD37rlRDfFERGakwBkm/hIwD+lH3SXIz9NLG1OwqxTZA7BjFqapEqYFer3hW\ncpVAmuzGS0GPcqSWtvKO1POUzAaWdNtuII23vaBRGltDFVrzOC19VDJdWIga\nZ+LcvFTVujRvlhrLYadS5DfXf8ewEvX256HJ0mtajdtY0IaXGVv/8UF0ZJmc\n9S+YXDlc4yPsLSJ1pXsl4Bss5aZVw8/QDomFNH+vFPAepWmJE8sUEq+X04A4\nPTsXn1EVSdj/YpAOdMbyoS2ADT83J/z0r/goSdBFOwovWfJng7U2OPDlJhZ3\na6uLE80gF+yFI3oILDmpRioaiTEGBZJJDx/PyAxwIAnCZBlvlTFbBvHMvyOJ\nVVjQvVOSxpmZ31k4Nv4E4DTbgDVXwPDZaVlQHdQOaxR+HY9KsZ+mIlnUQBn0\nIYWdKaOJPmaCxueJGabTIo0mNXa7RJ/HMgKl0s27o+H639pu36G+UbXLzAJJ\nUlQGlAq1ARm3/1Q1GPlTs1CJlhJgaVfKXIlgLEcl7CaQt+0a/i9ayNqAaMss\nBgjHgOdu4Hw8FZvgXXdCk78wPENbiBqMIGx9WKJxLYBa/MWif/2q0XAwu0HH\ngh4c\r\n=QloR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF8zYZeUrmkPoR0EPGvCMPGehyMqGHE3mcCpkxfHyZqtAiEAtDnRzxqeKPjGr+vdtKkWdpeYrDb0CeqKEwxLc6BTLM4="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.1.0-beta.1_1569369709576_0.5639651482006593"},"_hasShrinkwrap":false},"2.1.0-beta.2":{"name":"react-auto-edit","version":"2.1.0-beta.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"23ee0c552cc532d109483147e9ffb7533427ee0a","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.1.0-beta.2","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-18I7RwLmhAwAA7P+ynUhPSAkgWnAQGkrY35ikEkjxJ51+TRLMMdIXEKFQGVUGnylwS4IexhMxFdakL/AucwRmg==","shasum":"cea7c5cfb9b15df7fad7ba94ca5d2b2d2eea1d8d","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.1.0-beta.2.tgz","fileCount":78,"unpackedSize":240588,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdiq60CRA9TVsSAnZWagAATbAP+gMyqa32SMyyHM5q3uSl\neH6pc7cWRIzMHXKCFG8BmUBQlf6jEjJY9Df3/rT8hk8lW86EIjE/rWtUga0g\nioXgZQvNo432rsLeyMHC+rMxfD3Pl5q9iWLvBwZvU6NnduMe6Y7/7hp7/Ngf\nh8Lkn9WFmhk2B8T1kGW7XNF6f2fGirp5jO6TFCY7dyYmc5BVqDr+NXxqAn7s\nRgdqrBl36Oga7emALAOR9Vc5MWWSa2Fl1cGjg/wHjVoGoSFg1eVK4JprTOR7\ndmX7xaTu1MJTcsNUr6UMmEJWGSjrxCv/trPNoCRLtH0Bvt3gXPdy3o83h7OV\n8tfklQmTgU4fRHzEBB7RyXalHdXOIhPYMWrozc0qNkLn3+COJiMkyVHT6OhW\nFKSi9kg7IOt7N2sgYeYHgilfEmuRL21teOiSuOhRmDTHIk8AOciZhQYaSSBI\nUW5JtpysHhTRfokfZbAdFkWnL+VNtTSlGC83xI4Z34Oq4CNyPNJCsTDJeVz7\nW/8NyRRxQ8nv3LrxvDJrVXK9qQQRyFC00jxYADZrHa5KWTSPXIH2+/bLKoXQ\nNpbP+77hT5bITGFbzA2wolos43lG4c1hPvdeX+621xOY6elyPsvdwnpM9tfu\nUPN6KSdHINOI88BidZuATq2ZEWBvH7rvhpBVz6dNCovnAbFiMBsG+cv10HR0\nkcII\r\n=Mw8v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICxXwSgA9OVbcSQ2C5YV+H3JJTPXljLKDcka9lLLE78cAiAi5Xxq/GE/m+Z3fHQf5JBv6kYnV88xjT/afSomqxIpaQ=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.1.0-beta.2_1569369780369_0.2191374689686687"},"_hasShrinkwrap":false},"2.1.0-beta.3":{"name":"react-auto-edit","version":"2.1.0-beta.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"ee00e7630d93b53bcecacd6dc6028878024906e0","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.1.0-beta.3","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-1poPRmvCYUqGHpIspKc+W2ni22ilCMyIrRsdbFFxG9ReJMsv0iXXY85EoEGmFiInYHLhlI+/SGGI0LPPL14zbA==","shasum":"9b0a6609bcfcd4f6424eac5fa4d2e84411b79c27","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.1.0-beta.3.tgz","fileCount":78,"unpackedSize":240910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdisNjCRA9TVsSAnZWagAAUocQAJawza97c2wLDOIzgzd9\nPQGq/u0C9BB/DNuHcnp5hvIqvEbCtWBH43krLZJj0KIbjiROQK4zxY2avd22\nxAkyYqkreiliIzEU4jdHqToMjbYWEZYdi+xODlDwSiKU20E8n2AizNSbOHdg\ncX3wVq2JVilvpMN4HCJ3oqbIZOFV97bYKyY1diTu+pAurlrE6NY5ETZZnrg1\nlN+3fWXmN0qAMnOOtqZRncWHd8MiDVB+aY/8TI0U1jmicOXVDOVkrM8NFbih\nkiTi1htwd/ANC0C35SUr2qHW46vhWuXD0NbZfT7h9S2QI+3WIfAM3iog/s12\nehAbKQrso6qClTg7AbUy/8eq4AFybSJUzobAtrmz0gbVCGMmOr7MfbdjNRhO\ndPX6fudMEEsKrWGvLETQGEmSol/h0Tp5q+Z8WxVbZYJRKwjwkdnLOAdpj49x\nBFPOMUH3421Lu2U4OAVG+gN0Dj40EVwwFtFUqGYmiN9Tj++AHhy4trhr+0Sq\nywMlhLcg+OiLzqhnPL7wN12kKvPewTldxorX3j3rOsb3JVf6gbqDijV7FikR\nJhPDmuWa8XPdlKLfebdmR6AW7VYP80tE+FFn0rFF2cKiacHjQ3f/u5k72d3s\nwnbjSMyDf8iRKWRjDfdIz4ICf/HaJu38b7tDK9tG5VXSznhFz5ZPohFVepg6\nEFao\r\n=cFHm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICWs5e3ZCFW5O0SoHAEqgyx8xt/QF1pS75fSrIObpkulAiBpQSKlvf9T2Ovs+NoHwmfNYBqt1r7RJk8ylLo+qlUB/w=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.1.0-beta.3_1569375074292_0.7145685535385169"},"_hasShrinkwrap":false},"2.1.0":{"name":"react-auto-edit","version":"2.1.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"0a558fce8df3906d231eb6e6090f5abd3461298b","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.1.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-/IAi7B7RUTK1aX4wgDpSrE4s0V/wkLIB/O1sqw80YN4iUCA41wFBkRal+e2Fb+QDMRQJ0BdBmQ5XKLGGU3RR4w==","shasum":"a0e127e93bb3e5baf5e0e73eec7931f64d9b64c9","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.1.0.tgz","fileCount":78,"unpackedSize":240903,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdi+DZCRA9TVsSAnZWagAA7SkP/Awjr5Qcr1Zm6vY+1L62\n5m44AF7hhA2S2+uQv75VREUGq7ugHn0lovr/wZwDPNZ9SNfJDfLeNO8RqZOt\nnZBbfJPT5h1SIU84pDY2mai/FowkNSYyr14FhiM3VNXozsi8Uc+cWUHrjX3E\nfd9rijs906tlp5dCp1szNW0K8aa3pdC1xsHYJ78ro3h0+K+tKucYIHDp7eKU\nOtiMS27+Ibfbk++kjDUHzCvlbWwDSWRsQbugkaQMn0NKtPzEcSZsaEkITjNi\nZG8jFBOslZXyzquQm1KngV0yc8nMQQprSMQ67weoTA1QcZGakUVjJpAo+D7P\nM6dIiWqu91uLMT/c7uoJf7ZiahnGNQCF1sJlBb7e1Smg+rbbMwZykMZQVvl9\n0cEQqpoN/G37Oc68b34IWWF/ib6Q3yu8t8nQaatvEOU0uIsoYBMTgvD5LLFb\n2HcRBzCR715K1bfVWa3wRynczARFREn7ZPTSMa36X10N0ks//xnvgxw6tc45\nVwXdBitZPSK5NzNyD4R88ZNZDfek7x7zwjtVoPJguJN2BWrcONytJH2t1v1Z\n66FlGMGN3UKaHdJUBjyRlFPctBgNMYqIzoAPyjdSKV5nnM8A8ithHp1rI6wy\njdUVJe0f09IaXSjYDkuljxdW95eyUsrEgrQB/cM9O+pmASdcg8amTmr10H0q\nDDC6\r\n=LG//\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDctvI0L7lvWhn99qqA9lAAwVg+PasROBJUhxAqrzmYFQIgUesgDA+k0l81+Vw7fNnX88rfdFAjWlQenKOdydrqx2k="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.1.0_1569448152586_0.890625843944927"},"_hasShrinkwrap":false},"2.2.0-beta.0":{"name":"react-auto-edit","version":"2.2.0-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"13888c29698b45f301111e829b96698a1225066b","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.0-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-UfhOhicGpKqqfbs38mGwgQ6K/bIZdY2BKQyWilGEdXwZrEpcK8QfgF1P6+NvCjVcDjE1PL/i3NEhIbky/X1Frg==","shasum":"8bfcfdf2a2c04aa9aa9e063873e3128afc5bce7b","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.0-beta.0.tgz","fileCount":80,"unpackedSize":247162,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdjSdhCRA9TVsSAnZWagAAyjoP/i/cO6uSKv1u3VpBcOEl\nQDdMUBQU2fCsdL0AOP1GskR9To0At+w0HAcAzgS2TOpHuwNp6osNFask75Ol\n1XTDEss16tt+q6knfPT6CijCiJ0hLsmoR07VQLMXymlTLfvCVCuRkwm+Az+K\n8crDXOK6b1PGZBJcymmq1Ve20jr58aBLN1D8l6mMCPYxTKu0Dv2IU/QNc/R8\nk2fO6rHYQxDFCSrVRz1AMvJSy66DCbMSyvpEjBqP8xKZr/M8ky1w21i3vHYb\nw1iqLbLAGzAv6sX4PxeclLZCPn24Zm+Btg/v3eA4Fxwi7eNQNi/aM/0Fi/Z/\nXweH0u6ICk0qjxNs46N6SYGXj5nNalT9e+cxGLS0r68asGqcO2i0Tf4ox0/7\njYX95a29FCwYSPWTUmYoy2rtBdej57ge2RXfuHnHxSpJ71CVHJy15gHr2gau\nwr8LdS5fO8erVNxkxdAQZgu/3ap4acv1KbKSAWoJB7MnBj9ngaVMcn8mEFV9\nwqJ6ZLf2u5kywQBLWt2y5W7Jy5yJz/fsJ09ScPT5rXGzAiaK7sz/fXWbIMOh\n0xtJwGMG+zu7XNTftxq2P1485OCDTH51LQXiWzQmCPrpBH2BOvtXASo7fzhQ\nkn+ULRYCoSdf+Mc6PJSZwRZ046WITKMFUhFsm13/XZC6wBPrqqrOc18vAAv+\nalGH\r\n=7yHD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBX/xWspx+Ap+zCeNKtoqEfIQryhrETMI9JZGTlHrX2iAiBUsZPRAQeFKbOzxYlBHvYDrqo4Y9wULeAQLZOExmSAuQ=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.0-beta.0_1569531744727_0.04451765005860353"},"_hasShrinkwrap":false},"2.2.0":{"name":"react-auto-edit","version":"2.2.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"5d09eba2fbe8d8d5a89dca064f10e2f08ed73100","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-mIQMf16PM89pZ0V8NaQ9nQMEyH4NPjs0mTk+26JV5wMzfwpV77DhwYMosmT7gNJZna2Kll/JLqAKEq+8broHUg==","shasum":"5c60f063ee866c73f1dd3618ab2b55ac82c9ec62","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.0.tgz","fileCount":80,"unpackedSize":247155,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdjXKSCRA9TVsSAnZWagAAlOIQAJzKpzdcfWy7p2D22cVL\nmL2AZcogrh7HMpHwv93LkmnzjaZw9dGaxJ0nQnNKUAu1jSJZwnJbn1I8H7oo\nTroEJkIcr6c9c9CL9oeNpDa01dMurNQYwAk0n3J5kH8a+m4YE4MYw8WXqbhQ\nlRwHhotPbIcnAsKlqArRWqJIh7gt8eTHb9npvtS2Gak5CefwlX/BJ6R6ykQU\n/J7q03rsBkbTaawmxYsT7kSR90i0eMw0H4rV5I+3A9PqWds98JuQqkzl4yyO\nA/Koyve0TNn+7PeMD1gHqGlKvnd8atItmP0MkKHMRBVfEzYpncCo6SGFG7/F\nX0KviJx/SpOlTUz6JcfWgfqMV6TDc3jBoBnVd8EwrAYI3LYTtbLUXEvT3355\nGezHd4RombxyTiQxpxsQE7zMP+oiWBKuIdNcWoNvEhxFcfZLTlx9UBsjR0oS\n5V68gU7X1hnEewk6g0SihQpwLpEtskqQ4WvIauQkS11HUY75aI1mvyoUA5YD\n92pn563ygckQfW6VATxHm4REb6h1OwneZYmfWO602Y2KfKaPQVeTFNGGlYwS\nVnK21thuuQHBRJxqwM/YRjgZM5bLcJkkXNwErx4KhuP77QuuvKfnyuAEotKe\nla9NceANERuuu/2r/zCMYI0SKrqU++1pK6CmAhgUeWacb4lUX/H5Kd+LlE1G\niuJk\r\n=Flzq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDwiHjnevUQx7/nb2mhZdG9TwYFD4DdCw+wUtNT2ZNQ/QIgC+LVLgplKsctyMqsuI5nsqZVdXf/pUZ5uu9iVAS75MQ="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.0_1569550993761_0.973217782982952"},"_hasShrinkwrap":false},"2.2.1-beta.0":{"name":"react-auto-edit","version":"2.2.1-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"fab1a0ceb802822bca3d954913378b4bc9c76d65","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.1-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-DNVJwPsM40tS0hz5nOmKDBiAL4EFFi6NHQxvw6slzGv1bpKGlcZ36iXsGFUCb7xOLrB6s9YsJ2xFvqubA0YN1A==","shasum":"09a5e7e91f8cc308aa660c35ce55d3747b182a6a","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.1-beta.0.tgz","fileCount":80,"unpackedSize":252574,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdjYV3CRA9TVsSAnZWagAAL1IP/A/CHyPLRa0tFZabB/Lz\n/la6TtTAjOZ6DbCaS+BeJ+zgLF81LOF/dyUMm478xK8thPSbexSbkXskWPLv\nxh6wLGfxja8jAdbCNdzSfbiUob3/Z7z4Z4YNL32sEDdZz8c+5F3G5yjSxj7b\nRwQPg6RI805bjfuxu5080SqN+HlnhxDb/NDm7lsdzn8Kh2BnlS1pMibOiTJi\ny03iAevhg+DCsiYHwKJiFnj9+a8h4gfYBYWOFbnGXurs7mKKrWRR7YJThIu1\nftcK5BHFi9PniHR2uyiagW7zuqz0DYRhLpeKwo2TUttdupouVwFvKjqJnCKO\nA77vqNd/m/6XvUc/A5vo5sj0PoOR0mYE37V3MSE186/aLiLXEV1LxW+TJQpQ\ndP70thwAgdihkHKyC9UpaQ1fyLJw6zd2WmdVQ4Qc/D8/hJqyZTyU+L5Xl97U\nP62MUDXlngHF+ilDq020s19VBZPajb0gBk6T02aU3GmTKyBuIYIf86g2h24U\nxE1Kg+wy/7PEnuHXwwANPsrBmECDCVTaDzJVM6q7T2ycT+5dwGlDXFKdeJoa\ncxEg9CZAfdOMdTdZCNLfk8MJ38UpIdvRXbqUH7jM+24FTvg7bBHm9xCBrGTL\n3CNjt+tNY//1rL9oH7DEdOxCmM/3C3ooRIgYEq+4SiqLHL2nvMOi84D+VQ5X\nYPYo\r\n=BN/A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDo9tFHWXnMsoEreYWv5KIkoMjiNlIeKlJg8GRze2MSXAiEAnByneNtxL6rO6D24kKsqw9JoHUF7LR8DIKsOjrLLw/o="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.1-beta.0_1569555831034_0.08814725817717384"},"_hasShrinkwrap":false},"2.2.1":{"name":"react-auto-edit","version":"2.2.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"ac5e5b5acd2d30cfc8b210d673f22c60e88d6c58","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-kg1JmA2LzSygH8gLrGQOh+o/Fh6I6WhZGM9np7Kli4ERD5yKuUrenGmG/e3cHqZUWkhxSeXqxPdIPAIDXPX0Ig==","shasum":"9fcc749a6e32e2d1fe69af633c024b1f58e82321","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.1.tgz","fileCount":80,"unpackedSize":252567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdjY4TCRA9TVsSAnZWagAANnEP/idqNnzW9JS7ELbnhJz2\nW2YJSF/sa3/jmVQriabsZW+iUzieGRuOqpUS0400Yz488Rw0A2uXCEtTvgkE\nJUypjmfeEUkvGsTPMJaw+eJD3vDJD8pAAlpHUr6AeOD2pdy8XeCw42zFbwmX\nPmvR8bud5F5Tww5hZoqZAwuf7ujdvKyzLT1bVDDWepLi0B17VaNLqFSDP8Gu\nFyZZNr9cAs+QhZHY2S6LDP1gI/yV37gl7cpMiJSvl0yMWKB/V53Z2vcFIYbg\nHrISVdkmzLyxStuunb9uBZEXARkWV3QIB56a7bj1rqoFXqVIAmTZSuc8S4RM\n4TDn4Vmvut3f0jMCvEM4wzlWOXGNwPyDPzjvu7xAK9iSj8o+aRsaFqbTH5l1\nR3qmZu21kX+xM4fMiu9S2mwAdtGJeVEWxekGvn2nS/04ws8mlY8IoqaZaM9H\nUxODtInksS16rszUC32TBnz0TRVR5fCKiUNua8/XvuIf0ereWfnNx8ArRbnw\n2tlbu9teOk5BWPcWsgT6uSwO2EFQVYjDTBf7XO3epLDaRKP1xjcGdrBGp6eM\nukMUvme4fowAs6j/jv/pkDPeswe9nxYy6yjbcM+cjNX+9az7QRPi8VXeIMFD\nTSHKNGQH0nlmcHa/wTAUzDAL0FxVTLSEj7+DDH4Z0/KPvx8eZOelcg2Wr/MA\nF/f8\r\n=DdW9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDp1jHDSV+h3x8W6dkeX8bjaatKjLGs4F0VIdp9Cw7mwwIgHUHK2QcLyHoBrxII/g4i9xLenN0Mf0UxZaMIJHk97kQ="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.1_1569558034753_0.025352587006578275"},"_hasShrinkwrap":false},"2.2.1-beta.1":{"name":"react-auto-edit","version":"2.2.1-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"5482508b33a74027a5bd43806d8538bd96bbfeae","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.1-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-ejxBKK7qfDbCC4wDaPRRdKlvKTcqUsbRpRn1clB8JtJ9+foh/QKONzu8eLB6A/aTRLdhTn3L+Gst27uxSG8jYw==","shasum":"22a4c17886e2b0fed621d85eeeb3a7931fc08dde","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.1-beta.1.tgz","fileCount":80,"unpackedSize":252649,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdjZZdCRA9TVsSAnZWagAAbXkP/i1456bIVOxzNriyXodh\nkfGY45RVEVpugSbQ2zq2MZ8vblPqmiyP/SeQgC2uIBpNABZRJfWBznggwBM5\nQtvY30T93gebqlLtkmwbN1VL0kvqIAgRFAbPqpGlkh9DMHBru/ygHkqJYxs9\nWZJdz8aJR/rMezKocifAUOeRK4u/NS3C51YJNL5vK9MzPSytPnw1kyIsa25U\nQ9NI+5RxMGRRSjn9/sBMZcRQh7x1izQK34jP8LzPru+G31x3+Dbsnz2zNN+p\nJFAbuoY98oaIEZAx9NldICdpoAhvgy4+5DBrwwoiMQnLUsLRJEO2CVY/hUXt\nsz8ue3TMkDAXv+mcLD72Nr8i1YhGtXQNElWySERpbeplEA1lpfUPWgJcE7F+\n9klVu+eOfxrMuuPpZnLdtf8vkGbmWJUcAbUE+v5P98zJQK0CW5B55fnis6Fx\nvV9G6U2g2H3ClatlyUaAB1AFQOYgRftIhKDOxQlbJ0i1/7KSQAIBpZ469tj0\nH7UVBPOaZzGquYBSj4uDn4pZsUZU/KZMaMSAaLbX4LvSay8dpvE0fZjEvawG\nE/zmqvI9kd99bPAC8RmnksC5kFBtCOhxpllwsCLSxxdTLl8HjhdF4qhOroPB\nxKmyekg1CY6+om4KZkWkYVepVtqJ1/hOpvbClFPHb2xoSO4kwktfe4hSbAN+\nkTlT\r\n=IDSq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC/bIPVYN06XCTs0nklu52lqTESuUOa8XProMFgouYrWwIgWgtSdQLT5HusUEwUONOTZ6tfP8TGJ99r+Qr3zoKCZQk="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.1-beta.1_1569560156866_0.1635875226510699"},"_hasShrinkwrap":false},"2.2.2-beta.0":{"name":"react-auto-edit","version":"2.2.2-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1"},"dependencies":{"clone-deep":"^4.0.1","dayjs":"^1.8.15","mobx":"^5.9.0","mobx-react":"^5.4.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"3b53198acc331bfb1d479104d99de5a8bf0868d1","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.2-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-2PyJ8vLcUOwsxgLV8TxoUN1RySfC6eYA2B4JhTGn/WoqUcXI51wiCqVg9sUCwLJgHI6j7RTyYUMSlAxMByCb4Q==","shasum":"21b6cae773895ea4845bad2e2ad7abc25eea87cc","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.2-beta.0.tgz","fileCount":80,"unpackedSize":252649,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdkRZ/CRA9TVsSAnZWagAA4yYP/2tHlUWCd3g2GVEiRuRO\nAy3xErJ8xHzghf6DY7fP49VCZvvc9sS0p8dXC7VQusdbczH41VoRkm9Xces6\nPNiSTTq5lzrxlvvRrxZfQBVzRN8kJd7Jl2g3UptxzHrhNbFxeSiv44Q/AO9z\nHWm4DqkCTazApnC9Y3eV4ItHzDEafc/Dyu5Oml32/Dkm9GY8miGErQ4/NtT9\nRpzW6vNpuefQDQbtnn3QecPTvVpm2ND6mwkod/J0V7uEu34V8oyuErziCQt2\nqC8hIcEYfYjgJCD2PDOSkWFbVmm7jVVdYxM/Dcvyuxur9XnusFJvlIAwdJyJ\nhalOqfD2dOIQe1y1yarO0lPwmTOfifc+FBLHChQLHdCZ9dO+bkQUi9CHuH3D\nuP7IjrLJjoCWFKWboa5ijJ/FyCp4caSfTd/PqmbdwpOwezjw9aUgUmr2AKl7\nl2NxvsFT/bViKa9H4pc4UnE/VkT8UItyuJs2oRVhIle8+aB5kC2DpYDyoa7d\n+NmJsbNTil/um7e6aFwVj7FBBDeaSkRS6KtiBLJ3c0EMMzyUrga2URnr+rsu\n0qGMBSD/Drk7NKKnsntZKhDVJrWWDiDL3g6kC8D8BbWGhpQ4ITgGsP0ay4l/\ngay/TxLAjiLc5N/I0OZ7AdnXLZ8C8YWTX5sdzYMQ0IdD/DAKDMr8txouRRaS\nV3jx\r\n=1CU7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCMUPA24YNdqUz4vtZ0AqQQoT+DVBCG42Sphlm7bXizrQIgTXjxBtk16t6twv+1K5HOn09vaSPaREPu6M/42c30Bc4="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.2-beta.0_1569789566445_0.6379251693273973"},"_hasShrinkwrap":false},"2.2.1-beta.2":{"name":"react-auto-edit","version":"2.2.1-beta.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"617b9511852d79d11120387613ee5f651a9223e5","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.1-beta.2","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-68AWhpLQtVXRjHQNlHOLVXtrXPqpK5jPMniHfwkhU9SY7pKd18q2/7b+rnQMGjbB+/qc1XtxmrhLTo8+aTTNdQ==","shasum":"911cca2dd7312fce3af383a0118d78b2ba75c43e","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.1-beta.2.tgz","fileCount":81,"unpackedSize":255518,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdkVCyCRA9TVsSAnZWagAAANAP/1tIf9FWldrzjRWe9r0R\n/4YhxEecRuTca+Orm3F1CLOzNJbY+Am6pOh3S2kHsM3nBtnujUM5KNoaGijB\nwY3xRK/hp6sYXS/eVYO9xSQvhDAMfbTDJGsn96UzkSsgWiH+gajObaR9q+5C\nlDbaOk5fLx6rSzs6ymNV/N3MOUppLCDVCXNBGGusSuzTcc19HQkl2jRxIXxL\naDvVdfa1/Po7CqgxSZrqFRhZUIx1discJ6arKBvb5p84NbzR/zXnneNIhIJW\nYca/qrXJeexfhLNdZuzGXDkNHR6GPnKwyU6MOWnL7IT/P1P41ZZwweSfLR7u\n7cMpacbqvsDOIq+OmrDkQoUjLJpLCr+lMIGI3dAnlIPviXRQLqbD1pVprNQl\nEe392xUKan0fug2MKSRdUQF3MKWa+D9XLuw2wsPPyOeErZrcYwshRWF85Mu6\nEmkh+B6XYqeQieswX9H65fPsHfTKNjyz5jvDsoLQEy93jj0DXPSSOvFCn/+/\nFlXRFkvpYblnjDE/ryKgGL9HrXVTudzXRA1uOyUvVS1FzwOzp9Zm9E/2hrKi\nqsu2GlqSisIfsIMLBESSQwd3L6qR/XcD6o6qrF95r9roFOByOWG9v4MDQpnj\nEZsNmnzjZzgFL6eWzACxv26oaWrQjsLJFuWVwCFcYQkfROA+oHeffObXB1VK\nwMsN\r\n=UjIi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDymLrr2p5r4VF+0jpik37SZ/iuweeRbGbMKUAxiy62mgIgcCkfJhe7zuDZknRF5hoRZo+bLll/Qa9dRUIHXwGQxTM="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.1-beta.2_1569804465855_0.9659750186149934"},"_hasShrinkwrap":false},"2.2.3-beta.0":{"name":"react-auto-edit","version":"2.2.3-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"386ab67344fd49a317b0fc739d96a81a741c0a3c","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.3-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-50NTchP443A4NLve9xfhJoi7YPBP46dldLBpuNM1U17dqQLfhQeqSD2h/QAAXpACav4THZImH7pHAO4PkNtetg==","shasum":"6870a7359d3553563711e9e9f82baeb4dbb958e7","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.3-beta.0.tgz","fileCount":81,"unpackedSize":255518,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdkVQfCRA9TVsSAnZWagAA+kIP/2YoRvm/vDpXug9xPfE5\nTxRHKp4g25+ECQZGdiU55xgu2Z0HxT7/UgegFd7i4OeqF8JKyWCE09XeHMsX\nDrPX2GPp4QDFly9KweV8yYiN4nkq9yVRyrvCIChwmXtbk47FI0ojrZBCZshQ\nrReZragZZ6hisc2kFMwARpHoO0w1PcHVp6D1iouuXCWjI1nJsCrQmm7Ujqoz\nha4eNgVstXaWWspShHQQLvYBESq6pBMwiNA+WC4oiSrDDoKvPvCTQ12n9Bxf\niVLUDMX/f+x6grH6fOhvDljqdSNK9YecHTF1K+UF94DtSWYCcD3CmGDgr4Nu\ncFKY3o3vdtPHh0P0bW+1MgwOEToULw8RSNaxr/tMJzYeuGnphG0aR2P4PXgf\nxBRT76rpYnqXqNMGV4lyPjPpufd/+puofLgmHeOIaADRL/ih/7O0rxsrGFsw\n5jHpp3Xj/oLxmD7aCmc4hH+soBhJQQpDCPIj+WeHfaVbAPci3xChpPgvF4M9\nGC0B/pFDVlb4wMnpp/nnxYJvxDmGsQHMasQaZPXNLLQTKXK8qttMEn5Qu0/B\nMfO9jT2itEH8sdyCrTEHPd9ika9KD7sgEAlQ68krZJR5form7o/51B5WOgBm\nGwI3/9SBlUceXBRlcBW7h5wQA+gQ4aaqTwYQ+WLd4qNM0As+Dx9A6U5u2o/7\n0rjq\r\n=xlJC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC9WakLfUyL5C5tL0aN17ZjjJlyIQ688P6LnYEEsdmKvQIgexs+0H18DmQTQR2vLbucyNfMhvmuKLoGJJO9Va19Aww="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.3-beta.0_1569805343079_0.08644717616939768"},"_hasShrinkwrap":false},"2.2.3":{"name":"react-auto-edit","version":"2.2.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","scripts":{"test":"jest","build":"babel src -d lib","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"0153ea4089da30805e7834b3db2c71b56fd311ab","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.3","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-MKRQREooCqcCGlEX/nA4TBMDeRm+n1AfgiKGIMSkM75ErhYHpEX8XlL2QGmvEmuxylZBhxJYr8LA973WAemnEQ==","shasum":"21216ca6c1c7ae00aa3f46e6025dafa489f4935c","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.3.tgz","fileCount":81,"unpackedSize":255511,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdkVVNCRA9TVsSAnZWagAAQCwP+gJnD2H9y3jU6sxSqsdz\nWyMymccYVjjcsTxZ7iOeFuT81zOdL3ZTBYSHvKOk7hzAVfraSzC+s6k2Hp2H\nIVnl8Vz3/M2DutPQMh7rkqs//NT1TlR7eGwyzYNcHfUD/xyJn9CUbqxG4rzd\nYsdIh19K1B24MZxwtcp8NTCniTTPzspwUUoBgKxjYs6ehbRETe3F0+QJTHFX\nSlZH2p//2awiVsMZWbv3P+0kgWl6zeuhOdJnX5WOYEeoQOa3+JWbIPwCGbHq\noz1bGD+OpbJo9HwpKRY6ucytJXFUz8dcCRl/oYQx+1dNiQcJ7UsfmdTMeX2G\nCZIvno89RhyHqee/HwrAC4JZI/26X2iI5vDtm/GktohtzpUNMNP1Ju9SdYCT\nUmmLR5Zlb+NDyTveOTLb/dUnVT/V3w6PYgrJfp3wUdXewwivF/v9EZPnYVvS\nND7hWH5T4YZOgvOic2AB4DJ53jQBIOguh90rJorUb44niUuqtqrwXW8fTvzI\nEz/hgBnU0fdVmnnbL0fs0AH0bRsNvlwjWpH7oHqePo63si3Pvtwp/pkyDgLE\nsLcASHVxokvm7XsRqpiDi1rqCMD0bM5yE8E7eRvm+BDidDvDjrWXh0X0CHmQ\ngi6m9avZmrlXgxhj6HcdstPpDHFfSfGkAY5XEXPmXy2x4qiyFmSW5Dc4qZOI\nvoCf\r\n=wOOF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDrdZ/LlfQhScRb9MBcrk56w4Dr1EWIEEAL34a9Vc7K2AiB0ZN3j2Fl7Yg7Ow3p6x/Ri1SymTe1xAYnNZ/mnEwpbQQ=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.3_1569805644565_0.22436359187648458"},"_hasShrinkwrap":false},"2.2.4-beta.0":{"name":"react-auto-edit","version":"2.2.4-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"7711af365e84c7fffd3d6303cea0bc5ebfea5822","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.4-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-hLhcrZG6UtMcAadTtYEJvvzY45XxQVjEc3/AX1AAXRXkDsGFp9+WK1n3a+z5PXajoOdUIAFvnRKfIo0OeIdNIA==","shasum":"be7102a1eefcf848bae46b4b4eacbc99fa4e1b5f","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.4-beta.0.tgz","fileCount":114,"unpackedSize":279423,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlnTACRA9TVsSAnZWagAAYe8P+QA0b/Azk9ahys7Hyjgk\nEBtFqrnYMCa89enCdqoFZ6tbbr0UowNEEL/NmMxEMjtlhO1E35Xold26D4aB\nqYO7/r6inT4jaPXF9R4ff+EQd9IJtyBdrHR2n8p8Gzm6j1djGCxQ6rYXLdF0\nBy2yfG9aFLl2Vybbvmqds+tdeI2zDorIj7mMUOVFlvaVQ/aM14iedYOY4UcL\noxEV8IjrFsTVEiO16TQFan3hEj6uuVsExLSG2R3L0DLPkqunHss6JS92gjQH\nN4ZVqfoekfee/kJqypLYPxebXSxynl2g5yy2zQHmLZvs4v+4P/279ky4YGDe\nABtFKPIe97XllAyRgOk+Sf9nUDsseuy9cGl7bLbWDKPYQKTWiCE2pQ4PHPK6\nWiBDEK+gqDkVLU9LPbKGoI3IAER50Edqz1ycqq2wmBBfVCB/h7bdBrRBkWnd\nw8nj4ZIYvYinw4NFOZH3O9aSo8yXSK/O2mkUOcJp8/MjtLCBeLkKIu5kBUJr\niQfuXbkeAEUJT1zcQ0NjjeMQEKlgWHY0TRNlJ2V1zHo4xcw4YiuNeeXzfwmw\n9GAUui0+5JEGwDvJ2tzgnZhZXAvCnjSXiIStOyImw4o7ZTRaXHBFPjHKjEzM\nuU5zZ+jA61AaZ4teY7hYix01x5tvrBJnbCqcyaHr7+AabXBJKvJ13vjuKnJV\nvNMR\r\n=oRVo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHxIkU3joFadef1qUxMj+rR14RCeZIoXfUUt0B7cuumMAiBLHdaEcVbrtyre+w71WiBOemq6l21N/ZurCQflblp8Hg=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.4-beta.0_1570141376224_0.9314870452731401"},"_hasShrinkwrap":false},"2.2.4-beta.1":{"name":"react-auto-edit","version":"2.2.4-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"4242e30dbb27df325e481459b68e0b0f88a6c490","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.4-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-XQn7DMpuCDE8eFFMzPv/aZCPFZWiI+26Vd4qUiw3KVMg21QnhSQ+7+7evalr8xrGYY/Ap1kr432ECvdRTb4wqA==","shasum":"16b8f207b4a8904ce6ff9c8c6eb6d1295579703e","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.4-beta.1.tgz","fileCount":114,"unpackedSize":279922,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlnxFCRA9TVsSAnZWagAAPwoQAJ98UbkPFzyETDoZJzJT\nrVW6EnoggwRwVbYHPttdf3mR6KrTvlb9LOlaceaJZ6i645q5oRRUyswcxAjI\n3/ur1cPmcPyLAEZY1ijWtUtUqecEwY3UfjuZpnRo2xQYdSMpLwHlUfLZRy1s\nTHyCmFYQj+2cscpw4blQrafUVKL6rvwAd54hn5/okoMlNUcWAGXiopTzMQyu\n/P89gRfmRpwLoclNX4reOxd7Nw7DNWyxQigohxkF9XFwWCzffxKNoHZMgzwR\nL4PhOr0x62tDfuOrsmf/M5vE0XyW2XkUuUdvL0YvCr0c4ekEY/kU6kHhx5Rj\nXImUBjNgraOyzhHlM1jJqEj/lOSdr7UhDW1bmDsuKT1qKNoQyO2Wv/7Bm7cC\nOiECtL3Fct4BmLHCWdylN1utwbZ5WDHZUZ6GXJIA8AlKowpowmm5M9pPj0kf\ngFhmh/6JEtDnv7SPQPT/orUHTqHUOSWk0Qa4/fLhSzKl5ZkY9n44bWygvgQo\nY38n7MKfwsp5ebduITmCMlUyH6XQGo7R9VvNw0Sg+ZhcO2HvhEAUpENhoIKf\n4Uk3JZ5zhsdw/g5svhA6tJX0FYRINqRDQOLYiJSaXNrIj3WX8jKUJYBRSXka\nPDZDn0a8YScT06JWk7iXp0OeQn2/kZsxYBPsmIF9j4+cU46jbViEV86qnt8P\nZvK6\r\n=rP4I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCE/hCD6lskhNIqsXhn2cLBr1sbsShv9nyZqMSp2zi4fAIhAPEuAgshyF8mpXpnUeG62QOeqsEDFIrylaQ7Uxt2k1Ot"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.4-beta.1_1570143300449_0.07519059724466737"},"_hasShrinkwrap":false},"2.2.4-beta.2":{"name":"react-auto-edit","version":"2.2.4-beta.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"670e6123b7542ee6a99c2bd648be18045a56d025","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.4-beta.2","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-x0TzTOy+LJayrPHU30WdCk/8JqQKLFXtQxfGz6Jn/b4VZM4NjEmS9aUsjBCPYT3ZiSykon3SnefBycnXC/KXfw==","shasum":"0e38a450f984d64770c4f6ff8877834a7a0e4ec0","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.4-beta.2.tgz","fileCount":114,"unpackedSize":283227,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdloDACRA9TVsSAnZWagAAB4kQAKNyqIuWzg5Y42BdFBYd\nYsVIqXPE+H3RIjvKzPEJb/dfhXsG9WBXtZOfQs0ec6I8jlI4ULSOzRtuhOVB\nv1JFkMe9rKTKRDz/mgZOzk4ylLT9+HwjQFgAPAAd8mQ5KCfScctu0HeogP1V\ntYOZYVXhFcM0z0nPDTsA2IMCVxUubT2rGh8Kd67fBRmy5SCZlvS5uXyDm0v9\nhZjHFCGXRIPRNABctIKCunTONBoAq1P/dDoM2cmuZwqfquF3zu4WkUsZFCHh\n9ptED0I+/LU4qYUsAxg2gNUHhG9UlDLRiVh4NkkS6mYNf4nBxYdSgTg7o+g2\nzJeuRAlmL8sYwyntCWnfhEu9toQ7jnQNzaSaVrkNB0q8e1nRM4/kB4ZtsON0\nLcCaeBuR2TLa9dwzhmqvyYw4aVzKepNNvO1UXCoO/COEKM+SEqyyL7r1SnzA\nvFi4GbylVpyxJIXe3hlaINoIpT9Tlo/Dm06+83lVAL8urzATTcWc+j3wkHvt\nwliK9xOlgjg8Ka2puf3O+NS6RVoRXBhEgqVY5Z/P6gpliRnZy4ah5MU53NGU\nyfgxrbSy6yegChaUOFSLytpqRYgaxu3gqUoBTnApBjBZjQIFy3j4ibLmnXIh\nd5vsxvZe+GfBLbex8iDGNpqAVkX06O7fbY9b13nN0YHMt1kmtjBASJGKpiIs\nj2rD\r\n=tMv/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDyKET1PJLOef8CYopDK8mK4EVUcembBipB5gsuZc0eAgIgVL5x0WIe3Plkzt3XVzo4oc/MnfmEz2YHXCyh6zKAYCY="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.4-beta.2_1570144448098_0.07149265196449539"},"_hasShrinkwrap":false},"2.2.4-beta.3":{"name":"react-auto-edit","version":"2.2.4-beta.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"f6ca499e0c4cc9401708db365602d6db2b2603a8","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.4-beta.3","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-vaDhpE1uId2sEvVkZIqtZ6jjNcx0Qvnt3sMosPeE2nF1TurgLh3VkVP448+SWAYTEI99msdbZ95jrLlmOSiFfQ==","shasum":"702331a3f19750032209dea2a290cb957418ce19","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.4-beta.3.tgz","fileCount":114,"unpackedSize":283832,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdloODCRA9TVsSAnZWagAA/90P/R5YlZbABqT2UdihHO4M\ncQU+IcvrI/W1T/bv6bi5c3+G8lk7p6j6q706EvVlXi2F6aDmGGobsNb4Kw8N\nWkXfDQFz5S3jDFjTxfiCLy5XpKm+48XepPqV7/Q5GXCzdsc/JEOomhuAQwBq\nPBgPkxG908yksIOPfi8NJKTCSTEA7dpD1cITaCh3RqZUwrUc3sr3/wOprkTf\ndcApKvSaKuBrxQbi8+9pIk/69VB3VJMyuiYnqUc10pX1SF8MCi1aOblble7p\nMsbpWTh2GZZqNvBPfRIyWhMKJbVixWkwT2tCCt9CNEF176YfO0co+2UU0PkA\nWi1UJvFKobHfAZ0eiY52I8eV7jXXzxRd0a3Q1FMG12v3j1w5yRKpEn6gqpUD\nV7/2ZsB0rmfiKnHuXrROUo885y24T9sO1KGhDMuXWs7Kg5uHlRE0tm2Av5eT\nT4a6PQujB8HEwUnZp3b+Bg4L/jOlCNxhrnta7x/rZ1F6UtietgK7dj7pbMr6\nxXwAC8WbjavyM585JCWqm2tNSVl3YhJVN7sL3HGRquWY2F00AEaimdc2ZmMw\nzoLPgo7A+GmwxL2oC50euK7BIDyBznfF5Y/D2wcFa/CwSzMflxFfjTp/kpc5\nxXs8du11JfV1i0NYJ2Dq3ZqJH3PWZ4S0PN7HMfub1YYeff8++ph2b2wM4pu5\nrNAf\r\n=6OrZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCVxwjaQbIrhZcW8mR7QqVRP6vinLaADHdJAMqMZEH7VQIhAM4/zbZQfarAz4i0hWjOqX1cFXTCvnR/LIZ9bH91KzsE"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.4-beta.3_1570145154534_0.0773331061348792"},"_hasShrinkwrap":false},"2.2.4-beta.4":{"name":"react-auto-edit","version":"2.2.4-beta.4","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"039be82e0ca0ceedfac09ae4d737e17e581d44db","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.4-beta.4","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-ZhdfIvUTzEn4ocNmuA+Sa1DdZc+rP3bRls5exhALBHmxF7CUGdxfYOavCTai+nlUJM12MX+xy782Ukl38HvTtA==","shasum":"a4c418d1d21ed2a0fd0f797a8c64dfa23ce34006","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.4-beta.4.tgz","fileCount":114,"unpackedSize":283875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlpVpCRA9TVsSAnZWagAAbccQAJtan/KmJB2QwnGn7dsp\ng6wYQp/uJC0hD+jaH28S/fPRVilWOAwLR/AaUs4ACWj+UtZWq4axrlGTw6qN\nU8bWq6G3OODibAdV2q75MOpB5zqoieE/Dx3AkLlv7Iu5If7o6bZXGGVNmVzi\ni/7hvLo4W7bMsgONRDB4ZmP5liDqbcjKskVy5mihoxu7HhFoJaNtxNhEiOiF\nlrHMhwY4m1ilm7R66Tzt2TN7Wz+hXjVoiWV8DVf0+H26LorqiFg+o7VbNTa5\nBy37UoyIYxKsCAf2LHxEexxXlCIxtfxeXzYb9j6wkPpIZVoc/9cYQq4b6D5e\n4nUpCDK2MfmW/ZxZWPBIyoKyq7krq6+VFqHprgCfJngvUIorHpI1ShXx97rY\nsWcDIz7mqiNnt8JBBksZaYeSRxIEqg1quPw8CJ2i4rLz8hSHp8EGOK2Z3J1Z\n1rj89SnYMdHI183Tlrzq0j/WaaDCFtZOHMO2DCCQM3VLnLzG2BV0nkg4iI8o\nZnywExZ8oozhyxXEXHb1Fo65s1RC5lChrseC/GvIBM2EfXpjpvJLimuOm4IZ\n4KU/018JOLRYhv+a6SzHTZ+9CbwsMN+3jj+xOcpY+jNOC+5SOvaji4koCs72\ndLUje9tGZp+KFH7sWeKsMscaaZ/wicDcp8Zm57tBauvrZK0tspc4LjGKdWDv\nxl+g\r\n=yJW8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBIIwVySRHzq5H0lo5r9Jp/jfF5qGd8TcOQcqM9oCvxtAiEAubIL8N8r0r1YDeDy6mPT7YxYHm4CFeB21/y8d5CfigE="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.4-beta.4_1570149736342_0.9805339441466712"},"_hasShrinkwrap":false},"2.2.4-beta.5":{"name":"react-auto-edit","version":"2.2.4-beta.5","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"7eb351908f75e034630a5d6aa6052805e1587839","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.4-beta.5","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-ak5xVT9zlbk0sWEnV7cRpmT0iTM97YhjKKmGHj6Jxbh9alwJqFosxaX0kmfK2Zz/IoA2gmwcuqaJHepuRMIqCQ==","shasum":"fb2e8d84c89340871af621395e1260f0d6fac189","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.4-beta.5.tgz","fileCount":114,"unpackedSize":283753,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlqdWCRA9TVsSAnZWagAA+BUP/1gHO4nwzK8ERg2Lldoz\nCcv/QCs03thlpn4ABpGC5pR08j2AxbOv0sBy2XjZHE64Lvexznetncy+zaC9\nAkJyexR/fAfcijwVZR/4w1sxUNTEB10s3j6y0DiUKbUAxWiPt94Rxw/t07Ft\nnZGOOzZwOotwYLD0IKD6pCiHcrKiy9BrojN+EQchOGWQ2aCBsgUseaMgLPFf\nzYUanwPf0fqo1JomLY9OzSLPwUiYE1CmbzsCMojwuHjsNZ4+LuOKpfWWVPU8\n6/OFpAqxS8Qm9gu2qKMhpoqjv1tG+oMzk/Qpdx79wN7HUVbJt6xdjJelHxzG\nZNbx72bKHCV4bnN9K63nk/aUuoFu4zXgzj1S4m/F9m5RhEnFNVsQBqcNd7hz\na8dVduLHa+xIjevomw7XptLS5iUyubEg8iPsKx+D8m5PqtVD56GbJ5pHUsYh\n0jVdN4QaoeFJZg794dL2K3fvzStM0VgJrckqgHJu9dxBhZkxlV3yxlVzifar\nMN4HF1xaz85JcX+CrnCmXaIPohrCvoQ9Gw8UDW4VHKkl9A6kNxRZHsLH+qWW\nDOYRxdkXC3FgtgZUh5QgkglaHERzbqrTDpp1zwKVxHKUD9sqguVtNMakUdTP\nyU3xPByFHsyA3tkUHfJEbqUjjdwjT4TGMN18QVT0/elSANU7rk2zqgOWwLmg\nFeeD\r\n=JI3T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID5e3Ihq2JWZIjInDIUdHvARz7spdZX4qRUZd5NEU0JfAiEAtIbBx7actjKO5AtdRLyJkvgVmD5jWWv556aaO1uHP70="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.4-beta.5_1570154325735_0.32813651608624994"},"_hasShrinkwrap":false},"2.2.5-beta.0":{"name":"react-auto-edit","version":"2.2.5-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"33bfba084db5bc8bcd65e50e6443fd23aff026e9","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.5-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-nGbSSlVfUEW9iK1I/oskhpYbsCxfM2T7MEcLXpgbl9ven0xRAH/ppIV2O8TdQ2G3p0N3AlJaPvQNpLaqkYNuKA==","shasum":"d3312ef0646cdff92807f50214d880008afd8cfa","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.5-beta.0.tgz","fileCount":114,"unpackedSize":283753,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlqkJCRA9TVsSAnZWagAAoQcP/1SQb+QM+03jJClcAEX5\n8ugk+/ALZpjfCE3Ag19QiyhjS/iYd1xYtjpg5QYwacx2Tny/+LrZnSb2vJho\nFztec8DHgdh0EEwp6HC/fxrhbnYR1OtxN6yHxuOn2sh2hPnZzM4ORvimFsNN\nm5qPHWBZTqPWzrleyAmVfk220wCMEU0lQ0N9bbUPZe0/5N6skin5Gm+zSqdy\nXlZCNoWFc0Y7JpstS4PF3qZYDorbanRhb5SJ7sEbmZGbtA9NGy1Y/ceAN2Bi\nEcU+NOAkfCKQ0RqpXkgJbBbRNZIcOIcNHZdHb33I2O0vRGOkvEGpCDDnzCpN\n9TrvZ55xD9jlaW8TlCiJspRIdkb36xMN/6RyE0EzPSGegBoRR4b8rSWQzS/g\nFOhY4lc5QfWeWaLVvU3SyrLxpOu8iVtnfbShjXN1jEeyxuqVU0a4xgsRb8qC\nU8nHUHr+bKRCyRhtk2AGrmi1uprsBdKGxT1u/e9mkLsHTosTNtSYh2576Hid\ny5myH1qh4d9WlAoovIQhLy46ySGzmIjPW0sqZd/iQ+DIYRh0o60EYw7Umc9N\n4D4oa98VxzkLTlnza1BpxnQqKxeLQaKApd4NcRl3e7Wd0F1Y44X1IJF6HPjm\n0iZZZaozoDoV+pg8xScnZEV/FxlpGgIxKfmc49KY7tg1npcVFazcA9xYzRpE\nn/RD\r\n=iT9D\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC2B4v22lx+i2QmZtjvtKNb9rxgLrqzWNtukSK1X6/iwgIhAPzEkN4dgocRIDXZzB1OfwhmOqi9+EVuQxZkRlKBPlOW"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.5-beta.0_1570154760237_0.9541046958908437"},"_hasShrinkwrap":false},"2.2.5":{"name":"react-auto-edit","version":"2.2.5","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"2913f46e90f2e0995ac886c263535bbca6c13c4a","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.5","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-8NvjLSZGhUuaM7TRZ5gfyPQbJhYf4nqChkC36URbRB6hVRhfUHtGHM6LsQhnVjTrFY7X7OlZKB3lVChmRnW5Zw==","shasum":"abcd68b02b506e7e6063342f4b7290d440977fb7","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.5.tgz","fileCount":114,"unpackedSize":283746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlrXoCRA9TVsSAnZWagAAJnUP/AoVjL2caBgRBbJTGgQZ\nZ+st49HgvqlgRiS35qTPes9UYG1kV4YY6JCNdqithjKLZP3xax+5RTg2c/XC\nXuGjmyDEuWnDi/Th1CI9fho9IhZmY2AqqddybwZ/gq2WUp7CiG54AlkFApGP\npPsAw3pe5kiTQ/PErTLJmcLXnCOLt2T2CdFj1TVzn59kcBJgVV9QluYBMZI5\n5sUHGsX2NlS52ALJ/H2TR/IGdsLgkLf1sElCVyOib3YpOGesEZRMhqWaRdIN\nQXiVV9Gadbo+h//wOleMi6YbOE0zoNsehiOKL0gggV7sJQRuYsEwA/9gN2r5\niwCck/Z5Ijaw2480Iy3e1c1GCMccQATeE6N2N7vxCRA5CneYwap6xa2PRSO1\n5TDWulgFpR5l3eo1mHhYPkBqIN2MC4cPlBIn+MohWsJeFNWR2fIaeYm1Qxcs\nMIb2s2eWoyKGxjmlO5WFxQpiZ1bV795RLB0SxINFViuvCOnJUr0qsVOuXfDO\nw+HNwgk/bbmgHE3QewD0ocgHUzX9wnTyuMigIMII8N4rzICWGfVbnY5UPEmR\nLl+EkhSR+UUG2mJtRicUY9scUKPEsqGOhfQ5K7ceZGdE79WRj785/0JXGn/c\nbWC0B13XG9F+F6qXyIKRe/LHKXTWi9pmb5klZWVpoLhssZybO4QokKmetyrY\nMP/Q\r\n=Ui+k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDVU4pv2DbFg7cKMjUzY8FX3fzPTtCFG3UYWn/lXg3aMAiEA6nPp08uRtdlvaAB/2VpMzqwTdsY5N7GYBE4oDa0YAZI="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.5_1570158055896_0.6628005510636328"},"_hasShrinkwrap":false},"2.2.6-beta.0":{"name":"react-auto-edit","version":"2.2.6-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"9984e3b0bced7e7330faac6ce424ef34fc14d412","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.6-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-WHLijQ5elc4REHiKkVIg8lSR9QUoz4dD4kCWuw0urkHjBrR2oFkiw8GDxevZW5GI5uFkOQGTpy1Vjt8Q3x5b5g==","shasum":"d88ebfe2b052c345f2043441e253e4266769ab3b","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.6-beta.0.tgz","fileCount":114,"unpackedSize":287675,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlsESCRA9TVsSAnZWagAA3f8P/jXQQNkI4w1Y2nUehiM2\nf8PO8CpcOdq7ZMrgQetT160Df6jHXMei0GN18n2hiAUkL2qJMHBNC+Na/ReX\nICtIPoS8xN1O4Y142gzpcXL2B/HvvRn4MnQDMjlG9CtO5pzhGZMV5H7DXZWl\nT5zQyVHAUr56y8UNVzSdOBeUgnkqwCrrQRme4M7qJtnFvTDQb2kd2BM2IqBP\nIGzJxfBCIRPX13QUvMPh2KuDrN1uoc/JTUdIrO9SJzQvWSY4yLzjcF8OHKuE\nlyVwO/Lf8XV6gtW+q+uSlpSaE1qlnMPVF09SdTvqEqj2yodWYNzVCvh2dHcz\nAXDDqeWWOH8BoXaMF+0ESqKuq3oze00IKGtuPWYUQ9kP/5kry/d/aAia1wxK\nSnK8PmF8ZWYHbQEvPBvBFLTPIPYaJqGAjIqStvWZYyPrGZFEtB22s4hxhtwZ\ns8IEV2lEimnnkO3PebYU4GpsSFzaDbmilrSE0PWgRLsOI+eqwWv2hY3YQZNY\nCWFL78AWP9KuZJoxUNUtBU2y9YFHyPMQyr3X+0emaf3v/RAbmZhBU1+m6lsq\nE1xtvTa/lf+LHxUDHa0opHRWCun82PQ/1vBjD/Hg5zl4OrJVuH8WyVqR/q5s\nwlbjQ/UUh27ge0L5WyPxBuIs2XxV6t3b/kAeXhXyXlK+Qy8avrXwsYZhPCZS\nmAQ7\r\n=8dX9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChaBvSkZwhUyI+cHyWyKhPo9z4yc2eBsfiYcFSSyCwiwIgZJuo8NSupn/G95IMh0wsAYPkoSzJupLJWRKcVWVA8W0="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.6-beta.0_1570160914197_0.6874738993931706"},"_hasShrinkwrap":false},"2.2.7-beta.0":{"name":"react-auto-edit","version":"2.2.7-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"ee15be58b3b76ca28cb81670600d4b30a713fde6","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.7-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-TbkWD6rX/4NNrwscbm0o8lgyKD9nDZdJ3Pn3UetCpX3U7hDi6pPyD+eSnmL85l8uCwR0kSQf+hrd2c2cT2y4wA==","shasum":"b10958f62a0c73c96c74f8ddbd2b51328326d6cf","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.7-beta.0.tgz","fileCount":114,"unpackedSize":284588,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnQPwCRA9TVsSAnZWagAA7vgP/RQAgByiA+CCwyud3X46\n5hkDDLjRrbBK4GK/hwZIVIndqotGjx54tvvA5cAaRWQ4Yx21K/bmthgQxAMo\nJGMokOR8gxET2f7TCwCtwqO0VNPmZqoQLr/5jnN82odI6N/xUlqzlyLoYlef\n1XtymU5KYD7tJi0YAbqONK03GlMmgHpnhS8jLG6QxQmVFjsASiknzfSP++N/\nO8iKEJfk/0lHzOL82tFhfoicFeDLeBk8k3pTVqMPEKk7sVTMrpRWjnjVvVls\nYJlNpr0n9SQjgofTtdCl5POxBy+nzztHS5+wZnsDmhhu3qKjGyOQOq2IiVUl\nd9bMT1yKi1Ole7bc7fzkxgv2oinfBK1qusax6ODUVr+43OSb1Wc/D702whnN\nEzQy7mpFmvz/hIg1AsZTIMnOgqmFgLozdjekmNfphVFxYT57g+5ldLd+gDv8\nRhwlneum3M2rQi80L2yM93WcjnDgWjRaR6dQrNVY6iTTGaNEe83SuCB1+i9Z\nbNfbRcJd2heyfYEFxUHKt90EakqDBa44rUNVJwfI6sk/6i3j3dmC36MFyo01\n4YZ4bjlku60FnGKpifsrihHd8aGLcCoauA7/+61vbwCjW49P7r34W2WAQdy1\nWV7sl+qoh1w7iPylt+nfV1yC05lggG9kUvjoUdE2/QfvcRTYXMRGz0OotrSY\nNczX\r\n=YCRB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICerdBRmg/d4EXYlKPQW7T5oGkPgy2/WG2xTAsUjO8tIAiAujKRb2AQqJ4VabeN7mpgRrhNy/9JfgK0Rpj/OEfSLgw=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.7-beta.0_1570571247227_0.8914076369523485"},"_hasShrinkwrap":false},"2.2.7-beta.1":{"name":"react-auto-edit","version":"2.2.7-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"583ac4535fe4b19195af48cfab107ac62d51ee6c","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.7-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-jTvdJEO6IMKzeJRmNtGBQUlrVyQSIL6Facxf2TttqHRNtKSmfRjSx5da2kX5MMNH6vyHK0OLeUrfd1ZuVfnhdg==","shasum":"8ccd5de20a30a93a9e6a8c91edca5426e7eb8cbb","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.7-beta.1.tgz","fileCount":114,"unpackedSize":284907,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnRIWCRA9TVsSAnZWagAAXCYQAKRzEEtDHJt/wxbsDUtk\nLJg1Hu7ImgRqnaOiwU0fkdM0Ssysjv3tg46k54cmzMfnoDcK2jxuiqSh8Qaw\nkZvOINWIY1NKtPrQQ2rdc1W4cS3UgxyC6HXnPjP1HgL5dHjsk1pz2JGxArjM\n0JuNMFvjlZ8ZMaLraA6g++3N6C+MLeBZc2O/mbbfNRB3+79CwOEAD3fPCQG5\na69iSE+Ee6icam5G2tIgJNFoUWmCg2l/Id/A9iQAXz2XNjld2rRgXl0/EuWY\nEENIPyqlFN2lkM8u9TDc0Y0POWhHYlZ6R8LmZk+oPn+tZ7/pDnflZ+gbNc1V\nUs8BzkI5qEW135GxRR3pQZmu9qVgnR2WIXcRvin+ZsbJbOn+Uz9GDw0EidIM\nO+4iLboWffCeH4ghYz2L26IoDY51TuVBiEeeeSGQG4nu0nf4EmFh475ZiGSs\n6pMWu1D/n0h1h857akO4YFl9BhJxpTMVFRb2vC5laOR+zJIdSGOX2u93lIfD\nGLPAJ8w846JQDY+uMOHPlbVWOzOKpKNqs9YF2rMESd1I7QvutcBwPzABwUx8\nnude/8UEJAwNLFK32FxpjTuQFLRaImKyUzrAE3Q1PTyLmN3UVfipQdsOZGNW\nP+uN1jkN+KFV1njt51lYJflV7mRHY7CbdSmDOTO7GQpFLeXSe5uUMyCe2vhr\nj8YL\r\n=+WNq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDn5Nl4IEY9DLboyKnltnReir3aObZgQRklvE6CE7DjIwIgNYkEn0UBx0WzWqfe77sMafvIyixm34PTsu+RK1ymy0k="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.7-beta.1_1570574869856_0.7736871400108247"},"_hasShrinkwrap":false},"2.2.7-beta.2":{"name":"react-auto-edit","version":"2.2.7-beta.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"67031534d941d2920d396e88ffb30d7512297565","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.7-beta.2","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-pypK7Wj1t2y+qFeOquQU2d5ME2BpIoXVx3EFTKKmsXhHWuHAtZRvqvRu/Lrj6R4qHbv0AjsTU1XP/RTZbGMgKg==","shasum":"0b3ccfc1d358db5b91a16756e01f3c57bc8b0fae","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.7-beta.2.tgz","fileCount":114,"unpackedSize":285109,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnS8rCRA9TVsSAnZWagAAP/0P/2ce/W4UoUOQjj1tf73S\n4PFQ+fymuuLn+yo+nHjNqeDyxIxy7Dfz38HV3fThmS4WCZSGriQOoJFAmQpk\nq/axGR4OK28o01dzgur4G6AAqBezF8BXaaWuDNRnW44gelhCM/xTGTi+GJyi\nBHvKdWFKDTn9JUaBlGkjzKIy4aSHgbpaIhDjCsSa99H9ybzEH9oy0o3dB2dB\nPzeqTTulujdZCbIs6Pmri7cksmK06PSQD3WnJgURRabqzqhv2A31qTgszKbP\nSY8LMqG0H0LnrbDPdRG4olVFPAsdhzCnyyfnO+x83s8SYipts+Es7n3wZ9y4\nZQ7vq9V7jZgu2bNFb85JO8JnwCM7UCqPhGsMNTdmXmvaNyv0E0Wf7poyz32Z\n6BWXcYZY0GiL9OSdhxdedp48NvY0KkvmEEx+iwYyKjAGMxUbHS4EjePQIkQq\nqkVeJS0tMFtwG7U6Gr6sCAiSo0G1l7m+Zv6P/oNcTs2xi7YJDp58UQ0hKezl\nENEcjdaofp2a2oRK8g0mwWEf70v5/9hPPMa2cQb6354scC/d8HzGRHbxP46y\nak7UDUR0BXAoNjis2QGFUNGAddKQO4XPcNzYrQuO4R0ycE6mkRSJFYduiCcD\nwACTjhD4dl8ulBwzchvAJlMwp1qUe/GwZq4TOLh2I5yBYoVqPCqpGL16NcXb\nuGsB\r\n=3U5y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDwlazhw68eV6u+K4eCGmOFKfeDhQ4AiryBKA29ZbmvtAIhAKBHHvsghBqpTUlb5nh03dNFb8ASNneGBsR7xbBDD+B0"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.7-beta.2_1570582314930_0.6481965709832249"},"_hasShrinkwrap":false},"2.2.7-beta.3":{"name":"react-auto-edit","version":"2.2.7-beta.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"7e8ee17e2f5d2262e5218ed129b77dae13d08f54","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.7-beta.3","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-PpQsPjmbfVUVwk/snWbzLSPbhua9pJevrtwM1kGh2JFJDBqBqoCpI3/W+o1HH69LV5S3k0G0h6HGFHpY9cTvJg==","shasum":"150b401dec4dd99fb76693e0077d4f38c116b190","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.7-beta.3.tgz","fileCount":114,"unpackedSize":285109,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnS+YCRA9TVsSAnZWagAAKRwP+wTBF4Z9uOvI8vYbhtCV\nqXNsxHfCSJweaIDrxmDwPyWDx5S7jFyE++UluIPTykT2cep6qrnCu47GUZF8\nqQDES0800Dyf/uuFPxzpu2EBu4dCoo0oSkREiMPt1iZmJP6ExGP4F8iLYllv\nbhCyeUoEwo4EOJNcpbuLOR6syuCUD5rAP5j137tuZQRT12KC8VsJG7aDxwFT\nLGwn0ahdJZB8P0pj/2FQrXgfWuuO2WoGnIrULLrWq/Ht69yvy1p7qnZtO5Rm\n+/nynHzvsszGSyO8qJO6kXrtOkKBM/xI2Gd5b+uNaaA7iOSURyK3B8ig/gXd\n7pnNONabTkOGtumx7b/WaQeIsQ4bkXnyXbXJ1rwlVJYBGhTngJtSxUWy6rG8\nDiZg8qNcausYzr8KGuC89IPrxIAj2+e5yqKRUMxffeNdO8uTg7I4e5gIBjI/\nB1HAACbt3rtKT80C3HOw0GInEpaPQiPSGwKOwAnm8vuaVvXK7PpFMjW4HY1M\nJ77UlBtcrrtXnloexzwGBei3CTjxXlp8VehJFt7ka7i9T0xMlnZL+fLrAS6k\n1y2hIKg9lp8pTczdUabNFDYJjfNs7CMn6t+/EtL7gBDvZ6Z9+cuQ87uw83F0\nkwA97QSh+3oEBEKnHAOHYCajiPaJ2lSjQNE+2eP+SzRpnMwQhh1JBPmKXWpM\nLqQh\r\n=FkPf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCA2HgYq2RuaueT9DB4VIwzp0thK1zvJvNoMXiaCz9VqAIhAMPHaTODeEiBdCcWMGhTLvZsmnswEXAyDOgI9aqVF0UC"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.7-beta.3_1570582424115_0.5585206260418472"},"_hasShrinkwrap":false},"2.2.6":{"name":"react-auto-edit","version":"2.2.6","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"2b3898f1b5322cdd79915539523ee0e0421cfedb","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.6","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-NFIosR24nqevCtndiImvUngDmkQFqTmpJMjvV0AbINcpmCZe96zhae/oE5N+ko3+RKe1LEo9QjdXdMF8l7O8+Q==","shasum":"6e26de634becf5e70ea02ba712d5cc47da590293","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.6.tgz","fileCount":114,"unpackedSize":287668,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnTa2CRA9TVsSAnZWagAA3c4QAIvl/zykx3oWQdHrIu+4\nQn9LYVMk2ebMD6CnZQRG5k6fArdf1eQcXjvXKmYImMvGN1RJP3TmNap/xu1N\nC3fnfTGnV0ZiH2ib/IhdfUBPYMBOYtN42GaRo6KQnRB0E27lZDlzBR8JgRcs\n9pBxeKYoxmwuCt9RjJ78+qUWfGNC0kcJGinLfzSwZBepjOqfpc0qJO81cLfT\nuHHWiilhmHnQ1GDFjWmDCg7Nsa00uVg4Coyq/wubpnjiKai6W3WK0SYIbDt+\nGLyGK0ln0Fse3fyJGTEeTdSJDXHpsCKrhOXDhMoKLrS4CuKnktqngIvOJWr3\nyBhQM/TbvgbRdy9J2NEqOUa1GG9ek4da8k4Lh7xteIsDxmHlsM1i4z2CYK78\nWyOfGzd1WKzEuN/9PU07qrgElU0D8wAMB8DV22Q52MGH/eVSJ0CVr/1kB5TC\n0IW/DU2o0K48XFMRQLj+KOhm3DuAhmsdDPa/9muuVTs92Vj8yEJ25jZRC5u2\n6Ir7eVyf0JWKVhlmJ9Qy1LM8AQfJmD/mr77QZHJZb0nEwUMFEjJENEzUOEOk\n4nfkdN6ipqv14xSfIKn+zlIxRVNQQS1meQXKolJLqwgLOg+qryMUlBk+3xMP\nqTytcitjeyljJD6W8nPMHR1iXv77b6A9O0119eTmgWegM0pZIUuNwgfnp+Ow\n1DK3\r\n=fzki\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC29SxwBU+IeEPzh8TGmuZ5WJaGuJ7GZqwudDlyKdAXVQIgZZngoBTQMObTRMQl5bgqCQ98XKkPAWjocqFH7aCmKn8="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.6_1570584245649_0.18816168427738944"},"_hasShrinkwrap":false},"2.2.7-beta.4":{"name":"react-auto-edit","version":"2.2.7-beta.4","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"c8aec28ded1e7dedbfa671c53c4f00fcfa46a67e","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.7-beta.4","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-n2IHjh36QcdZZdhPPTMDv+qAlX5h++S92MByG8iYg5+xFhV1rr9zkUQjbxkP2IYLWLjXYqz7lzoALqjB/L9TSg==","shasum":"8edfa9b8fc0fd66c3ef1e64642bde38f2bc090e2","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.7-beta.4.tgz","fileCount":114,"unpackedSize":289031,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnTiXCRA9TVsSAnZWagAA6UAP/1cf9j+xVq8+k6u6z2ZA\n2jk4Z7+ykCEeBRNWjMysszi+uu5M5tMyHl//OP2HbJKFP7CcP346bzkz+kpb\nrwZ/godw+P18Siiu7F1u4zDQnMuW2UjAlA8kFk+PMmfAMxPtiA7xJk9oZZOo\n/jau1CCE/OzwDurDKpYa4+2PC7sgmSw3X3dNAeu9C3SFWPCGvL7RKLloeiMh\n5neDnITTo96owuVmQ1RHpXwtJCIGpNbtEkKTMM8t4ooTaJOshszJjZ2XlyCp\nb1B7A3UHFxFV8KdgyowFjQkn5ugMrdjO4g5IzYh74ZIO8dqWnAhNgepFwRmJ\n10C5E+bEWCz9Ltdl49JHyGujDXHJldw+GPY2SMjM4bzo5jY9aWONeazyYUbo\nmSKfffB2hHKjhb7wYRU/OZe0ZSDsVapGNSE2j6vTEclr/5KAzWjWMRA8gyI+\ntfxNaRsWv1+n1uiVuf1G2xsHK6szproPZxE0Zlrd80Ohu6HrjhCKORK2b6NL\nR2o2MO6f1H1rYKcbp0O44Ikb16XEh0Q3dn5kAPCT6AqpSRrxhTMwljoDLE8C\nI7Vya2FRfQSL6dokAuC2Jn6+l452+3chQygMUWK3CHZJG1lmDod9CDT1gLv7\nT6PkyPjd3c7hA10foYRXwOVn/RH/5bDSCgJemGzdcC3Bs5yPA4bO+KwP2krt\nvnp9\r\n=YneL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICmDlYv33sXNPubiHNFO9Emm5D9mkjOXnrmQraVBAJ8HAiEAraV7kWG7dCzlt52HEniUaYseL+ByRpn7hgBjwDWkfQs="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.7-beta.4_1570584726351_0.21257280551476754"},"_hasShrinkwrap":false},"2.2.7":{"name":"react-auto-edit","version":"2.2.7","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"d3c1d8897765c7120ac5bf1fc6eb461b6b5cb453","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.7","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-zam0tkLGylCMgxO9EMzgcEq/y28vpBEbT9jik7gupiYGaNSwA9Sp1hih1+oRmNLUe8asp58TNRYfuU2+XglIYA==","shasum":"9d684b6789680e25b2e3ebe8740d6b5e3dc0ff38","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.7.tgz","fileCount":114,"unpackedSize":289024,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnTmFCRA9TVsSAnZWagAAHsIP/AjSl+kX4KXnk0vwkq1k\n7psssHRTlhD/8KcS8Nh9NlEnCjbor9Hp8I/x4HnSIeCP692kumibDggZuXYp\nIaLrK5UmRBVwRbys4h+tzoGCs8YeQsf5fQJABPr2eJnUFAkvJD2RrW68h2ir\nmj3n2SFw1X5S+fJmAdaGKE3OIDq4c97gO2hfFEo7at5VjPD0fNtyGtD0jVhs\nAE7ORKjliWHSmiQZrNCVI+35970CCE9jlbrO3QcdjPyn//j1sjKGE8397lUN\n/C4+e1FSvEgNShQJMO9imlT4xZqD2LVYvHkaNP6CTFSZNzT0jhBx0P0jlDQo\nP4IuKhvuea7D4K/ifL9zbbnTvhvxuK0VYxCi2VyKTgggk8o3KUfK10ATud5I\nt+JB6a1aYUr6ItJtmXJ5HEGmM/e4D+uUS/gIHCL+4CqxW6WPS4AaXatJbA12\n2ZCeCCE3u6tERX4oRsG8KBhoNRDdioVTjpDWFeUBRxMKQptWkuMSoZ1IAqXO\n8bDdaEVpY3O/1Xf0D6+MxEVAEe727T/YJIUMI8mVhiXlYwpcJEk23F/UwLc2\nQmRRCqvDRkwApkB7lAtSIiBz+HEdKfvpLta6EMfrQgsDQnzMHmbANPg81aSa\nEUDb8DPevfdPvVxb3oSi3/kPZmPdYwVWlbTtzWkkTyEaE6geU2ZWfW6YC0QF\ntLxz\r\n=cQ/x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDgEfTLk6QWniP+AI55W6dZlYADZFFTbC/SeO+TmHq/JAIgRllrNZ/+4EFhm4P7DcPEOVGdRs+BaZDHMc1u2kAKSM8="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.7_1570584964720_0.28102191163934287"},"_hasShrinkwrap":false},"2.2.8-beta.0":{"name":"react-auto-edit","version":"2.2.8-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"5381d410ec8711e24bc7f2f6db100d8117c83272","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.8-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-RyuzyeYL7b4UIV/iHNTO18KsUswbDsCTcVqL9l//RIh2WJVOBP1EUDHuNRVtIWL56+24rPXqv5VsRpGsb4dLsg==","shasum":"1a57c1cb386cf9838704db7b502b154834253581","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.8-beta.0.tgz","fileCount":114,"unpackedSize":292191,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnVTrCRA9TVsSAnZWagAA8eYP/1OzsHqzDlWg41XKAK+1\nnry2r7ez6UCEkJMisd//NW9e/XDSBpJSzGhfQ6FJ9BwHFpBsUV1RCvZgwH5n\nngw+PIggG2cT5RRs4OXtYGgWGvtBW74O71eV52YGvmCnWX35voHc+Nn8TB+t\nAFr7CEDCPwRWau7MYdsViN5kKAO2yvCEmUu//rMmD77IgnjxS6sGgilcfgKz\n/MGLLEb0tm5Ew4WhOhEII+pwk0Da67jR/nDZFkjU4t5OqlDBlUPYbZOnUHy4\noZvqE5/m5wSg5avuZJJ1/PUicbox23QmH3wdj5R2uGVDiHt0psQpFTcqax2i\nUzUji76SRxQ99cn4Mv2D1wYJMrsEfCqzPKPuIwajUgxwxsbPpjQhC+a3NY7a\nA/P3f7EbjaUbU2XKCd2QcaoZjVqps46qyMNdJmv6rnAEAl/PI9jHZ64d89Q9\nNcLlW3VZco/7vu/wSu00tC7RvskYNBtyn4B6LbYihm4S7IbCRWgpjTeSF5pI\ng1mcq8YR3e8egIZQVHwqlNKECwnc48LE7FSTBlWwjT/vX8PSdj+lnTF1LXcJ\np+BgzBy6uYTz75S0Gh5tiC7hNut8Rxs9hMaERJPl2T79k8Rkyf6nwNN2TkWN\n8fdX029llU54f/GG4xwjUEkIz2fVfk7CG2M6TNEwsziJ2H8fYhpMdasfAvP9\nX2G3\r\n=V3k8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCtW7wJSSzgWRPkGA+35N39sKyusKOAT0gSN37ciT7IIAIhAPEUkl2JhR0sPY4jJLYTAbpXbz8acf/88amMv6KTj22p"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.8-beta.0_1570591978291_0.8509296058858491"},"_hasShrinkwrap":false},"2.2.8-beta.1":{"name":"react-auto-edit","version":"2.2.8-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"8398f40d09d27dc769918af9549486a018d89bcd","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.8-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-nVVFP3P+sXl08cJ32n/Qs6Fj3LzHhLk9IpUNWD/hf2hDFXnQmXUO/kxA6O9iPVAAThtzCrnR2SWXj0gRTE2JTg==","shasum":"15b64fbe20ac6f9febcc804e46981a16f103496b","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.8-beta.1.tgz","fileCount":114,"unpackedSize":293522,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnky5CRA9TVsSAnZWagAA9MsP/R4lJcnznU3FFF8sScE5\nySIRicOojO2bC9CmKDNI+5EbiLFHK+nDiyLvjmlTmp6gHFuO7jZe2wIGcoaD\nEOMvVQh1jCs071do9F3kNlnuuRoyDdO9JbdEnuobcn3/SV5jxQiGSWTCyeHj\n89wpQ3lgLvZ/BchWhZWfrBid72JB5yrcf9fEPe5miFFDMjPZjyAuwQ0n5Uv8\nw2JUmoTycUu3sL3lE+t07A+6wPq50LlQZ3u403EbJOJ6z7i5LoMGJUQHBKes\n/CrWB2HdKBTl8urtpXn42ad8sgTsiBn9IM6JTtw1T7FzyeIaV2ee/lOJro3C\nAEXPxMbzWN3x48KAxP442u40f36GylHMTw1guIKHhiJVuOdCsPrcZK1ClxjY\nkKZ7sgaxu6YtxWvUGOnG3kLyphR6iA/nwRTDOuXRI38Sb/txwql0JxwZh2P8\nY2jJxsSLXN6VlLgM7b/N51O46pAs/oBfRFF3kVXyTUvu6k/3spmYM9YDJtBL\nWQ56rluVi9O9TLlZhR2PwmaMm3jjC6hvGbmmj7cjIgeI5ordHM8XJ1NUZjtc\nkc0pNY22rwf3jrlCFRUz+jRbp6gXuN5cHvDYD0y3htGCHAkgXST/ht06/8gg\nH5t0HGUQfIAS6vtUJA1IuxremMtjbBau2o/7C/Dpx6SVZ96skDt7pmsp7F1W\nXAVz\r\n=ig3A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZagSXRKbHgP0RUxhm4pJvfRq7Iv6GEzDjenKCTK4lAgIhALlj4qT4eLf1nxx+E9l8+HJ+eaJIuTGa1I1A37Euswrk"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.8-beta.1_1570655416303_0.6821484968986056"},"_hasShrinkwrap":false},"2.2.8-beta.2":{"name":"react-auto-edit","version":"2.2.8-beta.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"e31a25643fa1ca41127d858544f2d3b1a24ec92a","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.8-beta.2","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-q7A8agn+aFYoMg7uBs+cBWot4dAe/dlBUh8WcDqX2VEOy8gpy+3B2K9EygO9lfgKnl0qi0ZmdxEyn6CpUnLTbw==","shasum":"ab292cb7f0e3e73fa9da7ef9ba738505fbc0ed0b","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.8-beta.2.tgz","fileCount":114,"unpackedSize":293522,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnlBLCRA9TVsSAnZWagAA+ZIP/A/ZZaZiqWZndI2q4Kes\nJMNf6mR8oyKLeEwmuhJDTm9aUcUFZU1det6f1kZDq/5UKfe5HRNCN5UzfKxw\nkt5jtFN5nvt/0JRtuam9RdtAxpIwthqSvFKeiAHxXUZdz95NrLQMTBaCnC+k\nXj958CGMgRTU8UdC0vgtAaC66PA57s9pmXgbo4ZdVJkaog8fp5rIGkzp/8sL\noEV4S8KPZmr9j0850QoS0NmtsUUZpzuzKelndcj5cVAKnfh7lrbV0+uYVq2N\nB05EdI89WSe6Hzg/kxDOjSr9Nruboyjt0x1HaBBSEWlGl7WH5Yfr4AqChrbg\n8X6FC68NKPIbgMVZLQ8h+0ppj5AYcBKwxtcw5qNFNRIUrQ6FAg8xeuPG2THT\nYVsEK64XeSJZjvZ9Ql954mJpKvpWiHGFXtq634eUt2pD/i5WrMXjJeq4aZjf\ngWnBqn77CX0Ui+XmDCEeC1De5vHRYO/5749xUay6SbQdwN+MkrpE/SypY9cZ\nhSxIfN9hRrWlw2nrCUJZiZRIcwmE2v2DU9hQEtTc65Rbr7F0AFLEYUhxWvTd\nPPW3lKE0Q/X5b4Uf0+jejhT/mWA0H4O8Ina4j54IkhKpv1MnRBkzSTQlteWS\n3N2SpG86g6OD4CxVzQVljuHdBIOo5j4DKloyPLJuNvE6mYSj6iLmLRY9Rohn\nUoG3\r\n=HRg6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGX4fWK6QjpsriU/PxIkmaQ07RXBvEk2aRwdPb1VC0k1AiAPMgKmnADiP+oL8ESc/egTbf+cGx5TpnmhQsCHR6+kbA=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.8-beta.2_1570656330335_0.3488975189184187"},"_hasShrinkwrap":false},"2.2.8-beta.3":{"name":"react-auto-edit","version":"2.2.8-beta.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"d2e748ab6b002afde4ec29bcf086b11a1b9cabc2","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.8-beta.3","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-4Tq6/56jwz+W9ZIi/VhsxMh2gZ6trEk9NjXqtPsmdFoflGr3kEOrsvoPSbwh4ROPEEWrZ0j/Hja+SUel6Wbinw==","shasum":"17a735d73e940277648f7805a4980687fdb4f6e7","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.8-beta.3.tgz","fileCount":114,"unpackedSize":293522,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnlCeCRA9TVsSAnZWagAALm4P/imaitTX/9UEGRIuA6jN\nY/BFF0gyBcbYtgzsJKlr/TleT5yD9TImnWpvEbE/uL2XAwF4j5xs75DlH9I2\nNNis1PIxnHFtVCSPuyyBpPooSx+hcXBgSGlnDnNt6CwTeCiF7WTwaJgENJ+M\nzodrEIsG+EdDaxyx+J5m/xBXPxZy3lX+7ssfids9sgcziAXR7HbH9U/8MGyJ\nDZ8PjC6k7Fe13ZJ7/gEhHhHSUskgjfxLlqWCuDP/RhQt4fNmhdwyIGyOzVcc\nmJDTSF3Z4kbyxMdXX0yvC6HBpIEJhSi+F8WqrfQOlZgdBDnSM7YtaTqsW9ZB\nN3U/FkKGsQ8WePcH5rQuuEsVBLnRC5a9S8dBDHyWRRRUE9Tx6ejLQ8VbINFe\nV7AyOtDwPn3udaCrP+06h1PEtwx7VqqZckZCKAFJpuclmovs0rw265a1VjqD\nWRsnANYXh5BYGRj/3ZR5lEvRUgdr2bkj58BDOhZnY5dXpUMZm8n7f9mPeMd4\nf39OezP8LH+ZUqdBKBeohqmDy395DZiMfwLi1W3hjQMhZjbONu0bk2wjdK5n\nFSYW36AGx7ZXVrxm3cMMv8SmaaslZq2BftcnJmELEkN36dA4qVGPVQEe2QEc\nUt3V32h0BN7rMfVI3I6NVQ02LVd8rLjbalDp8MxKVHiiFdV8UPYBsZRDnB/U\nhv7h\r\n=ApFi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDs6ffuegcU+AOQmOj67vsMUp9vSjNrZFi6pAJqJ/y8/gIgHbJ0W9rxnsnQtToYO/I9QnHgVAfJdrKqBRSlMA+Oqy0="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.8-beta.3_1570656413822_0.2834868923072076"},"_hasShrinkwrap":false},"2.2.8-beta.4":{"name":"react-auto-edit","version":"2.2.8-beta.4","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"742682c1910291c533f9b834f0d73eae0d10ed0b","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.8-beta.4","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-Il0r2hMnnUHwulCeKUnBbZh0FI/SaznwU9Ju0CwWMkvQL6DZfc1UWveg0IDgBAK/kHUyZNfDJV1LFQX1IuRvSw==","shasum":"82b76548c3a9c038b9379861795ad1d51e88a596","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.8-beta.4.tgz","fileCount":116,"unpackedSize":294104,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdo4m9CRA9TVsSAnZWagAA72gQAKAs+x/tZQBmc+iwHR1T\n6WdXk90QViZsRZoc/QMWBGTXfnY2wi2jKUSgdxZppzUVeIlEiFb5T4uWXsJ4\nUB/34sp6iRmSAGagQ9lioExHjEZ6/7S7ab8Enjy/8ZrtGpKc1NgDIg3ge+87\nG8VeflDR/g6NO3pfJu3QqiVEDcOjZwIT4cjnFY/UEB53bFa2PzouwQ9eVFQf\nfcvjfJQS9Cc4xT21HqWJUNUOow+Bid4WGiakNCJFYT19NRsHb67Htxs8ZtZn\n0SlJgnB+RlQV/t5NKmSQAIuRZ20iXKRsSJ1ymyOqDZV/WoLIDfl+VvxbIF9k\n5OwsvOQifJfXoHbtWiTV8HJSmLtLOOtseMVCQ44XkQpSCcYVVt83dtDnUWOl\ns92tdkBcPnwYhHGqwGSlJY2qQ/21OBsUJC7cBqA0yFt0Nx89BJzmMEePLDpP\noApruotaAx4YLkYMNt3jTTAzFwGuNgB6A8DZiFfjwknX3p6rN729kBO+Aqi6\nliVEhOsSrfmunuk6MGxEFBRYMixdD0TkHBUT/AR+9gzlLkBBukihazdWCHM+\nfkSIc/Txeple40v+cwlfREu0n8TrOkcGhDlwpOIBeXXIJttleBOP0uSd5x8d\nXMGf8IB52UcSSM+LSXlhzjBIuyFe/FuVOqe0rShtTnb6WIS/9KrkjgHSCqix\nDkTd\r\n=NQGb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAd2yjhxdDKFJ0qSYqMS8NXriqKjyM9l7CJrs0VwWWoTAiEAqhKxpjbWv3mTHt3Wjq5h+Sz4q68ZGM2XMokeOfPDbgU="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.8-beta.4_1570998716602_0.02150321990637183"},"_hasShrinkwrap":false},"2.2.9-beta.0":{"name":"react-auto-edit","version":"2.2.9-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"53b688cf04bc1bbce96857faf01bed1c657cffda","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.9-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-8yKIf9/snYZrEKnnBabClVMV3FUl3qMeflEKd6kuROLhxU96cBJIlbvz2yor0keKPIGweeBoLQcnsNxlX9NT6Q==","shasum":"752a4d0167f3cf1fc08edf234908adca3f1180b2","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.9-beta.0.tgz","fileCount":116,"unpackedSize":295891,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdo6LzCRA9TVsSAnZWagAAFnEP/1I+qm4D38QouGRyMc67\n859atSq77X3aALzSQfAky906OWoYxiGKHWbYSLjhI6QVtYRnrlWCURF8P01s\n8dWZQg23B502cfPVhy7wGcM8i+CwzkOJtJJbzFQXTbo0sGLMNrTFgOeRqsIm\nlZoXDedBWiwWMRGjG7jPmHWiWrNyYJa2iZqUutF4/Rr2ifJrgJMTCDtJ8nCQ\nCoIKdSPYScrvHs0oIvqTgzsF1T7LtoRasYtyMCr9VNrU9V+TNrIcijw2zUFH\nVpFkv7BG/hoLaHqlpi4gPqQyEXx0sx8myr9bW4llJo1/Ha1pAEM/5z5c+oTe\nWiSadlqdH8WfxsiwoI76j3vnHrs6Gnv8R1t/QaA1iBjyf2+UhChOuJXpGdFv\nZrXyIffhgZt7pyvqOwcbQ+I8GNaKTPEiiJaW3Ij3YO34RW6uIrUcah52lds2\nFP+RbCFbfB6RTQxBBRGTiDHKzeI0J+Nn4zZrnp5I4I3toyMCF9vUY7esQpb6\npOWXvzPYSq6FlM89H3aXpa8D6J+MSjTmCx6hgcUb8hYTh+xooCIraOFEyTnL\ny9abQaABmdq+gjT5NGBlEkWiubW2oAdMMFfDbqXcOSGF7rZiRAOkmJdFcIPC\niiQuhuL4HUs2oLCTndwccehZkmsGbDhHzf3At0Gs42FOUb/V/Szlxw2477jL\nYkdm\r\n=3PFG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEBLgNTlh3UBXQyKGv++KNS23zbJs2eC/IOJ3pU0hVmxAiEApbfv98Gwx5gMO9Ox4rUJaEUbuL+ulTTGPjSkd3R0CmQ="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.9-beta.0_1571005170909_0.0844263198661197"},"_hasShrinkwrap":false},"2.2.8":{"name":"react-auto-edit","version":"2.2.8","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"141916876a13377d6d11494ed4c825cda547c384","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.8","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-n/ylcTNDe6SkvO4SCMJGWzk6PjGAFGY3Nvim3BDf769hb67IAUju+g4bXxRtAiXnSzRbvU6gZltcuuDR2qf1YA==","shasum":"d8d1eb874756b14789c9fa7689175290122ed1d0","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.8.tgz","fileCount":116,"unpackedSize":294097,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdo7W4CRA9TVsSAnZWagAAWu0P/0nUllBBxrkOnWIsIen0\nfGLlpfRo/mVVaqCT4RW6cPua97efzcYO8A6zRplHoLlM7uJFlWuHg3QKjrvD\nllcz1/vpOfGDGoY9XJ7DQI96evMb/VBX2mzcvrwj5lXWbmzM7D1ZMb9DYrK8\nWyRjN6y3K8G2EiMTlfIquHIeObb4RzJnH+WHAjPC8mZLycCPGZ1FmOMWxPjK\nraSjbtLFiatKqkRHgCNEVRX0ypZVhnpOf5KYNYtaW+jndUW6TtpHhGTattON\nmWwWaxwgXswPHXbZtXYJ7X0dFUTiLauhdyotle7Ex8ERDoQioIufRbZqg2MM\nAqgnN3puZwnhVXhzph61eD39tdV8y9yapCzaIzlqCkX8wKo2soYB+sPHmFLz\nnuvO08waNnjfP9IXITxHfFDdkBv5R4YyGlCeiksO3euAx2HNrcTktBlXTxuM\nyK3n+dhyTGJMARWu3SV8gBov6fNb2Dly2q0qqGUpqIrwyJ2eP6ysECKJIiVo\n/yP80l2lGE8VDHH7Do3wHPvjuQoAqO45NHkVE3ER88L0wUeIerrkoRGKvwJp\n0n49TJ5gxZOnDFu5AwqpTTPUkz3HV1Rzf6GKclg3E23lQcoKfOb0cjVSwmHp\nhLquLWL3ip/xYECJMKZBSqVqugGs6qmc8FNle6GWPhEj73PRzxchlXCkaW9m\nVY7W\r\n=e5BS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEUEAkqtjXE1Hw0ySvRSgAf347hTq+WD/4JxoMWpMQ8QAiEAg4/zqWGg6HRFioHGmQ5lKI50OB0vJP3++cYsSrzF9LE="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.8_1571009975396_0.3299219662561246"},"_hasShrinkwrap":false},"2.2.10-beta.0":{"name":"react-auto-edit","version":"2.2.10-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"4da42f960f94b9e58126128e6e300c83fcda2b5b","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.10-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-FSQa/qK9RxpPB4zVJ51Bq1R6KofrIms+jISLRuENkD//FsbfKDdpfKB9F+9YsFcMGIczod1jWxVbufGunaFW9w==","shasum":"9e85be6292db2d41f52023056cccf17b9e0455d6","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.10-beta.0.tgz","fileCount":116,"unpackedSize":298027,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdo+EXCRA9TVsSAnZWagAAMDgQAIKc0NZigguGhbMsiQ46\noQW1q44rnNE6Tx+HZdHHWuCcyLv3UqkCpqofFmAElMf4UsWAom3znm/j49x5\n+HGm5MfHKSX9MRLOA6YfXgc85r3ZnmxdS9wJke/4qu3PJp5ieVOnefF+b/OE\n6JAHK5bxLBQlEC1+O7p44cmexGesgg97v99s1RmL5/lwicHpMEFv3o2hl6EX\nPszhRJP9uN2Ydyw4GvM2Vq9rdQJZhvobTXyEMygspN8gCb8jktMX0NMg6Kcr\nHJXes6X8chAUKCT4q8RgkoiGMEInia8SNGMlXFGI8fcy5j9A3Nt1tZ8M/+bd\nZDtkAeus3ATJShtA2D3e//GJ0H6cFLPCS7oOOxd/fScCcHfV5WwLWhLWmWZx\neYAxH0bFy9aT7So1r37d1vqBrUmHJ7qKyXZ0bCDYo+yQSuoPoKJKdQW3fkmG\nTXJ0x/hUxVLLU84Xwd+WfgOP7MoEm5FNK0JMJDn1E50gzrY2VZf+ODd5+PCX\nqIZ6GyhQQCcHF6kgj6ZLKfwi6gIeWKdnRHyIEJeE4Jut2cDLhP/BcLDAthKk\np5LSYk1sSFrVkMep18KUGVEf3NmnethrUIefKV6Z/OElkG6N8QfCUYgkkVTR\no6fbMk9sVzF1X4pIt06fgzASs9Rh/8eofRp/neWU83ub2OTKb8TxXlzH/4/5\nf/Vx\r\n=4Fl0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBnZMiXCty/M1KcM4VZ06RrDGnzzlsanXTEIMBznfCslAiAcl5dL42njpBr1yPagEAb9MQU2DnmADYuDG95JHvEVAg=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.10-beta.0_1571021078215_0.26305418583690066"},"_hasShrinkwrap":false},"2.2.10":{"name":"react-auto-edit","version":"2.2.10","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"6ca30c997ace81214bf7e14febd9c869fe89b7ad","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.10","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-1zwTiYmVAcJDw08WspqFau/uZOZS6QcIx1iGFHYqEGsCHGQtVGxUAoKTw65ne7/wkBWDPsLt9iJRo+E4nBFDJw==","shasum":"0485971f6908ff00eaaa77ffb5f92b484874be5e","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.10.tgz","fileCount":116,"unpackedSize":298020,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdo+UtCRA9TVsSAnZWagAAuhEP/3dQOdDif6jpydpwznk3\nR0oVRJwcwhSQWJdCAcSZeuKHjPkZtSY5pb/sdJ8ZQyS9jDd4rQogkGDTv3cU\n+vLxoWSUCV3f1lMV/tO3IXz4i9HkLzH8tvwfJ7x6GL/dqzRKhYguduCTJBYi\nXJ5ey28rV+dnrtUnUpb+hzY/QDsPvJk0QzirFUTAJHG8IkzgJiM0SzL3Ptlc\nHlufJ23kONwY4jYQDZm5PJn4QFMoRot0GOHjoSWdeuq4W77Y3bYBBnd0hbLr\nBPK6KiljBkiM1zPA0P8yzg7XdlRilFxeSwho6eFZAsi5wozdQyUXYjnb7ylE\nDS4Ry8Xm241Ix1o6VNpv2W5o49zHnjbdm0UEaeJ1nZ19XlSt0cgexwpDWiEN\n2WZ1V4PdpHerGrHiOdsk0zRwyrrVJZZMoHQ1MOTWS8vWOeTwdMByhJfj5L6Q\ngGyHQROOsxRr7N7y6gIaAJiho5xq0MP5MDDG52qKYKPv3Xt80afhdasyry1R\nsGXq6cIPL9SeJUn9+iPZQCde/41olNjKzhFasy9czLJcuB49l0w3JvY91jW+\n0hcposARRn7n03MS8upHg+wb3w872Mr1P4aQlDyOZkF2+wpv6hh4cAvJDMRI\nHJg+X3Ef9nXYhURYbXknck7drt8CFi5ZLVQE67QqMeFJ1iUWBZlCqG2GKcxn\nHIcq\r\n=Ht+3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID2yEsr/6DcGBxjSv+9+2D0EKYM8ziCjh71/XktjjfAqAiEAqr57bbTQYX+NWz9qMtwmz5Gww0zHhDv4mTOlxLBay+Y="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.10_1571022124481_0.7463798296560611"},"_hasShrinkwrap":false},"2.2.10-beta.1":{"name":"react-auto-edit","version":"2.2.10-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"514f85325ddded5dd58ac7f66828e8c1284089f6","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.10-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-9aglsgOMsDfqCm0z/t1vmbZ965qP7e45DlsIo5uWTLYVaN6AIl/tUVRXp2WmYdC+yRojIQAjF4FTTBVzsm+bOA==","shasum":"6b0f01e451d211fc6019d4128b68104b29ea16e2","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.10-beta.1.tgz","fileCount":120,"unpackedSize":322853,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdpUL6CRA9TVsSAnZWagAAzkEP/2n8S3UAImbPcQAoiEh5\nUAlyMqW2otW7zw92TKyozFQlDL0uOajGvwZX8jrUgalE64ZCzjZ394y7xc5C\n6y+dtQ0BmseGR3Q1/+ZmpJVVgICUpYq1JMRR7vFEd3mYk50fIJCz98m/hZGR\n45RxbpOiUW5EkCWaW6WmndHdYKkVoDEY5avddE4I+ktZGISjdiAlYjA0Kv5q\nzfkbWWbqyqO4nzzg5zPXbrcsbafl8wpOrPq4Tvpd4XluzGpNNw1Dwydnwdfg\n1J6va2GsZx97PD255z78FLU5KaK0akajDVJbxMTfH0uCs6FLy7nOmNfgEU4N\nVkocD6UL00LXJteV3VDc05G34F4WM9TKZaPypxMAHGyZol+6ZSCQCLBj80Ik\n8uBbx7UVNhGcEQPRGpEqTUzPYjHds+RQwAEZatmhi5LEnN10Y/54/U5j7GXB\nj6rtLOVrdxfn63Equb1JAn8VLYjqWkMXPp6/LQtz70q1aZ9g29h0jCyDS5BB\nXupzEmcg67YGLb1AYxCn4PQiM6rV2SbrDKCZz2RtHEFwOHCKDvUVBn5k7xpe\nGvb1pdY8X+mPSctTiVuIRZy4xkeI45Ek0ChNV89zhk7v6VBY24CBmHHD8At4\npdxUgEqzcKXFp1si1vg5jIZtAeRUPQOjko2/GgSTc7innIp6VxvZJ+daWndX\nERMP\r\n=NUvo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDyNTCQqo/bWBNTU+VIFWXBKaye7dZbctOOiKJfLVKsVQIgU74vOGHJSFWr1S55fe2qCHKO4WOJlckt9FOi2f0yxlg="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.10-beta.1_1571111673545_0.6827099500078757"},"_hasShrinkwrap":false},"2.2.11-beta.0":{"name":"react-auto-edit","version":"2.2.11-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"0ca4c05e090d6c4db6843a97658c82dcadb7c707","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.11-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-w06Q3yiAdXHo4znlK3sAU7wAbQVaE8dRMoWy5qmH4vMT/1PDKwDQeFA2YjNw8mCxxEWFcZ6eHHxr7eFiH6G/5g==","shasum":"64de3902a6b5ebb4f6ffd1fb48c338bd93c80dee","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.11-beta.0.tgz","fileCount":116,"unpackedSize":295892,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdph5sCRA9TVsSAnZWagAAxtYP/iZE2VOUEounQ7XUVzpG\n3KL01xMS4eOeTJd2le3MnndTL/WhrKhh48qhbkqI3EdZi/6M+21ae/BMGkHl\nW8tirERC2uCE/cyJIxradET8by7tvZdm24T6SnGCvwxELMI32azZjVoluBpn\nEtrapEoQxOH8fuucEUAzsFjo1KRaxiw7X1RRIsT0bVC4LW9pYFC+DBU09uK9\nxdSVoagdv6DN6AAKu+lq/nwo7Vf0df9GaRESy1887jbDGR/C6B7db1axUZrw\niruwCvWNht2N/l7lDqB0rpVVX3Kb410oIfbLCVASLNpCoT7q7CTOzGQrWBRg\nUQHx1zAAA5wWv2Qt0Ntaa4m9gMWaAn/ZQvM3bbFcoDX71S+4sCfxFifOZwB5\nfSP3kyNDbUcFHTA8D6gpXgDrFs3f8/LfTZcVmQObqcwR62iROmGErkRoXlew\nQWZ0ZaUXhLsTOWuo06qDKsKGjD48Pn0+58gtMWmWxLFqxPonHyCdILMgC4Yy\nSv2L52J6DWoOYhs0cqoOdJVbyZaiYvx47zkbICM3fpcPo63h8fYXU9sXNCXE\nteQDsuQKn3MiVLkQ/WJbx7TCuu0j6DywW0bUedvTyv4lBcLKzvhoi4WV2AFZ\n6dBbsLeJ8cw/uhTCM87DSlwFIXJE3xD/6Zb9OvMbLScqt19ECnopRbUS4XHs\nUpI4\r\n=QpKh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICnQXzQCcHD5FVTS+JYpRg4OaOLUv0kn1MEAtGhuzPvPAiEAyXNVKrqLz4zh/jXJbVgZq3avHUfskfdpxXKqT/IcNRk="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.11-beta.0_1571167852112_0.6532347713504152"},"_hasShrinkwrap":false},"2.2.11-beta.1":{"name":"react-auto-edit","version":"2.2.11-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"bab69136056427d4b664d6135d49b31876601d01","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.11-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-dV0Nt9rcZy2i7V88ISvR2Oam49znZhpMwGyi6M1ahg2nV5OXR2t/lN6DWkV3UWUQ/dzyTYFQN03+6EY6ux05rw==","shasum":"8a7ab37d5390153218f10d80a6fb12b5d3740d9a","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.11-beta.1.tgz","fileCount":116,"unpackedSize":299814,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdph9YCRA9TVsSAnZWagAAsnIP/ia8e0yNhUrktZHD+VOu\nDNVSdWioKMtrPYYcaJguwFey2cSE7lwRpIuadvLZJiueEWKoNRFy2NKQ+fVv\nFpakC98P+WBlF0mIULSqPDwjT30aRYk1xzMt3MNdhil1t9Tief1/1Dy+taWJ\njx0NAOHUsnw3RJ/U5ndiI8fg2BEqPke/YjVK/Pz/OJC/JQZNMNT+DuVzlogH\nZVCaJoCy0RDPr1aKBA7u63juj2cGKhO8FRQTiftesCmHfHoWciNWSOMyjvoc\ncijGlAP76e2vSTmOOUeDDYG57wdgCWFSGQPzgoJDh6IkN1WEs8zLceQRZhzv\nLutFur4wvjw7qqkjt9XFIYHsx/QV1gdsDQvw4Vb6ptkEhDGosthJ+GHX/vWP\nbVwswUGsR92qkRA74novOKnD5JWcyocLC8EIKq2aLZn350z6L7qzpXHQBKvY\n3l65aBrU78o1UOqYdopX3CFeGvdhauLQEG3quN8nU0yLWcKbKf0ADeRAtZ/H\nHWrXysxti6mfl7PqAdJlxO5UyzW8rAS6v/9/FJlCe4djMk6m1Bv/Gu6rLpak\nsrtEl4D5XRxgpJ1ibTwUJQXQJosQd9HcJ2ul3Zf430EbfTI4lH/uqvAZy4Kp\n/iTl3SNIIFRgTlA+3X5dw782oOLYuiPthy9c6K2kLYvF3WoMb13K55Sn3EJR\nYkwd\r\n=V/uh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxjPxl9Ltr6Lj85/9znvZTGgGe//sPdAiJm8Ja6nGyOwIgclmtkDAWwrx2G9YiI2jAJDy16FuUSkotQd78AWYL9uo="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.11-beta.1_1571168087519_0.6301210086722449"},"_hasShrinkwrap":false},"2.2.12-beta.0":{"name":"react-auto-edit","version":"2.2.12-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"b10ce88993bdc667167b52998a91d06bc234e905","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.12-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-e50MxunC4erX8xVbdWV7YTZ/taMW6AEqFvsmglfkc8Kd1IDnnYm9iiEe7FIDUi2TLU2kg+Vf/jhRLfl76wFQdg==","shasum":"268ffbab15221e0b8c72e5adcc8c24ebfa5adbeb","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.12-beta.0.tgz","fileCount":116,"unpackedSize":298095,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdpiYvCRA9TVsSAnZWagAAppcP/jrAqPDipvIBGenk9ixh\nRBNYHtCfXVAzDjsJUgAg36qV+EHbjOcfw9zluifKlCjWX1KEXaWc3aa0rAp5\n/YmwPfOpo4QLnhaxkT1VQJDIZB0W3PPIGCH2U4gTLlSbLWNT3wktUXEh8bwW\n7WixC+QvvkEtjQH4xf5VNMpZ+DehK7pul2Sc1X55s2AVYzHQka4NST0usfJh\nlI9L3koRCXvcDnXXU6RjrxvScwVNDvobqkWChNK0/dgTz/d+SIs4nOgmTQHl\npziLCZ1TlvALG58thf/wwxfOV/hSVzOuH7UaFWhb7YUcu+W3Ekr261VNcZ6E\nCbXKmevEewAGOMAuxLh/zjOUF7gX0gRdrvvUPoy2r2XOcWsQeq2mJ8b7YkJF\n2HA4hDHMTp3raJbdYrkZLggyAk5ae0iltqRk7VrYpZYfn7+qleWcJYZfEWCE\ndH3ANTMAOcW7wEYgCHWLU2E7nTu6wd8VO/JyUnoprybL9S3Qlwbx6j5Vhsx5\nYKR8E3leb32jKpMIKF68+L8yaoPctAgJYTHtDV5KBAX386KSNz03Gv+XEx6R\nx6I7Yxv69uQlY0+f1CmIXSBZAeJQTxM1RlnacvM2UC1FgyVykYYsIbb06qbb\n3AQh0aHwU2T3b1ZO6CPK0g7LZWk4SwJ/bnlnpQaaLOaEkD7i75dFXu58DmtO\nKAg8\r\n=rkPV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAgT/unNDTbKNsq/MKxOpi3Urz2CGF9lB0TSRMCma2pMAiEAlULAmWC051XBrdVqe4ktYcni5eXoUB6dBIXHvEpLqVU="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.12-beta.0_1571169838332_0.9727240494173917"},"_hasShrinkwrap":false},"2.2.12":{"name":"react-auto-edit","version":"2.2.12","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"dfb1fbbfce8064ffdc27ee199fd01f51cb04df59","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.12","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-JOhjbYhninQTOfXwvoxoHYpmgAQtCJ62mF95n3+ZYnOIY02et0/PEsAiOG6aNhMfnReOK44+bXmBs/tjg2CfjQ==","shasum":"f8cad99bb80487876799ff3d4bf9d72bbab1a5de","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.12.tgz","fileCount":116,"unpackedSize":298088,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdplfoCRA9TVsSAnZWagAAa88QAJ7wI80rGXLBsaz/CrkO\n8GvuHWFZN4PBuxAvlDmUL/fsnsCBgvDu4zSGwY2jCufhgbuku/u2IRqeOAY9\nhDZ+zuLzwqOLsK+CEIp9B2MSxKUuInRbUUMmzYAHTkZJ8uhisgBvTMQNka/x\nK+28c5TsiNXVRgg9vynao29I42H5rXnTRdf9KhL2osVucDcdFTKO8fK2ENaw\nkjfQVww8ncKknxpiqAsIK1joXAgtv9RRvxiKJqojUqqjdmduC1tx8fTRyxM6\nLGXvlwOmRbx4r2bC9bQ+wcRP7jLOP5oLDlp1b2ugSRm3YeFeJ0pVdSlaUJVN\nP3pWBpkN3HHR8d9Ov9mKWkrIema3huE6lo1KZbBj186ieFXthOY1j5v6/vIo\nE4RUbDiKJfp0rCcrRfsR+d7vlchvvwaQfBdFxmAw4VBRH2q0+eWIEKQ8AyG7\nHmEGYEamOcrHc6Kd6MWiKb5LtJVTYXZfd/ajeHbVyL6BN0ANWfzQibEdLIwe\nUydFY192ZYcW1JD+1eJashd/6okFBzBu7QTmbAoE/2jn26w6eCUexwLqX7uD\nKQ8387kV73C3fP+f1/DJAokKh71twsquaifBVkD70IDeP2bGht7h1WmDmjVe\nsLgA41APQ2PTKpnBV7jvya/KfBiHcCDs0ij6d9Sz8MHXi32gjeHyp5Sr/kRr\n+qh9\r\n=4G0x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH1w4LTsLhO1ARJzEStbBM/HvRirHRh2PgA93EGodecVAiBV4zzy3ZQxz4CYx4GNX9XsFZdXtbrV8jhOXXVmiTozDw=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.12_1571182567762_0.917125860030563"},"_hasShrinkwrap":false},"2.2.12-beta.1":{"name":"react-auto-edit","version":"2.2.12-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"027a0c588ab1615d790e1c44a1828ca067edc8cf","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.2.12-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-oMYIH4EcPizu0M+xLl5dTuhOUheNpEZGaPBwRgjEdMHadFhMIjwR8XCWHdFGYThpkVv0CBYqLU5kka0NZccEuA==","shasum":"e2660fed49ae9d8b100d89219561aa8048f07e54","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.2.12-beta.1.tgz","fileCount":120,"unpackedSize":322921,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdp3xLCRA9TVsSAnZWagAAS4AP/iynuzbbBFNrAwX/1kNN\nEn734ayu/Qz/peBUojKBIY5vePUxIFXB+OX2t91Eo0RVhjogEwa+DaLcs9uv\nvS+ePhGQaGnRjnN8oh8TrIPTqXuJ+ONQ56MRI1e8aqQY4mMRLEZQXTRlRqZo\nh8S9gSFZyqhUwjO9Jy7IFb31VJP417wyrALBY0vfIgJkj/XfIxoCz1kpkRVe\nx9212fIdW+Omm7R8zAvVCP4w5XNNrmPaWCali1UIYh15YvcZWIRvC1YJbDxV\n2RBZyjKbG4KPY3mf5Kl7ehFvSNcRoI7axN1WkvdAiDX70l5tvPHlbBkBjaCQ\ngQ8H4UOYTy1NL+GvhjIfbYDV+XgQ/cRfkZeJWp+bVtDorkb4Oezdu6QfXycS\nAtvI5awu7dF1x134aTPV4r0TraLoZTFB3a3EVKcvTZTo+LoB1yBHv/98qSTj\ngK3TEUszwH4WqZud1MKdbhdJQ32aj73tkYiVmOS5QA8p6IIdYghvcYyS9M0X\nrudwnC7Xa5FHyBgto9hGwXDDyq+5yr4uDkCGe4KIQ4iZ6MlPpEAwbMZEFr5Q\n7B1g7r2HCLZHfuaERGBQ0HGhDg7axQsRMFtsVA5/CxFjxApTWsxL6YuIcv0X\nTQYMffvBzTBrDLu+vZmv5fDV56zUAz3jVAAcjKRC18lT3qZXXT1FJiYYqMx/\nkcS6\r\n=PQuV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCg+XereAKN62BNT/OrUS0xr8FSxcxqDqKolQkJqouAOwIgIlkCbMGeNpXoqzbhu9/IPUfkWNREXkKe8qaIaV6FCAs="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.2.12-beta.1_1571257418528_0.7737969561181357"},"_hasShrinkwrap":false},"2.3.0-beta.0":{"name":"react-auto-edit","version":"2.3.0-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"348408c9b2ca2fe78b951ffe2a1a0c316db54439","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.0","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-AkCdKwUv/Q3/SkYN/3yabmJF1So22dHn9cCBxmvLaImOuj76pnPNYL/FdpMr3vW/o7hsgPhHzbxTGs2kXxsqVQ==","shasum":"e07b97c5154b39b882a781fd156ed79c3696a13e","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.0.tgz","fileCount":120,"unpackedSize":335307,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdp+TvCRA9TVsSAnZWagAALRQQAJYDHmTFs9GEGe4ek2IC\ng+On+IhGoqXM9jp5a79C4UsGlGMHBHLqxUkaffNjppxLhuHA/M9IBM3oz35/\nDLvzQOLv3aovsC1HtKlWwORwo3UMPzocPI5OG2UkFcl1sV9dujXQbFhVaZHA\n6mWcY2xsNUbYoIcqT+chfyDDQlRmbDlF5bnWJ4o/Tie+RUR3wwdt/KQJTOko\nGq+Vu31HWiQ9fLktI4nK6s+XmdyIbiWxnRXWG6amucGj8nlW5Rj7pDn2gKEw\njI7FEkonlUz+CXTVJJlso+92DM8KVvzQNTYzVVhWw79XQTdJm4xZeo1HTADC\nc3w9pE6KZNglsEyFS3Y0MEnR79wII4WC9qyj/Nd/CvG2nj7SvsxisJNwjZpG\nsjMb2GvrHn5wXPha9MPpoPkKPRkEbG0ahI5XPvEVkjfH7iMnyYYItTwgR5yS\nfihSkOP/yZTV/T84tb6wY4JDnfvFRfeYUW0StsC564Z4otJThSRZVugthu/e\nA8Knn7PpBkXVagcrPClb7vbFTkESn6xQsHEHZN9v7DQgGWPbJMyG3Q5SnIi9\nSu0hKJidHuw7D35/YB57DgshZp/x+zXbR7ESL43e3kwaqkkdhh8qqnghP6BS\nV35s0+aG1BepAxN0Z+z0k4bbpH5O9iF6qBWwKDPaon7BEYoWlV4kCOYEHJz0\nV0iE\r\n=nn6E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCpSGI9EOQJjhPWy4+eWQQgcWvqMMayteLAiMIJC2dzxgIhAMopx2L5qwcoVaQ+oHgnrfYQOaFkVYCgWCr6UpWHjGal"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.0_1571284206285_0.008909820281769232"},"_hasShrinkwrap":false},"2.3.0-beta.1":{"name":"react-auto-edit","version":"2.3.0-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"958a1030a096fc66c9caaa5a4ad065dddf30a6df","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.1","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-Gc9eYWplmzNLg47mZSy74kUCUZdzm74i/rfOA8SNbHaWi8WFd5TTIFe+AFeVjYwVgvWZch3GVCsCfaP8QCe43w==","shasum":"57c4c755686e77728995c703b7291dafb04454c0","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.1.tgz","fileCount":120,"unpackedSize":335693,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrN22CRA9TVsSAnZWagAAMwIP/0cD+ZoRzf9lG7f0/1je\nIhL0R6yNtDtZXP9L3jTv5eFUCxTqQOYJu381tBY5as+BcHbllGmRPxkTo2Wc\nOzhU3HpvlQ8DOIr4QRK0FWx96UxhpX171kvkvvIWC1Wbxh9zz1rwJ1pB7QeM\nAkwa0z2PD3eyuXQ8shrJ/qqh6DOfVUus1LMU/owRFeDWnfNjekKCn2DyNPJs\nQaMs9D/TI4GZYYBlKcDmV3evyc8Dwd7M4tvjIE+feQqpp8oHZoe0h4BVylsi\nza4oRppUmT1JQJyXfMuqbizYFrjkd1YyZCLHO4/Xrx4lAb46AV+/Fs+al8+T\n5eLKi1aoKnKK1dxeK0VMo36qCcK33ZBNtJcR0MaJIawIdpRmnFg5tIOuGFhO\nkHPREjq5gLO4TSUb5HHQ1/f288YP2wJliK8SyXRBTOZhINkBl97ArVPn0mwo\nw7G8GZA/wJrtxOFDcU/Qj9mnv8d7P8LPIm1dhje1lQopQsP6qWGJKZ2Ocush\nJHzzi+Q7lgGU6GNPqQV9t9tVkWM3Lz3eoUduPSYuu7jr7L9hcYcxE81ltLtp\nYJTe1XOPuz9ySuK7GBI7rEimLAkp+OfS7oVx7f/5Fme4iz8FV0bPv+t6BCeG\njMucGil6KnNL8os/ptVT75dQKlUjQOBeMWu/ANyXs0kdi4vOo6YPJXZHwKkU\nWEJy\r\n=feAl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDYNnEdVsEpiCopBGPfFCPe7hYqzd/TCXlzmQJig/J9LAiEA6+W0sVvOZDg6PD5gOuPdWBNYHtCZhn+GozjNT1dOJzY="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.1_1571610037704_0.24645479824720207"},"_hasShrinkwrap":false},"2.3.0-beta.2":{"name":"react-auto-edit","version":"2.3.0-beta.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"3b50eef8c17f65a04723df45e3721fc1ec14bab9","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.2","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-nfKeHYcrfHv828R5Yt/dbLePSEVgNFPSvHuO+V6yD1Sy0jo1mKgfskcl905NOtKdGHnNvcPmu9Q4hBFaCrMLkA==","shasum":"4303c5698d4800cd231c9eb71f1eeb6a1537a90f","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.2.tgz","fileCount":120,"unpackedSize":338645,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrP3rCRA9TVsSAnZWagAANxEQAIWG20tzN0YRAwxPAXXV\nANgL7im/lUoyeTqOQJMPRWvAAsqRoAjnWSmdDBhd4LhTlXEei2e+JG+Ay4Sq\nVKR1BaWCMQ6+/fOiO3cqs6EquzcY75arPmg/R/pCK+oDJ7EgqaaDhuvURZ3e\ni0r8iPu3X0Jd4dl4L+Snkxomtjv/i/RBxFmgDqnNl3sKJMnSCUVa5rhP31Xi\nW5vW/rzpi5ErkCgIXQlkYJnLeM0p/VjEsCT6t48EtxAe3bw+mhagabJq+jqA\nUK4s5gLO3GDWr18kmcXsVxbi4x5OtmTLCTZAq5W2FJvQDcDVPnh5Fji7oEmq\nIiaTDYNFfCs2OR8Wr+cEoFedb57rNzMCZ1QHfTk7oVgzITJCJ5TboQPRWkuh\nbSTN4SkTH4s47Hw2NNXW9td+rJ9MT0ccc9kBFN2wjRIEMD2HRPLay3/uuk1W\nxIDbcS7Rqziv6m9skKmJmZn09jN34E3m3XpZPrQCsuoRdVWMzSUIbXhs9Nnq\n8mFHwEtXsFtugIauoapkG8l4vdnNBNRZTg2O+xARR/VLjxPcyRz+9W6qxXMR\nziOGjaYS+rIZcCKpcw2uQ8tLAKUDkHHvHuAkZ5kmePUsjIKBlXdHesw7c77U\nSXDpG714mlnLQawagMXqAuhrbwLurEnakij+8IfEoB35eXpAdPN2/2CvoDD8\n6JvA\r\n=JOdm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC+z6hJNtxrYea4gCM4Gu9X08herysq7jJAfFSkxVLBvwIgRo4flQV9ul4QBPxWtlRpYA8cGARaApEgWG1tMPw/RWc="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.2_1571618282501_0.24610693995075406"},"_hasShrinkwrap":false},"2.3.0-beta.3":{"name":"react-auto-edit","version":"2.3.0-beta.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"224faee110ee35c8e9f011efb99549e5f571ba8f","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.3","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-KCaiBF3zuROznkNXQZBiXsUgrVq3SacdYw593NrIeVOPPTtsWouGB9qWdK3XWc/zJzqhZd5sx4JgWhVlBAdGoQ==","shasum":"1730f274d465f510547e090c5be92fcd68060c44","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.3.tgz","fileCount":120,"unpackedSize":338961,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrhBtCRA9TVsSAnZWagAAG5QP/RwfpRxkehdMHtjAQb1F\n+uy+Vuj4Vr1dr2t9rRwBueXS+9J4IrnUb/2CtknfVI/spVsLvNbLjrE2BmGg\nEe4WgTooXcQjSFxz/Xkt2h62nOulDtSl7GhFj6NqB4CF6ylL8Ta/s5XLAv7A\n+/szK8cOEaDjggFUVludUmsG8TkOUbKY+qBl4Zk6/prowvmJbWl3i2160yLG\nKEdqWaXrREJp1TbtPPyy45lPXQeB/8MKciLgQFBr0xusvPuLVnKlrY5vgM/K\nQ7kd6WvQky/ppbaYKXaSFpy6GqV/XMS3nB1qMJQw+d9nF58BQFq2ar7EpnU5\nJrwT3YWeqS4DvnDH3OI/vWXUbNuUjwPh/IZbp7IWIkGamLOozS1BYz6FGTVU\nXLsBZeJ2oOgN4UDU0Mpkn2bBwao0f/2hPxP4VvEf7xIEhiqDzZe+DfEUB38x\nKMGYuTX+JLfeotlzjaDanrmT6mfg+jaySu4dg19tVS/AorypCxqlpFI6QYMc\npjtffCZ8TSlEiSXzqEGbKg/nHE//GNAULLkNhoucx/C0+LTLYlD+/6ZlzHlr\n7lMahqh/YbbT8t5WQsX5EcHp7JzJpK4F+dKaAJuytoBT0VT6eZE85+Rmee1g\naLcW6cNpGKHQrStLj02XIzY7x4UjRFvxOZYEHnOsNf4yi/hbpmcL+fPNf/E+\nZoqC\r\n=9T45\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICxNU9cPgXyQZ9P0YQicnwxKZkpQd3WJI+O/hmqa/2i4AiAFq7TC0OmlYwWcgRXcewDDbeK9KpQRi9gzx+6Jlg3glQ=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.3_1571688556304_0.2912240798959318"},"_hasShrinkwrap":false},"2.3.0-beta.4":{"name":"react-auto-edit","version":"2.3.0-beta.4","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"c1a5389f6d301f72002b797d812b288f459f44e8","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.4","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-VPxAwxvLI41XbW4I0Lb5GirqHslOzxQ/NrwsN4aCCR328Z11Adff5knIdk86+XsChae9sz2FkGB6+6xrlNJD0g==","shasum":"537aeaf6787283fedf619bcd08ec7c661632097d","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.4.tgz","fileCount":120,"unpackedSize":339140,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdris7CRA9TVsSAnZWagAAEbsP/2+vifXJzfVdalxc5MdF\n7lrFdJzarhvAshKqgXp8xFZUA7YRWAoVbz1GJCIjZQrmSZ8KJmLgESHkW/nQ\nXtzB5Vv/3wgZNossej+iE3/9z9Sfw4hE3cL0JkvGFwAuVUAM14jdBgauQpaa\nD8hxzT9dzirRPPEwZAd7s8st+tBdnqVJouGijTNHRqH7/x4RtFx8T2/g4GXa\n+Pr9kX/x4fsINl9ykfP7NKtQlegyCnS5Ff8v4oA4zEC4ptefLZSESe6J9O04\nPZzLUyyUFBSRUQQUyu9HkUcNSlzHBZXewZ3npd4NQB8C34hZuib23Vm+mLi7\nbkuAVJ+9dEE2oUjBA8PP2DqX8ls5Rwr9yyBPkivt7yNwAHIXWY7G5VivCEU3\nWwU26eC6etPGZr8hXCQK3TgOyW9955aJNhUxO89Ln4RoPMS5hAkti/swXEo1\ndnk91ksHhlGC5HRjtfr25bx5ohXbLYShJBBekJo1CWFPb71myiEEIPMcFeId\noPtARCwTgRHJFVXwsKOPIDxzJTko7ipyYIdV3B/IAbaB00oc0Hh4yO9f2l48\ndWH6Few9Aw5aPPZVGzgX2rTZqy2V1AY+ulijCJbIM2lz/lzKIsz/jgA/k/AQ\nmODoOofrVRHKJKDFMIxlw5r6jhRb/UtdpkkDYWHPtHWLN+DxOULMiX0DurFd\nEYAU\r\n=9nZn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAKHhS0BmEnOVrz1yr6mnaJateyAgTthoraCggR10X/tAiBKXDhF6aBJd9PdDwU2sYeTcJ+ycZtX+KZeCcIA/5s5VQ=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.4_1571695418829_0.06917352947457767"},"_hasShrinkwrap":false},"2.3.0-beta.5":{"name":"react-auto-edit","version":"2.3.0-beta.5","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tether":"^2.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"5a52b5f3ab445e9708e33ca0ac44f2773106ac58","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.5","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-Gs2kvnqlBpyO1p28DBTNo1cBdGJrll24lFplT4GhMvawUb42iURGe09i2NSbdEpcY5h4HbdMXKNfOt2RpRDpQg==","shasum":"3b7331c9b2b136ed5d976f79f02312bd909a5dcc","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.5.tgz","fileCount":120,"unpackedSize":339240,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrkmBCRA9TVsSAnZWagAAsnAP/1cvIO0n28ZAcoSlUF0A\numf6wSNG5TT07qhR7AwBCORNl2Gks2xoHHWKnOGKo1K7HPM+kmlERoTS1zOf\nEOEDixDqGdHPMEWOcwrEcDqSH79ncJ6ewpAWp3Kj3aTWPbgFY03p0MVCpbGK\nOaRJhkduk/xI4JRls8ezLQ/0WHqhvHGFFtoAPtRZmU+aLgw5ETFdwYNn1XX/\nz27GaL/cb75TGE+0LJpIjC5jJondj3KyutJ5ShDdQJSSJR+kcAIheyAzVPAO\n7BBogn8p6IFqxBlrCQ/eNTNCzLXyGk/LG5z4VfoI88ikr74mI/D+RLliUOF4\nJdIR7yntpuXbGcglUx3D6OOGzYtSlBsosRz6e0vxLKjAbbwBmuZd6G0Pw8yb\nj3l84DQI93N8GTWEUuNHaNCTXjD8up0GIYisPqKvE0P2gO3zMrbvfXxXSlYO\nWPwCccfQXoJdnTj8S8y+R+6wfpQs7ce8QwvZgjyHw4FtTbbHrc3dDBmgExYU\n6NN1qFrmfMUhfxtNo/RmlRkiRlOyu0NyqQx54nZ+jLmd2darqI14PyUd7wWL\nlrQOc1vF3px7aiDmy6/VYeW9B1lM5ZR7u0TxXevqzuIR3HibQG3VO0wPf5sx\nIwZHm8EzeZLQGJPsXeyj3lKBm7PsfmUwbz3aWg4hdthmnatTUZcscKCumiS2\nYdT8\r\n=fYZM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFH8EejE5OfIy9ejvzkeL9jafgEBnZg7SdsL3XGEqhngAiEAy+HgSFonI52o/VPJWZXvm/qEJ3y0wobnNEOGyGg5AB0="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.5_1571703169019_0.29805742760828546"},"_hasShrinkwrap":false},"2.3.0-beta.6":{"name":"react-auto-edit","version":"2.3.0-beta.6","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tether":"^2.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"90ebb4f58742a1cd0bcb3c66af94f89b6c267390","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.6","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-rpsKzW/fj9t9Hwyq5Wl5y7tEyxeFm0+XTdUvUgqh40NUPom7VkBUoLPWdUD786Iongxf860c71K0k96JdY9QZg==","shasum":"ec6d376603cd9e72ea65bf365b2ec70bd07106de","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.6.tgz","fileCount":120,"unpackedSize":339240,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrlruCRA9TVsSAnZWagAAgF8P/2DcPBhp7TQ2urpWEDWy\nXVFrKt2erZ84m5YmN6h1+FbXzx4rJT12mUr59M/1i7AfBZMnwdIzH/uHDVP4\nOcmRlyUISQL+EBzyhhX4EqTvoo3OOU2JIwkxh9/eL9n/8yjh9P+x6L1FpA+D\npxljyVdcigPE0RY6xComxnQIOjea+7nfLPV8vs0h1oxcHA/bW8QB3Q1O3zk7\nDVNBTa49hpnD+L1QCql+diOvxUVwG0Xaz1ocpYEGIQPGe5y4gvsqiLyu4XBL\nQrpkRLHFjZ5mkS4wAh65pjJCZ5xZrAX9PawcPg055bW3ymNf1Ac7kLIWGPwg\ndPuNkAVWdO2HpiB0bhPbHDAANw022aeWsdlh6pD6HF5c0BL03ob8G+KyaUxS\nQj9kqcuc31iI8olZSomBKkgnrYWve9rpb8mARSEodo+0O8P+F8sFn0ss7Mx3\nQzH66TK6/rxW/V0CEbZHkTBVHS0lXUdhNeJ0U+zFLFTiyAKt/Npk4Yvg2yP1\nuqASIzTSjHYQlZdllnJerPn4a3SlPy5nUL2vB+cDVmqOPT8imJg/popba5+B\nHfEaZwiFgt+49tvJNdeCRxSGI+h3+3lZka9Wq+foUSIuYLS44b2vmPxeEAZX\nyWaInE5lRqAY/vJ1+kda0uu9FAuBsbecqQamqFSPl/EkFIWi8MpR+f4Iz9Qi\nHcW+\r\n=cX/J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG2LAHTAms6le/r/XsGjs2eJPXG7EmkYH6ukYIzOnCOvAiB8rzO1OEpj2961YRgRsDA5jsorlkANKwCZpZFy1SExhA=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.6_1571707629680_0.43904935729474737"},"_hasShrinkwrap":false},"2.3.0-beta.7":{"name":"react-auto-edit","version":"2.3.0-beta.7","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tether":"^2.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"44e28a8267dab1d0693fe889b4604b1311c0f0e1","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.7","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-RvbF0B7N9OYOUWsPHUdCF9k6VtngSB9zpyr3HZbtjei8Lnzr8YoCwDDW2T5jrmK5ufwjUbhdlXrGoCgThhbQ4A==","shasum":"1aa2798b7b678d55b963543b7a91fb24980e3e0d","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.7.tgz","fileCount":120,"unpackedSize":339240,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrltMCRA9TVsSAnZWagAADlAQAIbQvUYuoVmAZPxUA9h4\neM3ELFV+kus2miMxLeEP0XDK5TFBqZlTdg6FGBRVoM+eGPXrhx7EG6EtrBkG\ndsi+jpgj0ns4JgKO8WfkJmpKag2J9KlmxjgCWF2/Y/zTwPjVmwg3/IGo1b0c\nsH+/2MeiIFZ5EUrxqgNmLQgI8cL4v/WILoXqkOP3uYPGYl71CVconSzIIa5t\n8zKpmZiTvsR3p3Vn33Lj0r7L3jT/CXh8K5kV2PacXyotO1X1fo7T+7QsyjiD\nf415hSWnmQ7+liC5918eWrqEy4lSRTn0JPj3udzzXa/ZLzVPKHNec/wAfuul\nG674aOhHlznae1Bx6+EFaDL/OPJF58wfe4Igfe37Qtg57zyKJ/090uXgnhNN\nrLFPArnIUedEJXyUAe4Qx4nVmlXWFXYBNhsYyZT18mEsGOywaXeaJ1h797LL\nuV1QQ3MY89KAujPMbtbiaPmf2BKBfk/nm+kuJmYyy7vEdHPeeIXIJkiY9GAm\nNvpwfTBHUc8sNRUxxAhUklsuRTPs5ZEPIHidWBYwH/oQwUa3UcY3kGnyy7R0\n9gNbfXF81Wp++hpAZGoEV4jqeAneMoiBf1pr6AY8rnsX5UEpHatwlC9o2QIL\ncXbgqMQLc9EDsG7swzaZYJCwF9c0IOKBkCq5Xjqa6qR2YkGQXkSqk4Gls1ki\nr8F/\r\n=o9kM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIApAaUFfsPk+pnpS+OLr7ZHEhijlbEbLWguApRYjeyM0AiEA5FXUNR4kGQpblXX8p9gBGIZRFsc0PKf4AmhpdgKdjR4="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.7_1571707723534_0.916894289793595"},"_hasShrinkwrap":false},"2.3.0-beta.8":{"name":"react-auto-edit","version":"2.3.0-beta.8","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tether":"^2.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"27d731491f1a64928819dfdf5bf49d9fb61ef71f","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.8","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-dkSazhPuWDqA7mNd/slSXGp4SdNhv2X+i+FS4WALgWVS0jB2adCiapR6FNwisC+plwu+rfANg3EiLfURsHsb1w==","shasum":"09a384d92f7da1b97ecb4a3122a9bb4a934eba23","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.8.tgz","fileCount":120,"unpackedSize":342068,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrl9GCRA9TVsSAnZWagAAqhgP/1rMU/rLQEUpRXqCAFSn\n8omZIWh9yYCxVd6JL4FoURc7hn2xgQsbio/DG/7hh3BdrM6fu0YEYCqLqYaw\no1sNBQVnu3urGaPU1ObGKPtZcYQJyUJcNuekSp/yKB+s9IpX5NOfRHkztMWC\nROseGPYPYeFb2cs/C0JEVQWeDqwLOYyLcltZpzGEXyRw5AMcsjiigjMx0Gx/\nbYxmOyiAss+aefdF+Vl7uMf5sWxJN3ZwLeFH3cxSYfsScBEnQufxuKpO21jk\nnTBni+7di0uqsDhY0Q4j5ap0ocG8s4kJbZe0YxOOjP/GHN4WrSyLHQrV13qQ\nInYb68A/2dScLOzMAy2blMVEHR69+N0Xa8YqARVtNmouwuxp/HwMOpRd9pao\nErC4nKGq21Q8FpUc/XB9e45ire/gOkDxyXdjTiex5onIEndP/b0HnjHcVk27\nb/J3EIfKAQgNcyZXyyGhWVFyDxeRviN8zNbAwtN/Gs8eo+m4N6qb9d52ZjS/\naeGaoezuttXx85ZX6AfyVNwMcqx1eQ2QE3PolB5B/pHQvrB6CQ/RsuHS4lIZ\nUZkSUwwxTpLGpAAWBeN5uN+dO4aRwK11MtPmowI2/hn5z8XH+F4gK0etBecE\nxl/L1DmMUMhDXnCRCdr95C2nZfWrR/rkNSX3wvRD/qq51ZsKPMj/SA60WQk5\npmrV\r\n=qnNj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCIv0+Skc8/OaZXFKgSy+YNBhhbZHpW13DYutgI2Un3YAIhAOWbmKRCy6roNi1JT0LXTOIMiefqNQpub8r3o/2EDkNJ"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.8_1571708742267_0.21876298194461752"},"_hasShrinkwrap":false},"2.3.0-beta.9":{"name":"react-auto-edit","version":"2.3.0-beta.9","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"15f0fa583c47e11802e8d14afb0c7e7468cbdd39","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.9","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-Q6JkzVQq7aUSsC96MLpXc1eTF0JX9V5tOOLWkDyVzYLC4X9ZacdHN/X+eASXqtmYkEGEp+v+bzbCWiT6EMtApw==","shasum":"f9552a11cc986c003ad9c30df517ba85abbaa974","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.9.tgz","fileCount":120,"unpackedSize":342013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrmwpCRA9TVsSAnZWagAA1bkP/3o0tmw3DoETHFtQ5UMh\nJcKOvM+O7W4oKFd3bAQK9/SNwbsLeOmsoOQDaNYkYvBBpfFaQBZYBe6zYD62\n5lX7DFl6tm/a7ZCclU3G08KxYKBOMT3TGjGy3KPenGNEjuRRWzoySnPRfppA\naXgLjAip/Qg8qTwWDTGs2x9UIidhNhpZRGJGwOHG2RG0k1eU8PMJjDjCI7dL\nqQLSko8y6RZS65mk18jM4OPsIPdNA0xtYXhnqRpfERMHSBa+yOW29VzjqgnU\noCj+OeEP6FXtPRygt4y8YZmRVn+QHEB2ob16HbVC7rOP9jXTzuVxNa/qEKWn\nLimD6qreIAz+tQg3BZVdZ6G2G4w2VShSxlw9HHUt7VlUKf7rMHD2RzbbhfH1\nSVn2gGZj3bvKZlRPCKQ5SsYdbwduotY/9vq/QekNKUrXz0DNkZuV9o1ob3Vf\n7puCVMdtOPWy8ckd0Wm4Edmx+If/hQkeUDeaGnmCWPY2CgzUjMLBG6xiesV0\nstpwKDPW6BJw36SpidXGuXRDfFdFrCZFW9ubIJn1zS6wuvrK+T4XhRgbAFaz\nFlptwDJIn/S5Z0D7DMePTI6i1nh7SPKydSM9At5LrPwUu1we7SY2Zk+Nt4+D\nVJPGuOVR3C7xLtX3jb0dZSDqkH5Tja7CNNQoY8yBab90MjnD+9LdHNRR2sPV\nLzLH\r\n=jSNt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEnZfV5o1//h92qo56cWbK3S9/rPwByHpZjFpE4YORplAiAt/N55+nx0voK7F1rXs46Wo65XkKX132Sh4uVQ/4yOxw=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.9_1571712040780_0.36452723640382767"},"_hasShrinkwrap":false},"2.3.0-beta.10":{"name":"react-auto-edit","version":"2.3.0-beta.10","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"96ef13ef3ae97d94dd7f700dce1eb8136946015b","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.10","_nodeVersion":"10.16.3","_npmVersion":"6.9.0","dist":{"integrity":"sha512-zs4BF3OMQM5o7ztJV4lKRIO/AzXJTTYTuG6OObQAc8vfogwrm4EQOlFgJSu76fKdvdrnZF2TPhBmf/W4Pt7Kkg==","shasum":"bd14b813a7926197a699f3efffb9adf58b5e3430","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.10.tgz","fileCount":120,"unpackedSize":342033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdrnDBCRA9TVsSAnZWagAAcfoP/j667U/z7M4GcuDlXsgm\nvr5XGmzQKs1cdZowU/pwZpRwlYtqhO1HtS6ChTwxAAcLRyu+AYA5bd0gHC7p\nnb5aIgqAZAeLqNBQaG4J9UrbuSXVN8zxG/iO24FimQy5FF7nOe6trxQvxSb5\nc/NRT9AXER5Nb7TNImciP8G5N0h/mkR4GXndv0ygMI7hrD33lTLepb+xAbc+\nCBFmZQL7LcDVcifrpZmDWY0/ssLslnDvw/VfICaarIsEnSBQ/jBJFQ94qHTM\nFKMRUNjE2Rm1uV5kNEXUs3Cpj0+D9xb5BtjWmoE3Sp6vpACRx8qI5IV3vTzx\nh1nMz9q/j+Jo+n0Lrr86ZsnxLTnG+4nEuOXsUK8uE+J0uZd2EDvpwbuKQtSF\nBYKbbk+zfqwKDib2acNdRzXUmhR/Yjlb2txWfvuCwfJTRHarPn9kdGNhFMA+\n1Rj/2eiMfzfz8hTc5oESEC1C75PEAxdzD7kBKOIkmJvQOP5oFUN4zXbEc5/a\nkLG4u3ti6Jm4uBm8gBT3JO1ZVhNz0aiQZqDosVlWnDbDGTd2bJKvcfTj66jA\nlerYBq46XwVF8mLrZR+bQgtHlVk3K8PjEFy+yYxASNpeqsrH8tjP9nNRnIBh\nOWLz5UHfOfZj2yQ8dWhTE//LBOXBiX5rKUxwp0z3HxshvsT1ZfKIv7i8tmxs\n1IaI\r\n=2USW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCEnuajSjvnEA1SQRdTUvb+tNbAZQvfIBXc52wL1ZPmUQIgVdWpC9yhFjEO85IZSV/LmhFHbe4iJl/iEa3Nf+pXQoU="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.10_1571713216986_0.7610580773640954"},"_hasShrinkwrap":false},"2.3.0-beta.11":{"name":"react-auto-edit","version":"2.3.0-beta.11","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"7015afae55fb20083a5d87ba6f5530fb518675d8","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.11","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-kuTwQ/MfGaUvWaatmOPJHnC/AfizqeHH0TPPOrvSbADLPSxrJbItwpk2FBPHxkTnohNz3XTaM6qbyJBE5L7Icw==","shasum":"9bd1b8f866c857210a8b44a0d8fae9899c6a9df6","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.11.tgz","fileCount":120,"unpackedSize":343695,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdr3HMCRA9TVsSAnZWagAAsk0P/2isX+RqYm1tIgoYdVIE\nQCb4jfyonIO/ZzKiyV2G4j1SCNalVrrcULLHX35quFzrtKyuI+3qKuZ3/xlY\nIDSsrjxPhJP7S5l0iGc/hzoh+sbMTBLxJlo1pCCYbxGNdpmje4QJYIzAP7UH\nQSRyAvzIGVfH0ITFuLHaq+x+NF519ONuS9RvaCOuDezcs5TlqIDtnnGNdcn9\nmst3zDSDoeRRgaVNf1IDYrTCxgUiv1kEMMrclkwuNUF9jrLnX8j1K3NEFkvg\nJ+/rYEDo7bKUCpPh8Ufz0ESibv1/4IxXFFmOHw/p4vEROyu6H/G72xsGy45c\n2dAkfey2eFqzeEAG21Ei65rbDe8di68Ajl/cPlkAqBTr31dCIhMC/WMQWXFc\nXEUnPTBey91msyEnBxU5aBSif79noofqpsa1q0Xq4tAwTTjm7j2+0A3ptlZN\nrSCFQKtRagCgaEtKQigyrtEMm9Cq/IKg6oa7duMi0hM55FwFPyrKuDB6kbp2\nbxaJ7yh89VxjBu3jWgWJhmJri/EfYI6ib7iibzX5eULzMHTRBKQ3KdLKYZUe\ngI5RX+v1u0gKGJ4Lt/aFZcuVZUZESJcKUKhQqkLgich5DYQDAuq4qBrrlx9Y\ndVdJMCvTh/NXkSPVYOMKv6jLgd7kNw80/pLvQNg0h4prXpmHYoJnmuIUb+Q2\nrHnL\r\n=Miqp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAhxKuuJtrk5Q219GpG9OmYgGSDa5WZl4hD/AQl9PzJcAiAqsSGyE8ERX+PPQIA4HpeT23s+JWQStCBGVVkIhIvlWQ=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.11_1571779019532_0.04686600170607713"},"_hasShrinkwrap":false},"2.3.0":{"name":"react-auto-edit","version":"2.3.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"d0bb1e42ee68cdc3a1ab9c84b6272fdfe2f8ca3c","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-TFfR/9Br6u62JoXh11qjJIaKbYMQkEUhr8MQpEEdL1X3YbfYwOwht4p0rD1meNAdt0YgANKsndPxmi92nzibvQ==","shasum":"12e27c30bdce15377cf33bc1614dc8a8d81ea48f","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0.tgz","fileCount":120,"unpackedSize":343687,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdr6zzCRA9TVsSAnZWagAAcV8P/R12ovLaJo3x7DLe8SyW\nRAIWqN21OQN4eEvy/2QRdr1Za6GtvHaBz/KGk/5TtoAfEwLNZIE9aaDQoxXi\nno4pNZENFBbj+y05StlfhMOkwOAa6Ur795ERAQMuRYzquFn6Wik5bVFAoI2L\n4bqUVqlB06Qzc3tDDWB1D0K+u/z0j8POT6WnPqnhYspUEycqFMFulYgwlrvd\n2YuGFwgKEP3Un5wnpRQhM7PwfpG4DhZXDAHQk4JCMg0QcJJix2+Qj41KoV/5\nkVcm3tnDgIHN9EH5cjFHrkbEgJJBi5jvH9Unv/1CTCURrD6z7r8MTsnXZxJ2\nQN4mdFSRRannbJY/rhNQuEobMyvcJ2se8RadHVzn9tfddfIltw7feI0QbKCq\nA8Cln1pserMCXqeYXHKNwFOLI+oq0Q3Uye28EZ53P/m0adTzy47uoiftCCDf\nSakyLKe9VPw30iJUAB6ZATtbstcrB+6tlQvYv7WKjs16VraAaXtNq6xWslr2\ny+v6iV8YcVVRU591w2i+Sljx/+4VT/E7gtxy3chczGnF2qpJaKofx+gWv4Pf\nFESr5O/PgRouUw5IRiUiHeJurgG/QVQU9DuT4e86Fyck2W0NV6CvfI5LNZVu\nrKWSAdfsgsjxKBH86kH4ErI4kVcd9Pi9hkCtKIhaNd/ZO33pjqJp7NuN0Yg2\nkWb2\r\n=9SNx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDBN/RIt+rmM0M93/30vV/HkIbNqfeXhhlEDwXH6EHSfAIgQDxueQKoEKYUrqfMQa+yxFIwJgRCU4dcjz7xx+6rvjk="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0_1571794163073_0.02093988216708964"},"_hasShrinkwrap":false},"2.3.0-beta.12":{"name":"react-auto-edit","version":"2.3.0-beta.12","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"74db760312fcf9fe9d196e023d1ce641dc11293c","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.0-beta.12","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-+XNj1wSjAur6Z79XmwvtHn2Xn0SCgNrdXuCqu54bZYySuecSSgH0DDU427rXbN5NS1Qi3i6oWa7G3HKpxV6w2A==","shasum":"8ea192de44c372ad24826562608c553632d7c1ff","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.0-beta.12.tgz","fileCount":120,"unpackedSize":344108,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsQOmCRA9TVsSAnZWagAArTQP/i/BH6hyNK1frqj4qG3j\ndVN0s7K+u8xuS7u7QWJv3MvHZfh8oop3oOpSObjJY7IeKGed9MfFeBwWSF7h\npt2uzHXDz2QX8/kIauaf/kf/5xBwpQ+VuScTSJdmojhgeGMMkDvg/eXcuk3Y\nAnZ6oGGLqNXFjDJVlWKNqKjF6vXeGLHPjlOh1lG8AnGGEsLxx/ylbbzdnP1H\nXldqTGWfDAAUPHRzldWEZH66rmelRIeKcccFsRRCfp5en81vMCjNHZ0K6tTg\n58mNuPfu+zbrPSonn+k9XaeK2Jrd4hK/jk54dW858sjo+BDxFDXtugctl6sE\ndOwKpC9aucExzSFDTolNT3324VyM9nb/V2K2b0D4pR7COfAk+VUEzo7QadEO\n7StJsqRZiB7qu7V9i5gWHUlAfsboZ3hEsEF4aNRh8WiVsvClwwPKMDkGGs8z\ncjn68Xr2wPiiM38F5SxfCXs3kLtldVvxEjEeAbRuxz5AjiHqBXtWQXSCXtfU\nI3RtSfMvq28sz/SHiNyTjd1C7Oh+dY6PyIz0+ZAJMr1ZAdAfYKFy4cMb5Gkh\nw31ZofhJVpNsr5mSVm2NOSDRy7RvxrwlusSRWC/20hb7Ot5Q5gDznEym4TDN\nzPJjb77bcOMdMwSd5j7O9uWd00B+xbnoTTswT+j8KJPZGuOMBJ4kHgFKXqFT\nkT+R\r\n=+ACn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFaN7pagcWtNcwvZKnCJ7PnncQ7gnQ49Xa0h7tpts3rDAiAk5RYqJR1WDDfwhEigBWDoIRPGdm2yNEazFOSOSXKMSQ=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.0-beta.12_1571881893837_0.1431292002007971"},"_hasShrinkwrap":false},"2.3.1-beta.0":{"name":"react-auto-edit","version":"2.3.1-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"cc10cd5efa3523864034dcfab515727a4c78062f","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.1-beta.0","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-0op2xWEa0PHi6Js8W4/c9pxXisw/5wTbrfYDdJ+7vZWM/DA6PJE11jLsMmYvmJZToZ+03R5Yulp1ntAgf6cVhA==","shasum":"4471d3c3c2567853ad2d3007267105701ecb32a1","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.1-beta.0.tgz","fileCount":120,"unpackedSize":344107,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsQRTCRA9TVsSAnZWagAAZwUP/jY0HdpARxa8x7c9fiav\ntGXDCPkRvlr0Z3t/TooOwDxGCN/YYxjxH/NRp/f0FwZf2PIpG0GPSop4ql2E\n7r4JgXLIQEnPYnFmUaHdPMOhMt+1AYrhsBycWtpuVxDV5tSnSppw0Qq5+oIX\nz3Th7yZgABbNnPaiRCICNbmxlmumcexGwVEsOXUwCPGPDkfjPVpM01tTMSNM\nl8Q/U3msGYok18usx9N6+SkgevFdBlclbWGzFvZgKDLFzHzxEWPa+ryjdPPn\n0NkK68p2rVz7bxK0ZgZOcP72qqxfNQNjtoLF0HnbLp5D95pe2xD9db0uaBMo\ncfAttOjRpyUYSoK8NPOBQpoWhgpjszs2Zymwph9XmMIkU9WtFLOGgz9bOCRN\nf5QyfiBI3N0pS/f/gmZM5JZIewlzzxqPp04HfGJj8WyKUkIFhazmXHs9awQZ\nxNnQ/UpFSvkVdwFHw4X4sMiQWg6GUoEV30Dhe5NG19lHdj5f7lSQTuBaUXO0\nRgBPsHTeuTcvaswDQSnDXLFHRwYVaCZ61lJdX/5Pg0EYp8FsJAhoi0t4aqtK\nj+lGEUXYhXcgRGdVs4R86cV9CsGmBraFNKthSrg29YgIVC/CBhlkrqUVSk7z\nvBlwv4cmoavMsFadNbydN54T/IRBE2RiscQEb0oTuRrjmbFORMUt47Y48pgd\nJem5\r\n=Gqcs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEmL08mH1X2H8C/9wl6zTWtGWJjofTmLSSwn6BzFz6drAiEAg2fDTVYYCi8wu7nSbloE8B5rYu9To2oOtqL/SxEmPOw="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.1-beta.0_1571882067252_0.1093487599914178"},"_hasShrinkwrap":false},"2.3.1-beta.1":{"name":"react-auto-edit","version":"2.3.1-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"571bbcd86f8e3e642459edf7f8eda6c6ff1cb41c","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.1-beta.1","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-Z0tqQ/XiXzG+2nqkHkm21O0SeILOAm8xO88QzgkJeWbrFriFfrxZnaznoIpqtdktXK4MdefEjJZy8KZM87tSEw==","shasum":"291f3c637a1ae3752ba5b2a5ffd41c9f6c17c67b","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.1-beta.1.tgz","fileCount":120,"unpackedSize":344107,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsQStCRA9TVsSAnZWagAAP+wP/RTwAMGAY2MNDNjrYXjL\nkJGA677E9SHovSu3x91yUphOOTOtdinwOELrMNW8D3zvbxIYs3N2P2KJB4U1\n88D7kM6BeMKGLM075dBxE1dzNOsIGnpB2neoj5rYfb0Tl80jMAowo8st6B9o\nPZLdEnKW5i654gKKqQbrsRxF0PRacaljx1Pm9LvDnCnMs8RQtLK1/dBXOFc8\nXko8to/7rBV25OM45bGP7OcomAUdADsxu9b/zAJ1GBOBjq8GuAgTqfQT6pr0\n1rArfdI8IOaWzPIsERdv2MX3gVEyp0kFSRI8NkbsAM0mFrqu3P6riQzf/Pnz\nE+sLVmlLcYvOxQPvkstpg4/HbPUcs0ypcLX4jOf/8bmk8mqd9Cd0XTU6jrEQ\nRD3XpuSlrFbhkggOjOoujUmyiAVLzEMvMONJJaeJ4YNOcpPES8aHIKlIDvzY\n4xRAGgO+u/Boq4O8rBqtA7hwT9qFtL0CEaE5VwfghKOdglcLu350BZRogQaE\nLIX1DtyeWJhs8rryRPB1rzTP0WZ/ChwYG9D3syNLyPJMZCoNxpyO4BhjAxgw\nm2txsDPdVV3b0eHRTL/FZWdtRbbxC2Oidjbndf6hiC4RIFiYpoWVlcEO0ZUS\nQ1oqXcwPYfkYooEgodFb1EVYwvCy/Wsi81vR2fN7wp4AiJHMWo1dEB414dj0\n6tNF\r\n=Gmox\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFsGlnfhtPiZ8Y5z8OaAUikKGawt3sh7aaAYFdu3iBRxAiA6WfgCsip7S/IOVPJa1nwts2EtRb2s7qMI6zGjiKyF7A=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.1-beta.1_1571882156734_0.30045515089448593"},"_hasShrinkwrap":false},"2.3.1-beta.2":{"name":"react-auto-edit","version":"2.3.1-beta.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"8a3834d6b1fce1ef691e3bc637ad71cebb2ea82e","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.1-beta.2","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-HDaCLqBk8Gr2pB9w4LYLoHiKDmtTyvrJ/hvczrBZ3b+8Yp6KRDJMbnO96udyerb4L/PVD/Ah1I1H9dl8zdNRVQ==","shasum":"e6180bbf6e3cc013f6fe0471d7fc499f46b32763","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.1-beta.2.tgz","fileCount":120,"unpackedSize":344107,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsRpDCRA9TVsSAnZWagAAoyIQAJAz0HwfLBoGK77FrKA4\nrUU011vbk4hOmB2szty6bwo/7oe3J07dO3qmkmWAYPblqYC7QUr+Kjas8CNW\nixtMJymKjR4MM3P9Qx+7Ckzi43WIZtkuSMEaUiaU2/AfUvqw288lREXVjODF\neRyBfjpXyyrLdLsluapHu7ttG0uL9FHtY9GU2N0Y66e3d55whlFMpB4R0Ynf\nG6LjKQWysb4NpiWWppd4qT3FmfL7O/Nek8Y/s44YcPRi71GCH/ePf+WE8mMY\nDBIaojJUqzgX6s5skf6muCUK9uNY0fWNbdqKFk11G4cJFX7DgpaL/9lHLEdo\nJaviSOiSmnt9WYDSjKXzvpqCCK9kDW6+Re/By6Xi8zHuyt0VHDaBTP+MCpE4\nsxj2zEqtHzfvUp2ArvB94RJABhuN8cltil7HXfzdts7Cpljk7EYoUm7TLy2d\nC2vlSu5j6BjE/LAFOASl90MFUPyKYxiaKPME4EVpXGOczI4tkN4sdwls2dmd\nPJ4J0N/rTz1B0FFO5lh+MmO8TDDIIIRZWmUxxdlxXXJ9oJcEz3XaasSPKEXr\nuH5UWzBONDJgmL5oDWfeZAzeSCS0DKdMlJ9stEOCnazdZ++W6mlM9a4+uEuV\nZ/Hka6uV5Otbp1idYwomiS3Gl4SrvdcepqPa31czLNMJtfNHCR5DD35IQfnC\nPgJu\r\n=4pxs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCAufXpwSvU2BBkxNT7raj0HUD+bijfSUoL53Z3b9UBCAIhAOk/I+0wrjMsv/2pEH/70hUVa2xY1pXOrNTFOhTBjmOA"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.1-beta.2_1571887683059_0.31588258480414955"},"_hasShrinkwrap":false},"2.3.1-beta.3":{"name":"react-auto-edit","version":"2.3.1-beta.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"190ca44c4311d33ac605d44d3b1c1ea60d0f7048","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.1-beta.3","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-ZbmgDNzKSuD1S8SZm4U9txiKIp3i2DLuTGE2Ijpzw0BTYJMP+mQ0IdzKNLm/amI80DmdypbHzX7mtSOKSP+gDg==","shasum":"b269827f18f98a39c8a822923740d7513af061d6","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.1-beta.3.tgz","fileCount":120,"unpackedSize":344107,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsSH3CRA9TVsSAnZWagAANswQAKQ525U2/ypW7sSUtmrW\nUvA4KBCvvFcdqZYVXhnUsS3dcJeugnA/lYnZDD7TFgTVGiWe93EfEjBZj8vQ\nlJylXBuDF2PP8vGcQLCauanH/ha4rqfmXf7y7oOfCB7o3PjTCoO53JYGonwj\nKiyIwsRfu1U6aXXkqsDoLB1u2Os3xTVhr5Bl14oEk6cRqjIgBrlqAlGROryr\n2SC7ZC1Ov6Hz4SYtTTfp9QNvZYVWzY4WN94GoRilxKmcOB3MwT3VTLLw7cgH\nqm/tg5xExh1euwf52xuARE6eKq50nWsi4xGubT6IO4md+PcAXxhqlCYwiyii\naMVDr0HfOP8UAPsfAyHXiUVYvl/QdYiaQWfB3qpinypEFV781f7U+Bup4uBe\n+xjbFRMhDW+GmM45JBksJ5RrJts8Evo3enCIcINyoANnx8lmuY5wcAh607Ag\neRR9emmXqnvZAdQgmCoCQ50kNgdxzGL4UiVZJCjJZfqRiOLyWRyQbBUtrWq1\njM3T6ZUENv4ZX/kIJuUGpZHlvtFX7TljogVDbQtAPPyrn0d/hLxyJdjpot+N\n8pbDQ56ipbO6F7rqcjO8vsP7x3kze6JHHElcOVkNQQ4ts9cecE6pga2djRcR\nZt0cybhWHkPZo1Y0CUmCcXY5KvIQVG2LwGUm1unBOPJRDmuV1eil5BWYbA3Y\n8vst\r\n=MZBg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDp0lJDGLTmMM3EEU+asFK4pp2ZFcIg60Jn4rnaUUswDAiBfqCIVp4S5NytoQIJpfcEW1u8ilr+VkwgCcAAENpdC8w=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.1-beta.3_1571889654802_0.47027900864194216"},"_hasShrinkwrap":false},"2.3.1-beta.4":{"name":"react-auto-edit","version":"2.3.1-beta.4","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"6c407d4b866fabdf00f985fc3e2c84bb13e9c507","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.1-beta.4","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-PhkuE/f+Qo3Ay4BJ/kKe5LRXLdD9YQXcH0DMqtAAHxNjciCKxjK+wuHj5zzbXI0eKMvTVjgN6KoXenvYJm001g==","shasum":"9f32e80e14b83b58ce70164103f7b548fa9938a4","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.1-beta.4.tgz","fileCount":120,"unpackedSize":344107,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsSK7CRA9TVsSAnZWagAAAtYP/0/j5eN4DMRk5N+to0yJ\nmkn/Pkg76cnX3hg1VmbZEFbBJxqv3f26f1oG3zaO2IZB3YiQDYPI4LOQpfq5\nds5FlzEcnfWWkpJt0ydmvN//ICoFvxXNrTgKuxFpzXvYy2FP/YWzsO7WlYfp\ngs3D6K+L7Jx8tswfUIBjiSaiSwlD4lzGndxbou+el8V8tA3yjH921UAmjg2x\nAO2dNAwZ1gta3ccwZBTquMHzXcKnP077PRI2/KMz8EvgXpwmeUQUO3IktN4K\n8iqQgaAfiWNfLQfYO35+kiH8w3gr6jCgjcp90wDog4+Rmy0Hy7CY4X0upCqe\naGlZ+QEhNpJJxz7K467gpF3eioGrvyIw9OKS3FQ1h/skxBcRHAC9BqRf+ZJM\n0ub8j81nucCglk2GBHVq3fhpXDPN+xdrFpN1Z03i2NX3pJcKzd67q0Z7m91V\njbV9FTsyLtU/D3eMKbvOh0OnECcnq9EZzoOv/N44IGi740Gn4WmMbkbRcHtg\n7GJJ+yn2UBUGbNDmhKdYlFmuoSjXAWhk2zGrE9/Ru58qOb6tEicVPO3ZxS8c\nXA3Ky8Mjp7SlkaYyi3GuwCXr1GwGESI21jAvoyKs9TOdcP66z3S7MXx4yHBv\n2kOJ+grxpgaNdWlMCq7PmqclH3x6m9mh1hivDaIkdFZaiHEAGQPijWOH116L\nYAft\r\n=FF3i\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDVHpZkXcsBCO3odrn8kGWiq5zYtbjR7tcpdhX9PHFD0AIgMHmFOXQt49UqJY3pIHdX7WHQrsgwqnVUEtk6e/cs5jU="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.1-beta.4_1571889851039_0.3679765564009281"},"_hasShrinkwrap":false},"2.3.1":{"name":"react-auto-edit","version":"2.3.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"1070ee2d14ec036a5ea4ea6bc2ba73963046126a","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.1","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-UQI6z9J8CXbDCN+GRTv9Y1RDymm1WrwfK2BTuiw0HpdoGyd/hYGF8Jjy8qTNtvlzD8WL2wgdmDnTuK88L719yw==","shasum":"73acf65d3a525cbd084bebcc2b9d8195b994c90d","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.1.tgz","fileCount":120,"unpackedSize":344100,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsSM7CRA9TVsSAnZWagAAQT4P/2Zn9IVZl4y89rR3dkyv\necJfpDI50V5nA9OFnpUTXENN+TiX/2CG0OojOrXkSeDPo5eCWUy7emL0Rtpt\nn254QcxKLEIBI5z29MIdmf+Rd4CM3rGKVS8niJcpea8rpMMMBpDsLr+XXt+v\n1u8TBzRgd7nwetwv1zmgdpISJhuyJcvWdS+zpIFx0wJOTxeTGuJLFcvy1CcF\njky8IJuWvvURJOa9qBV/4B/10UiepApDOkhMVcXci/NtToXZjQ9SW2kPKVCD\nFC9oRw2QvEmYXEE03sHw7UNPiIcsFYrE02/6kRsHERtj2Q7r0chJIbLMKScu\nCzG0OGwOEwAdcHnbQkZBW5rLBhhJ2UiLZc0Yyf0Xj/esA5LKhVCD4Nquf50E\nSMMXrR1VEBdwrjIo+zEK/vIZCDiylyONTJ7pRMrnZQ0sbf2U5Ss2ey/mWyCz\nloHTvVo6hXlN61RbGYEsJaGwfjiU5OoMJ3nkJIgdMeWj/uCl969H/xMNqKqN\nlJZQ3STqmoditdfC9R1+Lo1ravhgOM132myvHq9JJmASHCAIPWAhGtDEdkTv\n/V5VHSeI74fxIYYvxDWwBTedEAkGsjo+2vGd4ZNchzMz3gsDZVCNL5rx9yuD\naWodbHY4AJn8wZgEaVBZ4kpudmOy7kL2VDHOo75+L6f1biiJLhoP0Ta/dMGp\nBZra\r\n=01JM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBXyN8auGjsEk0HSC0ztnyxmxzYnOLIyX5L5OG5Ds5osAiEA9MyxLD/NA9+S/s8J7+Q2Uss42ttlwW7A5L/+Q+NZmkU="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.1_1571889979195_0.7442497334905256"},"_hasShrinkwrap":false},"2.3.2-beta.0":{"name":"react-auto-edit","version":"2.3.2-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"1f552987070fa916e2dea00959afd611b5bc7f58","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.2-beta.0","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-87H3mX+ObrsDOfKL1opQrqWSLnToH2dPqUc7DYs++ETSxhaVQqIdLvZDm7yhMZIKMDoxkpAiOKdwb8eze84Ltw==","shasum":"e7e143a64aa729c2d542f0e3c0d8650aedab7cc5","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.2-beta.0.tgz","fileCount":120,"unpackedSize":344211,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdu0fXCRA9TVsSAnZWagAAvIgP/1VHaXBXGC1kA9oZrlk9\nrn/a7K+I4lNP7ZebMzKRp6L7IOOmOOlTFckjHZDcblNp8W7//TZIBMryUkiM\n0sbRGz0u9WfkP3i/rxuloM+rQu4JhwBOjAuJjT3RPodz3PFtE3D0ORC32jlr\n6EbFkfaad4VIGT8hAXOxWLui99JkdvKRzpY28IoqE7ajf/lhti++LKjcoYbx\nqEH1GCNVY78yt5gmYBCupg5gI6ti4+tYOcWOOb03n6ZRgqRAUVYG4U3O5zYa\nTHUo3lqUs2uET+PVknQW5O+X7puvKtLoEF7MJFkKMzJRfMu6fQdUEXRO1zc/\nWgWvHxOXcWDMyuewbQHuB9jujXfpwVLaHvbzHXJ5yd15yUbwEjflwrDN9YAq\n44t+booyv1Z9UbGfRTz2+CpzyogoKsEsIaHJqMMbVKlTso253knfbucHM2zZ\nZgPEjg7qBI6tz4dNs4skJ0qVUuFQL/g/A2hVh5gPkxkMDoBRNM5AbKR53YyZ\nWy+PbuNgDUzdpkhzlM8to9SBOS4Q+LuvpqcThKVh6ujX7z0/SRT7LLJ8tW0W\nweAxWMenwUNWtZcnXRXLSjgXvT/wpabsorTfSVmNzKGYCc16wX8zkNC0iXBv\nhCaILnMEE7sgzYu9scespmRzozMqnxk7UnaqF6RvjInKwwF36+cn95VMqKva\nVkKM\r\n=RHf6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIESyrOFXwz7XwYqdJBSsat6zNvFXhTIaAy/uXj5NEDpoAiEA7+rvQW5TZhi4RAWQjNW2H7hJyV12ArS8ywSJSeNX2zM="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.2-beta.0_1572554710893_0.7995439463448466"},"_hasShrinkwrap":false},"2.3.2":{"name":"react-auto-edit","version":"2.3.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"4d56c0718a2d569146b2708f7faa5e0362072395","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.2","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-oZp8GksKThFuqIb11SSzDJeMP0las8q2lwyuX2uvcTX82RnVm3yLNL9Q7FiowL3ZBOgAUEH2a0dlyCHE7i+Pjw==","shasum":"5a38fa09c136739a1024a72e8214e61d207e60f2","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.2.tgz","fileCount":120,"unpackedSize":344204,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvzT/CRA9TVsSAnZWagAAMiAQAIE7YM50GReppxwetk/1\n9ufYefAlc0ZZrhmcjWRl6s585vdpIHvRKqlYcHe3XtM0OBAFPi0HR15Qyskr\nOSmS4GzYseiU3to6DsWC6uj7HncIEaMcMgcADv4K74iak0f69H9+6HB2SnlP\nfkRhaR6gZMDIRKFbHeGgfmGPx+7eVVHbX5qgcgfDXhvsNMJrG60+KM6Wk2En\nb9NCvtR5bKC720TBBu9OLLEtTkMLhr9V+omYgRsOKG/tTgd4tbbJmzSPxPsi\nUEGDS8gArAHro+hceNVXfRoT2ffUwJWO1xYEe+UCNxXJPoszK3CRgcdMYlcm\nGwhfJ0xUzKXykfNb+sRm3oFeBl254bGEn355QHohNFelDjrO3yCTqlSLGFZQ\nQTYp6/+cEYw5u/mBH+AMmUntlDUJFHASLT/gGN9HmFB5mHPyQcnlMx7spkkc\nBo7jjK99zos1qPkK3a1BcCCo5jXsJ97a3mgHAcBCMqPD6OM/b741O5dgTACf\nWjbJqPlP9OGI8sxMDw7zrpX0jUvgq8LMaRtCB6KX7hn5QrJ8jn05jtN6cGXu\ncbnMA10HWjZr+pRk0MzL57zMMFgFqIAZUrrFRLR5W/YpdwMl1YNU74UT5P09\n3im7fAL/bwqY86b8dKWp3TUJLxZh3kxFfGXLa/6cCsplX3CwNFyhGVBMJdTH\n4sXM\r\n=NPqe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCr1Q8S36OFoijY66KLL109nm2rZx3gBQ5dUTm7rGiSswIhAImSpvsrDtNZPMlyZ7r2n5zJVeCV3zWBqEMsn5/dkCWz"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.2_1572812030630_0.5428476695102245"},"_hasShrinkwrap":false},"2.3.3-beta.0":{"name":"react-auto-edit","version":"2.3.3-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"ea5c50f2734a5e0b8671bf519b54965e89afab07","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.3-beta.0","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-qrDME2kkEsUOjC3ewmdR2YaBvhs8lZWrROaH8H55BeQsRqCUt347i3ZcZjtYUStKI1e9++3b10bstc8ltHhJFA==","shasum":"8e55216d4a4e5289af055f5f69e3fcfc7c52e367","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.3-beta.0.tgz","fileCount":120,"unpackedSize":344285,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdv6HCCRA9TVsSAnZWagAA0C0P/jJFjoEbeV4TknKi8mUg\nasAiyVmjqGq2JSfNrnTC9LJ9CUwyWrDypMFw9OmgM2ros/FReD2ZPup/vUqM\nffnuy4M4n2XrxEKwy2PEzoAWMg7qXXSD5eQdd2Sy+5TXGfR8ROHEZUgZkEyB\nhLp0o2ZtoLAaozbXPeoMevnFq4VxDEgWrPyahKbyY5tKnBFVTtveu7WeaS1u\no0oGEXKxQJc6UjA99udOw0dBltLATAYo/pFca/bP5PXs+isFaaw2JkGVs45m\nBsFZ0vSgKlPejgqPKhJfO/FRfXd3+7C1yJFUH92nYGqnBeSGx1x6/guBQoT3\ngfRfeqMMj95Cffi/Sue8epPnYjut7PIpBkWHxlelhHpJF8v4d4Ghu+ZB65Vr\n0H8c/iNNJ9DCAsy3gZC7ct4oIrr++u4L4wz08HKJkZYk1VZ/Dt8h+mJJIvUF\nBD/DdlnvUsZXCoypCpP7APlovVbDqZn1hROQsioOC81f5xa6LmHEmq3EWGcU\nIiSvnE2W18G+suLeIIbE6X2wM6r6/EnaaWWkbi3PlExOZn4xsXz0lBOuJZEL\nFTbD+h1IpPWKSin2i/UEB6VWRXva1071NXsVivnFpMvCpV9AGDYumK070XXb\ngg5ohQNYgi/he/9tbxVUnWMugSjjgoOp6GbUEk+P2Ix6od86dJy6B9KFcNx8\ngLVT\r\n=ZRXy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFmFrGJelAQyDDdF74U+Bi4bVh1v5FKQpQMW03H65BwZAiEAnZXmGQzZrz/g5wmObr4500APNjMs16zacqB1LI+9Szc="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.3-beta.0_1572839874280_0.5952439121154764"},"_hasShrinkwrap":false},"2.3.4-beta.0":{"name":"react-auto-edit","version":"2.3.4-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"de20739d31fd8c7ee53907fc0b88080622760e48","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.4-beta.0","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-gji5PkF470iPqosV+DFT6fUVzcgsmDK1TNq68mI74OMZdVnegTXy7oYL5T+AI/MN1mDSA1AmlELQ18We/lAEMg==","shasum":"7e2e1f4e5e930aa3a9a44b4dc7d84e1dcb401f9f","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.4-beta.0.tgz","fileCount":120,"unpackedSize":348620,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdwI2ACRA9TVsSAnZWagAALswP/RdHyRM+PBWhsWuhpApW\naSqy6tgqy1uqUj0kc7wa/V2iHPGEn4M3bL0y1Qjt4DQVBS26DCRMLzeyvqMh\ntgDJktaAda+jRiJDvxJOYQ0nkoo5GHWgSru1/NgfCi6RHt+POCB4hTtQLZYu\nHZ+yGSbLtsxKj3ok02svGHZ7LHyDezn1+rCtvlvl5SVQeEWyNKzaCoiC0jDu\nW3mwRAP4SSiD6VqmRV9IQwVS3sB4QtHyCvAabiepIc7Ht1gDKWEueP1clhv7\nJpKdG/u75m7HIyeXS9ND+QqhkeYRU4NmxDnSm0H1JtZf0H46E44/OVJs3teH\nm9mBIqK0yDWSKP6Kklh2iHB+9D3lDhBQcZtJ838l+JKkJlatIn0/mUGJv33w\nUliIeXz+K/tjdkBCvkYkMTlvgvTt1X7Xmioivpnr4MnCoZB/H7FTL5SW0UNz\nUKkKfp3GTe5Se8Je572Gw54i47jg90xuMPVK6C+QT1MFtEgXxQUi+JdKE2Bw\nhd3crDeeBiCrTBIwmPWC0P6RI5WYbsNq+VWiXWAU6eG21Mh5wYkaoWAqviaB\nqDptxxAk5zPHaK/kOxXOP9FDPrc7GEbqAa8ynLAqZJ2V9pVQ5MzFHYvVCiEH\npq1AnUiqF42a/Zrlvm8ERMWyXcl/OVWVtTSElMW9nPAltJIQkWHFDO4PFhLX\nZKaw\r\n=VSp+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAgEF8jG1d+nbw3OATcTT8l4cKTz5IoUoVGiBH/QHYQ0AiBghH4Swm+pP273rfOFiOJTKeE4LoBA9Bxwq1TLzWi6hw=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.4-beta.0_1572900223845_0.5896943173638156"},"_hasShrinkwrap":false},"2.3.3":{"name":"react-auto-edit","version":"2.3.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"ad492ab559e80f1ca693c74574dfdac3990d9d0c","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.3","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-K6dRrXKmE8CS2tl4t7r0U2MiEgPsxp4XjAH438UZoM0rntG2Zlzb6BPLnRn4HJywLvcs6HEJXmCmmQSqY3U2fA==","shasum":"faabf6fa84ce31f84dc39433ed9e6da2ced7fa57","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.3.tgz","fileCount":120,"unpackedSize":344278,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdwKTnCRA9TVsSAnZWagAAtTMQAKMuUCqSdwg8csW8OSau\nS7biTvquo7IkvK1z0+H20AU+FEg5FlY15PI83qv0ErXVDkXMh0q3I/4KArJB\nTYmslNFRpuLPJG0kOgMnbsk5OMUi1ZHcBimWvBjQRAA8h+m7W8dxjNkgMjLQ\nR7z7JbdOajjUUFkyO10JxrI47UfI0UhTRREVcmh5y3SJOoBweIgOC8b2ct9X\nmg/WAkvJiNB0Eiyx9pbu7WsD1PB1/85MyLBzwsRx90C3ADAb4OmFW19mwnx8\n32l+5bTteJCzUDvqXMUcYECSsULc4BmAXtgUfekxk5nWzP13RA/iNxsTuI9m\nMQqim3dvuEe2Ko+D38axB0OKUaT39zKcRPWCi8aVa8lcFGkogJoYLkP1Bs4l\nq0cY6CbZeZ+etjfPk+sUY58Pp7b7fPIDbbP8UCTnda8KeuIKgYsQNVVV9JtV\nGq9pUZb7jQzNM2SgaNlbv8iR0Kltsvs7JB0VTwxWnE6ViciuopvTdu/1/16u\nd7ggnL51JwRtlHKFXKKxgbDvDXUUOYK82gChRukYak8bGfdOg32aJ9BF3A/f\nt29zlLqQP2Qm5k6OfJQF2Hy933gShcYW0VL+WRbaqmereVuHD0U2wtoOAfbT\nDh4x4QdB0u/v9AzuLQHbH1+itGFP1bDpvxc4/46gKS432mDYCTDLX5YwrvS1\nq3Kv\r\n=5F/w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHNYapGj2YfVZmOhGKTda/0GGIAxnDEpe4Q8JWPwod21AiATao9OZWyF7o9KGAMFvM3zZR6jTrFaiGjxBgdwy0nk7A=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.3_1572906214590_0.8213133753096242"},"_hasShrinkwrap":false},"2.3.2-beta.1":{"name":"react-auto-edit","version":"2.3.2-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"d3a4706d13688831866ee9137e19c26b9993e0ce","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.2-beta.1","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-nWS6L3MNyYtQI1Ar2uSjRJFdiZ0BHuNAlK6d9lMsUQ7EDWodH9QcjnMtNVbOFUWu5ABAwtRfeOPs2YiZOLYYOQ==","shasum":"359b411accc443d3c6f128c7ee0d4968bb53cad8","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.2-beta.1.tgz","fileCount":120,"unpackedSize":344235,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdxLZGCRA9TVsSAnZWagAAShoQAJB+KRwqHHkoaV6W+Jyt\n+icud4EA0uZW80frkUNq9bF8A5L1MHuUuVdirnE709Whir3CKexNeI6KbpTR\n3tBi8tvFQ7onJQtxzhEAdNRx8/RQWfE8/mONRxAU8ACAWsu5ko+wIkIUhVDf\nn4vCw7yan/EivAzuDQZj7Lc/Tvu3Aw+swh9mnQBmafOROr0iNrNF17e5AERk\nSm2Rw6HUMlotNskP2HqHrMiKp6KlyM036axDm+rrABCNM2KESHrAQP7jSx4J\n1N5ZrNcbfRSuwiYyJtzAZcEDHiV6fGcrI9nan7D0SF7sqra9cflMm4++04Vu\nHavh/PTkTonHEoQOQNpg0dEwSrb/mT/cg5uUcpBkQRy9MA3exMbn9Gt6j1Pe\nLvXBvku09ig12CJt0Mk7zOhXJ5rbdAlsBV8HD1bgEycyjwoOk5i+3WKVVnfK\n4TdNsMwx0WOWwb1eI5nh+Irtk70Hd/wVtWK0URxTyNu+RgnePjLV0ykjt/V9\nzJVnjerUMPCneKsrRZSmadksKr9vgzoptTaRmojjZjZsqJjTbQO1vK6gZE6N\n4rWyQq1rLEsyGCZ4jauTL7UI47JWEiOoLk1e6w4qe+jmT0/XnpnvvLZrxN6G\n9o4IUPE82LQi5E680d/CsZ0E/M257iq/P5ZBdiTAUsJipv5wxclyEvIvUh1S\nm9+q\r\n=8keJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAqy3tM+4XS6IKVc+jQfuQ1ojc/e60bEkiCebWs7aWzlAiAkYRqjm/OHa3qkXn53tpeE5fiSbCaWeq7y7ZhREBjthw=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.2-beta.1_1573172806064_0.40055026390753556"},"_hasShrinkwrap":false},"2.3.3-beta.1":{"name":"react-auto-edit","version":"2.3.3-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"8efc2a4c698fe4fc793832345bebf9dfed685771","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.3-beta.1","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-bIxtwp9KrZDA0VqUq2y9l8/iGKgk6Fs55Em+RjddCeggzBMyvuyJGhmBtiL/E5FxOa+cXk13Cn31tvo18xpPoQ==","shasum":"f0f35bf65cc295cdee9517aed53773c5364a7f2d","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.3-beta.1.tgz","fileCount":120,"unpackedSize":344309,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdxLdPCRA9TVsSAnZWagAA5tIQAJJTfizwqRmg+PSb4vch\n17LL1kZhH3tA22uk9e798v5uu+SiFbsRo6Pi5bb0r3QKnuRPdbck2ZBhvCCR\nlzMO4UDHtjdAuKR8rqXI1pqJwaSiPVwlu1HUKMkjRFvUB3NAS5Ad1vvLZo8N\ng2lcS43+BNtDAzwFSybxYjjCWLQcvmHb/PtX33yQOdrvuBwx9r1sWBc3GmUU\n3sAAsodDg/hXMRJHNSz/vAI2CZxxJ6Le9z2uHDM/7gk69nM2TgR0j0Bd+Tat\nyLxT2armf4y8AmqLhDZ87Kxg/3PBHPDiWpFrKeG6Mg+qyx6/X3v2+9YSVzo8\nJrahbK0SRzqJmMA6g2VrZDDLjWoS+WFAeJbSdp+tGu8nYtNi4Es0/fqwp1px\ngDDYzamewf9L2R1W5d7o8wNzjGpvL8B6QdeRvgor/zxkQ0Ya+58OL0TEmfD5\nQpxiZpkFGeaP+63RNlQOIF0U/Y6qLn4l85CXaVNREQWjESdxt5TE8iBCgs3Z\nZNNIZJ5Jb0G5c9NJcVxQ60TQLcd66MqRlqaKyEF3RmbEijPxAutLT37f00sw\nSWSw1MtFhWU9Y0cYdgB4tgezlpfWkltx2cfvdBmbDkA4wS0Pkp6YMzcEQIPp\n4pMjqdm6QbtVwnPvRSxJTqBnppnvrmC5X3c7ouD58ZcGaxSRZMO0fz+ikcxV\nWMYE\r\n=dL2v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD5jjCl4JA3lw0nJh40Kkuv8SV/89wTpCcCDnzOITYl1QIhAPU3RJbdOabs2X5IWlrUXIBZ9+alRFERfD4UrFrlhpib"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.3-beta.1_1573173070987_0.6705551782111625"},"_hasShrinkwrap":false},"2.3.4-beta.1":{"name":"react-auto-edit","version":"2.3.4-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"abbb20669c9595381cb9e18795f855f44ccb2f18","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.4-beta.1","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-uMyNoE8Hndc5n8wnem+JhQGRrvU4hx18OybcmJstCJWKB8eM8po3KVveezuQtvP/CWYArfjYUqndoJO9DGvezQ==","shasum":"e771f9a63d1ecdcea1c8410d8458692c12ee5022","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.4-beta.1.tgz","fileCount":120,"unpackedSize":344309,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdxLiXCRA9TVsSAnZWagAA9vsP/2Ne0/AmziWTOoSw4rcY\nf0XHDl/JJ/DD7nQAP9SKscuhF1xq+NVNuFKy+yONvYH81P8LyY0JDieQsfh8\nA81bHlz+iRWio2AFbnumlUlimPy1DvwwTOzU1FWK5BETbJV2I/8BPvvt4xcB\nCCFs3/AfTr0nzEs1c2ushDi+idE8roB5gP+No5xnKo1URM4epLQ6ujDGiFjG\nYR3AkAyrPH47myFk1JjPuwikXbV+7abr6pMohKimW2loKnGgyBzBrKFwBnXk\nFf5Tu2EJWCsf0NRF+6+6kicopKXyRy/AuXdXQ1XFbwmzJF+mblDPLtVVHBRs\nX+rRQjO1Dfj6eVIl7EmzBvqjY+XMsop+jN/Gn7PcWuyad65uIwTvp0O/f3BZ\nK7NAPyJvcQnyvS/3W1Niy5qTmzRbIDsdp7DsILWrPHlCs9aQ8eN1PaV09NJG\nNrJWheav1cGVwetDtFNo9JJrNyjCc0JEyfisd0c+7ophuqDdBDSRpn6PKNgS\n4D2tVhpG5BR3mPLadDBmQ2Tb0VPKT5cTF9WpfnMVkO0lnD2S3+ih8A3rt6WJ\n2J1GYqtwXAv+BNoDktbs9b97FxuKB0rovq7WD4MILi4VTiR6uLRIqi4GtrNa\ngohYliQ7uzZQe1HJoha8P1lrPMKKKD19Y2KrJs4yXalZEkLGIEmMe9JjXGds\nidFd\r\n=OTOz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHyq0Sw4DEzgqyFJXw1xUbrgw842A5iY49CPkQYmTIZ9AiEAzNIszULUStt9e4ZvJAUmvjfNgJnNmpaTKwUIiIH3YYY="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.4-beta.1_1573173398562_0.6441773940208417"},"_hasShrinkwrap":false},"2.3.4-beta.2":{"name":"react-auto-edit","version":"2.3.4-beta.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"e1649a289995e0294f0225c4cc24f4e4b5ab0f17","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.4-beta.2","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-IAdfxsGwY92g0cVMYMVNYtmBYv1ilSORGUjyC3oeK5VCklX5UPL4LIdgEHdje0y9yy+WGUCyqntQ8OmZr9iORw==","shasum":"632e9a8b27d2897c62a9219d2cc67e3a1614e5a1","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.4-beta.2.tgz","fileCount":120,"unpackedSize":344309,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdxLmPCRA9TVsSAnZWagAAvtMQAJGC9naB7zvtBcRGMQh0\n5N/0WmNEf/Po73CD95gx4StSh3THT07jIpdc3GqIR0I6nIb994UbEOJaO1L8\nQisd8aZKLEi53RLOCDDHHQqlpxglKieLESCDkpkJpw2b2hC/+Z5use5Vos6H\nqRdIa3JHB4+JI2WBXmpCdMDxQL8KiBSww9vDdFYCrOg5GswKRieeO9ZMgz8w\n6WiLNYDeCGyTk5Tqjuc4LrXANCYLirJ+IuyZ9H8JLj5YO4E5Zp/BeV+2SCON\nMReiuQMgk8sG4kQl/ouHiohhKKqIG4PXjlp+KTfNBVnDkp4B9kwOM0YNt7x7\nS2caLAk7g5BGlJzE3SVMTwUFxFj9SaPCLJYCcajxA0flIJalHMfzmLGLLBek\n1UO+HyI9HZ4RrLCK/PXVuNsjC3GLT1UAbWUawKPV4BHQvMpwX3bZL7FDw0dF\nKA2HtN3wIhr2eaXBFvxQZqIvHTMAlb1rMRXgqr1PqD/PKk3auEAEfCvJ24Hz\npxh87kxeduM9fA4a5Tk5zTMKqySCIGabOQzKvnKpdzXdkxXmlZ7tSiM65geh\n5aT59CPZyFhNJjE4kUEN6q6MK7uw717MfiTwLu9HZVOpKaIOydm3V6inmfPI\nPI1CFDkggHF2nZkZYlgsKH5rYu9kTpT18SVoYZXeytpKe0dQIV2FEVWvxRKE\nNm/J\r\n=M7uH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDCxF3SQEvWRZwX8ocIjVnk2VDieUuPCe+q4Tc8C3cn0QIhANhzWpSywZwq4eILx5J6ryqmPZiSmE6zjdO5sBvBzKH+"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.4-beta.2_1573173647316_0.6868399757027475"},"_hasShrinkwrap":false},"2.3.4":{"name":"react-auto-edit","version":"2.3.4","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.9","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"3938551540e6b5ceac6b2e1fa12f93c621b8bbf7","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.4","_nodeVersion":"10.17.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-iGwuhUnjxUrefc/35pFrg2S5C1pArOZ1UTX/hRk4fImyd79eFMF6AOMKmJvPy/Mh8jn9lsvxB/ywz6HXjylZmQ==","shasum":"a9e0e25c98513140cfc145bd575aa55e9c5d0e11","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.4.tgz","fileCount":120,"unpackedSize":344302,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyHcWCRA9TVsSAnZWagAA7lEP/3SvnuojvzBzqpacDIYn\nsmT0UgSvEAE/FX5XQP57kQRSGEFzpA5XJrhN77SrFXQyW75TajcFlWjp+7Ux\nohFP9lKeUtqtfmOCQTd0A7w4dNUIan3iFcuRJX/AgWNjWjCVzqIXcZARP+Fi\nfUsp+57kivuXgdoTOV4b2fSUNPcqb3z1pgsdQltc7JUz+fl/jK8E6wjkQVbC\n8qfiZ1AYMM7sCbIHm47QcsxWpJTtNn7i/d+qBrTMi7rDCdG+I0Me6Kg58tlX\nEVBnrK/I4YMM3Wr0mXva7xA6PU7zc32i7X1wg2M9CG9GyP3Ajz8zX1StcSPb\nsFcjWRJ9RN5+nzxynY9uofih/c8GGvCL4/bXALdWxzdPSo7mispWHdySSS15\nuUzkdbSA4y35gzegjli+onGcxvjYG+oq/2Blkc6sC4FoIgt8t7nHkWoXCV+h\nidY0PkPrS/p2GIyhQdYTxWqUTP6ALhdWXtCqF0c98CqzNxJCDW+4WFuL7u2E\nXTaYXue08CTNu9NisC8FkbMaEqczhPn5Nq9/7UCy3mefw8MHxclKcE29wUkO\nr/whsWMe9uR/Nvu7zWsGfbR1n4VgbcbFwN/kvg1Y6DW2cFPD/k6sadh/HiKZ\nOute1k9PhggvAuqOCaWdG5OwQhtmqIMsbJbf804ABEfPkuz5J8Hz2QFbQfrD\naF84\r\n=MzbX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDVxXYoAMbqfNGTrsKaE6QIB1hUEI/Grnk0ez7lriYVjgIhAOqWK0WfxRK8ejrQvzFEgEuVGUPFgZREq/RjbQXpfZT1"}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.4_1573418773693_0.045016129077068534"},"_hasShrinkwrap":false},"2.3.5-beta.0":{"name":"react-auto-edit","version":"2.3.5-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.10","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.3.1","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"291afb2fdf200d403b83076d5de963df5f202a76","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.5-beta.0","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-Gjmg6c3tb2/k32OLX/Rcv6MdN9zWv1Dt+EAcjN6nwIQlwNH6072W6MqLgKeHBuTVkhmNUqgkiOOR7IyUHlD8zg==","shasum":"64ea664201cb7c698e5587131e6db1fd15f60c29","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.5-beta.0.tgz","fileCount":120,"unpackedSize":345979,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeeCZYCRA9TVsSAnZWagAA1qUP/0x5G64dRMzDG4g9rJtt\n6REZg59WrdtjagpXX2etPBV/eXNC1jPiIqxBXvFWHg5eFvDP7cRTj/oO826/\n9pr//rE4SVnwkKUdTR49QoXnF8nrjHKHN/slqCRErgBK9fOX85trjvANQJeu\nOfD6m2uLoQ3jiF6/e3vKpNy8Sn8mhSvq8uz327avYdNSX7ec9IOb7PRGC+0x\nkYEeuEgoTpUj5XLePZN2FvlVS5B9O4Ro7uL4MPHOfLmUe82BvS1yJfA0hrlD\naCPY0vrsq6H3ArsqHoc0Kni3MDqaNrE6FlWYHDm86E7Ablp+FNOsAjVoSKb0\nsbecK9820pXB3R1kcn2Hy5pviDXJwZdqMsU8Bd9KHNes3R0nNcu89H8zYcj6\nHxsxcofKX+5mhux2a62jJrZuiQwkhhlBHyeR7fVL+6tKgxdCUUAed2tVRNsP\nKLgIdtY8zzwyw6DcAkeoCnvEcquuXBoZavG3GQN1WooBzYhZ+wSnGZKZeUSB\nfHjaxv2jRtzrmXNLXyPvIDG1UbFKnjhnc6Z7g3/yP/9j+33B/Qs/dj4fZY4r\nptJUgxwp0u9oKrqD3kFl9MVEYx3rpLL2FLACAUnFHsdga42OmKl4n2hgrhli\nCkidUt5eSbQC+s4qPdbHoIaEmdvN03LPtRzMrFPlZnxNRtuiJnrCtkIzp5xT\nA95D\r\n=rqRM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBkYQNkSyddWU7GDbDlxAchGkTjVudTMnILXQOlrTbTSAiAXPxLUTnE+S1MbrogpUDlY+N61AFF8k5rT6BDKlcLynw=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.5-beta.0_1584932439447_0.44462315036131894"},"_hasShrinkwrap":false},"2.3.5":{"name":"react-auto-edit","version":"2.3.5","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.4.4","@babel/core":"^7.4.4","@babel/preset-env":"^7.4.4","@babel/preset-react":"^7.0.0","enzyme":"^3.10.0","enzyme-adapter-react-16":"^1.14.0","eslint":"^5.15.1","eslint-config-airbnb-base":"^13.1.0","eslint-plugin-import":"^2.16.0","eslint-plugin-jest":"^22.3.2","eslint-plugin-react":"^7.13.0","jest":"^24.8.0","joi":"^14.3.1","joi-key-extensions":"^1.0.10","nodemon":"^1.19.1","react":"^16.8.3","react-dom":"^16.8.3","react-router-dom":"^4.3.1","react-test-renderer":"^16.10.1","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.3.1","clone-deep":"^4.0.1","dayjs":"^1.8.16","install":"^0.13.0","mobx":"^5.9.0","mobx-react":"^5.4.3","npm":"^6.11.3","p-limit":"^2.2.0","react-aria-menubutton":"^6.1.0","react-hotkeys":"^2.0.0","react-select":"^3.0.4","react-tooltip":"^3.10.0","uuid":"^3.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"gitHead":"9f41cfff0fa8995dccc4f513afc93183ca3a52f0","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.5","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-3ycV5aYyfG1USNsqhanULKNRk9YovMNHVkpaomGQR8Q1UK3+8WUp+NWtcBqkZoCiywTKngzgsTJdY2m5MZchrQ==","shasum":"d3d7791bb0dbd0f78d8ef66df80d76fc81c9f3e7","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.5.tgz","fileCount":120,"unpackedSize":345972,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeeqP5CRA9TVsSAnZWagAAursQAJyvuc6vh1bIzWh5pTZC\nomHCu0LcYXemXMPcuKjyILyrtQarD1rnqMpVvcpBWXRI6rBtDBVURGkUmVYX\nH7gupxGkLI/atig+MZnvTzqkrbpWlcfM4p0lJR2+daT0+K60C2qSUIMj7UkA\nOGhcB+6Xif9Q/ukTI99a2MasiYD/fX4m2L3bLMBj4XbJR5UXChNuO1YaqQXZ\nMWTjmTYGgKS3PeTmSiWXCT8mcaRUmO8sNnUZ0dQLpurptLVzwdwfu/Wscquc\ncECf8+bK/N+cmS+vywPi6j83gFeipsd9KKcHas9zq46WgLb46JZQBWtQOWVd\nWT91lCVbIfl0q/a4BPMltuhW6Uw610AlSf2hU8Lfphy1+rkrW+cu6z0E2/6i\nYi3DrwNI5SDRTzHXWKk28XwLqA8nQ5UlC3LHOBvcGX9GSXKgOgvJ59+5oK8U\n7C1cy5mbCSQcj2UVB+nFtK5r1OEIWHr0TpLZJvPHhEq26O2II6wd5+/ZdXG6\nOajnKvdyCzjpvvDuS8q8ViFXDNERLLaRS3KqZazEhIkCbuQCu+iJhdOLa1v3\nbbWD/eH3iP2Ad6qdPc+VDGUdXNRgeAeLISf7+gside1yPHwZpYqy1ttngVBK\nz2njAXVFXoESFfAhlB88iZhlxu3oHewG4GtowVj+KaCRvmmhAdZiofhpIvOl\nwbvC\r\n=ryZl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHMjOiCfJDu5EIMMJtrKUiaqE0525ESBwdSgOrv8VaihAiBFRyN9/XU+RaL6x2pfDlE3EXwaqJE4llz2mGXl6jWe1w=="}]},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.5_1585095672946_0.3376455717783857"},"_hasShrinkwrap":false},"2.3.5-beta.1":{"name":"react-auto-edit","version":"2.3.5-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"876869862bd6f12904c472bb16a18f0a94d81f1e","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@2.3.5-beta.1","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-LGSQWhyazClKIVNP/Z2XVbnedrPuFdBswGeoRrm0Yw43ayY4FoEiPhrucHcmxKj+G9ViG9TKATgZ0LxKzf+R4Q==","shasum":"396de8f8bcd1e5c6ba541c22e8633403dce41998","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-2.3.5-beta.1.tgz","fileCount":122,"unpackedSize":347698,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMAMOCRA9TVsSAnZWagAAl0QP/2WDzFtBQGHPYcPx1V2s\n1tAa6NQks8KGOYV244viHwXtfnBqWsCaCF2Sa71XOKVJdf1Zm0ETEvvkMPsp\nmol5bcsU4kH5Q+Xcw06e8Pu1sSIuMr5najhEoNf7CX0+OD/keM2hVCD4u4hc\nHJCvyOaeBwt/xXzwTl4NfmaZ2ycOoZxAoUL9HsJVu3R3GJlFi3lK4QRvvqKV\naUkYwRYN6U/2peT3gFUkTYkuEASIW5Wnvm0matMMmiy6vVsf4uWuBZKU5VyP\nSV2+GQ2IYxO+GoXSM7MrMfA9ScKGXoJIOkkxmq5X2f/hZaqgihRgbt+i5+8I\n88zwsBP9Wwsk5ijBMds7xIh+BVLevkDnQVFdLxa/hMf/zM5UO/KEij9zylh4\nYMLVdkC+HSDtKSPOwPVtHBSCx8Vu90PYvEt4iYYQSoEeTNNhtsifgtzJ+kuF\nb0qNEva/hYCHDWmPNc+NS2inbkcb3Y4tQUYLNlQ2bKjBr0xGXHkzCdHAzeJV\nU3i/3mXkUjeieqNoC0mbuhCDefm7Y2CmnYXwtUiiuQAfvo2MxBqApYvRxGta\nqgfvo9M5ZMQwd9WS8LA9X++oai5KllGg3oDaGCl2fYqCNWyQ5OlD47Ff/KeY\nlcHJq9RF4J9r8xTkXu4MxbDg32zOwK/FeXxxwdsB/hSXtSDZurDnTeqF2kDL\n2fYS\r\n=DBwg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD0v1X7PacLiGkllc6ZVW+YlR4S68ulkqsGKuFEyQa17gIhAMw7VxSRH+qO4K9FLgmVt3ENLVFxhpMEkS0+UPySCC3e"}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_2.3.5-beta.1_1630536461972_0.6042218258088872"},"_hasShrinkwrap":false},"3.0.1-beta.0":{"name":"react-auto-edit","version":"3.0.1-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"691ad0f83c57b15b2cf1b4441beb060e467d447e","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.1-beta.0","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-LMQMg0b3zUPkdqdhJopnqxK3913on7+p8wbLDzdoreW3mC9NudTS2aDY1RJjeCD7siCVwZUgLXkPraddlxmk4Q==","shasum":"b4c16837aac5b263395f1aa78fc4d804f4842334","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.1-beta.0.tgz","fileCount":122,"unpackedSize":347698,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMANlCRA9TVsSAnZWagAACtQP/j8+Pt4Fy/kAUN4hji4R\nqtYX8fyMl4/X9ACCZZgVcwvvVH4i/ZC4EtSKxUJpLEhpRgTDVLt41HrtBaga\nfnNLpSYqQgRy+l9UqNZ+rtx6y39GVvFjnKqWhm19/Ss7RFvB3PEs7+QWT/7N\ngohd7/ozYHu4Rg3T6XuK8Znbc8k/MAnsnqPcWhc+yIQh1fewLLq9MIsGTPlI\nx61jj3+NGYRtCf8foHzaPITX1XQKTmgniKKB3HSrcXvH2t3csxqN86DQDHmd\nSEKrQluUvBBtix9rxhpBJuiIOaHn57y0MI1OdEP8RoroDcnXkbq0RSOzQVSn\ndXDpR3vAkx/Z3aSHn5gKyGbi7MBi0eZiKSumrroe9toJkOA3mxoIVAt9iIbK\nE7F8a3+w9FEO2s5K3LV4p2+3fzjPz+X1wVyDrLkd+NT152z7qL+uPp5VsXr6\nOsyh9VdDlugGYGLA740Bf/q4mFmRO46pRPK65zYXlE+R2Gswz4jYRLIQMtwi\nmexIuowv4d/epsaJKV39rbju0JDQAt4x2W6zy5GFLTJw7TknSTpgSV/UW0XI\nrHBT96j41vRZ6IXjWSd7n1tPKLK47MMZWvtPpWKDfkc2Go92oiCCYx1DfLQ2\nmBxEoLTGizJtOE4J30/rU5LzyWKL/Z8ZoedqSEd3J5dvq9CuQltfcIwVLuAP\n4H6q\r\n=h4pZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC0W4YO6abFM0EZ1T2LDZ5FAl/TBtTyVqXmb+6g6mLNqgIhAPsIU2aCvcaggS42b9Njg2zxfwaJgOPIaCtk+sITsCgG"}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.1-beta.0_1630536549052_0.5161862078522297"},"_hasShrinkwrap":false},"3.0.1-beta.1":{"name":"react-auto-edit","version":"3.0.1-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"a89e37abea7ee405732ce385ada53a79e01db58f","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.1-beta.1","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-n1BOAuxj5jJSuIQhulJgyWu8GtTXM18q+WbKDiCiQKEhjLMKS4QOsoGzXxryIaIelwbx0RH2nXZGaMEjOUhq/A==","shasum":"02e124d15345b06d9578fd9c4053034002ab0db3","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.1-beta.1.tgz","fileCount":122,"unpackedSize":348671,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMDU5CRA9TVsSAnZWagAAx/4P/3XwCTyXPlMCoOxO5LmC\nGZ1sngO1tdZm1384QhEmSwPkzrCT8gSBfsr+JnUHmegigFv5QuIfsYmQxfWH\nrfRz/Nft6sV3riS3tfJ2TqPhFbSpd7AS52PeCcpuKRsrPuoS3yV/6pfS5dVR\nCBRoWnpMbY2fu+CxFL0kIZoqqfVGy7sXRIDdLoEGG46iQWY2T5Gttc1cnwnj\n6CTnNMgZurm7iGuaeUR9Z3JwBkhSrgdpcYZkSqn1VmxDqHWixqD5XX0jCZV5\nVFr4VbFVnXah3dB/XZMlrVujLKDFjPR1nhCAd0f4NVvzQ9CVp5itE1ezwk8x\nknBvK8OjQeu/p6vGp1FA95aa/1dVPZFFJCHdNd8h3G8Qg/fDcPuEYREr7Mqt\nxbajbk0aAsGQSQNZvaKVKUB9DM/NK2pAF7g7i1lz8Ntqw1G6Z//4Ct5+/JOB\nPCXLNi8p0mHZd/XB+q1Uy2suJWWGDSr4Qae6GXK8Ox+mgREA7J5osXdoeKy2\nB+r85txb+P+jJzAX1+xycvqWkLnK+cQ4M3HrP5d8+GS/7cBE6r0mknFeqVBc\nhXSUzx/E0qAR8AH73PqJy7xawfmdmfomxccMRj8x4n8vm3c0/t/3mj9mK8qC\nged6cr0/lv7DmCY2RZtxVwcGWhf2qRVjpk4LSUiuSGF8w7224APweWgxdY45\n6BRz\r\n=lUhK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHN2ibAoSCsVQHtih6k6w8rwYeRL+0+OTmYixAGFNVo0AiAjKBgAM5nEK6scPmObX6/8l5ke0jl3BhFC7PI0du0GDg=="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.1-beta.1_1630549305568_0.946067171783366"},"_hasShrinkwrap":false},"3.0.1":{"name":"react-auto-edit","version":"3.0.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\""},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"80e7e28ae32d140a82191b1f67173a24ab7efeba","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.1","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-N4Khr+G1aFQc6HXX4Q4InuStDTYjS44WyhaX/HgGYguoOY2/+Ro3fPFn9GUDZ+86jT2qoNdkhYB5rckt8RRSMA==","shasum":"644101ab2893a55952c2bfb2e757ac0f6d46d9e9","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.1.tgz","fileCount":122,"unpackedSize":348664,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMTI5CRA9TVsSAnZWagAALmQP/ieumKbxkPxxrtrKvK9C\nQfNN/IIST+4Dx9tKGPbnylJJcv6563rnqtIGCSFYVTtQFsvCXtlaV/DEXyku\nc49zmWr/U7CvnyFmoluHDYqoyXdZ6To7xgBwImGc9QdWpNEbHkprtOmkNAAG\nWVFSHNXJwng7I7+AqCQarqURNDpFaPWWoisJZNlWIWMZb4VuvtsJJNKOpuCA\nfxZX2GAz7z+y1kRJ1f9+gyjPOSGZX/ajNLXg8dokFCW+DTCyB5rGr7dMbq/T\ny6iQSBcqCHoXJLONvUlX98118GAGHUk/vqjMqn6zYl5nbFOyL5ZRVShoFvG2\naWH8vzhKVNqZrhccgE4dwukvyl/OKGqUbzq1rrivO3fQyI1bpDb6aGibuMkN\n/vlUB9234ImOxcK9wv6YpCW8Uct93U/q3TfQ4yDA6iEefu7fHg77uLmiX13L\nsaUDSbhZkruFYspWjyMjNON1vUaphGwejUviQHxS8sGWuyeiGSwMCU41FwQC\nfPHRoGPFZU+uKK2gpq67MpW5/6rhcq11QIqYoPPI9qMJ3ZvpWAt4glRDPrci\nP6wd8yLtIk8IJ+Z7tdMIz622TK+mSDAjeIHK1aO4fUY1fBjA8yZguXCsP7XM\nlON4ai5eo0AzpNOLj3GWZz19wwjcDsTb6LxlTNQFym92yCLTlKjtzLmJmzBJ\nifeT\r\n=4Lrs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCFu7enu85JInovJmbeznRJtva9JpXUy3f+F4rIeqmt4QIhAKh04aBHg7ivG+AgDW6yVNsGWWVdTEygWzdGlDRJM3Y3"}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.1_1630614073147_0.9186446347634487"},"_hasShrinkwrap":false},"3.0.2-beta.0":{"name":"react-auto-edit","version":"3.0.2-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"c55477fd0a9703694860be7d66b885ef68c896ee","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.2-beta.0","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-e99jok+gAelLc2fa4/FUGo+roVDibv6YbOQKE+93wEQXTKiLB/T114hTzBBuM2xrz1ThYjU7Y0fDwenrTzfHcw==","shasum":"447428436e09e1701c319039994585c0906c7af0","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.2-beta.0.tgz","fileCount":123,"unpackedSize":351717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMW/dCRA9TVsSAnZWagAA9DoP/jAomnzE9VzPs2NoFZQ8\nW9JL++a+KogzxTHIG+iiNskhxVYZXyvdPYRizPJS9inZ4cdvpFlZ1HLX8v13\n0tcAVA8n8JFbj3TWaU+r6VnYXAM16iUVkOA8crNiH5s5MeN83Mdr8/EF8JbP\n/pQhf0JY4sxkLlz54EUODTZ0EwKq9oBV6Ui0EKkhg+5m8y69JZikVwlt1ysh\nVH/cStuOsghDroYFiZgnsgxBwdRvB3oB8KpoFNeX3qGYHW1TJSrgAok8Cq43\nZXHmyWOxZDXXv4j2bzJJzm3jD9cFv5QrMVjXLBq0BrW2RwZOqKeD1xOlO0v8\n+GLr0AKVeRRvmuyLd0lNIiyjYW7/nuY0vrYTl0KmYm7I3Qovtm3eXjUnAClB\ngxdSuZm9+XcMShJGJk5SCujcUFKKWYaePDbpBJSQQQ7IRCIEUL/06aqSDDDE\ncYIbMbRaNOnkXdOS1S0jtpfREqKwGPr91Kq2ubDaXmWVFi7xzuSn7+mN1XtV\nwxyXwWO1cM7Jrv75KwSXBiHVgAls0EcSN+IE1JZbeCBbGDHBXE4ArLvLDbdn\n8JbfJldU9pTdWE8SrER6sTkREN25joqkSmCcp2t0JR2QOnXO2RrY6lubBMx5\naP7LMXqO6Xaa72X52bJ2j9GiprEX4roQ0y3TNc9JSbpN6NuKqnuZJUnSe6ON\nxxPU\r\n=00M7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHgDGK0CSBP6DG5/qbpuTwkPAgJq7oC5jhYyP8RkiTjxAiEAwnGXksCWFp9oi5Z4vAdIyhfUdSGvFD+8HbPZDU1/+MA="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.2-beta.0_1630629852860_0.3120995155537145"},"_hasShrinkwrap":false},"3.0.2-beta.1":{"name":"react-auto-edit","version":"3.0.2-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"d13d7dbab14ff79792e9a4a8df749a7346657bf0","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.2-beta.1","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-e0UDbjt7mpNTa2TBq4FgHfB+oOqTm6/wtIYXWudbF0vuVK3AS77KxFU5Z3v2vTgqeZJ9Pqcdni6L6rk9+gZDow==","shasum":"db47851ef026a0f2259efaaf029d4bde320c90b6","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.2-beta.1.tgz","fileCount":123,"unpackedSize":351838,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMZRcCRA9TVsSAnZWagAAVSEP+gJ9koFYxeJa9GFCEH05\nkNhN2rKT7WwBKvzcUvTsjfUS7vPtt6uBSvbyt+ogk8+leRKWlCUW6Nw1omsG\nUzvqlViVBTZCvYQm57T02Dtpe4xTnL0Q+DjjiZZvfXw7GVI7Oi0kF7vs1evI\nwAdbZbPdhdE0X2Xm77bakO0dbQg/ir9eYZ0PnJ3nXQaLCbpjNET5FhV6YPlb\nzKqgtgxRMC0shG5EfleujCrskScfIKMHJJSjh+WJL5olayOirmVrQjOeJz9e\nxTDtpTSM0SiJflt4BEEQBHM4pnbrQv5cgLbGnKfznkcE5s2d+5f57W7s+zFt\n6gy+D8iOFPpAWaDxTAlgRXFzhBhY9dmMthLnWuBYjnDqUDHOXohvH6409ev4\nmjeQK6lC27nTH0Q05t0VdjkA7e7HRkaYPuWfJYk2e947krJAHYbFrywNiy2K\nAX1dqZpWHGqMk/nCIW0o4h3xAWA9EfQYBNl0Rz9Jcavz+HljAz8FVB5pRGRo\nQkRbYUXo6A0N7AqbWo3fcQxjLzr4SL2X1of6OfLF+8Jq1giA2nwOv38GwCyG\naoQL3IgrwgW/Fqb6TvvjYmbOwcQnUHP4r0srXXaFBxNS0HJmoYu3CfQq1rrq\naPc9g+EUTZt2etyA/g18PNHiOnSF7ryPkei2Xhiy1VvFrhaB4azEto2XqoaB\n97Ef\r\n=J+zt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC/quG9QfONR+xtFh6/1LsfYKY8ELZ79uQpWwKCHI9HKwIgF7RSdbGciq+JJcGOf7I4HrqZst0sTvCt9TRr87XFSss="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.2-beta.1_1630639196128_0.47535527969581826"},"_hasShrinkwrap":false},"3.0.2-beta.2":{"name":"react-auto-edit","version":"3.0.2-beta.2","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"4b82925d342bf1e2313f6616b05e49cc8cb4b1ca","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.2-beta.2","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-mkUEgMwBSBG62rEEarmYAwJ+v7hda9xei3OxNN77yrvqXo4UgnJambl816fdd6+mdVduvTP355YZkKjWtBjFkQ==","shasum":"cd54bf9051c9976b143cb3d85de542b8fce14f30","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.2-beta.2.tgz","fileCount":123,"unpackedSize":351836,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMZdQCRA9TVsSAnZWagAAVNQP/jmk7gFO6C4G0of/8LtF\njKFjVDHg5tzmck13RuSLFM99VCgebzuDLR0K1vhgZ1/r8JSHi2ZNr6eur6wn\ng9+SeHnI939wuIsBbFfTe6/TpUVHmOti8DlJT15sYVkoiB3IvElyeaQIDoxm\nRlE2RuJHWzOUj01wLqHqvM5d6mXIgYO60rY1l6EOkKM4WFZ6ZZqGsF2b7nol\ntDLl3fosr7rVaLrQeJ88LhUckDNGDf39ZQkjfesMfEppaWwwTD6uvoPfmlKd\nrvjap0eo6jnh6KUtOgfbC5kgrDN78/0t7Z9cG/0KIYt/8rn2u9Nuxr5vQXug\nk+tIMgAtdU68BOD9jXZEDy5tq/A65EoNA6RIESwz9Z/Tbu9pYcxQDgMPnaIq\n+0h2QyHoSxuxP/a4WJPcq6xG/btTtKmO+a800iOSA4qlzlMpXk0UcLaEaUmq\nPZMhCkeQaI4/WuDsPF+C1457TCWBVnf/KyUR2OjtQ1z4LDw3xBblCe/AbhvI\nQmQaZi3ESe4xEe1GMTvoiVGtBNLXsVqVgyB2uy9FocDPWHh11EpZVm1dbrhe\nlRBllvtDP4wMs4x9L1394Jvimg5f68ag/Y40UY+7hxMocd/WffYB7qfPYrze\nJkZXU7nUeH4b+O+97yILKYcj9aYgSoLTg3Xh8DtvghHe9CC4mcCdrAMPY6f+\n8e3S\r\n=UYBU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcE7973DBBHpShNoawSH5GTcOtOfI0265q24lj0uT1agIhAL4yZG5QE464Hwi5aTjQYo50GrA62I1Gcqv0y/mlzPni"}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.2-beta.2_1630639951872_0.8280124489388925"},"_hasShrinkwrap":false},"3.0.2-beta.3":{"name":"react-auto-edit","version":"3.0.2-beta.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"47533493083608f0d2e9b97045949bf0d69a319e","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.2-beta.3","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-gD0sWQH8GJ/7YujSazavTdU23fl/xUlcvcITtzKk5q9OwhpWmVWP83tixK6u4KWVIV12c9XpEzOBfv0QafM2qg==","shasum":"9b94dfb2d707a83b8f347f89fe28581490161913","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.2-beta.3.tgz","fileCount":123,"unpackedSize":351836,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMZjVCRA9TVsSAnZWagAAJNQQAJ+/Qez1GvtJVxsKfXZt\nYwy3efuRR24QpL5yCIS7SH75uvFqWHzzuDD4IksTOSar04jNmQSaEkCcLqmq\n6GKJI2ribsLGB5cRIhCFP0R410LNvBLhAlzkt1SZ8KPibLKFNEDko943I1IS\nKedTn6FASRIz9Ab5zVp3yOs5gxzEzhPNu0j5erQCWFazvV1neUGlGpFLXTUn\npawFuowmSjdfmWfmIhV397uf4EpVFNJeq4A76cQMV+Qbrgw/33eyUibFQMxV\n3EsDEuHi5UHikPLIVUMJGRy4qBpkfXuC4D2WkzU/M1XXudA6yJ26LQctNP/v\nwDCyxdf1bCte/46hUnRSINJRNb5cQoS+B2qbve82LGgcsIjXVCL2Gbh5ocUV\n/nf5wPbuT6zDt2NLsi3MLwbTFoh9FtGelis+D/CFY548+0DT2SgckWLJZGAZ\njuu1pmeIniZIv+cVAWQlH0WjoIrtvS5NUYn5b1nN6Ib/foW80KlI0KFUPGK9\nShJNczC7Ukba05trOngvPhRhwdrmJSLSN+kyn/G9U/FVlN5K++dSoidCC1iF\nR5hm/siycCsyu2sUaw7Qw/rsIzTNJWQp/d43+zCGtcCc/UgJtSS6rcT8oImi\nLrH/rBBjwBo9Yzv6XFZ1EgktJfsRlf7O1TCvz7vYX0rI6DpfyShS2ag1Dz8t\nN9j+\r\n=wXmr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCiSFoSfKI4zeT1S3aX5oIvVdxeIf3kwxTYTc4PgTL2YgIgb3NHifR5geU9ECSJi8BgPkOPuL0QZ5PISg0pBWM26kw="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.2-beta.3_1630640341045_0.08949920840039938"},"_hasShrinkwrap":false},"3.0.3-beta.0":{"name":"react-auto-edit","version":"3.0.3-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"d10094a10d51c7f6161408c1ffb103506cb09f25","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.3-beta.0","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-WE/FQ7zNXtxHXx7trIatqfFWp6ntSMDI6SddfWAR6sjGWnW5aNF00fVAKdWP2dm9ybgAnqtSsoY7KWmWMbqDkg==","shasum":"ac46eb1eadbf0e77c20d85e4a1aa6bcc006470b4","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.3-beta.0.tgz","fileCount":123,"unpackedSize":351836,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMZosCRA9TVsSAnZWagAAw6MP/jwdvRCnymdrbxyU1PAO\no7jIY6xwCe/dDFqaiwmZadOW7io0Pa4i1kUSee+lSN4vAMlZ2vodj9le4DWl\nW91fFoR3pxv7lzGHVNiTygs7gWs+d1U6POvBJlz9WnvnGJwdAPNDigYH6o7p\nmv5cX5VY6JRKzGWXwWcPbte+SEmetEuzxdfODQ8gMJ87hY4Jj/LMOxKKABDD\n3X3j088A5isNdmqBITFEq68DuzfmU+g8bSKaqxpA+7S9jgvlkNXVupetohJg\nxJ6i7wXQQeKsfSCbI7m/s4I8XzqxoGWpn/y7vEWdUJG5dYVmNN9Xqew4Snu6\n3+1Q/K45tVY5WhFg/U+ryd+W7BTSH2ypKFLwVSQVn6LLapWk41wZ58/4mOhj\n2iH+vNaxQBIbPYmFtJKeM08jiw/ufB/uQv8qK/NvJ6c8csPrj+fMDsePpuA0\niLAujYcL+ndLdDkffuQt6bPTiqgs/nOvGjcH2tcZ1y8MMOihmbQRRit4t+F1\n/CBnLoMvKS5bqdMhm1Ko1XI/b8wUTKvL8Xzcp3Ih16YpO3GfjsNsQ7L2sz98\nVb2s+u7EvATGu6k4WHAkF1sGPXZrOe8VPOyUWe2iIvdSPOl4wUfflQCCmqKE\nJ7SWsm8JMU/Ed+wFTpokDQkn8zcFJ082QxDcrf2yrAq7CBnjUMQ/65rY0Hb/\nm/JU\r\n=2lXf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDjhDQ0fZW4Ud1D/7p8YSLFmRxVHbIrSkNn8Oz6cm31bQIgCbpRov8IB8PwfC5+dMgcq397XrSfE3P7ZdSv5nVNxYM="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.3-beta.0_1630640684060_0.33813881300046167"},"_hasShrinkwrap":false},"3.0.3":{"name":"react-auto-edit","version":"3.0.3","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"a8f210caec1dfaea745f51fc3513bf622120efac","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.3","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-YATY0AzQrXEm+f54QsUs05DRgonPQuQsxUGHeZMBEETfgkzpX8GLNNapWpQmx07J3YgSXqUs+r+MOztsX4ObnA==","shasum":"8ad67167f29053c32c2ddf1403441beade718fa5","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.3.tgz","fileCount":123,"unpackedSize":351829,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMZ6zCRA9TVsSAnZWagAA88IP/3o1DtveZecd+8NFq0kg\nmOpmdgXbc6pnJrpEi59AiAse8WIbPmPSukKmeuVDd3AsWdYKrZlm6gSvvMVb\n9klUzS3ajumkjw71RwtnuQJ6pkbDZab3oyzIoWQxgFGpLWuOEdFNOMVGM+VW\n3+wFHH08y8h/jVT39mtlIGqMIsECbKul2TcF+o0IRqiB20ZWjw1eZyCBN6MD\n/vBMM4y4aqqGakK76cFUrLe/gFV6kW1D3qB6qqoIwCUeUrXNr3HZv+bO1YFF\nsRd/NRmPEUD/Nr+B9grVtDB5JwA86b+Oy63c5aGLAxd+DOiY/Da1KrCerCH0\nNcGz+gyQJN2ia9YzuINAfCF9jFYVGrGQ7UKkOS8fD9tI+zcYEwrq26Hp7LzI\niFXpDuSweLLuZ2AynxDNirVVDSLTRJjWXZDT4B029mHzftRv+Tw8BKrH6aEq\nzRBSOhzdqWENv+dE+AF0+UjGJHpmbBc3BvShepohc9W2NZ3G3/I+kdCPc9J6\nUdehmYFBrLt0csrNDAOHYOnAhX+f6yVJ1GQWVEvTZPW6vR71xgi4n+TOhfdN\nQ1dfOZr5zdPwRbz13IZf2gitjpumD1/sA+oS8revUevRA2ybB/LBTJ0eKS2h\nGPaIPbOUc4bm13VP7aJSd1pcHHcuOwzq9TvPWyXPOvvyYJcONq23z3wGcInU\n9yqI\r\n=Fqyp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA1tvg8C833EE8Wok/AdltBvt5+fSpyMcR+FpGQgiiBAAiEAzq0Veav+bl40HIqWdSwn/s8YHICoGhaKUybS70wJjKA="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.3_1630641843317_0.2875209182625309"},"_hasShrinkwrap":false},"3.0.4-beta.0":{"name":"react-auto-edit","version":"3.0.4-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"8cee89f1b8ec039449ceaf08e63230e9fd3c6ca9","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.4-beta.0","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-rOPOEyGBR/PBzUh2zQWI7Sdez2wcNxPxfmXtbqk+CMawGxwE2g82BAqnWAWelhQDRieP8BTv3h8EbPl4G2yJlw==","shasum":"8bf518cbf8c76c6d130ff09a13074bb4369564bf","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.4-beta.0.tgz","fileCount":123,"unpackedSize":351836,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDTh84Aem29P88ZmbxfSc50KGxZ4hn6C+pwZz5ns6F4zQIhAIjuIbiQssbebeNOoU4DMpD8VZs6xzBva6N57QBeCsyE"}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.4-beta.0_1632083212514_0.48954193071645946"},"_hasShrinkwrap":false},"3.0.4":{"name":"react-auto-edit","version":"3.0.4","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"f5d9aa9ed993d28ddd3ce49d9796ba24b2733c0e","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.4","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-ny8QWKAyj+JKB/p1Y9QvOFnNuvJjmYF1KdjLMC3hDKUAybgIy9SLVzFBcKFJ1jZ7tkkqcPCvR+bPiaZnwIYLwA==","shasum":"19a980a149978f3d5ce8af2ceedb671035d4f7f9","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.4.tgz","fileCount":123,"unpackedSize":351829,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCjTHujOkpLsZwnAIgYJbsiJh/073A7ZmDKeWzqX2KVbQIhAPQX/UI7BHARKRzT2o46SBwGHWNjfmd9P/FUZdQOL07F"}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.4_1632084120892_0.59930637454116"},"_hasShrinkwrap":false},"3.0.4-beta.1":{"name":"react-auto-edit","version":"3.0.4-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"f5d9aa9ed993d28ddd3ce49d9796ba24b2733c0e","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.4-beta.1","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-xx275I5Hwos+olExJr6IphdESEywHvJ92f9HYXINDUMgXwbwS8b1zS5wvcCDjvHOU8tYZaEbqUvq+Nq5gbGs5Q==","shasum":"c1f9b87a26cdd639bba11086bf90b3dc080cd409","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.4-beta.1.tgz","fileCount":123,"unpackedSize":351836,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCcvuR7Zvsk/Lvs6+B2lUJRIruSN+Xkw7GoC9nbsloDyAIgSL08VdyeNh3DB+C7pirVs13c+sM/T7Oi3LgFzsY7o+0="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.4-beta.1_1632177195823_0.48502660990345103"},"_hasShrinkwrap":false},"3.0.5-beta.0":{"name":"react-auto-edit","version":"3.0.5-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"8b42559c6786a6a608be09cb92c668fa3fd3993c","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.5-beta.0","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-+JxpJyO8jw3PgJW3l/ZbWCtn7m45fKkWzKkvhvc1Td43PqzZbA5VJ98GybD/cyUwjzBZWj86Zb3rBALQo5dUkg==","shasum":"50a570814b4956b721702014df2b37db9f9a459c","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.5-beta.0.tgz","fileCount":123,"unpackedSize":352354,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEn6uUYnaPdQQU542Mdznj7Yq7sx3fp8M5x1AQpn9ZjnAiBctffn7KYzi26BCoCoXjCsaCnXjbCBkL7sZ7psdVUxQw=="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.5-beta.0_1632179228199_0.12528698424566342"},"_hasShrinkwrap":false},"3.0.5":{"name":"react-auto-edit","version":"3.0.5","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"2c2f3e892960a978b7cc606c76be53e6df487cc3","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.5","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-bGK3AodfSs9OtrTUwZB+i3ktL8TvFeYgd+jfkEDezfREM4VNKVegojFqbDPYKyJulqZfzkCNEaN7kK/R9IYVqw==","shasum":"cf704c6a1cd73f61500faa45b81617aa7ccabb3d","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.5.tgz","fileCount":123,"unpackedSize":352347,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDeLdbXjdJj9Nr+LvrkrWBDTK1bASTQbB8t6jmI/b0/bQIgTWyuKAVbV8ZoHlb8tJlqhYwjp7i6aQtebxWkHAmj0Cw="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.5_1632179455515_0.47302339104027036"},"_hasShrinkwrap":false},"3.0.6-beta.0":{"name":"react-auto-edit","version":"3.0.6-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"4b3b29a7bc4be1667800ed833f04a8bab9f1402e","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.6-beta.0","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-+c1fvp7Q1BfHrqYfvTS4wMvu8lfyofrMuWjwuvWWCjs0Vga0kBHkGUkT2/XClgYAvfk3GyjgqQFaxJEpPlTdeQ==","shasum":"b8b8053fc2cc75d61f80a85696ae0decbd7336b1","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.6-beta.0.tgz","fileCount":123,"unpackedSize":352889,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC42muSIJv3nachkLKUUeGtJ2etmYy1rxjYEaqEkZ0S8AiEA+QdGUcWase4zDRwXrmuQ20Sd5IDL0n7LAd+29H8K86s="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.6-beta.0_1634069196405_0.9497401119787849"},"_hasShrinkwrap":false},"3.0.6-beta.1":{"name":"react-auto-edit","version":"3.0.6-beta.1","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"661dc0c4545b70b980568a0087887cdd1f6a8871","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.6-beta.1","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-M/FzjWM5yrWtwH7GpbaxPMP9KZXEnGnbMGIhJ1m0BSLflMna9lpimhrXSYKZGp5ZusaM/nCevHZyHEmQQppuhQ==","shasum":"fae73ccfa3119bb1ba926eb1d4abce05cf90c5a5","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.6-beta.1.tgz","fileCount":123,"unpackedSize":352949,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDmtywzZTXjO7dyMMqYL/DnlqWVJsnoG7YpMwlupDKIaAIhAK0+B5lP8LrYYx92OQyhDf/z8ksvnDAz1e2zwUjs7n0z"}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.6-beta.1_1634079132672_0.7872948928234209"},"_hasShrinkwrap":false},"3.0.6":{"name":"react-auto-edit","version":"3.0.6","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"bf1823e981e42d9f2386c7e74119b8e690c078eb","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.6","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-+cf/kh5+7ZTj28mODx8Mg/ZDLKfVNsXbiuuor5X3SYk04SI3Q5D4uWRhxYHaLw0uf78Jbem2cUWAN976IoU36w==","shasum":"4c086ebe76065ecc8131ca82a93d6bd20e301662","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.6.tgz","fileCount":123,"unpackedSize":352942,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF3dH0tUewT/RtEJkEtC4GxVdClrh1fkNFExeIzpegWlAiEA4B7mj+wp3emaZOzmdNjgFoQwCWRyJTDEfbVZ4UvZWYs="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.6_1634079764925_0.7691590554292074"},"_hasShrinkwrap":false},"3.0.7-beta.0":{"name":"react-auto-edit","version":"3.0.7-beta.0","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"9dd4d845443aec4b6c1d103db26b7821c5cf33db","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.7-beta.0","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-nHSmkRNvi9M6K2RZarW9j7DxVLXQfgdj0GeD0qmfKBVWc97nZnxxCvNMwJ1byq8wj0e4xAnoRtJ2ObdocqYw+g==","shasum":"e0499c2eb1686722b5b4283e8224fd7a3e7e29bf","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.7-beta.0.tgz","fileCount":123,"unpackedSize":353097,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAbef2HFlC2y7iobKnZ+Xfmi3nexpyz8QolSZ6SRQDP+AiEAyCCm3UTYXf1PM++ANKog8/Ew76SzLjmj4c7L17LLzx8="}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.7-beta.0_1635281398364_0.5256307589569633"},"_hasShrinkwrap":false},"3.0.7":{"name":"react-auto-edit","version":"3.0.7","description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","main":"lib/index.js","types":"types","scripts":{"test":"jest","build":"eslint src && babel src -d lib && tsc","watch":"nodemon --ignore lib --exec \"babel src -d lib\"","test-debug":"node --inspect-brk node_modules/.bin/jest --runInBand"},"author":"","license":"MIT","devDependencies":{"@babel/cli":"^7.14.8","@babel/core":"^7.15.0","@babel/preset-env":"^7.15.0","@babel/preset-react":"^7.14.5","enzyme":"^3.11.0","enzyme-adapter-react-16":"^1.15.6","eslint":"^7.32.0","eslint-config-airbnb-base":"^14.2.1","eslint-plugin-import":"^2.24.2","eslint-plugin-jest":"^24.4.0","eslint-plugin-react":"^7.25.1","jest":"^27.1.0","joi":"^17.4.2","joi-key-extensions":"^2.0.2","nodemon":"^2.0.12","react":"^17.0.2","react-dom":"^17.0.2","react-router-dom":"^4.3.1","react-test-renderer":"^17.0.2","typescript-temporary-fork-for-jsdoc":"^3.6.0-insiders.20190802"},"dependencies":{"base64-arraybuffer":"^0.2.0","chokidar":"^3.5.2","clone-deep":"^4.0.1","dayjs":"^1.10.6","install":"^0.13.0","mobx":"^6.3.3","mobx-react":"^7.2.0","npm":"^7.21.1","p-limit":"^2.2.0","react-aria-menubutton":"^7.0.3","react-hotkeys":"^2.0.0","react-select":"^4.3.1","react-tooltip":"^4.2.21","uuid":"^8.3.2"},"peerDependencies":{"react-router-dom":"*"},"keywords":["joi","react","mobx"],"repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"},"jest":{"testEnvironment":"jsdom"},"gitHead":"936a077f608f73c2dbc1218b9b5136091aecd9b8","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","_id":"react-auto-edit@3.0.7","_nodeVersion":"10.24.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-+7THvMATVHVEdYrxE0IeN3odvF3mqeooW2bCmK8o8UhIZQ1UPxcVtFecmEaAQQR9s/R75YTkBKTPeaZ9KaJykA==","shasum":"df9c04acebda59279de55b6771ca05ff287df3fb","tarball":"https://registry.npmjs.org/react-auto-edit/-/react-auto-edit-3.0.7.tgz","fileCount":123,"unpackedSize":353090,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh24FPCRA9TVsSAnZWagAABwoP/RRAfoVyyyJOBcgkuFtE\nAfc9iB6vwQM3HtvbEhDkeaXIh22spU+Do4zteC33cbRBdtjvL4rI8wVQRF3L\n37jBdvwbDfGJZM/R4LfwIeHbkimjyTO+rXfkNcjcA7HQnOUeHSuD79GOohYk\nojJqHf6T0+MOinIbJb6gSRnwxFDEtjt0OEyWcFejTaE0TH9YVxOtdcoIpa3K\nBK9sh8yYTLt4uJ7zNgreEiOH7n5GoyBK9KvxS4ZOvaN980HPqGXH2QOeqUFo\nsYnmsJNbpsrJNhpJUfbrcgtkskEMLkGGyAzJAi34ZzE9MBniMzfItBT8chw8\n8hXAaMNg3t/vmL1/ZpMfVqnevHxatRE1BNfjldvWRDwkhuOXztF88O2VtGLQ\nckaq6/K5eC13QNfPCLERZ32gbq8Se/4kZIGNB7T8541QvV7cRuP1GWu3lE8i\nRRxjz8BTqUx4z89Her3u1Y8x3pebyW0iQU1P/NDakdSKcAKvyo8viAhOMOZM\nvBaTIIs9GIdoMpsu8kAHSj9aasz53UGHJ2S/QtYyoH1f3Ays9xch5XqiEF3U\nRXAvHbFt9pLoi69aTsqXT3yC6NoyCCVHlUrvSJpa2DEhyYOmAqZPWJ6Zilpb\nlXBZ4G2eQ9Vr5BxblNmbYqq+fjUOxP+mNIeqvuuFSCHEODqn4bPqSP/USNtb\nkIK5\r\n=0Lvx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCfIzYqLlaHkK0PMSB003+A97gGudzhdRWKXpDUs3Ji7AIhAISSu5utl4F8F4+gtMdOjtuSRNJPSGUCBy6XFRTKJ5rX"}]},"_npmUser":{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"},"directories":{},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-auto-edit_3.0.7_1635282664624_0.560399191343665"},"_hasShrinkwrap":false}},"time":{"created":"2019-05-15T20:49:53.898Z","1.0.0":"2019-05-15T20:49:53.999Z","modified":"2022-05-14T06:33:01.382Z","1.0.2":"2019-05-16T23:00:43.486Z","1.0.4":"2019-05-21T03:46:26.907Z","1.1.0":"2019-05-23T21:42:00.107Z","1.0.5":"2019-05-27T23:12:31.529Z","1.1.1":"2019-05-27T23:18:03.620Z","1.1.2":"2019-05-30T03:25:41.791Z","1.1.3":"2019-06-24T02:41:19.380Z","2.0.0":"2019-08-08T03:23:04.285Z","2.0.1":"2019-08-13T04:50:08.952Z","2.0.2":"2019-08-13T21:49:10.168Z","2.0.3":"2019-08-13T22:04:10.447Z","2.0.5":"2019-08-14T22:45:48.139Z","2.0.6":"2019-08-14T23:15:49.519Z","2.0.8":"2019-08-15T02:44:33.493Z","2.0.9":"2019-08-15T05:00:01.658Z","2.0.10":"2019-08-20T03:59:52.347Z","2.0.11":"2019-08-22T21:17:53.272Z","2.0.12":"2019-08-25T21:13:41.657Z","2.0.13-beta.0":"2019-09-03T03:26:23.899Z","2.0.14-beta.0":"2019-09-03T22:10:15.025Z","2.0.14-beta.1":"2019-09-03T22:41:05.621Z","2.0.14-beta.2":"2019-09-03T22:49:44.699Z","2.0.14-beta.3":"2019-09-03T22:56:50.108Z","2.0.14-beta.4":"2019-09-03T22:58:01.994Z","2.0.13":"2019-09-03T23:00:04.124Z","2.0.13-beta.1":"2019-09-04T03:59:32.151Z","2.0.14-beta.5":"2019-09-18T02:31:06.879Z","2.0.14-beta.6":"2019-09-23T20:07:31.747Z","2.0.14-beta.7":"2019-09-23T21:09:19.690Z","2.0.14":"2019-09-23T21:23:22.010Z","2.1.0-beta.0":"2019-09-24T23:48:46.887Z","2.1.0-beta.1":"2019-09-25T00:01:49.855Z","2.1.0-beta.2":"2019-09-25T00:03:00.488Z","2.1.0-beta.3":"2019-09-25T01:31:14.500Z","2.1.0":"2019-09-25T21:49:12.798Z","2.2.0-beta.0":"2019-09-26T21:02:24.846Z","2.2.0":"2019-09-27T02:23:14.045Z","2.2.1-beta.0":"2019-09-27T03:43:51.232Z","2.2.1":"2019-09-27T04:20:34.904Z","2.2.1-beta.1":"2019-09-27T04:55:57.062Z","2.2.2-beta.0":"2019-09-29T20:39:26.674Z","2.2.1-beta.2":"2019-09-30T00:47:45.982Z","2.2.3-beta.0":"2019-09-30T01:02:23.244Z","2.2.3":"2019-09-30T01:07:24.680Z","2.2.4-beta.0":"2019-10-03T22:22:56.392Z","2.2.4-beta.1":"2019-10-03T22:55:00.665Z","2.2.4-beta.2":"2019-10-03T23:14:08.261Z","2.2.4-beta.3":"2019-10-03T23:25:54.788Z","2.2.4-beta.4":"2019-10-04T00:42:16.800Z","2.2.4-beta.5":"2019-10-04T01:58:45.910Z","2.2.5-beta.0":"2019-10-04T02:06:00.543Z","2.2.5":"2019-10-04T03:00:56.056Z","2.2.6-beta.0":"2019-10-04T03:48:34.462Z","2.2.7-beta.0":"2019-10-08T21:47:27.437Z","2.2.7-beta.1":"2019-10-08T22:47:50.045Z","2.2.7-beta.2":"2019-10-09T00:51:55.098Z","2.2.7-beta.3":"2019-10-09T00:53:44.375Z","2.2.6":"2019-10-09T01:24:05.819Z","2.2.7-beta.4":"2019-10-09T01:32:06.520Z","2.2.7":"2019-10-09T01:36:04.882Z","2.2.8-beta.0":"2019-10-09T03:32:58.480Z","2.2.8-beta.1":"2019-10-09T21:10:16.433Z","2.2.8-beta.2":"2019-10-09T21:25:30.518Z","2.2.8-beta.3":"2019-10-09T21:26:53.979Z","2.2.8-beta.4":"2019-10-13T20:31:56.722Z","2.2.9-beta.0":"2019-10-13T22:19:31.092Z","2.2.8":"2019-10-13T23:39:35.571Z","2.2.10-beta.0":"2019-10-14T02:44:38.404Z","2.2.10":"2019-10-14T03:02:04.592Z","2.2.10-beta.1":"2019-10-15T03:54:33.695Z","2.2.11-beta.0":"2019-10-15T19:30:52.267Z","2.2.11-beta.1":"2019-10-15T19:34:48.333Z","2.2.12-beta.0":"2019-10-15T20:03:58.597Z","2.2.12":"2019-10-15T23:36:07.892Z","2.2.12-beta.1":"2019-10-16T20:23:38.753Z","2.3.0-beta.0":"2019-10-17T03:50:06.427Z","2.3.0-beta.1":"2019-10-20T22:20:37.863Z","2.3.0-beta.2":"2019-10-21T00:38:02.648Z","2.3.0-beta.3":"2019-10-21T20:09:16.399Z","2.3.0-beta.4":"2019-10-21T22:03:38.946Z","2.3.0-beta.5":"2019-10-22T00:12:49.328Z","2.3.0-beta.6":"2019-10-22T01:27:09.927Z","2.3.0-beta.7":"2019-10-22T01:28:43.717Z","2.3.0-beta.8":"2019-10-22T01:45:42.393Z","2.3.0-beta.9":"2019-10-22T02:40:40.898Z","2.3.0-beta.10":"2019-10-22T03:00:17.191Z","2.3.0-beta.11":"2019-10-22T21:16:59.674Z","2.3.0":"2019-10-23T01:29:23.227Z","2.3.0-beta.12":"2019-10-24T01:51:33.976Z","2.3.1-beta.0":"2019-10-24T01:54:27.424Z","2.3.1-beta.1":"2019-10-24T01:55:56.901Z","2.3.1-beta.2":"2019-10-24T03:28:03.308Z","2.3.1-beta.3":"2019-10-24T04:00:55.014Z","2.3.1-beta.4":"2019-10-24T04:04:11.150Z","2.3.1":"2019-10-24T04:06:19.425Z","2.3.2-beta.0":"2019-10-31T20:45:11.265Z","2.3.2":"2019-11-03T20:13:50.840Z","2.3.3-beta.0":"2019-11-04T03:57:54.454Z","2.3.4-beta.0":"2019-11-04T20:43:43.960Z","2.3.3":"2019-11-04T22:23:34.725Z","2.3.2-beta.1":"2019-11-08T00:26:46.168Z","2.3.3-beta.1":"2019-11-08T00:31:11.163Z","2.3.4-beta.1":"2019-11-08T00:36:38.765Z","2.3.4-beta.2":"2019-11-08T00:40:47.469Z","2.3.4":"2019-11-10T20:46:14.037Z","2.3.5-beta.0":"2020-03-23T03:00:39.729Z","2.3.5":"2020-03-25T00:21:13.136Z","2.3.5-beta.1":"2021-09-01T22:47:42.153Z","3.0.1-beta.0":"2021-09-01T22:49:09.187Z","3.0.1-beta.1":"2021-09-02T02:21:45.775Z","3.0.1":"2021-09-02T20:21:13.300Z","3.0.2-beta.0":"2021-09-03T00:44:13.048Z","3.0.2-beta.1":"2021-09-03T03:19:56.314Z","3.0.2-beta.2":"2021-09-03T03:32:32.051Z","3.0.2-beta.3":"2021-09-03T03:39:01.235Z","3.0.3-beta.0":"2021-09-03T03:44:44.257Z","3.0.3":"2021-09-03T04:04:03.512Z","3.0.4-beta.0":"2021-09-19T20:26:52.684Z","3.0.4":"2021-09-19T20:42:01.017Z","3.0.4-beta.1":"2021-09-20T22:33:16.052Z","3.0.5-beta.0":"2021-09-20T23:07:08.436Z","3.0.5":"2021-09-20T23:10:55.682Z","3.0.6-beta.0":"2021-10-12T20:06:36.588Z","3.0.6-beta.1":"2021-10-12T22:52:13.079Z","3.0.6":"2021-10-12T23:02:45.126Z","3.0.7-beta.0":"2021-10-26T20:49:58.526Z","3.0.7":"2021-10-26T21:11:04.822Z"},"maintainers":[{"name":"sfhardman","email":"s.f.hardman@massey.ac.nz"}],"license":"MIT","readme":"# React Auto Edit\n\nReact Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automatically.\n\nIt is suitable for use cases where you need a basic CRUD editor UI, e.g. line of business / admininstrative apps.\n\n![alt text](/docs/img/screenshot1.png \"Screenshot\")\n\n## Quick Start\n```shell\nnpx create-react-app myapp\ncd myapp\nnpm install --save react-auto-edit joi joi-key-extensions react-router-dom\nnpm start\n```\n\nEdit myapp/src/App.js:\n```js\nimport React from 'react';\nimport VanillaJoi from 'joi';\nimport { fkExtension, pkExtension } from 'joi-key-extensions';\nimport { BrowserRouter as Router, Redirect, Route } from 'react-router-dom';\nimport {\n  ApiProxy, Controller, EditRoutes, Menu, Loading,\n} from 'react-auto-edit';\nimport 'react-auto-edit/static/Edit.css';\n\nconst Joi = VanillaJoi\n  .extend(fkExtension.number)\n  .extend(pkExtension.number);\n\nconst schema = Joi.object({\n  albums: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n  }),\n  posts: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    body: Joi.string().max(5000),\n  }),\n  comments: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    postId: Joi.number().integer().fk('posts.[].id'),\n    name: Joi.string().meta({ displayName: true }),\n    email: Joi.string().email(),\n    body: Joi.string().max(5000),\n  }),\n  todos: Joi.array().items({\n    id: Joi.number().integer().pk(),\n    userId: Joi.number().integer(),\n    title: Joi.string().meta({ displayName: true }),\n    completed: Joi.boolean(),\n  }),\n});\n\nconst apiProxy = new ApiProxy(schema, 'https://jsonplaceholder.typicode.com', {\n  pageSize: 10,\n  concurrentFetchesLimit: 1,\n});\nconst controller = new Controller(schema, apiProxy)\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <EditRoutes basePath=\"\" controller={controller} />\n    <Loading controller={controller} />\n    <Route exact={true} path=\"/\" component={() => <Redirect to=\"/posts\" />}/>\n  </React.Fragment>\n</Router>;\n\nexport default App;\n```\n\nUnfortunately Joi is now being published with the \"browser\" property in its package.json pointing to a cut down\nversion of Joi that does not support some required features.  Also unfortunately, a workaround is required with Create React App to get this property ignored.\n\n```shell\nnpm install --save craco\n```\nEdit myapp/package.json:\n```diff\n/* package.json */\n\n\"scripts\": {\n-   \"start\": \"react-scripts start\",\n+   \"start\": \"craco start\",\n-   \"build\": \"react-scripts build\",\n+   \"build\": \"craco build\"\n-   \"test\": \"react-scripts test\",\n+   \"test\": \"craco test\"\n}\n```\n\nAdd craco.config.js at the same level as your package.json:\n\n```js\nconst path = require('path');\n\nmodule.exports = {\n  webpack: {\n    configure: {\n      resolve: {\n        alias: {\n          // ignore the cut down browser distribution that \n          // joi's package.json steers webpack to\n          joi: path.resolve(__dirname, 'node_modules/joi/lib/index.js'),\n        },\n      },\n    },\n  },\n};\n```\n\n## Conventions\n\n### Schema\nReact Auto Edit has some expectations of the Joi schema that controls it:\n* The root of the schema must be a Joi.object (not a Joi.array)\n* Each child object must have one or more fields with the pk() validator applied (e.g. `myfield: Joi.string().pk()`)\n* Each child object must have one or more fields with a displayName meta applied (e.g. `myfield: Joi.string().meta({ displayName: true })`)\n* Objects as children of objects are not supported.  Arrays of objects as children of objects _are_ supported.\n* Supported Joi base types are:\n  * Joi.string\n  * Joi.number\n  * Joi.date\n  * Joi.boolean\n  * Joi.object\n  * Joi.array\n* Some Joi attributes that React Auto Edit will respond to are:\n  * fk() from [joi-key-extensions](https://www.npmjs.com/package/joi-key-extensions) - a select component will be shown, offering candidate values based on the foreign key reference\n  * required() - a mandatory field indicator will be shown in the UI, and select components will not be clearable\n  * valid(...values) - a select component will be shown, restricting options to those in the values parameter, plus an empty value if the field is optional\n  * allow(...values) - for string fields, the options in the values parameter will be suggested / autocompleted, but the user will be free to enter any text that passes validation\n  * meta({ displayName: true}) - fields tagged with this attribute are used as the display name fields for an entity\n  * meta({ hidden: true}) - fields tagged with this attribute are hidden on the UI\n  * meta({ generated: true })\n    * used to indicate the field is generated server side (e.g. SQL identity columns, audit fields)\n    * tagged field will be rendered as a readonly element\n    * when a newly created entity is saved React Auto Edit expects the POST API to return the object with server generated fields supplied.  It will update the client side entity with the server generated fields.\n  * label() - field label to display\n  * description() - a tooltip will be displayed by the field with the supplied description\n  * max() - applied to number and string fields\n  * min() - applied to number fields\n  * string().uri() - a type='html' input will be used\n  * date().utc() and date.format() from the [joi-date](https://www.npmjs.com/package/@hapi/joi-date) extension\n    * dates will be converted to and from the supplied format\n    * if the format includes a time part, a time picker will be shown as well as the date picker\n    * React Auto Edit will try to do something sensible regarding the utc setting (and it might even work).\n\n### Target REST API\nReact Auto Edit also expects the target REST API to conform to some conventions:\n* URLs are formatted like `/<EntityName>/<EntityPK>`\n* Child collection URLs are like `/<ParentEntityName>/<ParentEntityPK>/<ChildCollectionFieldName>/<ChildEntityPK>`\n* Entity and collection names in the URLs correspond exactly to the names in the Joi schema (e.g. no variation between singular and plural form)\n* If there are multiple primary keys for an entity then they are supplied one after another in the same order as they appear in the Joi schema (e.g. `/<EntityName>/<EntityPK1>/<EntityPK2>`)\n* For each entity there is a:\n  * GET method to get a single entity detail - including all fields\n  * GET method to get a summary (optionally paginated, see the options for the ApiProxy class) of a collection of the entity\n    * This method is only expected to deliver the primary key and display name fields for the entities.  It can return the full entities but at present React Auto Edit assumes it needs to use the get detail method for this.\n    * If ApiProxy is configured with server side pagination, then the API is expected to:\n      * Take query string parameters 'page' (page number) and 'pageSize' (size of pages to return)\n      * Return an object with the signature `{ totalPages: 1, items: [] }`\n    * If ApiProxy is configured with client side pagination, then the API is expected to return an array of entities\n    * If ApiProxy is configured with server side filtering, then the API is expected to take a query string parameter 'filter'\n  * POST method to add a single entity (the entity primary keys will _not_ be included in the URL)\n  * PUT method to update a single entity (the entity primary keys will be included in the URL)\n  * DELETE method to delete a single entity, identified by the entity primary keys in the URL\n\nIf need be you can work around these conventions by subclassing ApiProxy\n\n## Classes\n\n### ApiProxy\nThe ApiProxy class is in charge of mapping data requests to REST API calls\n\n* constructor - new ApiProxy(schema, baseApiPath, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * baseApiPath - the base url for the target API e.g. 'https://example.org/api'\n  * options\n    * options.concurrentFetchesLimit - a number specifying the maxmimum number of concurrent API calls from the client.  Defaults to 100\n    * options.pagingMode - controls whether collection API calls should be paginated on the server side or client side. One of ApiProxy.pagingModes.clientSide and ApiProxy.pagingModes.serverSide.  Defaults to ApiProxy.pagingModes.clientSide (i.e. the target REST API is not pagination aware).\n    * options.filterMode - controls whether filter/search of collections is handled server or client side.  One of ApiProxy.filterModes.clientSide and ApiProxy.filterModes.serverSide.  Defaults to ApiProxy.filterModes.clientSide (i.e. the target REST API does not know how to filter results).\n    * options.pageSize - controls the size of search result pages.  Defaults to 10.\n    * options.collectionSummariesIncludesFullEntities - by default we assume REST API methods that return a collection of entities only include summary fields, and we need to call another API method to get a single entity to see all the fields.  If collectionSummariesIncludesFullEntities is true, then we accept the result of a collection API method as including the full entities, and don't make the extra calls.\n\n## UIFactory\nThe UIFactory class creates instances of the React UI components.  If you use the standard React UI components then you don't need to worry about this class.\n* constructor - new UIFactory(options)\n  * preferredDisplayMode - controls which display mode should be used where possible.  One of UIFactory.displayModes.tabular or UIFactory.displayModes.masterDetail.\n\n## Controller\nThe Controller class is in charge of managing application state\n\n* constructor - new Controller(schema, apiProxy, options)\n  * schema - the Joi schema you create to drive React Auto Edit\n  * apiProxy - an instance of ApiProxy (or a subclass of ApiProxy)\n  * options\n    * options.baseClientPath - if you want the client URL routes for React Auto Edit to be mounted somewhere other than root, then specify the path here (e.g. '/editor)\n    * options.uiFactory - if you need to render different React components for editing a collection, item or field than standard, you can provide an instance of a UIFactory or subclass here.\n\n## Components\n\n### EditRoutes\nReturns a set of React Router routes supporting url based navigation to items in the schema tree.  Note that you must wrap this with a React Router \"BrowserRouter\" component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### Menu\nProvides a menu to allow navigation to the top-level fields of the schema.  This assumes you are using the EditRoutes component.  Save and Cancel buttons are included for convenience - if you don't want to use the Menu component you can add them seperately.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* title - a string to use as the title for the drop down menu - optional\n* titleComponent - a React component instance to use as the title for the drop down menu - optional\n\n### Loading\nDisplays a loading spinner when API calls are in flight\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n## Components - Advanced\nYou do not need to explicitly use the following components for simple use cases, as they are encapsulated by EditRoutes and Menu.\n\n### SaveButton\nDisplays a button to trigger saving pending changes to the target API.  If you use the Menu component you will not need to add a SaveButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### CancelButton\nDisplays a button that can trigger discarding pending changes.  If you use the Menu component you will not need to add a CancelButton.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n\n### EditCollectionTabular\nDisplays a tabular (columns and rows) editor for a collection.  Note that this editor is not suitable for all data types - for other collections use EditCollection.\n\nStatic Method:\n* canShowCollection(schema, collectionSchemaPath)\n  * schema - Joi schema for the full object tree\n  * collectionSchemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n  * returns true if the collection is suitable for display with this component.\n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n### EditCollection\nDisplays a master/detail editor for a collection.  \n\nProps:\n* controller - an instance of the Controller class (or subclass of this)\n* schemaPath - a string identifying the collection in the schema tree to render (e.g. 'enclosures.[].animals')\n* parentIds - if the collection is not from a field in the root of the object tree, parentIds is an array of the primary keys of the parent elements that must be traversed to get to this collection.  E.g. for schemaPath 'zoos.[].enclosures.[].animals', parentIds might be `[{ zooId: 3}, { enclosureId: 4 }]`.\n\n## Customisation\n\nReact Auto Edit is designed to be highly customisable.  The project in `examples/customisation` demonstrates some of the available options.\n\n### CSS\nYou can override the classes in the supplied CSS file (static/Edit.css), or leave it out of your project altogether.\n\n### UIFactory\nIf you want to return different React components than standard, subclass UIFactory, override the methods as needed and pass an instance of the subclass in the Controller options argument.  Refer to the `examples/customisation` project for examples of usage.\n\n### ApiProxy\nIf your target API does not correspond to the conventions expected by ApiProxy you can subclass ApiProxy and pass an instance of the subclass to Controller.\n\ne.g.\n\n```js\nimport { ApiProxy, Controller} from 'react-auto-edit';\nimport schema from './schema';\nimport getAuthToken from './somewhere';\n\nclass MyApiProxy extends ApiProxy {\n  constructor(schema, baseApiPath, options, getAuthToken) {\n    super(schema, baseApiPath, options);\n    this.getAuthToken = getAuthToken;\n  }\n\n  getPageAndFilterParams(page, filter) {\n    let result = super.getPageAndFilterParams(page, filter);\n    // say our target API has the page number parameter called 'pageNumber'\n    // instead of 'page'\n    result = result.filter(p => !p.startsWith('page'));\n    result.push(`pageNumber=${page}`);\n    return result;\n  }\n\n  async fetchJson(url, options) {\n    // for this example we need an authentication token to access the API\n    const authToken = this.getAuthToken();\n    const headers = { Authorization: `Bearer ${token}` };\n    const newOptions = Object.assign({ headers }, options);\n    return super.fetchJson(newUrl, newOptions);\n  }\n}\n\nconst apiProxy = new MyApiProxy(schema, 'http://localhost:8080', null, getAuthToken);\nconst controller = new Controller(schema, apiProxy);\n```\n\n### Routes\nYou use the React Router 'Switch' component to seize a route back from React Auto Edit and do whatever you please with it.\n\ne.g.\n```js\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n// etc...\n\nconst App = () => <Router>\n  <React.Fragment>\n    <Menu controller={controller} />\n    <Switch>\n      <Route path=\"/comments\" exact={true}\n        component={() => <div>Mine!</div>}\n      />\n      <EditRoutes basePath=\"\" controller={controller} />\n    </Switch>\n    <Loading controller={controller} />\n  </React.Fragment>\n</Router>;\n```\n\n## Technologies\n\nReact Auto Edit is based on:\n* [React](https://reactjs.org/) - to render the components\n* [Joi](https://github.com/hapijs/joi) - to define the data schema\n* [MobX](https://mobx.js.org/) - for state management\n* [React Router](https://reacttraining.com/react-router/web/guides/quick-start) - for routing\n\nThe example apps uses https://jsonplaceholder.typicode.com as a sample API, with thanks.\n","readmeFilename":"readme.md","keywords":["joi","react","mobx"],"description":"React Auto Edit is a library of components for producing low-code, schema driven editing UIs.  You define the data schema, provide the REST APIs, and React Auto Edit automagically provides the UI.  Validation of data against the schema is included automat","homepage":"https://github.com/hermetic-architecture-portal/react-auto-edit#readme","repository":{"type":"git","url":"git+https://github.com/hermetic-architecture-portal/react-auto-edit.git"},"bugs":{"url":"https://github.com/hermetic-architecture-portal/react-auto-edit/issues"}}