-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.jsx
156 lines (147 loc) · 6.13 KB
/
index.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import React from 'react'
import ReactDOM from 'react-dom'
import lodash from 'lodash'
import {Package, Resource} from 'datapackage'
import '../node_modules/handsontable/dist/handsontable.full.min.css'
import HandsOnTable from './components/dataPackageView/HandsOnTable'
import MultiViews from "./containers/MultiViews"; // eslint-disable-line
import LeafletMap from './components/dataPackageView/LeafletMap'
import * as dputils from './utils/datapackage'
import * as dprender from 'datapackage-render'
/**
* From the back-end we expect a template to have div elements with specific
* class, data-type and data-resource attributes:
*
* - To tell a front-end that a given div element is for React component, it
* should have class=react-me.
* - To tell a front-end which component to render in that div element, it
* should provide data-type attribute - resource-preview for HandsOnTable and
* data-views for the graphs.
* - To tell a front-end which resource to render, it should provide
* data-resource attirbute with index of resource or name.
* - Also any properties can be passed from the back-end using data-* prefix.
*/
let dpObj
const divElements = document.querySelectorAll('.react-me')
const divElementsForViews = []
const divElementsForPreviews = []
const normalViews = []
const previewViews = []
divElements.forEach(element => {
if (element.dataset.type === 'data-views') {
divElementsForViews.push(element)
} else if (element.dataset.type === 'resource-preview') {
divElementsForPreviews.push(element)
}
})
fetchDpAndResourcesAndRenderViews(DP_ID, divElements)
async function fetchDpAndResourcesAndRenderViews(dataPackageIdentifier, divElements) {
let basePath
if (lodash.isString(dataPackageIdentifier)) {
basePath = dataPackageIdentifier.replace('datapackage.json', '')
} else if (lodash.isPlainObject(dataPackageIdentifier)) {
basePath = dataPackageIdentifier.path
}
dpObj = await Package.load(dataPackageIdentifier, {basePath, strict: false})
dpObj.descriptor.resources = dpObj.resources.map(resource => resource.descriptor)
// Split out normal and preview views
if (dpObj.descriptor.views) {
dpObj.descriptor.views.forEach(view => {
if (!view.datahub) {
normalViews.push(view)
} else if (view.datahub.type === 'preview') {
previewViews.push(view)
}
})
}
// Identify which resources are needed for normal views
const resourcesForNormalViews = []
const resourcesForPreviewViews = []
normalViews.forEach(view => {
if (view.resources) {
view.resources.forEach(res => {
let resourceForView, idx
if (lodash.isString(res)) {
resourceForView = dprender.findResourceByNameOrIndex(dpObj.descriptor, res)
idx = dpObj.descriptor.resources.indexOf(resourceForView)
} else if (lodash.isPlainObject(res)) {
resourceForView = dprender.findResourceByNameOrIndex(dpObj.descriptor, res.name)
idx = dpObj.descriptor.resources.indexOf(resourceForView)
} else if (lodash.isNumber(res)) {
idx = res
}
resourcesForNormalViews.push(idx)
})
} else {
resourcesForNormalViews.push(0)
}
})
// Render preview views for which derived/preview versions exist.
// If not exist then identify corresponding normal resource.
previewViews.forEach(view => {
let previewResourceFound = false
const resourceForPreview = dprender.findResourceByNameOrIndex(dpObj.descriptor, view.resources[0])
const idx = dpObj.descriptor.resources.indexOf(resourceForPreview)
if (resourceForPreview.alternates) {
resourceForPreview.alternates.forEach(async res => {
if (res.datahub.type === 'derived/preview') {
previewResourceFound = true
res = await Resource.load(res)
res.descriptor._values = await dputils.fetchDataOnly(res)
renderView(view, res.descriptor, idx, dpObj.descriptor)
}
})
}
if (!previewResourceFound) {
resourcesForPreviewViews.push(idx)
}
})
// Also add resources that have 'geojson' format to 'resourcesForPreviewViews' list:
dpObj.descriptor.resources.forEach((res, idx) => {
if (res.format === 'geojson') {
resourcesForPreviewViews.push(idx)
}
})
// Concatenate required resources for normal and preview views.
// Get only unique values.
let requiredResources = resourcesForNormalViews.concat(resourcesForPreviewViews)
requiredResources = [...new Set(requiredResources)] // Unique values
// Load required resources and render views
await Promise.all(requiredResources.map(async idx => {
dpObj.resources[idx].descriptor._values = await dputils.fetchDataOnly(dpObj.resources[idx])
if (resourcesForNormalViews.includes(idx)) {
renderView('view', dpObj.resources[idx].descriptor, null, dpObj.descriptor)
}
if (resourcesForPreviewViews.includes(idx)) {
renderView('preview', dpObj.resources[idx].descriptor, idx, dpObj.descriptor)
}
}))
}
function renderView (view, resource, idx, dp) {
if (view === 'preview' || (view.datahub && view.datahub.type === 'preview')) {
if (resource.format === 'geojson') {
ReactDOM.render(<LeafletMap featureCollection={resource._values} idx={idx} />, divElementsForPreviews[idx])
} else if (resource.format !== 'topojson') {
let compiledViewSpec = {
resources: [resource],
specType: 'handsontable'
}
let spec = dprender.handsOnTableToHandsOnTable(compiledViewSpec)
ReactDOM.render(<HandsOnTable spec={spec} idx={idx} />, divElementsForPreviews[idx]);
}
} else {
if (divElementsForViews.length > 1) {
// Render each view in a specific div element. A view index should be
// equal to div element index:
normalViews.forEach((view, index) => {
dp.views = [view].concat(previewViews)
ReactDOM.render(<MultiViews dataPackage={dp} idx={index} />, divElementsForViews[index])
})
} else {
// We'll render all graphs in the single div element:
ReactDOM.render(<MultiViews dataPackage={dp} />, divElementsForViews[0])
}
}
}
exports.renderView = renderView
exports.fetchDpAndResourcesAndRenderViews = fetchDpAndResourcesAndRenderViews