├── .gitignore ├── README.md ├── __fixtures__ ├── old_style_datapackages │ ├── datastore_tabular.js │ └── map.js └── with_widgets │ ├── chart.js │ ├── data_explorer.js │ ├── data_explorer_2.js │ ├── geojson_simple.js │ ├── pdf.js │ ├── table.js │ ├── tabularmap.js │ ├── unsupported.js │ ├── web.js │ └── web_view_on_datastore_resource.js ├── cosmos.config.js ├── data-explorer-hello.png ├── dist ├── App.css ├── App.js ├── App.test.js ├── AppWithProvider.js ├── actions │ └── index.js ├── components │ ├── DataView │ │ ├── index.js │ │ └── index.test.js │ ├── DataViewBuilder │ │ ├── index.js │ │ └── index.test.js │ ├── Pagination │ │ └── index.js │ └── Share │ │ └── index.js ├── css │ └── index.css ├── i18n │ ├── i18n.js │ └── locales │ │ ├── da │ │ └── translation.json │ │ ├── en │ │ └── translation.json │ │ ├── fr │ │ └── translation.json │ │ └── pt_BR │ │ └── translation.json ├── index.js ├── reducers │ ├── datapackage.js │ ├── datastoreFilters.js │ ├── rootReducer.js │ ├── serializedState.js │ └── widgets.js ├── serviceWorker.js ├── store.js ├── testData │ ├── actes-criminels.json │ ├── inlinedData.json │ ├── montreal1.json │ ├── remoteData.json │ ├── testChartBuilder.json │ ├── testData.json │ ├── testDataInit.json │ └── testDataTableInit.json └── utils │ ├── index.js │ ├── loadDataset.js │ └── utils.test.js ├── package.json ├── postcss.config.js ├── public ├── README.md ├── app.css ├── example-with-geopoint.html ├── example-with-lots-of-columns.html ├── favicon.ico ├── index.html └── manifest.json ├── src ├── App.css ├── App.js ├── App.test.js ├── AppWithProvider.js ├── actions │ └── index.js ├── components │ ├── DataView │ │ ├── index.js │ │ └── index.test.js │ ├── DataViewBuilder │ │ ├── index.js │ │ └── index.test.js │ ├── Pagination │ │ └── index.js │ └── Share │ │ └── index.js ├── css │ └── index.css ├── i18n │ ├── i18n.js │ └── locales │ │ ├── da │ │ └── translation.json │ │ ├── en │ │ └── translation.json │ │ ├── fr │ │ └── translation.json │ │ └── pt_BR │ │ └── translation.json ├── index.js ├── reducers │ ├── datapackage.js │ ├── datastoreFilters.js │ ├── rootReducer.js │ ├── serializedState.js │ └── widgets.js ├── serviceWorker.js ├── store.js ├── testData │ ├── actes-criminels.json │ ├── inlinedData.json │ ├── montreal1.json │ ├── remoteData.json │ ├── testChartBuilder.json │ ├── testData.json │ ├── testDataInit.json │ └── testDataTableInit.json └── utils │ ├── index.js │ ├── loadDataset.js │ └── utils.test.js ├── tailwind.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # development 12 | __fixtures__/projects/ 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | *.sw* 26 | 27 | /build 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Docs at: http://tech.datopian.com/data-explorer/ 2 | -------------------------------------------------------------------------------- /__fixtures__/old_style_datapackages/datastore_tabular.js: -------------------------------------------------------------------------------- 1 | import 'react' 2 | import App from '../../src/AppWithProvider' 3 | 4 | const datapackage = {"resources":[{"mimetype":"text/csv","cache_url":null,"hash":"","description":"","created":"2019-08-19T20:37:50.141937","datastore_active":true,"format":"csv","package_id":"538da88b-b409-49bd-b5ac-b24cfc82dfb9","name":"test_resource","cache_last_updated":null,"state":"active","mimetype_inner":null,"archiver":{"is_broken_printable":"Downloaded OK","updated":"2019-08-19T20:37:58.237014","cache_filepath":"/tmp/archive/5e/5e18c734-3331-49b5-8df4-a5775d061ef4/interventionscitoyendo-1.csv","last_success":"2019-08-19T20:37:58.237014","size":13253667,"is_broken":false,"failure_count":0,"etag":"\"17415da26209536f1f43dfc6efc5e605\"","status":"Archived successfully","url_redirected_to":"https://cc-p-minio.ckan.io/ckan/montreal/resources/5e18c734-3331-49b5-8df4-a5775d061ef4/interventionscitoyendo-1.csv?AWSAccessKeyId=2effdd1004072cb9&Expires=1566247137&Signature=S43L%2FG%2F4HkXvpHY5PS%2FhcEP5XI0%3D","hash":"3bf10da9370c55491f9639a71c9991f9119b80e6","status_id":0,"reason":"","last_modified":"2019-08-19T20:37:51","resource_timestamp":"2019-08-19T20:37:53.680248","mimetype":"text/csv","cache_url":"https://montreal.l3.ckan.io/5e/5e18c734-3331-49b5-8df4-a5775d061ef4/interventionscitoyendo-1.csv","created":"2019-08-19T20:37:58.339970","first_failure":null},"last_modified":"2019-08-19T20:37:50.063748","position":0,"revision_id":"fcfa7f9a-0cf1-45d2-bfbc-bbed48af22bb","url_type":"upload","id":"5e18c734-3331-49b5-8df4-a5775d061ef4","resource_type":null,"size":null,"title":"test resource","path":"https://montreal.l3.ckan.io/en/dataset/538da88b-b409-49bd-b5ac-b24cfc82dfb9/resource/5e18c734-3331-49b5-8df4-a5775d061ef4/download/interventionscitoyendo-1.csv","shareLink":"http://0.0.0.0:4000/bixi/test-2019-09-19/5e18c734-3331-49b5-8df4-a5775d061ef4","iframeText":"","index":0}],"views":[{"id":0,"title":"test resource","format":"csv","resources":["test_resource"],"specType":"table"},{"id":0,"title":"test resource","format":"csv","resources":["test_resource"],"specType":"simple"},{"id":0,"title":"test resource","format":"csv","resources":["test_resource"],"specType":"tabularmap"}],"controls":{"showChartBuilder":true,"showMapBuilder":true}} 5 | 6 | export default { 7 | component: App, 8 | props: {datapackage: JSON.stringify(datapackage)} 9 | } 10 | -------------------------------------------------------------------------------- /__fixtures__/old_style_datapackages/map.js: -------------------------------------------------------------------------------- 1 | import 'react' 2 | import App from '../../src/AppWithProvider' 3 | 4 | const datapackage = '{"name":"test","resources":[{"name":"geojson-test","format":"GeoJSON","path":"https://montreal.l3.ckan.io/en/dataset/587de1ea-817b-4e10-b387-83ec89f94e1b/resource/bc1e71e4-269a-4fa9-a278-ab6bfd80ec23/download/murales.geojson"}],"views":[{"specType":"map","resources":["geojson-test"]}]}' 5 | 6 | export default { 7 | component: App, 8 | props: {datapackage} 9 | } 10 | -------------------------------------------------------------------------------- /__fixtures__/with_widgets/chart.js: -------------------------------------------------------------------------------- 1 | import App from '../../src/AppWithProvider' 2 | 3 | const widgets = [ 4 | { 5 | active: true, 6 | name: 'Chart', 7 | datapackage: { 8 | views: [ 9 | { 10 | "resources": ["inspection_des_aliments_–_contrevenants"], 11 | "description": "", 12 | "title": "Graphe", 13 | "resource_id": "51026016-7d82-49dc-93e0-2176df8790c6", 14 | "view_type": "recline_graph_view", 15 | "id": "4b94afa6-2fbc-4d34-95b6-dbb63a2f3348", 16 | "package_id": "a5c1f0b9-261f-4247-99d8-f28da5000688", 17 | "specType": "simple", 18 | "spec": {} 19 | } 20 | ] 21 | } 22 | } 23 | ] 24 | 25 | const datapackage = { 26 | name: 'test', 27 | resources: [ 28 | { 29 | api: "https://montreal.l3.ckan.io/api/3/action/datastore_search?resource_id=54d7ffa0-04bf-442c-bacd-a84c6aab888d", 30 | path: 'https://montreal.l3.ckan.io/dataset/a5c1f0b9-261f-4247-99d8-f28da5000688/resource/54d7ffa0-04bf-442c-bacd-a84c6aab888d/download/inspection-aliments-contrevenants.csv', 31 | name: 'inspection_des_aliments_–_contrevenants', 32 | format: 'csv' 33 | } 34 | ] 35 | } 36 | 37 | export default { 38 | component: App, 39 | props: {widgets, datapackage: JSON.stringify(datapackage)} 40 | } 41 | -------------------------------------------------------------------------------- /__fixtures__/with_widgets/data_explorer.js: -------------------------------------------------------------------------------- 1 | import App from '../../src/AppWithProvider' 2 | 3 | const widgets = [ 4 | { 5 | active: true, 6 | name: 'Table', 7 | datapackage: { 8 | views: [ 9 | { 10 | "id": 1, 11 | "specType": "table" 12 | } 13 | ] 14 | } 15 | }, 16 | { 17 | active: false, 18 | name: 'Chart', 19 | datapackage: { 20 | views: [ 21 | { 22 | "id": 1, 23 | "specType": "simple", 24 | "spec": {} 25 | } 26 | ] 27 | } 28 | }, 29 | { 30 | active: false, 31 | name: 'Map', 32 | datapackage: { 33 | views: [ 34 | { 35 | "id": 1, 36 | "specType": "tabularmap", 37 | "spec": {} 38 | } 39 | ] 40 | } 41 | } 42 | ] 43 | 44 | const datapackage = { 45 | name: 'test', 46 | resources: [ 47 | { 48 | api: "https://montreal.l3.ckan.io/api/3/action/datastore_search?resource_id=54d7ffa0-04bf-442c-bacd-a84c6aab888d", 49 | path: 'https://montreal.l3.ckan.io/dataset/a5c1f0b9-261f-4247-99d8-f28da5000688/resource/54d7ffa0-04bf-442c-bacd-a84c6aab888d/download/inspection-aliments-contrevenants.csv', 50 | name: 'inspection_des_aliments_–_contrevenants', 51 | id: '54d7ffa0-04bf-442c-bacd-a84c6aab888d', 52 | format: 'csv', 53 | datastore_active: true 54 | } 55 | ] 56 | } 57 | 58 | export default { 59 | component: App, 60 | props: {widgets, datapackage: JSON.stringify(datapackage)} 61 | } 62 | -------------------------------------------------------------------------------- /__fixtures__/with_widgets/data_explorer_2.js: -------------------------------------------------------------------------------- 1 | import App from '../../src/AppWithProvider' 2 | 3 | 4 | const widgets = [ 5 | { 6 | "name": "Table", 7 | "active": true, 8 | "datapackage": { 9 | "views": [ 10 | { 11 | "id": "66632027-3d7d-4ca6-911f-bc4d003f3ddf", 12 | "specType": "table" 13 | } 14 | ] 15 | } 16 | }, 17 | { 18 | "name": "Chart", 19 | "active": false, 20 | "datapackage": { 21 | "views": [ 22 | { 23 | "id": "66632027-3d7d-4ca6-911f-bc4d003f3ddf", 24 | "specType": "simple" 25 | } 26 | ] 27 | } 28 | }, 29 | { 30 | "name": "Map", 31 | "active": false, 32 | "datapackage": { 33 | "views": [ 34 | { 35 | "id": "66632027-3d7d-4ca6-911f-bc4d003f3ddf", 36 | "specType": "tabularmap" 37 | } 38 | ] 39 | } 40 | } 41 | ] 42 | 43 | const datapackage = { 44 | "resources": [ 45 | { 46 | "mimetype": null, 47 | "cache_url": null, 48 | "hash": "", 49 | "description": "We publish embedded wind and solar forecast up to 14 days ahead at a daily resolution. ", 50 | "name": "embedded_solar_and_wind_forecast_daily", 51 | "format": "csv", 52 | "datastore_active": true, 53 | "cache_last_updated": null, 54 | "package_id": "91c0c70e-0ef5-4116-b6fa-7ad084b5e0e8", 55 | "created": "2019-11-07T21:25:27.215305", 56 | "state": "active", 57 | "mimetype_inner": null, 58 | "last_modified": "2020-02-18T04:34:45", 59 | "position": 0, 60 | "revision_id": "9adf3925-0f34-4235-984a-d1c536ecbe1a", 61 | "url_type": null, 62 | "id": "db6c038f-98af-4570-ab60-24d71ebd0ae5", 63 | "resource_type": null, 64 | "size": null, 65 | "title": "Embedded Solar and Wind Forecast DAILY", 66 | "path": "https://docs.google.com/spreadsheets/d/10suCRrhbljC1VX-9lVMGBncUZbvTJ4KVBV0wfVoVRUU/gviz/tq?tqx=out:csv", 67 | "views": [ 68 | { 69 | "description": "", 70 | "title": "Data Explorer", 71 | "resource_id": "db6c038f-98af-4570-ab60-24d71ebd0ae5", 72 | "view_type": "recline_view", 73 | "id": "66632027-3d7d-4ca6-911f-bc4d003f3ddf", 74 | "package_id": "91c0c70e-0ef5-4116-b6fa-7ad084b5e0e8", 75 | "specType": "dataExplorer", 76 | "spec": { 77 | "widgets": [ 78 | { 79 | "specType": "table" 80 | }, 81 | { 82 | "specType": "simple" 83 | }, 84 | { 85 | "specType": "tabularmap" 86 | } 87 | ] 88 | } 89 | } 90 | ], 91 | "descriptionHtml": "
We publish embedded wind and solar forecast up to 14 days ahead at a daily resolution.
\\n", 92 | "api": "https://national-grid-admin.ckan.io/api/3/action/datastore_search?resource_id=db6c038f-98af-4570-ab60-24d71ebd0ae5" 93 | } 94 | ] 95 | } 96 | 97 | export default { 98 | component: App, 99 | props: {widgets, datapackage: JSON.stringify(datapackage)} 100 | } 101 | -------------------------------------------------------------------------------- /__fixtures__/with_widgets/geojson_simple.js: -------------------------------------------------------------------------------- 1 | import App from '../../src/AppWithProvider' 2 | 3 | const widgets = [ 4 | { 5 | active: true, 6 | name: 'Map', 7 | datapackage: { 8 | views: [ 9 | { 10 | specType: 'map', 11 | resources: [ 12 | 'geojson-test' 13 | ] 14 | } 15 | ] 16 | } 17 | } 18 | ] 19 | 20 | const datapackage = { 21 | name: 'test', 22 | resources: [ 23 | { 24 | path: 'https://montreal.l3.ckan.io/en/dataset/587de1ea-817b-4e10-b387-83ec89f94e1b/resource/bc1e71e4-269a-4fa9-a278-ab6bfd80ec23/download/murales.geojson', 25 | pathType: 'remote', 26 | name: 'geojson-test', 27 | format: 'GeoJSON', 28 | mediatype: 'application/geo+json', 29 | encoding: 'utf-8', 30 | } 31 | ] 32 | } 33 | 34 | export default { 35 | component: App, 36 | props: {widgets, datapackage: JSON.stringify(datapackage)} 37 | } 38 | -------------------------------------------------------------------------------- /__fixtures__/with_widgets/pdf.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datopian/data-explorer/0dbb7cad13a3f2f96b80860adf97669e79a44d9f/__fixtures__/with_widgets/pdf.js -------------------------------------------------------------------------------- /__fixtures__/with_widgets/table.js: -------------------------------------------------------------------------------- 1 | import App from '../../src/AppWithProvider' 2 | 3 | const widgets = [ 4 | { 5 | active: true, 6 | name: 'Table', 7 | datapackage: { 8 | views: [ 9 | { 10 | "description": "", 11 | "title": "Table", 12 | "resource_id": "54d7ffa0-04bf-442c-bacd-a84c6aab888d", 13 | "view_type": "recline_grid_view", 14 | "id": "16ce7d84-0db1-4d39-aeab-d74e3dcf7d31", 15 | "package_id": "a5c1f0b9-261f-4247-99d8-f28da5000688", 16 | "specType": "table" 17 | } 18 | ] 19 | } 20 | } 21 | ] 22 | 23 | const datapackage = { 24 | name: 'test', 25 | resources: [ 26 | { 27 | api: "https://montreal.l3.ckan.io/api/3/action/datastore_search?resource_id=54d7ffa0-04bf-442c-bacd-a84c6aab888d", 28 | path: 'https://montreal.l3.ckan.io/dataset/a5c1f0b9-261f-4247-99d8-f28da5000688/resource/54d7ffa0-04bf-442c-bacd-a84c6aab888d/download/inspection-aliments-contrevenants.csv', 29 | name: 'inspection_des_aliments_–_contrevenants', 30 | format: 'csv' 31 | } 32 | ] 33 | } 34 | 35 | export default { 36 | component: App, 37 | props: {widgets, datapackage: JSON.stringify(datapackage)} 38 | } 39 | -------------------------------------------------------------------------------- /__fixtures__/with_widgets/tabularmap.js: -------------------------------------------------------------------------------- 1 | import App from '../../src/AppWithProvider' 2 | 3 | const widgets = [ 4 | { 5 | active: true, 6 | name: 'Map', 7 | datapackage: { 8 | views: [ 9 | { 10 | "resources": ["inspection_des_aliments_–_contrevenants"], 11 | "description": "", 12 | "title": "Map", 13 | "resource_id": "51026016-7d82-49dc-93e0-2176df8790c6", 14 | "view_type": "recline_map_view", 15 | "id": "4b94afa6-2fbc-4d34-95b6-dbb63a2f3348", 16 | "package_id": "a5c1f0b9-261f-4247-99d8-f28da5000688", 17 | "specType": "tabularmap", 18 | "spec": {} 19 | } 20 | ] 21 | } 22 | } 23 | ] 24 | 25 | const datapackage = { 26 | name: 'test', 27 | resources: [ 28 | { 29 | api: "https://montreal.l3.ckan.io/api/3/action/datastore_search?resource_id=54d7ffa0-04bf-442c-bacd-a84c6aab888d", 30 | path: 'https://montreal.l3.ckan.io/dataset/a5c1f0b9-261f-4247-99d8-f28da5000688/resource/54d7ffa0-04bf-442c-bacd-a84c6aab888d/download/inspection-aliments-contrevenants.csv', 31 | name: 'inspection_des_aliments_–_contrevenants', 32 | format: 'csv' 33 | } 34 | ] 35 | } 36 | 37 | export default { 38 | component: App, 39 | props: {widgets, datapackage: JSON.stringify(datapackage)} 40 | } 41 | -------------------------------------------------------------------------------- /__fixtures__/with_widgets/unsupported.js: -------------------------------------------------------------------------------- 1 | import App from '../../src/AppWithProvider' 2 | 3 | const widgets = [ 4 | { 5 | "name": "widget0", 6 | "active": true, 7 | "datapackage": { 8 | "views": [ 9 | { 10 | "id": "217d95ca-fbbe-4a28-b123-0ce3c6b8bfcb", 11 | "specType": "unsupported" 12 | } 13 | ] 14 | } 15 | } 16 | ] 17 | 18 | 19 | const datapackage = { 20 | "resources": [ 21 | { 22 | "name": "inspection_des_aliments_–_contrevenants", 23 | "format": "xml", 24 | "id": "54d7ffa0-04bf-442c-bacd-a84c6aab888d", 25 | "title": "Inspection des aliments – contrevenants", 26 | "path": "https://montreal.l3.ckan.io/dataset/a5c1f0b9-261f-4247-99d8-f28da5000688/resource/54d7ffa0-04bf-442c-bacd-a84c6aab888d/download/inspection-aliments-contrevenants.csv", 27 | } 28 | ] 29 | } 30 | 31 | 32 | export default { 33 | component: App, 34 | props: {widgets, datapackage: JSON.stringify(datapackage)} 35 | } 36 | -------------------------------------------------------------------------------- /__fixtures__/with_widgets/web.js: -------------------------------------------------------------------------------- 1 | import App from '../../src/AppWithProvider' 2 | 3 | const widgets = [ 4 | { 5 | "name": "widget0", 6 | "active": true, 7 | "datapackage": { 8 | "views": [ 9 | { 10 | "id": "217d95ca-fbbe-4a28-b123-0ce3c6b8bfcb", 11 | "specType": "web", 12 | "page_url": "https://datahub.io/core/co2-ppm/view/0" 13 | } 14 | ] 15 | } 16 | } 17 | ] 18 | 19 | 20 | const datapackage = { 21 | "resources": [ 22 | { 23 | "name": "inspection_des_aliments_–_contrevenants", 24 | "format": "xml", 25 | "id": "54d7ffa0-04bf-442c-bacd-a84c6aab888d", 26 | "title": "Inspection des aliments – contrevenants", 27 | "path": "http://example.com", 28 | } 29 | ] 30 | } 31 | 32 | 33 | export default { 34 | component: App, 35 | props: {widgets, datapackage: JSON.stringify(datapackage)} 36 | } 37 | -------------------------------------------------------------------------------- /__fixtures__/with_widgets/web_view_on_datastore_resource.js: -------------------------------------------------------------------------------- 1 | // Web view in a datastore resource. 2 | // This fixture is for testing that self contained web view shouldn't display 3 | // datastore specific components such as "total rows", "filters (aka rules)", 4 | // and pagination. 5 | 6 | import App from '../../src/AppWithProvider' 7 | 8 | const widgets = [ 9 | { 10 | "active": true, 11 | "datapackage": { 12 | "views": [ 13 | { 14 | "id": "217d95ca-fbbe-4a28-b123-0ce3c6b8bfcb", 15 | "specType": "web", 16 | "page_url": "https://datahub.io/core/co2-ppm/view/0" 17 | } 18 | ] 19 | } 20 | } 21 | ] 22 | 23 | 24 | const datapackage = { 25 | "resources": [ 26 | { 27 | "name": "inspection_des_aliments_–_contrevenants", 28 | "format": "xml", 29 | "id": "54d7ffa0-04bf-442c-bacd-a84c6aab888d", 30 | "title": "Inspection des aliments – contrevenants", 31 | "path": "https://national-grid-admin.ckan.io/", 32 | "api": "https://national-grid-admin.ckan.io/api/3/action/datastore_search?resource_id=db6c038f-98af-4570-ab60-24d71ebd0ae5", 33 | "datastore_active": true 34 | } 35 | ] 36 | } 37 | 38 | 39 | export default { 40 | component: App, 41 | props: {widgets, datapackage: JSON.stringify(datapackage)} 42 | } 43 | -------------------------------------------------------------------------------- /cosmos.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | containerQuerySelector: '#root', 3 | webpackConfigPath: './node_modules/react-scripts/config/webpack.config.js', 4 | publicPath: 'public', 5 | port: 9090 6 | } 7 | -------------------------------------------------------------------------------- /data-explorer-hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datopian/data-explorer/0dbb7cad13a3f2f96b80860adf97669e79a44d9f/data-explorer-hello.png -------------------------------------------------------------------------------- /dist/App.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports.default = exports.App = void 0; 9 | 10 | var _react = _interopRequireWildcard(require("react")); 11 | 12 | var _reactRedux = require("react-redux"); 13 | 14 | require("./App.css"); 15 | 16 | var _datastoreQueryBuilder = require("@datopian/datastore-query-builder"); 17 | 18 | var _DataView = _interopRequireDefault(require("./components/DataView")); 19 | 20 | var _Share = _interopRequireDefault(require("./components/Share")); 21 | 22 | var _Pagination = _interopRequireDefault(require("./components/Pagination")); 23 | 24 | var _chartBuilder = require("@datopian/chart-builder"); 25 | 26 | var _mapBuilder = require("@datopian/map-builder"); 27 | 28 | var _reactTabsRedux = require("react-tabs-redux"); 29 | 30 | var _actions = require("./actions/"); 31 | 32 | var _utils = require("./utils"); 33 | 34 | require("./i18n/i18n"); 35 | 36 | var _reactI18next = require("react-i18next"); 37 | 38 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 39 | 40 | function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } 41 | 42 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } 43 | 44 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } 45 | 46 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 47 | 48 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 49 | 50 | var App = function App(props) { 51 | (0, _react.useEffect)(function () { 52 | var payload = { 53 | datapackage: props.datapackage, 54 | widgets: props.widgets 55 | }; 56 | props.fetchDataAction(payload); 57 | }, []); 58 | var activeWidget = props.widgets.find(function (widget) { 59 | return widget.active; 60 | }); 61 | 62 | var _useTranslation = (0, _reactI18next.useTranslation)(), 63 | t = _useTranslation.t; // Check if any of widgets requires datastore specific components: 64 | 65 | 66 | var nonDataStoreViewTypes = ['web', 'document']; 67 | var datastoreComponents = props.widgets.find(function (widget) { 68 | return widget.datapackage.views.find(function (view) { 69 | return !nonDataStoreViewTypes.includes(view.specType); 70 | }); 71 | }); 72 | var totalRows = props.datapackage.resources[0].datastore_active ? props.datapackage.resources[0].totalrowcount ? props.datapackage.resources[0].totalrowcount.toLocaleString() : '' : ''; 73 | var distinguisher = '-' + Math.random().toString(36).slice(2, 5); 74 | var retrieveSelectedTab = activeWidget ? activeWidget.name : props.widgets[0].name; 75 | var selectedTab; 76 | 77 | if (retrieveSelectedTab.includes('-')) { 78 | selectedTab = retrieveSelectedTab; 79 | } else { 80 | selectedTab = retrieveSelectedTab + distinguisher; 81 | } 82 | 83 | var illegalCharacters = /\W+/gi; 84 | selectedTab = selectedTab.replace(illegalCharacters, "-"); 85 | var tabLinks = props.widgets.map(function (widget, index) { 86 | return _react.default.createElement(_reactTabsRedux.TabLink, { 87 | to: (widget.name + distinguisher).replace(illegalCharacters, "-"), 88 | className: "mr-4 tab-".concat(widget.name.toLowerCase()), 89 | key: "tabLink-".concat(index) 90 | }, t(widget.name)); 91 | }); 92 | var tabContents = props.widgets.map(function (widget, index) { 93 | return _react.default.createElement(_reactTabsRedux.TabContent, { 94 | for: (widget.name + distinguisher).replace(illegalCharacters, "-"), 95 | className: "mr-4 tabpanel-".concat(widget.name.toLowerCase()), 96 | key: "tabContent-".concat(index) 97 | }, ['table', 'web'].includes(widget.datapackage.views[0].specType) ? _react.default.createElement("div", { 98 | className: "container flex py-6" 99 | }, _react.default.createElement("div", { 100 | className: "w-full py-3" 101 | }, _react.default.createElement(_DataView.default, widget))) : _react.default.createElement("div", { 102 | className: "container flex py-6" 103 | }, _react.default.createElement("div", { 104 | className: "w-3/4 py-3 mr-4" 105 | }, _react.default.createElement(_DataView.default, widget)), _react.default.createElement("div", { 106 | className: "w-1/4" 107 | }, _react.default.createElement("div", { 108 | className: "w-full" 109 | }, _react.default.createElement("div", { 110 | className: "p-4 mr-4" 111 | }, widget.datapackage.views[0].specType === 'simple' ? _react.default.createElement(_chartBuilder.ChartBuilder, { 112 | view: widget.datapackage.views[0], 113 | dataViewBuilderAction: props.dataViewBuilderAction 114 | }) : '', widget.datapackage.views[0].specType === 'tabularmap' ? _react.default.createElement(_mapBuilder.MapBuilder, { 115 | view: widget.datapackage.views[0], 116 | dataViewBuilderAction: props.dataViewBuilderAction 117 | }) : ''))))); 118 | }); 119 | return _react.default.createElement("div", { 120 | className: "data-explorer" 121 | }, totalRows && datastoreComponents && _react.default.createElement("div", { 122 | className: "total-rows" 123 | }, _react.default.createElement("span", { 124 | className: "total-rows-label" 125 | }, t('Total rows')), ": ", _react.default.createElement("span", { 126 | className: "total-rows-value" 127 | }, totalRows)), _react.default.createElement("div", { 128 | className: "datastore-query-builder" 129 | }, (0, _utils.showQueryBuilder)(props) ? _react.default.createElement(_datastoreQueryBuilder.QueryBuilder, { 130 | resource: (0, _utils.getResourceForFiltering)(props.datapackage), 131 | filterBuilderAction: props.filterUIAction 132 | }) : ''), _react.default.createElement(_reactTabsRedux.Tabs, { 133 | renderActiveTabContentOnly: true, 134 | handleSelect: function handleSelect(selectedTab) { 135 | props.selectTabAction(selectedTab); 136 | }, 137 | className: "data-explorer-content", 138 | selectedTab: selectedTab 139 | }, tabLinks, tabContents), props.datapackage.resources[0].datastore_active && datastoreComponents ? _react.default.createElement(_Pagination.default, { 140 | datapackage: props.datapackage, 141 | updateAction: props.filterUIAction 142 | }) : _react.default.createElement("div", { 143 | className: "no-pagination not-datastore-resource" 144 | }), datastoreComponents ? _react.default.createElement(_Share.default, { 145 | serializedState: props.serializedState, 146 | apiUri: props.datapackage.resources[0].api 147 | }) : _react.default.createElement("div", { 148 | className: "no-share-feature" 149 | })); 150 | }; 151 | 152 | exports.App = App; 153 | 154 | var mapStateToProps = function mapStateToProps(state) { 155 | return _objectSpread({}, state); 156 | }; 157 | 158 | var mapDispatchToProps = function mapDispatchToProps(dispatch) { 159 | return { 160 | filterUIAction: function filterUIAction(payload) { 161 | return dispatch((0, _actions.filterUIAction)(payload)); 162 | }, 163 | fetchDataAction: function fetchDataAction(payload) { 164 | return dispatch((0, _actions.fetchDataAction)(payload)); 165 | }, 166 | dataViewBuilderAction: function dataViewBuilderAction(payload) { 167 | return dispatch((0, _actions.dataViewBuilderAction)(payload)); 168 | }, 169 | selectTabAction: function selectTabAction(payload) { 170 | return dispatch((0, _actions.selectTabAction)(payload)); 171 | } 172 | }; 173 | }; 174 | 175 | var _default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)(App); 176 | 177 | exports.default = _default; -------------------------------------------------------------------------------- /dist/App.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _react = _interopRequireDefault(require("react")); 4 | 5 | var _reactDom = _interopRequireDefault(require("react-dom")); 6 | 7 | var _App = require("./App"); 8 | 9 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 10 | 11 | it('renders without crashing', function () { 12 | var div = document.createElement('div'); 13 | 14 | _reactDom.default.render(_react.default.createElement(_App.App, null), div); 15 | 16 | _reactDom.default.unmountComponentAtNode(div); 17 | }); -------------------------------------------------------------------------------- /dist/AppWithProvider.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _react = _interopRequireDefault(require("react")); 9 | 10 | var _reactRedux = require("react-redux"); 11 | 12 | var _store = _interopRequireDefault(require("./store")); 13 | 14 | require("./App.css"); 15 | 16 | var _App2 = _interopRequireDefault(require("./App")); 17 | 18 | require("./i18n/i18n"); 19 | 20 | var _reactI18next = require("react-i18next"); 21 | 22 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 23 | 24 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 25 | 26 | var _default = function _default(props) { 27 | var _useTranslation = (0, _reactI18next.useTranslation)(), 28 | t = _useTranslation.t; 29 | 30 | var datapackage; // Allow datapackage json or obj 31 | 32 | if (typeof props.datapackage === 'string') { 33 | try { 34 | datapackage = JSON.parse(props.datapackage); 35 | } catch (e) { 36 | // TODO -- would be nice for the app to still load in an empty state on fail case 37 | datapackage = {}; 38 | console.warn('Invalid datapackage', e); 39 | } 40 | } else if (_typeof(props.datapackage) === 'object') { 41 | datapackage = props.datapackage; 42 | } 43 | 44 | var views; 45 | 46 | try { 47 | views = JSON.parse(JSON.stringify(datapackage.views)); 48 | delete datapackage.views; 49 | } catch (_unused) { 50 | console.log('No views set on datapackage'); 51 | } 52 | 53 | var widgetsFromViews = function widgetsFromViews(views) { 54 | var widgetNames = { 55 | 'table': t('Table'), 56 | 'tabularmap': t('Map'), 57 | 'map': t('Map'), 58 | 'simple': t('Chart') 59 | }; 60 | return views.map(function (view, index) { 61 | return { 62 | active: index === 0 ? true : false, 63 | name: widgetNames[view.specType], 64 | datapackage: { 65 | views: [view] 66 | } 67 | }; 68 | }); 69 | }; 70 | 71 | var widgets = props.widgets ? props.widgets : widgetsFromViews(views); 72 | return _react.default.createElement(_reactRedux.Provider, { 73 | store: (0, _store.default)({ 74 | widgets: widgets, 75 | datapackage: datapackage 76 | }) 77 | }, _react.default.createElement(_App2.default, null)); 78 | }; 79 | 80 | exports.default = _default; -------------------------------------------------------------------------------- /dist/actions/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.fetchDataAction = exports.dataViewBuilderAction = exports.filterUIAction = exports.selectTabAction = void 0; 7 | 8 | var _loadDataset = _interopRequireDefault(require("../utils/loadDataset")); 9 | 10 | var _datapackageRender = require("datapackage-render"); 11 | 12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 13 | 14 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } 15 | 16 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 17 | 18 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 19 | 20 | var selectTabAction = function selectTabAction(payload) { 21 | return function (dispatch, getState) { 22 | var widgets = JSON.parse(JSON.stringify(getState().widgets)); 23 | widgets.forEach(function (widget, index) { 24 | widgets[index].active = false; 25 | 26 | if (widget.name === payload || payload.includes('-') && payload.split('-')[0] === widget.name) { 27 | widgets[index].active = true; 28 | } 29 | }); 30 | dispatch(selectTab({ 31 | widgets: widgets 32 | })); 33 | }; 34 | }; 35 | 36 | exports.selectTabAction = selectTabAction; 37 | 38 | var filterUIAction = function filterUIAction(payload) { 39 | return function _callee(dispatch, getState) { 40 | var datapackage, newResource, updatedDatapackage, loadingWidgets, loadedDataset, widgets; 41 | return regeneratorRuntime.async(function _callee$(_context) { 42 | while (1) { 43 | switch (_context.prev = _context.next) { 44 | case 0: 45 | datapackage = JSON.parse(JSON.stringify(getState().datapackage)); // For datastore resources, we need to remove loaded `data` attribute to 46 | // trigger re-fetch of a resource. This is required since we initially fetch 47 | // only subset of data from datastore, eg, first 100 rows. When user applies 48 | // filters, we need to hit datastore api and update the data. 49 | 50 | newResource = JSON.parse(JSON.stringify(payload)); 51 | updatedDatapackage = Object.assign(datapackage, { 52 | resources: [newResource] 53 | }); 54 | 55 | if (updatedDatapackage.resources[0].datastore_active) { 56 | delete updatedDatapackage.resources[0].data; 57 | } // Update state.datapackage with new resource path that includes filters (datastore query) 58 | 59 | 60 | dispatch(datapackageLoad({ 61 | datapackage: updatedDatapackage 62 | })); // Set state of widgets as loading 63 | 64 | loadingWidgets = JSON.parse(JSON.stringify(getState().widgets)).map(function (widget) { 65 | widget.loading = true; 66 | return widget; 67 | }); 68 | dispatch(fetchDataBegin({ 69 | widgets: loadingWidgets 70 | })); // Load dataset using new path and update state 71 | 72 | datapackage = JSON.parse(JSON.stringify(getState().datapackage)); 73 | _context.next = 10; 74 | return regeneratorRuntime.awrap((0, _loadDataset.default)(datapackage)); 75 | 76 | case 10: 77 | loadedDataset = _context.sent; 78 | dispatch(datapackageLoad({ 79 | datapackage: loadedDataset 80 | })); // Compile new data into widget views 81 | 82 | widgets = JSON.parse(JSON.stringify(getState().widgets)).map(function (widget) { 83 | widget.datapackage.views = widget.datapackage.views.map(function (view) { 84 | return (0, _datapackageRender.compileView)(view, loadedDataset); 85 | }); 86 | widget.loading = false; 87 | return widget; 88 | }); 89 | dispatch(fetchDataSuccess({ 90 | widgets: widgets 91 | })); 92 | 93 | case 14: 94 | case "end": 95 | return _context.stop(); 96 | } 97 | } 98 | }); 99 | }; 100 | }; 101 | 102 | exports.filterUIAction = filterUIAction; 103 | 104 | var dataViewBuilderAction = function dataViewBuilderAction(payload) { 105 | return function (dispatch) { 106 | var actionType; 107 | 108 | if (payload.specType === 'simple') { 109 | actionType = 'DATA_VIEW_CHART_BUILDER_ACTION'; 110 | } else if (payload.specType === 'tabularmap') { 111 | actionType = 'DATA_VIEW_MAP_BUILDER_ACTION'; 112 | } 113 | 114 | dispatch({ 115 | type: actionType, 116 | payload: payload 117 | }); 118 | }; 119 | }; 120 | 121 | exports.dataViewBuilderAction = dataViewBuilderAction; 122 | 123 | var fetchDataAction = function fetchDataAction(payload) { 124 | return function _callee2(dispatch) { 125 | var loadingWidgets, datapackage, loadedDataset, widgets; 126 | return regeneratorRuntime.async(function _callee2$(_context2) { 127 | while (1) { 128 | switch (_context2.prev = _context2.next) { 129 | case 0: 130 | loadingWidgets = JSON.parse(JSON.stringify(payload.widgets)).map(function (widget) { 131 | widget.loading = true; 132 | return widget; 133 | }); 134 | dispatch(fetchDataBegin({ 135 | widgets: loadingWidgets 136 | })); 137 | datapackage = JSON.parse(JSON.stringify(payload.datapackage)); 138 | _context2.next = 5; 139 | return regeneratorRuntime.awrap((0, _loadDataset.default)(datapackage)); 140 | 141 | case 5: 142 | loadedDataset = _context2.sent; 143 | dispatch(datapackageLoad({ 144 | datapackage: loadedDataset 145 | })); 146 | widgets = JSON.parse(JSON.stringify(payload.widgets)).map(function (widget) { 147 | widget.datapackage.views = widget.datapackage.views.map(function (view) { 148 | return (0, _datapackageRender.compileView)(view, loadedDataset); 149 | }); 150 | return widget; 151 | }); 152 | dispatch(fetchDataSuccess({ 153 | widgets: widgets 154 | })); 155 | 156 | case 9: 157 | case "end": 158 | return _context2.stop(); 159 | } 160 | } 161 | }); 162 | }; 163 | }; 164 | 165 | exports.fetchDataAction = fetchDataAction; 166 | 167 | var selectTab = function selectTab(res) { 168 | return { 169 | type: 'SELECT_TAB', 170 | payload: _objectSpread({}, res) 171 | }; 172 | }; 173 | 174 | var datapackageLoad = function datapackageLoad(res) { 175 | return { 176 | type: 'DATAPACKAGE_LOAD', 177 | payload: _objectSpread({}, res) 178 | }; 179 | }; 180 | 181 | var fetchDataBegin = function fetchDataBegin(res) { 182 | return { 183 | type: 'FETCH_DATA_BEGIN', 184 | payload: _objectSpread({}, res) 185 | }; 186 | }; 187 | 188 | var fetchDataSuccess = function fetchDataSuccess(res) { 189 | return { 190 | type: 'FETCH_DATA_SUCCESS', 191 | payload: _objectSpread({}, res) 192 | }; 193 | }; 194 | 195 | var fetchDataFailure = function fetchDataFailure(err) { 196 | return { 197 | type: 'FETCH_DATA_FAILURE', 198 | payload: { 199 | err: err 200 | } 201 | }; 202 | }; -------------------------------------------------------------------------------- /dist/components/DataView/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | require("../../i18n/i18n"); 9 | 10 | var _react = _interopRequireDefault(require("react")); 11 | 12 | var _reactLoader = _interopRequireDefault(require("react-loader")); 13 | 14 | var _datapackageViewsJs = require("@datopian/datapackage-views-js"); 15 | 16 | var _reactI18next = require("react-i18next"); 17 | 18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 19 | 20 | var _default = function _default(props) { 21 | var _useTranslation = (0, _reactI18next.useTranslation)(), 22 | t = _useTranslation.t; 23 | 24 | var views = props.datapackage.views; 25 | 26 | var showGuideText = function showGuideText(specType) { 27 | return _react.default.createElement("div", { 28 | className: "dx-guiding-text" 29 | }, specType === 'simple' ? _react.default.createElement("p", null, t('Select chart type, group column (abscissa x-axis) and series (ordinate y-axis) on the right hand side panel.')) : '', specType === 'tabularmap' ? _react.default.createElement("p", null, t('Select geo data column on the right hand side panel.')) : ''); 30 | }; 31 | 32 | var checkIfGuideIsNeeded = function checkIfGuideIsNeeded(view) { 33 | if (view.specType === 'simple' && !(view.spec && Object.keys(view.spec).length > 0)) { 34 | return true; 35 | } 36 | 37 | return false; 38 | }; 39 | 40 | return _react.default.createElement(_reactLoader.default, { 41 | loaded: !props.loading, 42 | style: { 43 | position: "relative" 44 | } 45 | }, _react.default.createElement("div", null, checkIfGuideIsNeeded(views[0]) ? showGuideText(views[0].specType) : views.map(function (view) { 46 | return _react.default.createElement(_datapackageViewsJs.DataView, { 47 | key: Math.random(), 48 | datapackage: { 49 | views: [view] 50 | } 51 | }); 52 | }))); 53 | }; 54 | 55 | exports.default = _default; -------------------------------------------------------------------------------- /dist/components/DataView/index.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _react = _interopRequireDefault(require("react")); 4 | 5 | var _reactDom = _interopRequireDefault(require("react-dom")); 6 | 7 | var _ = _interopRequireDefault(require("./")); 8 | 9 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 10 | 11 | it('renders without crashing', function () { 12 | var div = document.createElement('div'); 13 | 14 | _reactDom.default.render(_react.default.createElement(_.default, null), div); 15 | 16 | _reactDom.default.unmountComponentAtNode(div); 17 | }); -------------------------------------------------------------------------------- /dist/components/DataViewBuilder/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | require("../../i18n/i18n"); 9 | 10 | var _react = _interopRequireDefault(require("react")); 11 | 12 | var _testChartBuilder = _interopRequireDefault(require("../../testData/testChartBuilder.json")); 13 | 14 | var _reactI18next = require("react-i18next"); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 17 | 18 | var _default = function _default(props) { 19 | var _useTranslation = (0, _reactI18next.useTranslation)(), 20 | t = _useTranslation.t; 21 | 22 | return _react.default.createElement("div", null, _react.default.createElement("h2", { 23 | className: "text-2xl" 24 | }, "DataView Builder"), _react.default.createElement("button", { 25 | className: "bg-blue-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded-full m-2", 26 | onClick: function onClick(e) { 27 | props.dataViewBuilderAction(_testChartBuilder.default); 28 | } 29 | }, t('UPDATE CHART'))); 30 | }; 31 | 32 | exports.default = _default; -------------------------------------------------------------------------------- /dist/components/DataViewBuilder/index.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _react = _interopRequireDefault(require("react")); 4 | 5 | var _reactDom = _interopRequireDefault(require("react-dom")); 6 | 7 | var _ = _interopRequireDefault(require("./")); 8 | 9 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 10 | 11 | it('renders without crashing', function () { 12 | var div = document.createElement('div'); 13 | 14 | _reactDom.default.render(_react.default.createElement(_.default, null), div); 15 | 16 | _reactDom.default.unmountComponentAtNode(div); 17 | }); -------------------------------------------------------------------------------- /dist/components/Pagination/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | require("../../i18n/i18n"); 9 | 10 | var _react = _interopRequireDefault(require("react")); 11 | 12 | var _reactPaginate = _interopRequireDefault(require("react-paginate")); 13 | 14 | var _reactI18next = require("react-i18next"); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 17 | 18 | var _default = function _default(props) { 19 | var _useTranslation = (0, _reactI18next.useTranslation)(), 20 | t = _useTranslation.t; 21 | 22 | function handlePageClick(data) { 23 | var selected = data.selected; 24 | var offset = Math.ceil(selected * 100); 25 | var resource = JSON.parse(JSON.stringify(props.datapackage.resources[0])); 26 | var urlObj = new URL(resource.api); 27 | 28 | if (resource.api.includes('datastore_search?')) { 29 | urlObj.searchParams.set('offset', offset); 30 | } else if (resource.api.includes('datastore_search_sql?')) { 31 | var sql = urlObj.searchParams.get('sql'); 32 | var regex = /OFFSET(%20|\s)\d+/; 33 | 34 | if (regex.test(sql)) { 35 | urlObj.searchParams.set('sql', sql.replace(regex, "OFFSET ".concat(offset))); 36 | } else { 37 | urlObj.searchParams.set('sql', sql + " OFFSET ".concat(offset)); 38 | } 39 | 40 | resource.api = resource.api.includes('offset'); 41 | } 42 | 43 | resource.api = urlObj.href; 44 | props.updateAction(resource); 45 | } 46 | 47 | return _react.default.createElement(_reactPaginate.default, { 48 | previousLabel: t('Previous'), 49 | nextLabel: t('Next'), 50 | breakLabel: '...', 51 | breakClassName: 'break-me', 52 | pageCount: Math.ceil(props.datapackage.resources[0].totalrowcount / 100), 53 | marginPagesDisplayed: 2, 54 | pageRangeDisplayed: 5, 55 | onPageChange: handlePageClick, 56 | containerClassName: 'pagination', 57 | activeClassName: 'active' 58 | }); 59 | }; 60 | 61 | exports.default = _default; -------------------------------------------------------------------------------- /dist/components/Share/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | require("../../i18n/i18n"); 9 | 10 | var _react = _interopRequireDefault(require("react")); 11 | 12 | var _reactI18next = require("react-i18next"); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 15 | 16 | var MAX_LEN = 1500; 17 | var slimProps = ['archiver', 'schema', 'shareLink', 'iframeText']; 18 | 19 | var slim = function slim(serializedState) { 20 | if (serializedState.length <= MAX_LEN) return serializedState; 21 | var state = JSON.parse(serializedState); 22 | state.datapackage.resources.forEach(function (resource) { 23 | for (var prop in slimProps) { 24 | if (resource[slimProps[prop]]) delete resource[slimProps[prop]]; 25 | } 26 | }); 27 | return JSON.stringify(state); 28 | }; 29 | 30 | var _default = function _default(props) { 31 | var _useTranslation = (0, _reactI18next.useTranslation)(), 32 | t = _useTranslation.t; 33 | 34 | var serializedState = slim(props.serializedState); 35 | var urlObj = new URL(window.location.href); 36 | urlObj.searchParams.set('explorer', serializedState); 37 | var shareLink = urlObj.href; 38 | var iframe = ""); 39 | var shareable = shareLink.length < 2000; 40 | 41 | var copy = function copy(str) { 42 | // Create new element 43 | var el = document.createElement('textarea'); // Set value (string to be copied) 44 | 45 | el.value = str; // Set non-editable to avoid focus and move outside of view 46 | 47 | el.setAttribute('readonly', ''); 48 | el.style = { 49 | position: 'absolute', 50 | left: '-9999px' 51 | }; 52 | document.body.appendChild(el); // Select text inside element 53 | 54 | el.select(); // Copy text to clipboard 55 | 56 | document.execCommand('copy'); // Remove temporary element 57 | 58 | document.body.removeChild(el); 59 | }; 60 | 61 | return _react.default.createElement("div", { 62 | className: "dx-share-container" 63 | }, shareable ? _react.default.createElement("div", null, _react.default.createElement("div", { 64 | className: "m-4 ml-0 dx-share-link" 65 | }, _react.default.createElement("input", { 66 | id: "share-link-" + Math.random().toString(36).slice(2, 5), 67 | title: "Share link", 68 | className: "border-solid border-2 border-gray-600 w-1/2 px-2", 69 | value: shareLink 70 | }), _react.default.createElement("a", { 71 | href: "#/", 72 | id: "copy-share-link-" + Math.random().toString(36).slice(2, 5), 73 | className: "m-4", 74 | onClick: function onClick() { 75 | copy(shareLink); 76 | } 77 | }, _react.default.createElement("em", null, t("copy share link")))), _react.default.createElement("div", { 78 | className: "m-4 ml-0 dx-embed-link" 79 | }, _react.default.createElement("input", { 80 | id: "embed-" + Math.random().toString(36).slice(2, 5), 81 | title: "Embedded link", 82 | className: "border-solid border-2 border-gray-600 px-2 w-1/2", 83 | value: iframe 84 | }), _react.default.createElement("a", { 85 | href: "#/", 86 | id: "copy-share-link-" + Math.random().toString(36).slice(2, 5), 87 | className: "m-4", 88 | onClick: function onClick() { 89 | copy(iframe); 90 | } 91 | }, _react.default.createElement("em", null, t("copy embed text"))))) : _react.default.createElement("p", { 92 | className: "no-share-link-message" 93 | }, t('No share link available')), props.apiUri && _react.default.createElement("div", { 94 | className: "m-4 ml-0 dx-apiuri-link" 95 | }, _react.default.createElement("input", { 96 | id: "apiUri-" + Math.random().toString(36).slice(2, 5), 97 | title: "API URI link", 98 | className: "border-solid border-2 border-gray-600 px-2 w-1/2", 99 | value: props.apiUri 100 | }), _react.default.createElement("a", { 101 | href: "#/", 102 | id: "copy-share-link-" + Math.random().toString(36).slice(2, 5), 103 | className: "m-4", 104 | onClick: function onClick() { 105 | copy(props.apiUri); 106 | } 107 | }, _react.default.createElement("em", null, t("copy API URI"))))); 108 | }; 109 | 110 | exports.default = _default; -------------------------------------------------------------------------------- /dist/css/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @tailwind components; 4 | 5 | @tailwind utilities; 6 | 7 | /** CSS Styles here **/ 8 | -------------------------------------------------------------------------------- /dist/i18n/i18n.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.initTranslations = void 0; 7 | 8 | var _i18next = _interopRequireDefault(require("i18next")); 9 | 10 | var _i18nextBrowserLanguagedetector = _interopRequireDefault(require("i18next-browser-languagedetector")); 11 | 12 | var _reactI18next = require("react-i18next"); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 15 | 16 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } 17 | 18 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 19 | 20 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 21 | 22 | var initTranslations = function initTranslations() { 23 | return regeneratorRuntime.async(function initTranslations$(_context) { 24 | while (1) { 25 | switch (_context.prev = _context.next) { 26 | case 0: 27 | _context.next = 2; 28 | return regeneratorRuntime.awrap(_i18next.default.use(_i18nextBrowserLanguagedetector.default).use(_reactI18next.initReactI18next) // init i18next 29 | // for all options read: https://www.i18next.com/overview/configuration-options 30 | .init({ 31 | // for all options read: https://github.com/i18next/i18next-browser-languageDetector#detector-options 32 | detection: { 33 | order: ['querystring', 'cookie', 'localStorage', 'navigator', 'htmlTag', 'path', 'subdomain'], 34 | // keys or params to lookup language from 35 | lookupQuerystring: 'lng', 36 | lookupCookie: 'defaultLocale', 37 | lookupLocalStorage: 'defaultLocale', 38 | lookupFromPathIndex: 0, 39 | lookupFromSubdomainIndex: 0 40 | }, 41 | resources: { 42 | en: { 43 | translation: _objectSpread({}, require('./locales/en/translation.json'), {}, require('@datopian/chart-builder/src/i18n/locales/en/translation.json'), {}, require('@datopian/map-builder/src/i18n/locales/en/translation.json'), {}, require('@datopian/datapackage-views-js/src/i18n/locales/en/translation.json'), {}, require('@datopian/datastore-query-builder/src/i18n/locales/en/translation.json')) 44 | }, 45 | da: { 46 | translation: _objectSpread({}, require('./locales/da/translation.json'), {}, require('@datopian/chart-builder/src/i18n/locales/da/translation.json'), {}, require('@datopian/map-builder/src/i18n/locales/da/translation.json'), {}, require('@datopian/datapackage-views-js/src/i18n/locales/da/translation.json'), {}, require('@datopian/datastore-query-builder/src/i18n/locales/da/translation.json')) 47 | }, 48 | fr: { 49 | translation: _objectSpread({}, require('./locales/fr/translation.json'), {}, require('@datopian/chart-builder/src/i18n/locales/fr/translation.json'), {}, require('@datopian/map-builder/src/i18n/locales/fr/translation.json'), {}, require('@datopian/datapackage-views-js/src/i18n/locales/fr/translation.json'), {}, require('@datopian/datastore-query-builder/src/i18n/locales/fr/translation.json')) 50 | }, 51 | pt_BR: { 52 | translation: _objectSpread({}, require('./locales/pt_BR/translation.json'), {}, require('@datopian/chart-builder/src/i18n/locales/pt_BR/translation.json'), {}, require('@datopian/map-builder/src/i18n/locales/pt_BR/translation.json'), {}, require('@datopian/datapackage-views-js/src/i18n/locales/pt_BR/translation.json'), {}, require('@datopian/datastore-query-builder/src/i18n/locales/pt_BR/translation.json')) 53 | } 54 | }, 55 | react: { 56 | useSuspense: false 57 | }, 58 | initImmediate: false, 59 | debug: false, 60 | // allow keys to be phrases having `:`, `.` 61 | nsSeparator: false, 62 | keySeparator: false, 63 | // do not load a fallback 64 | fallbackLng: false, 65 | interpolation: { 66 | escapeValue: false 67 | } 68 | })); 69 | 70 | case 2: 71 | case "end": 72 | return _context.stop(); 73 | } 74 | } 75 | }); 76 | }; 77 | 78 | exports.initTranslations = initTranslations; -------------------------------------------------------------------------------- /dist/i18n/locales/da/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "copy share link": "kopier delingslink", 3 | "copy embed text": "kopier indlejret tekst", 4 | "copy API URI": "kopier API URI", 5 | "No share link available": "Der er ikke noget link tilgængeligt", 6 | "UPDATE CHART": "OPDATER DIAGRAM", 7 | "Next": "Næste", 8 | "Previous": "Tidligere", 9 | "Table": "Bord", 10 | "Map": "Kort", 11 | "Chart": "Diagram", 12 | "Total rows": "Samlede rækker" 13 | } 14 | -------------------------------------------------------------------------------- /dist/i18n/locales/en/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "copy share link": "copy share link", 3 | "copy embed text": "copy embed text", 4 | "copy API URI": "copy API URI", 5 | "No share link available": "No share link available", 6 | "UPDATE CHART": "UPDATE CHART", 7 | "Next": "Next", 8 | "Previous": "Previous", 9 | "Table": "Table", 10 | "Map": "Map", 11 | "Chart": "Chart", 12 | "Total rows": "Total rows", 13 | "Select chart type, group column (abscissa x-axis) and series (ordinate y-axis) on the right hand side panel.": "Select chart type, group column (abscissa x-axis) and series (ordinate y-axis) on the right hand side panel.", 14 | "Select geo data column on the right hand side panel.": "Select geo data column on the right hand side panel." 15 | } 16 | -------------------------------------------------------------------------------- /dist/i18n/locales/fr/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "copy share link": "Copier lien de partage", 3 | "copy embed text": "Intégrer code d'intégration", 4 | "copy API URI": "Copier API URI", 5 | "No share link available": "Pas de lien de partage", 6 | "UPDATE CHART": "Mettre à jour le graphique", 7 | "Next": "Suivant", 8 | "Previous": "Précédent", 9 | "Table": "Tableau", 10 | "Map": "Carte", 11 | "Chart": "Graphique", 12 | "Total rows": "Entrées totales", 13 | "Select chart type, group column (abscissa x-axis) and series (ordinate y-axis) on the right hand side panel.": "Sélectionner le type de graphe, la colonne de groupement (axe horizontal) et la série (axe vertical) dans le panneau de droite", 14 | "Select geo data column on the right hand side panel.": "Sélectionner la colonne de type géographique dans le panneau de droite" 15 | } 16 | -------------------------------------------------------------------------------- /dist/i18n/locales/pt_BR/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "copy share link": "copiar link para compartilhar", 3 | "copy embed text": "copiar código para incorporar", 4 | "copy API URI": "copiar URI da API", 5 | "No share link available": "Nenhum link link para compartilhar está disponível", 6 | "UPDATE CHART": "ATUALIZAR GRÁFICO", 7 | "Next": "Próximo", 8 | "Previous": "Anterior", 9 | "Table": "Tabela", 10 | "Map": "Mapa", 11 | "Chart": "Gráfico", 12 | "Total rows": "Total de linhas", 13 | "Select chart type, group column (abscissa x-axis) and series (ordinate y-axis) on the right hand side panel.": "Selecione o tipo de gráfico, a coluna de agrupamento (eixo x ou abcissa) e série (eixo y ou ordenada) no painel lateral direito.", 14 | "Select geo data column on the right hand side panel.": "Selecione a coluna de dados geográficos no painel lateral direito." 15 | } 16 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 4 | 5 | var _i18n = require("./i18n/i18n"); 6 | 7 | var _react = _interopRequireDefault(require("react")); 8 | 9 | var _reactDom = _interopRequireDefault(require("react-dom")); 10 | 11 | require("./App.css"); 12 | 13 | var _AppWithProvider = _interopRequireDefault(require("./AppWithProvider")); 14 | 15 | var serviceWorker = _interopRequireWildcard(require("./serviceWorker")); 16 | 17 | function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } 18 | 19 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } 20 | 21 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 22 | 23 | function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } 24 | 25 | function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } 26 | 27 | function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } 28 | 29 | var instances = document.getElementsByClassName('data-explorer'); 30 | (0, _i18n.initTranslations)().then(function () { 31 | var _iteratorNormalCompletion = true; 32 | var _didIteratorError = false; 33 | var _iteratorError = undefined; 34 | 35 | try { 36 | for (var _iterator = instances[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 37 | var instance = _step.value; 38 | 39 | try { 40 | var props = JSON.parse(instance.getAttribute('data-datapackage')); 41 | 42 | var datapackage = props.datapackage, 43 | rest = _objectWithoutProperties(props, ["datapackage"]); 44 | 45 | _reactDom.default.render(_react.default.createElement(_AppWithProvider.default, _extends({ 46 | datapackage: datapackage 47 | }, rest)), document.getElementById(instance.id)); 48 | } catch (e) { 49 | console.warn('Failed to render data-explorer', e); 50 | } 51 | } 52 | } catch (err) { 53 | _didIteratorError = true; 54 | _iteratorError = err; 55 | } finally { 56 | try { 57 | if (!_iteratorNormalCompletion && _iterator.return != null) { 58 | _iterator.return(); 59 | } 60 | } finally { 61 | if (_didIteratorError) { 62 | throw _iteratorError; 63 | } 64 | } 65 | } 66 | }); 67 | serviceWorker.unregister(); -------------------------------------------------------------------------------- /dist/reducers/datapackage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _default = function _default() { 9 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 10 | var action = arguments.length > 1 ? arguments[1] : undefined; 11 | 12 | switch (action.type) { 13 | case 'DATAPACKAGE_LOAD': 14 | return action.payload.datapackage; 15 | 16 | default: 17 | return state; 18 | } 19 | }; 20 | 21 | exports.default = _default; -------------------------------------------------------------------------------- /dist/reducers/datastoreFilters.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _default = function _default() { 9 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 10 | var action = arguments.length > 1 ? arguments[1] : undefined; 11 | 12 | switch (action.type) { 13 | case 'FILTER_UI_ACTION': 14 | return state; 15 | 16 | default: 17 | return state; 18 | } 19 | }; 20 | 21 | exports.default = _default; -------------------------------------------------------------------------------- /dist/reducers/rootReducer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _widgets = _interopRequireDefault(require("./widgets")); 9 | 10 | var _datapackage = _interopRequireDefault(require("./datapackage")); 11 | 12 | var _datastoreFilters = _interopRequireDefault(require("./datastoreFilters")); 13 | 14 | var _serializedState = _interopRequireDefault(require("./serializedState")); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 17 | 18 | var _default = function _default() { 19 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 20 | var action = arguments.length > 1 ? arguments[1] : undefined; 21 | return { 22 | widgets: (0, _widgets.default)(state.widgets, action), 23 | datapackage: (0, _datapackage.default)(state.datapackage, action), 24 | datastoreFilters: (0, _datastoreFilters.default)(state.datastoreFilters, action), 25 | serializedState: (0, _serializedState.default)(state.serializedState, action, state) // we pass root of tree so it can export entire app state 26 | 27 | }; 28 | }; 29 | 30 | exports.default = _default; -------------------------------------------------------------------------------- /dist/reducers/serializedState.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _utils = require("../utils"); 9 | 10 | var _default = function _default() { 11 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 12 | var action = arguments.length > 1 ? arguments[1] : undefined; 13 | var root = arguments.length > 2 ? arguments[2] : undefined; 14 | 15 | try { 16 | var clonedRoot = (0, _utils.deepClone)(root); 17 | delete clonedRoot.serializedState; 18 | var datapackage = (0, _utils.unloadDatapackage)(clonedRoot.datapackage); 19 | var widgets = clonedRoot.widgets.map(function (widget) { 20 | widget.datapackage = (0, _utils.unloadDatapackage)(widget.datapackage); 21 | widget.loading = false; 22 | return widget; 23 | }); 24 | return JSON.stringify(Object.assign(clonedRoot, { 25 | datapackage: datapackage, 26 | widgets: widgets 27 | })); 28 | } catch (e) { 29 | console.warn(e); 30 | return {}; 31 | } 32 | }; 33 | 34 | exports.default = _default; -------------------------------------------------------------------------------- /dist/reducers/widgets.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _utils = require("../utils"); 9 | 10 | var _default = function _default() { 11 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 12 | var action = arguments.length > 1 ? arguments[1] : undefined; 13 | 14 | switch (action.type) { 15 | case 'DATA_VIEW_CHART_BUILDER_ACTION': 16 | var stateForChartBuilder = (0, _utils.deepClone)(state); 17 | stateForChartBuilder = stateForChartBuilder.map(function (widget) { 18 | if (widget.datapackage.views[0].specType === 'simple') { 19 | widget.datapackage.views[0] = action.payload; 20 | } 21 | 22 | return widget; 23 | }); 24 | return Object.assign((0, _utils.deepClone)(state), stateForChartBuilder); 25 | 26 | case 'DATA_VIEW_MAP_BUILDER_ACTION': 27 | var stateForMapBuilder = (0, _utils.deepClone)(state); 28 | stateForMapBuilder = stateForMapBuilder.map(function (widget) { 29 | if (widget.datapackage.views[0].specType === 'tabularmap') { 30 | widget.datapackage.views[0] = action.payload; 31 | } 32 | 33 | return widget; 34 | }); 35 | return Object.assign((0, _utils.deepClone)(state), stateForMapBuilder); 36 | 37 | case 'SELECT_TAB': 38 | return action.payload.widgets; 39 | 40 | case 'FETCH_DATA_BEGIN': 41 | return action.payload.widgets; 42 | 43 | case 'FETCH_DATA_SUCCESS': 44 | return action.payload.widgets; 45 | 46 | case 'FETCH_DATA_FAILURE': 47 | return action.payload.widgets; 48 | 49 | default: 50 | return state; 51 | } 52 | }; 53 | 54 | exports.default = _default; -------------------------------------------------------------------------------- /dist/serviceWorker.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.register = register; 7 | exports.unregister = unregister; 8 | // This optional code is used to register a service worker. 9 | // register() is not called by default. 10 | // This lets the app load faster on subsequent visits in production, and gives 11 | // it offline capabilities. However, it also means that developers (and users) 12 | // will only see deployed updates on subsequent visits to a page, after all the 13 | // existing tabs open on the page have been closed, since previously cached 14 | // resources are updated in the background. 15 | // To learn more about the benefits of this model and instructions on how to 16 | // opt-in, read https://bit.ly/CRA-PWA 17 | var isLocalhost = Boolean(window.location.hostname === 'localhost' || // [::1] is the IPv6 localhost address. 18 | window.location.hostname === '[::1]' || // 127.0.0.1/8 is considered localhost for IPv4. 19 | window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)); 20 | 21 | function register(config) { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | var publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 25 | 26 | if (publicUrl.origin !== window.location.origin) { 27 | // Our service worker won't work if PUBLIC_URL is on a different origin 28 | // from what our page is served on. This might happen if a CDN is used to 29 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 30 | return; 31 | } 32 | 33 | window.addEventListener('load', function () { 34 | var swUrl = "".concat(process.env.PUBLIC_URL, "/service-worker.js"); 35 | 36 | if (isLocalhost) { 37 | // This is running on localhost. Let's check if a service worker still exists or not. 38 | checkValidServiceWorker(swUrl, config); // Add some additional logging to localhost, pointing developers to the 39 | // service worker/PWA documentation. 40 | 41 | navigator.serviceWorker.ready.then(function () { 42 | console.log('This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://bit.ly/CRA-PWA'); 43 | }); 44 | } else { 45 | // Is not localhost. Just register service worker 46 | registerValidSW(swUrl, config); 47 | } 48 | }); 49 | } 50 | } 51 | 52 | function registerValidSW(swUrl, config) { 53 | navigator.serviceWorker.register(swUrl).then(function (registration) { 54 | registration.onupdatefound = function () { 55 | var installingWorker = registration.installing; 56 | 57 | if (installingWorker == null) { 58 | return; 59 | } 60 | 61 | installingWorker.onstatechange = function () { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the updated precached content has been fetched, 65 | // but the previous service worker will still serve the older 66 | // content until all client tabs are closed. 67 | console.log('New content is available and will be used when all ' + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'); // Execute callback 68 | 69 | if (config && config.onUpdate) { 70 | config.onUpdate(registration); 71 | } 72 | } else { 73 | // At this point, everything has been precached. 74 | // It's the perfect time to display a 75 | // "Content is cached for offline use." message. 76 | console.log('Content is cached for offline use.'); // Execute callback 77 | 78 | if (config && config.onSuccess) { 79 | config.onSuccess(registration); 80 | } 81 | } 82 | } 83 | }; 84 | }; 85 | }).catch(function (error) { 86 | console.error('Error during service worker registration:', error); 87 | }); 88 | } 89 | 90 | function checkValidServiceWorker(swUrl, config) { 91 | // Check if the service worker can be found. If it can't reload the page. 92 | fetch(swUrl).then(function (response) { 93 | // Ensure service worker exists, and that we really are getting a JS file. 94 | var contentType = response.headers.get('content-type'); 95 | 96 | if (response.status === 404 || contentType != null && contentType.indexOf('javascript') === -1) { 97 | // No service worker found. Probably a different app. Reload the page. 98 | navigator.serviceWorker.ready.then(function (registration) { 99 | registration.unregister().then(function () { 100 | window.location.reload(); 101 | }); 102 | }); 103 | } else { 104 | // Service worker found. Proceed as normal. 105 | registerValidSW(swUrl, config); 106 | } 107 | }).catch(function () { 108 | console.log('No internet connection found. App is running in offline mode.'); 109 | }); 110 | } 111 | 112 | function unregister() { 113 | if ('serviceWorker' in navigator) { 114 | navigator.serviceWorker.ready.then(function (registration) { 115 | registration.unregister(); 116 | }); 117 | } 118 | } -------------------------------------------------------------------------------- /dist/store.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = configureStore; 7 | 8 | var _redux = require("redux"); 9 | 10 | var _reduxDevtoolsExtension = require("redux-devtools-extension"); 11 | 12 | var _reduxThunk = _interopRequireDefault(require("redux-thunk")); 13 | 14 | var _rootReducer = _interopRequireDefault(require("./reducers/rootReducer")); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 17 | 18 | var initialState = { 19 | datastoreFilters: {// Datastore specific filters 20 | }, 21 | datapackage: {}, 22 | widgets: [], 23 | serializedState: {} 24 | }; 25 | 26 | function configureStore(props) { 27 | return (0, _redux.createStore)(_rootReducer.default, Object.assign({}, initialState, props), (0, _reduxDevtoolsExtension.composeWithDevTools)((0, _redux.applyMiddleware)(_reduxThunk.default))); 28 | } -------------------------------------------------------------------------------- /dist/testData/actes-criminels.json: -------------------------------------------------------------------------------- 1 | {"maintainer":null,"relationships_as_object":[],"private":false,"maintainer_email":null,"num_tags":5,"update_frequency":"daily","id":"5829b5b0-ea6f-476f-be94-bc2b8797769a","metadata_created":"2016-02-26T21:00:10.960143","metadata_modified":"2019-08-13T20:03:56.381220","temporal":"2015-01-01/","state":"active","version":null,"type":"dataset","resources":[{"mimetype":null,"cache_url":null,"hash":"","description":"","name":"criminalité_-_2015_à_aujourd'hui","format":"CSV","datastore_active":false,"cache_last_updated":null,"package_id":"5829b5b0-ea6f-476f-be94-bc2b8797769a","created":"2016-02-26T16:01:33.269959","state":"active","mimetype_inner":null,"last_modified":"2019-08-13T20:03:56.371932","position":0,"revision_id":"647ace07-36ee-444e-8315-5710a5433127","url_type":"upload","id":"c6f482bf-bf0f-4960-8b2f-9982c211addd","resource_type":null,"size":null,"title":"Criminalité - 2015 à aujourd'hui","path":"http://donnees.ville.montreal.qc.ca/dataset/5829b5b0-ea6f-476f-be94-bc2b8797769a/resource/c6f482bf-bf0f-4960-8b2f-9982c211addd/download/interventionscitoyendo.csv"}],"num_resources":1,"language":"FR","methodologie":"###Vie privée\r\n\r\nLes données ont été obfusquées et modifiées pour garantir le respect à la vie privée et la protection des renseignements personnels:\r\n\r\n- Localisation : la position géographique de l'événement a été localisée à une des intersections du segment de rue où s'est passé l'événement.\r\n- Temporalité : le moment de l'événement est représenté par la date et le quart de travail (ou moment de la journée : jour, soir, nuit) durant lequel l'événement a été enregistré.\r\n\r\n###Encodage de caractères\r\n\r\nLe fichier CSV utilise l'encodage de caractère ISO-8859-1 (latin1)\r\n\r\n###Mise à jour des données\r\n\r\n- Les données peuvent évoluer dans le temps; le type d'un événement peut être modifié si des éléments s'ajoutent à la déclaration initiale. De manière générale, ces évolutions ont lieu dans les jours qui suivent la création de l'événement.\r\n- Les données sont regroupées par année calendaire. Quelques mois après la fin d'année, les données cessent d'être mises à jour (p.ex les données pour 2015 cessent d'être mises à jour à partir d'avril 2016)\r\n- Lorsque plusieurs infractions sont commises lors d'un même événement, l'infraction la plus grave est utilisée dans les données ouvertes.\r\n\r\n### Couverture territoriale\r\n\r\nLes données couvrent l'ensemble de l'agglomération de Montréal (e.g toute l'île de Montréal)\r\n\r\nLe poste de quartier 50 correspond à l'unité en charge du métro. Pour les événements prenant place dans le métro, la localisation est celle de l'édicule concerné ou un point fictif entre deux édicules.\r\n\r\n### Dictionnaire de données\r\n\r\n- `CATEGORIE` : Nature de l'événement. Liste de valeur :\r\n\r\n - `Introduction` : introduction par effraction dans un établissement public ou une résidence privée, vol d’arme à feu dans une résidence\r\n - `Vol dans / sur véhicule à moteur` : vol du contenu d’un véhicule à moteur (voiture, camion, motocyclette, etc.) ou d’une pièce de véhicule (roue, parechoc, etc.)\r\n - `Vol de véhicule à moteur` : vol de voiture, camion, motocyclette, motoneige tracteur avec ou sans remorque, véhicule de construction ou de ferme, tout-terrain\r\n - `Méfait` : Graffiti et dommage de biens religieux, de véhicule ou dommage général et tous autres types de méfaits\r\n - `Vol qualifié` : Vol accompagné de violence de commerce, institution financière, personne, sac à main, véhicule blindé, véhicule, arme à feu, et tous autres types de vols qualifiés\r\n - `Infraction entraînant la mort` : Meurtre au premier degré, meurtre au deuxième degré, homicide involontaire, infanticide, négligence criminelle, et tous autres types d’infractions entraînant la mort\r\n\r\n- `DATE` : Date du signalement de l'événement au SPVM au format `AAAA-MM-JJ HH:mm:ss` (note: la partie de l'heure n'est pas utilisée)\r\n- `QUART` : Moment de la journée du signalement de l'événement au SPVM. Liste de valeur :\r\n\r\n - jour : Entre 8h01 et 16h\r\n - soir : Entre 16h01 et minuit\r\n - nuit : Entre 00h01 et 8h\r\n\r\n- `PDQ` : Numéro du poste de quartier couvrant le territoire où s'est passé l'événement. Le territoire couvert par chaque poste est disponible dans l'ensemble de données des [limites de PDQ](limites-pdq-spvm)\r\n- `X` et `Y` : Position géospatiale selon la projection MTM8 (SRID 2950)\r\n\r\n - La valeur `0` est utilisée lorsqu'aucune position géographique n'a été fournie lors de la saisie de l'information (moins de 5% des cas).\r\n- `LAT` et `LONG`: position géographique de l'événement après obfuscation à une intersection selon le référentiel géodésique WGS84.\r\n\r\n - La valeur `1` est utilisée lorsqu'aucune position géographique n'a été fournie lors de la saisie de l'information (moins de 5% des cas).\r\n","groups":[{"display_name":"Loi, justice et sécurité publique","description":"Domaine d'affaires qui correspond à tout ce qui a trait à la législation et son application, tout en reconnaissant les droits des citoyens et leur accès au système de justice. En ce sens, cette catégorie inclut le domaine législatif: préparation des lois et règlements (civil et pénal) : administration de la justice: protection des personnes, prévention de la criminalité, encadrement des activités policières et administration des décisions des tribunaux.","image_display_url":"http://donnees.ville.montreal.qc.ca/portail/wp-content/themes/donneesouvertes4/images/thematiques/gif/250/loi-justice-securite-publique.gif","title":"Loi, justice et sécurité publique","id":"ba34c0f5-8c9b-448d-b14e-77baf3e345ed","name":"loi-justice-securite-publique"}],"creator_user_id":"7f0e4738-27da-473a-a351-d260d12667c4","relationships_as_subject":[],"ext_spatial":"ville-montreal","organization":{"description":"La politique de données ouvertes repose sur le concept \"d'ouverture par défaut\". Montréal a l'intention de rendre disponible la majorité de ses données d'ici la fin 2018.","created":"2016-01-13T10:21:33.203770","title":"Ville de Montréal","name":"ville-de-montreal","is_organization":true,"state":"active","image_url":"2017-11-07-154803.038695JPGnrb.jpg","revision_id":"1c82bc9e-95c1-4a7d-930b-7dda142ed6a7","type":"organization","id":"11287be6-b543-4e28-97b4-99b7111f8de0","approval_status":"approved"},"name":"actes-criminels","isopen":true,"url":"","owner_org":"11287be6-b543-4e28-97b4-99b7111f8de0","territoire":["AHU","agglomeration","ANJ","CDN","LAC","LAS","PLA","LSO","IBI","MHM","montreal","MTN","OUT","PRF","RDP","RPP","VSL","STL","VER","VIM","VSE"],"title":"Actes criminels","revision_id":"ef57c4fa-7c92-49f6-83b4-4c6abe67dfb8","description":"Actes criminels enregistrés par le Service de police de la Ville de Montréal (SPVM).\r\n\r\nUn outil de visualisation des données permet également d'afficher sur les données sous forme de carte: [Vue sur la sécurité publique](https://ville.montreal.qc.ca/vuesurlasecuritepublique/)\r\n\r\n## Avertissement \r\n\r\n- Les données sont divulguées telles que rapportées au SPVM. Il est normal qu’il existe un écart entre celles-ci et celles compilées officiellement par le Centre canadien de la statistique juridique puisque le statut d’un incident peut être modifié en cours d’enquête.\r\n- La section méthodologie contient plusieurs éléments importants pour l'interprétation des données.\r\n","license":{"type":"cc-by","title":"Creative Commons Attribution 4.0 International","url":"http://creativecommons.org/licenses/by/4.0/"},"sources":[{"name":"Service de police de la Ville de Montréal - Division des ressources informationnelles","email":"donneesouvertes@ville.montreal.qc.ca"}],"keywords":["Crime","Criminalité","Introductions par effraction","Police","Sécurité publique"]} 2 | -------------------------------------------------------------------------------- /dist/testData/inlinedData.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inlined-data", 3 | "resources": [ 4 | { 5 | "name": "test", 6 | "schema": { 7 | "fields": [ 8 | { 9 | "name": "a", 10 | "type": "string" 11 | }, 12 | { 13 | "name": "b", 14 | "type": "string" 15 | }, 16 | { 17 | "name": "c", 18 | "type": "string" 19 | }, 20 | { 21 | "name": "d", 22 | "type": "string" 23 | } 24 | ] 25 | }, 26 | "data": [ 27 | [ 28 | 1, 29 | 2, 30 | 3, 31 | 4 32 | ], 33 | [ 34 | 2, 35 | 3, 36 | 4, 37 | 5 38 | ], 39 | [ 40 | 3, 41 | 4, 42 | 5, 43 | 6 44 | ] 45 | ] 46 | } 47 | ], 48 | "views": [ 49 | { 50 | "id": 1, 51 | "specType": "table", 52 | "resources": ["test"] 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /dist/testData/montreal1.json: -------------------------------------------------------------------------------- 1 | {"maintainer":"","relationships_as_object":[],"private":false,"maintainer_email":"","num_tags":4,"update_frequency":"daily","id":"1d785ef8-f883-47b5-bac5-dce1cdddb1b0","metadata_created":"2016-01-14T19:50:32.697205","metadata_modified":"2019-08-14T05:46:55.555851","temporal":"2005-01-01","state":"active","version":"","type":"dataset","resources":[{"mimetype":null,"cache_url":null,"hash":"","description":"","name":"remorquages_après_le_15_nov_2015","format":"csv","datastore_active":false,"cache_last_updated":null,"package_id":"1d785ef8-f883-47b5-bac5-dce1cdddb1b0","created":"2016-05-24T15:55:26.349190","state":"active","mimetype_inner":null,"last_modified":"2019-08-14T05:46:55.542302","position":0,"revision_id":"7adcdefa-b792-4135-a07c-02831a11445f","url_type":"upload","id":"e62322fb-3e14-4ee0-b724-a77190dac8e7","resource_type":null,"size":null,"title":"Remorquages après le 15 nov 2015","path":"http://donnees.ville.montreal.qc.ca/dataset/1d785ef8-f883-47b5-bac5-dce1cdddb1b0/resource/e62322fb-3e14-4ee0-b724-a77190dac8e7/download/remorquages.csv"},{"mimetype":null,"cache_url":null,"hash":"","description":"","name":"remorquages_de_2005_au_15_nov_2015","format":"csv","datastore_active":false,"cache_last_updated":null,"package_id":"1d785ef8-f883-47b5-bac5-dce1cdddb1b0","created":"2016-01-14T14:50:33.124240","state":"active","mimetype_inner":null,"last_modified":"2018-02-12T15:43:54.508077","position":1,"revision_id":"bc464b44-944b-42fe-999b-58244183f1c9","url_type":"upload","id":"2edcdba3-ba83-470f-a441-01fd72dd965e","resource_type":null,"size":null,"title":"Remorquages de 2005 au 15 nov 2015","path":"http://donnees.ville.montreal.qc.ca/dataset/1d785ef8-f883-47b5-bac5-dce1cdddb1b0/resource/2edcdba3-ba83-470f-a441-01fd72dd965e/download/remorquages2005-2015.csv"}],"num_resources":2,"language":"FR","methodologie":"Chaque enregistrement représente un remorquage de véhicule demandé par un employé de la Ville de Montréal, incluant le Service de police de la Ville de Montréal.\r\n\r\nCet ensemble de données couvre les données collectées avec deux systèmes d'information différents:\r\n\r\n#### Avant le 15 novembre 2015:\r\n- Le système utilisé ne supportait pas les informations géospatiales, donc seuls le nom de la rue et de l'arrondissement sont fournis\r\n- Le système fut implémenté en projet pilote au courant de 2005 et progressivement utilisé par les arrondissements au fils des années à leur discrétion.\r\n\r\n#### Depuis le 15 novembre 2015:\r\n- Le système a été mis en place pour l'ensemble des arrondissements\r\n- La saisie des remorquages se fait à l'aide d'une tablette électronique équipée d'un GPS permettant de se localiser dans un secteur pour pointer et stocker la position géographique (latitude/longitude) d'origine et de dépôt du véhicule remorqué.\r\n- Seuls les remorquages dont l'origine est sur le territoire de la Ville de Montréal sont colligés dans l'ensemble.\r\n- Les positionnements des remorquages ne permettent pas de savoir de quel côté de rue à été remorqué et déposé le véhicule.\r\n\r\n###Structure de données:\r\n\r\n- `DATE_ORIGINE`: date de l'événement (remorquage) au format `AAAA-MM-AA hh:mm`.\r\n- `LONGITUDE_ORIGINE` (seulement après nov 2015): Longitude d'origine du véhicule selon le référentiel WGS84\r\n- `LATITUDE_ORIGINE` (seulement après nov 2015): Latitude d'origine du véhicule selon le référentiel WGS84\r\n- `RUE_ORIGINE`: rue où était stationné le véhicule gênant.\r\n- `SECTEUR_ORIGINE`: Vide, pour usage futur.\r\n- `ARRONDISSEMENT_ORIGINE` : arrondissement où était stationné le véhicule gênant.\r\n- `DATE_DESTINATION` (seulement après nov 2015): date de fin du déplacement du véhicule au format `AAAA-MM-AA hh:mm`.\r\n- `LONGITUDE_DESTINATION` (seulement après nov 2015): Longitude où le véhicule a été déposé selon le référentiel WGS84\r\n- `LATITUDE_DESTINATION` (seulement après nov 2015): Latitude où le véhicule a été déposé selon le référentiel WGS84\r\n- `RUE_DESTINATION` (seulement après nov 2015): rue où le véhicule a été déplacé\r\n- `SECTEUR_DESTINATION` (seulement après nov 2015): Vide, pour usage futur.\r\n- `ARRONDISSEMENT_DESTINATION` (seulement après nov 2015): arrondissement où était stationné le véhicule gênant.\r\n\r\n- `MOTIF REMORQUAGE` (optionnel): raison ayant déclenchée le remorquage, pouvant prendre les valeurs suivantes:\r\n - `Constat d'infraction`: le véhicule était en infraction\r\n - `Événement policier` : le véhicule a été déplacé pour les besoins d'une opération policière\r\n - `Remis au propriétaire` : le véhicule a été remis au propriétaire arrivé sur le site avant le remorquage (véhicule non déplacé)\r\n - `Déplacement non autorisé` : le véhicule a été déplacé sans l'autorisation du préposé en stationnement de la Ville (cas potentiel de litige avec le déneigeur/remorqueur, des mesures sont alors prise par la Ville)\r\n - `Autre` : Autre motif (appellation ancienne éliminée des opérations actuelles)","groups":[{"display_name":"Infrastructures","description":"Ensemble des équipements collectifs de base nécessaires à la vie économique de la nation: routes, ponts, voies ferrées, canaux, ports, réseaux de télécommunication, d'énergie, gestion de l'eau.","image_display_url":"http://donnees.ville.montreal.qc.ca/portail/wp-content/themes/donneesouvertes4/images/thematiques/gif/250/infrastructures.gif","title":"Infrastructures","id":"010088d2-14c0-4d66-a005-d5b459f990cb","name":"infrastructures"},{"display_name":"Transport","description":"Ensemble des moyens publics et privés permettant de déplacer, à l'aide d'un équipement de transport, des personnes ou des marchandises sur une distance relativement importante.","image_display_url":"http://donnees.ville.montreal.qc.ca/portail/wp-content/themes/donneesouvertes4/images/thematiques/gif/250/transport.gif","title":"Transport","id":"5a937c19-d7fc-4598-a01a-b776030806e4","name":"transport"}],"creator_user_id":"58a2b89e-280b-405b-8807-2614d258d25a","relationships_as_subject":[],"ext_spatial":"ville-montreal","organization":{"description":"La politique de données ouvertes repose sur le concept \"d'ouverture par défaut\". Montréal a l'intention de rendre disponible la majorité de ses données d'ici la fin 2018.","created":"2016-01-13T10:21:33.203770","title":"Ville de Montréal","name":"ville-de-montreal","is_organization":true,"state":"active","image_url":"2017-11-07-154803.038695JPGnrb.jpg","revision_id":"1c82bc9e-95c1-4a7d-930b-7dda142ed6a7","type":"organization","id":"11287be6-b543-4e28-97b4-99b7111f8de0","approval_status":"approved"},"name":"remorquages-de-vehicules-genants","isopen":true,"url":"","owner_org":"11287be6-b543-4e28-97b4-99b7111f8de0","territoire":["agglomeration"],"title":"Remorquages de véhicules entravant les opérations de la Ville de Montréal","revision_id":"16eccbf2-4aa4-4e28-8746-a1e55afe34b6","description":"L'ensemble de données présente une liste des remorquages de véhicules gênants effectués par la Ville de Montréal. Ces remorquages sont effectués, par exemple lors des opérations de déneigement, des travaux ou encore lors d'événements spéciaux.","license":{"type":"cc-by","title":"Creative Commons Attribution 4.0 International","url":"http://creativecommons.org/licenses/by/4.0/"},"sources":[{"name":"Service de la concertation des arrondissements","email":"donneesouvertes@ville.montreal.qc.ca"}],"keywords":["Déneigement","Neige","Remorquage","Transport"],"views":[{"id":0,"title":"Remorquages après le 15 nov 2015","resources":["remorquages_après_le_15_nov_2015"],"specType":"table"},{"id":1,"title":"Remorquages de 2005 au 15 nov 2015","resources":["remorquages_de_2005_au_15_nov_2015"],"specType":"table"}]} 2 | -------------------------------------------------------------------------------- /dist/testData/remoteData.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inlined-data", 3 | "resources": [ 4 | { 5 | "name": "test", 6 | "path": "https://raw.githubusercontent.com/datapackage-examples/sample-csv/master/sample.csv", 7 | "format": "csv", 8 | "schema": { 9 | "fields": [ 10 | { 11 | "name": "header 0", 12 | "type": "string" 13 | }, 14 | { 15 | "name": "header 1", 16 | "type": "string" 17 | }, 18 | { 19 | "name": "header 2", 20 | "type": "string" 21 | }, 22 | { 23 | "name": "header 3", 24 | "type": "string" 25 | }, 26 | { 27 | "name": "header 4", 28 | "type": "string" 29 | }, 30 | { 31 | "name": "header 5", 32 | "type": "string" 33 | }, 34 | { 35 | "name": "header 6", 36 | "type": "string" 37 | }, 38 | { 39 | "name": "header 7", 40 | "type": "string" 41 | }, 42 | { 43 | "name": "header 8", 44 | "type": "string" 45 | }, 46 | { 47 | "name": "header 9", 48 | "type": "string" 49 | } 50 | ] 51 | } 52 | } 53 | ], 54 | "views": [ 55 | { 56 | "id": 1, 57 | "specType": "table", 58 | "resources": ["test"] 59 | } 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /dist/testData/testChartBuilder.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "specType": "table", 4 | "resources": [ 5 | { 6 | "name": "test", 7 | "_values": [ 8 | [ 9 | "x1a", 10 | "x1b", 11 | "x1c", 12 | "x1d" 13 | ], 14 | [ 15 | 1, 16 | 2, 17 | 3, 18 | 4 19 | ], 20 | [ 21 | 2, 22 | 3, 23 | 4, 24 | 5 25 | ], 26 | [ 27 | 3, 28 | 4, 29 | 5, 30 | 6 31 | ] 32 | ], 33 | "data": [ 34 | [ 35 | "x1a", 36 | "x1b", 37 | "x1c", 38 | "x1d" 39 | ], 40 | [ 41 | 1, 42 | 2, 43 | 3, 44 | 4 45 | ], 46 | [ 47 | 2, 48 | 3, 49 | 4, 50 | 5 51 | ], 52 | [ 53 | 3, 54 | 4, 55 | 5, 56 | 6 57 | ] 58 | ] 59 | } 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /dist/testData/testData.json: -------------------------------------------------------------------------------- 1 | { "views": [ 2 | { 3 | "id": 1, 4 | "specType": "table", 5 | "resources": [ 6 | { 7 | "name": "test", 8 | "schema": { 9 | "fields": [ 10 | { 11 | "name": "a", 12 | "type": "string" 13 | }, 14 | { 15 | "name": "b", 16 | "type": "string" 17 | }, 18 | { 19 | "name": "c", 20 | "type": "string" 21 | }, 22 | { 23 | "name": "d", 24 | "type": "string" 25 | } 26 | ] 27 | }, 28 | "data": [ 29 | [ 30 | "a", 31 | "b", 32 | "c", 33 | "d" 34 | ], 35 | [ 36 | 11, 37 | 22, 38 | 33, 39 | 44 40 | ], 41 | [ 42 | 21, 43 | 31, 44 | 41, 45 | 15 46 | ], 47 | [ 48 | 32, 49 | 42, 50 | 52, 51 | 62 52 | ] 53 | ] 54 | } 55 | ] 56 | }, 57 | { 58 | "id": 2, 59 | "specType": "table", 60 | "resources": [ 61 | { 62 | "name": "test", 63 | "schema": { 64 | "fields": [ 65 | { 66 | "name": "a", 67 | "type": "string" 68 | }, 69 | { 70 | "name": "b", 71 | "type": "string" 72 | }, 73 | { 74 | "name": "c", 75 | "type": "string" 76 | }, 77 | { 78 | "name": "d", 79 | "type": "string" 80 | } 81 | ] 82 | }, 83 | "data": [ 84 | [ 85 | "a", 86 | "b", 87 | "c", 88 | "d" 89 | ], 90 | [ 91 | 1, 92 | 2, 93 | 3, 94 | 4 95 | ], 96 | [ 97 | 2, 98 | 3, 99 | 4, 100 | 5 101 | ], 102 | [ 103 | 3, 104 | 4, 105 | 5, 106 | 6 107 | ] 108 | ] 109 | } 110 | ] 111 | } 112 | ]} 113 | -------------------------------------------------------------------------------- /dist/testData/testDataInit.json: -------------------------------------------------------------------------------- 1 | { "views": [ 2 | { 3 | "id": 1, 4 | "specType": "table", 5 | "resources": [ 6 | { 7 | "name": "test", 8 | "schema": { 9 | "fields": [ 10 | { 11 | "name": "a", 12 | "type": "string" 13 | }, 14 | { 15 | "name": "b", 16 | "type": "string" 17 | }, 18 | { 19 | "name": "c", 20 | "type": "string" 21 | }, 22 | { 23 | "name": "d", 24 | "type": "string" 25 | } 26 | ] 27 | }, 28 | "data": [ 29 | [ 30 | "a", 31 | "b", 32 | "c", 33 | "d" 34 | ], 35 | [ 36 | 1, 37 | 2, 38 | 3, 39 | 4 40 | ], 41 | [ 42 | 2, 43 | 3, 44 | 4, 45 | 5 46 | ], 47 | [ 48 | 3, 49 | 4, 50 | 5, 51 | 6 52 | ] 53 | ] 54 | } 55 | ] 56 | } 57 | ]} 58 | -------------------------------------------------------------------------------- /dist/testData/testDataTableInit.json: -------------------------------------------------------------------------------- 1 | { "views": [ 2 | { 3 | "id": 1, 4 | "specType": "table", 5 | "resources": [ 6 | { 7 | "name": "test", 8 | "schema": { 9 | "fields": [ 10 | { 11 | "name": "a", 12 | "type": "string" 13 | }, 14 | { 15 | "name": "b", 16 | "type": "string" 17 | }, 18 | { 19 | "name": "c", 20 | "type": "string" 21 | }, 22 | { 23 | "name": "d", 24 | "type": "string" 25 | } 26 | ] 27 | }, 28 | "data": [ 29 | [ 30 | "a", 31 | "b", 32 | "c", 33 | "d" 34 | ], 35 | [ 36 | 1, 37 | 2, 38 | 3, 39 | 4 40 | ], 41 | [ 42 | 2, 43 | 3, 44 | 4, 45 | 5 46 | ], 47 | [ 48 | 3, 49 | 4, 50 | 5, 51 | 6 52 | ] 53 | ] 54 | } 55 | ] 56 | } 57 | ]} 58 | -------------------------------------------------------------------------------- /dist/utils/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.deepClone = exports.getResourceForFiltering = exports.getDataViewMapBuilderView = exports.showQueryBuilder = exports.getDataViewChartBuilderView = exports.getEmptyView = exports.unloadDatapackage = void 0; 7 | 8 | var unloadDatapackage = function unloadDatapackage(datapackage) { 9 | var unloadedDatapackage = deepClone(datapackage); 10 | unloadedDatapackage.resources && unloadedDatapackage.resources.forEach(function (resource) { 11 | delete resource.data; 12 | delete resource._values; 13 | }); 14 | unloadedDatapackage.views && unloadedDatapackage.views.forEach(function (view) { 15 | view.resources && view.resources.forEach(function (resource) { 16 | delete resource.data; 17 | delete resource._values; 18 | }); 19 | }); 20 | return unloadedDatapackage; 21 | }; 22 | 23 | exports.unloadDatapackage = unloadDatapackage; 24 | 25 | var getEmptyView = function getEmptyView(datapackage) { 26 | try { 27 | return { 28 | resources: [{ 29 | schema: datapackage.resources[0].schema 30 | }] 31 | }; 32 | } catch (e) { 33 | return {}; 34 | } 35 | }; 36 | 37 | exports.getEmptyView = getEmptyView; 38 | 39 | var getDataViewChartBuilderView = function getDataViewChartBuilderView(datapackage) { 40 | if (!datapackage) return {}; 41 | var views = datapackage.views || []; 42 | 43 | switch (views.length) { 44 | case 1: 45 | return datapackage.views[0]; 46 | 47 | case 2: 48 | return datapackage.views[1]; 49 | 50 | case 3: 51 | return datapackage.views[2]; 52 | 53 | default: 54 | return getEmptyView(datapackage); 55 | } 56 | }; 57 | 58 | exports.getDataViewChartBuilderView = getDataViewChartBuilderView; 59 | 60 | var showQueryBuilder = function showQueryBuilder(props) { 61 | var activeWidget = props.widgets.find(function (widget) { 62 | return widget.active; 63 | }); 64 | var isWebView = false; 65 | 66 | try { 67 | var nonDataStoreViewTypes = ['web', 'document']; 68 | isWebView = nonDataStoreViewTypes.includes(activeWidget.datapackage.views[0].specType); 69 | } catch (_unused) {// just continue -- not a web view 70 | } 71 | 72 | if (isWebView) return false; 73 | return props.datapackage.resources[0].datastore_active; 74 | }; 75 | 76 | exports.showQueryBuilder = showQueryBuilder; 77 | 78 | var getDataViewMapBuilderView = function getDataViewMapBuilderView(datapackage) { 79 | if (!datapackage) return {}; 80 | var views = datapackage.views || []; 81 | return views.find(function (view) { 82 | return view.specType === 'tabularmap'; 83 | }) || getEmptyView(datapackage); 84 | }; 85 | 86 | exports.getDataViewMapBuilderView = getDataViewMapBuilderView; 87 | 88 | var getResourceForFiltering = function getResourceForFiltering(datapackage) { 89 | if (!datapackage) return {}; 90 | return datapackage.resources[0]; 91 | }; 92 | 93 | exports.getResourceForFiltering = getResourceForFiltering; 94 | 95 | var deepClone = function deepClone(obj) { 96 | return JSON.parse(JSON.stringify(obj)); 97 | }; 98 | 99 | exports.deepClone = deepClone; -------------------------------------------------------------------------------- /dist/utils/loadDataset.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _data = require("data.js"); 9 | 10 | function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } 11 | 12 | function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } 13 | 14 | var toArray = require('stream-to-array'); 15 | 16 | function parseDatapackageIdentifier(stringOrJSON) { 17 | try { 18 | return JSON.parse(stringOrJSON); 19 | } catch (e) { 20 | return stringOrJSON; 21 | } 22 | } // needs to be encapsulated 23 | // should be library code 24 | 25 | 26 | var _callee2 = function _callee2(dpID) { 27 | var DP_ID, tabularFormats, dataset; 28 | return regeneratorRuntime.async(function _callee2$(_context2) { 29 | while (1) { 30 | switch (_context2.prev = _context2.next) { 31 | case 0: 32 | DP_ID = parseDatapackageIdentifier(dpID); 33 | tabularFormats = ['csv', 'tsv', 'dsv', 'xls', 'xlsx']; 34 | _context2.prev = 2; 35 | _context2.next = 5; 36 | return regeneratorRuntime.awrap(_data.Dataset.load(DP_ID)); 37 | 38 | case 5: 39 | dataset = _context2.sent; 40 | _context2.next = 8; 41 | return regeneratorRuntime.awrap(Promise.all(dataset.resources.map(function _callee(file) { 42 | var response, result, fileInline, headers, rowStream, data, _response, _result, geoJsonTypes; 43 | 44 | return regeneratorRuntime.async(function _callee$(_context) { 45 | while (1) { 46 | switch (_context.prev = _context.next) { 47 | case 0: 48 | if (!(file.displayName === 'FileInline')) { 49 | _context.next = 4; 50 | break; 51 | } 52 | 53 | return _context.abrupt("return"); 54 | 55 | case 4: 56 | if (!(file.descriptor.api && file.descriptor.api.includes('datastore_search'))) { 57 | _context.next = 25; 58 | break; 59 | } 60 | 61 | _context.next = 7; 62 | return regeneratorRuntime.awrap(fetch(file.descriptor.api)); 63 | 64 | case 7: 65 | response = _context.sent; 66 | 67 | if (response.ok) { 68 | _context.next = 11; 69 | break; 70 | } 71 | 72 | file.descriptor.unavailable = true; 73 | return _context.abrupt("return"); 74 | 75 | case 11: 76 | _context.next = 13; 77 | return regeneratorRuntime.awrap(response.json()); 78 | 79 | case 13: 80 | result = _context.sent; 81 | // Remove fields that start with `_` as we don't want to display internal values, 82 | // e.g., `_id`, `_full_text` and `_count` 83 | file.descriptor.data = result.result.records.map(function (_ref, index) { 84 | var _id = _ref._id, 85 | _full_text = _ref._full_text, 86 | _count = _ref._count, 87 | etc = _objectWithoutProperties(_ref, ["_id", "_full_text", "_count"]); 88 | 89 | return etc; 90 | }); 91 | 92 | if (result.result.records.length === 0) { 93 | file.descriptor.totalrowcount = 0; 94 | } else { 95 | file.descriptor.totalrowcount = result.result.total || result.result.records[0]._count; 96 | } 97 | 98 | if (file.descriptor.schema) { 99 | _context.next = 23; 100 | break; 101 | } 102 | 103 | // Infer schema but re-open the file as it is now "inlined": 104 | fileInline = (0, _data.open)({ 105 | data: file.descriptor.data.map(Object.values), 106 | format: 'csv' 107 | }); 108 | headers = Object.keys(file.descriptor.data[0] || {}); 109 | fileInline.descriptor.data = [headers].concat(fileInline.descriptor.data); 110 | _context.next = 22; 111 | return regeneratorRuntime.awrap(fileInline.addSchema()); 112 | 113 | case 22: 114 | file.descriptor.schema = fileInline.descriptor.schema; 115 | 116 | case 23: 117 | _context.next = 68; 118 | break; 119 | 120 | case 25: 121 | if (!(file.displayName === "FileRemote" && tabularFormats.includes(file.descriptor.format))) { 122 | _context.next = 44; 123 | break; 124 | } 125 | 126 | _context.prev = 26; 127 | _context.next = 29; 128 | return regeneratorRuntime.awrap(file.rows({ 129 | size: 100, 130 | keyed: true 131 | })); 132 | 133 | case 29: 134 | rowStream = _context.sent; 135 | _context.next = 32; 136 | return regeneratorRuntime.awrap(toArray(rowStream)); 137 | 138 | case 32: 139 | data = _context.sent; 140 | 141 | if (data.length > 0) { 142 | file.descriptor.data = data; // This makes it FileInline 143 | } else { 144 | file.descriptor.unavailable = true; 145 | } 146 | 147 | _context.next = 36; 148 | return regeneratorRuntime.awrap(file.addSchema()); 149 | 150 | case 36: 151 | _context.next = 42; 152 | break; 153 | 154 | case 38: 155 | _context.prev = 38; 156 | _context.t0 = _context["catch"](26); 157 | console.warn(_context.t0); 158 | file.descriptor.unavailable = true; 159 | 160 | case 42: 161 | _context.next = 68; 162 | break; 163 | 164 | case 44: 165 | if (!file.descriptor.format.toLowerCase().includes('json')) { 166 | _context.next = 63; 167 | break; 168 | } 169 | 170 | _context.next = 47; 171 | return regeneratorRuntime.awrap(fetch(file.descriptor.path)); 172 | 173 | case 47: 174 | _response = _context.sent; 175 | 176 | if (_response.ok) { 177 | _context.next = 51; 178 | break; 179 | } 180 | 181 | file.descriptor.unavailable = true; 182 | return _context.abrupt("return"); 183 | 184 | case 51: 185 | _context.next = 53; 186 | return regeneratorRuntime.awrap(_response.json()); 187 | 188 | case 53: 189 | _result = _context.sent; 190 | // The '.json' files can contain geo data - check by its 'type' property 191 | geoJsonTypes = ['Feature', 'FeatureCollection', 'Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon', 'GeometryCollection']; 192 | 193 | if (!geoJsonTypes.includes(_result.type)) { 194 | _context.next = 59; 195 | break; 196 | } 197 | 198 | file.descriptor.data = _result; 199 | _context.next = 61; 200 | break; 201 | 202 | case 59: 203 | // It isn't a valid GeoJSON 204 | file.descriptor.unavailable = true; 205 | return _context.abrupt("return"); 206 | 207 | case 61: 208 | _context.next = 68; 209 | break; 210 | 211 | case 63: 212 | if (!(file.descriptor.format.toLowerCase() === 'pdf')) { 213 | _context.next = 67; 214 | break; 215 | } 216 | 217 | return _context.abrupt("return"); 218 | 219 | case 67: 220 | // We can't load any other data types for now. 221 | file.descriptor.unavailable = true; 222 | 223 | case 68: 224 | case "end": 225 | return _context.stop(); 226 | } 227 | } 228 | }, null, null, [[26, 38]]); 229 | }))); 230 | 231 | case 8: 232 | return _context2.abrupt("return", dataset.descriptor); 233 | 234 | case 11: 235 | _context2.prev = 11; 236 | _context2.t0 = _context2["catch"](2); 237 | console.warn('Failed to load a Dataset from provided datapackage id\n' + _context2.t0); 238 | return _context2.abrupt("return", DP_ID); 239 | 240 | case 15: 241 | case "end": 242 | return _context2.stop(); 243 | } 244 | } 245 | }, null, null, [[2, 11]]); 246 | }; 247 | 248 | exports.default = _callee2; -------------------------------------------------------------------------------- /dist/utils/utils.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _loadDataset = _interopRequireDefault(require("./loadDataset")); 4 | 5 | var _inlinedData = _interopRequireDefault(require("../testData/inlinedData.json")); 6 | 7 | var _datapackageRender = require("datapackage-render"); 8 | 9 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 10 | 11 | it('compiles views!', function _callee() { 12 | var loadedView; 13 | return regeneratorRuntime.async(function _callee$(_context) { 14 | while (1) { 15 | switch (_context.prev = _context.next) { 16 | case 0: 17 | _context.next = 2; 18 | return regeneratorRuntime.awrap((0, _loadDataset.default)(_inlinedData.default)); 19 | 20 | case 2: 21 | loadedView = _context.sent; 22 | // We expect 'resources' key to be defined in the compiled view: 23 | expect(loadedView.resources).toBeDefined(); 24 | expect(loadedView.resources).toBeInstanceOf(Array); 25 | expect(loadedView.resources[0]).toBeInstanceOf(Object); 26 | expect(loadedView.resources[0].data).toBeInstanceOf(Array); 27 | expect(loadedView.resources[0].data.length).toBe(3); 28 | 29 | case 8: 30 | case "end": 31 | return _context.stop(); 32 | } 33 | } 34 | }); 35 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@datopian/data-explorer", 3 | "version": "0.4.15", 4 | "private": false, 5 | "main": "/dist/AppWithProvider.js", 6 | "module": "/dist/AppWithProvider.js", 7 | "dependencies": { 8 | "@datopian/chart-builder": "^0.1.6", 9 | "@datopian/datapackage-views-js": "^1.4.4", 10 | "@datopian/datastore-query-builder": "^0.4.15", 11 | "@datopian/map-builder": "^0.1.9", 12 | "data.js": "^0.12.11", 13 | "datapackage-render": "git+https://github.com/frictionlessdata/datapackage-render-js.git", 14 | "i18next": "^19.0.3", 15 | "i18next-browser-languagedetector": "^4.0.1", 16 | "react": "^16.9.0", 17 | "react-dom": "^16.9.0", 18 | "react-i18next": "^11.2.7", 19 | "react-loader": "^2.4.5", 20 | "react-paginate": "^6.3.2", 21 | "react-redux": "^7.1.0", 22 | "react-scripts": "3.0.1", 23 | "react-tabs-redux": "^4.0.0", 24 | "redux": "^4.0.4", 25 | "redux-thunk": "^2.3.0" 26 | }, 27 | "devDependencies": { 28 | "@babel/cli": "^7.5.5", 29 | "@babel/core": "^7.5.5", 30 | "@babel/plugin-proposal-class-properties": "^7.5.5", 31 | "@babel/preset-env": "^7.5.5", 32 | "@babel/preset-react": "^7.0.0", 33 | "autoprefixer": "^9.6.1", 34 | "postcss-cli": "^6.1.3", 35 | "postcss-import": "^12.0.1", 36 | "react-cosmos": "^4.8.1", 37 | "redux-devtools-extension": "^2.13.8", 38 | "tailwindcss": "^1.1.1" 39 | }, 40 | "scripts": { 41 | "css": "postcss src/css/index.css -o src/App.css", 42 | "start": "yarn css && PORT=5000 react-scripts start", 43 | "dev": "yarn cosmos", 44 | "build": "yarn css && react-scripts build", 45 | "build:package": "rm -rf dist/ && mkdir dist && NODE_ENV=production npx babel src/ -d dist/ --copy-files", 46 | "test": "react-scripts test", 47 | "eject": "react-scripts eject" 48 | }, 49 | "eslintConfig": { 50 | "extends": "react-app" 51 | }, 52 | "babel": { 53 | "presets": [ 54 | "@babel/preset-env", 55 | "@babel/preset-react" 56 | ], 57 | "plugins": [ 58 | "@babel/plugin-proposal-class-properties" 59 | ] 60 | }, 61 | "browserslist": { 62 | "production": [ 63 | ">0.2%", 64 | "not dead", 65 | "not op_mini all" 66 | ], 67 | "development": [ 68 | "last 1 chrome version", 69 | "last 1 firefox version", 70 | "last 1 safari version" 71 | ] 72 | }, 73 | "sideEffects": [ 74 | "./src/i18n.js" 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | require('tailwindcss'), 5 | require('autoprefixer'), 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /public/README.md: -------------------------------------------------------------------------------- 1 | The `index.html` is run when you: 2 | 3 | `yarn start` or `npm run start` 4 | 5 | If you need to see other examples: 6 | 7 | * copy and paste entire content of files with name `example-...` into `index.html` 8 | * check the localhost:PORT for updates 9 | 10 | If you want to test data explorer from your live site: 11 | 12 | * open your the dataset/resource page from your live site 13 | * inspect the data explorer element 14 | * find a div element with `data-datapackage` attribute 15 | * copy the div element 16 | * paste into `index.html` 17 | -------------------------------------------------------------------------------- /public/example-with-geopoint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 |{t('Select chart type, group column (abscissa x-axis) and series (ordinate y-axis) on the right hand side panel.')}
: '' } 16 | {specType === 'tabularmap' ?{t('Select geo data column on the right hand side panel.')}
: '' } 17 |{t('No share link available')}
61 | } 62 | {props.apiUri 63 | &&