@cdc/data-bite 4.22.10 → 4.22.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/cdcdatabite.js +2 -2
- package/examples/example-config.json +24 -24
- package/examples/example-data.json +833 -833
- package/examples/gallery/calculated-average.json +3166 -3166
- package/examples/gallery/calculated-with-pic.json +3172 -3172
- package/examples/gallery/max-value.json +3166 -3166
- package/examples/gallery/sum-of-data.json +3160 -3160
- package/examples/private/WCMSRD-12345.json +1026 -1026
- package/examples/private/totals.json +67 -1
- package/package.json +3 -3
- package/src/CdcDataBite.tsx +266 -272
- package/src/components/CircleCallout.js +8 -6
- package/src/components/EditorPanel.js +378 -636
- package/src/context.tsx +3 -3
- package/src/data/initial-state.js +37 -37
- package/src/index.html +19 -23
- package/src/index.tsx +9 -9
- package/src/scss/bite.scss +23 -18
- package/src/scss/editor-panel.scss +83 -70
- package/src/scss/main.scss +8 -8
- package/src/scss/variables.scss +1 -1
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import React, { memo, useContext, useEffect, useState } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
Accordion,
|
|
5
|
-
AccordionItem,
|
|
6
|
-
AccordionItemButton,
|
|
7
|
-
AccordionItemHeading,
|
|
8
|
-
AccordionItemPanel,
|
|
9
|
-
} from 'react-accessible-accordion'
|
|
3
|
+
import { Accordion, AccordionItem, AccordionItemButton, AccordionItemHeading, AccordionItemPanel } from 'react-accessible-accordion'
|
|
10
4
|
|
|
11
5
|
import { useDebounce } from 'use-debounce'
|
|
12
6
|
import Context from '../context'
|
|
@@ -16,151 +10,176 @@ import Icon from '@cdc/core/components/ui/Icon'
|
|
|
16
10
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
17
11
|
import { BITE_LOCATIONS, DATA_FUNCTIONS, IMAGE_POSITIONS, DATA_OPERATORS } from '../CdcDataBite'
|
|
18
12
|
|
|
19
|
-
const TextField = memo(({label, section = null, subsection = null, fieldName, updateField, value: stateValue, tooltip, type =
|
|
20
|
-
const [
|
|
13
|
+
const TextField = memo(({ label, section = null, subsection = null, fieldName, updateField, value: stateValue, tooltip, type = 'input', i = null, min = null, max = null, ...attributes }) => {
|
|
14
|
+
const [value, setValue] = useState(stateValue)
|
|
21
15
|
|
|
22
|
-
const [
|
|
16
|
+
const [debouncedValue] = useDebounce(value, 500)
|
|
23
17
|
|
|
24
18
|
useEffect(() => {
|
|
25
|
-
if('string' === typeof debouncedValue && stateValue !== debouncedValue
|
|
19
|
+
if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
|
|
26
20
|
updateField(section, subsection, fieldName, debouncedValue, i)
|
|
27
21
|
}
|
|
28
22
|
}, [debouncedValue, section, subsection, fieldName, i, stateValue, updateField])
|
|
29
23
|
|
|
30
|
-
let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}
|
|
24
|
+
let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
|
|
31
25
|
|
|
32
|
-
const onChange =
|
|
26
|
+
const onChange = e => {
|
|
33
27
|
//TODO: This block gives a warning/error in the console, but it still works.
|
|
34
|
-
if('number' !== type || min === null){
|
|
35
|
-
setValue(e.target.value)
|
|
28
|
+
if ('number' !== type || min === null) {
|
|
29
|
+
setValue(e.target.value)
|
|
36
30
|
} else {
|
|
37
|
-
if(!e.target.value || (
|
|
38
|
-
setValue(e.target.value)
|
|
31
|
+
if (!e.target.value || (parseFloat(min) <= parseFloat(e.target.value)) & (parseFloat(max) >= parseFloat(e.target.value))) {
|
|
32
|
+
setValue(e.target.value)
|
|
39
33
|
} else {
|
|
40
|
-
setValue(min.toString())
|
|
34
|
+
setValue(min.toString())
|
|
41
35
|
}
|
|
42
36
|
}
|
|
43
|
-
}
|
|
37
|
+
}
|
|
44
38
|
|
|
45
|
-
let formElement = <input type=
|
|
39
|
+
let formElement = <input type='text' name={name} onChange={onChange} {...attributes} value={value} />
|
|
46
40
|
|
|
47
|
-
if('textarea' === type) {
|
|
48
|
-
formElement =
|
|
49
|
-
<textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>
|
|
50
|
-
)
|
|
41
|
+
if ('textarea' === type) {
|
|
42
|
+
formElement = <textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>
|
|
51
43
|
}
|
|
52
44
|
|
|
53
|
-
if('number' === type) {
|
|
54
|
-
formElement = <input type=
|
|
45
|
+
if ('number' === type) {
|
|
46
|
+
formElement = <input type='number' name={name} onChange={onChange} {...attributes} value={value} />
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
return (
|
|
58
50
|
<>
|
|
59
|
-
{label && label.length > 0 &&
|
|
51
|
+
{label && label.length > 0 && (
|
|
60
52
|
<label>
|
|
61
|
-
<span className=
|
|
53
|
+
<span className='edit-label column-heading'>
|
|
54
|
+
{label}
|
|
55
|
+
{tooltip}
|
|
56
|
+
</span>
|
|
62
57
|
{formElement}
|
|
63
58
|
</label>
|
|
64
|
-
}
|
|
59
|
+
)}
|
|
65
60
|
{(!label || label.length === 0) && formElement}
|
|
66
61
|
</>
|
|
67
62
|
)
|
|
68
63
|
})
|
|
69
64
|
|
|
70
|
-
const CheckBox = memo(({label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes}) => (
|
|
71
|
-
<label className=
|
|
72
|
-
<input
|
|
73
|
-
|
|
65
|
+
const CheckBox = memo(({ label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes }) => (
|
|
66
|
+
<label className='checkbox'>
|
|
67
|
+
<input
|
|
68
|
+
type='checkbox'
|
|
69
|
+
name={fieldName}
|
|
70
|
+
checked={value}
|
|
71
|
+
onChange={() => {
|
|
72
|
+
updateField(section, subsection, fieldName, !value)
|
|
73
|
+
}}
|
|
74
|
+
{...attributes}
|
|
75
|
+
/>
|
|
76
|
+
<span className='edit-label column-heading'>{label}</span>
|
|
77
|
+
<span className='cove-icon'>{tooltip}</span>
|
|
74
78
|
</label>
|
|
75
79
|
))
|
|
76
80
|
|
|
77
|
-
const Select = memo(({label, value, options, fieldName, section = null, subsection = null, required = false, updateField, initial: initialValue, ...attributes}) => {
|
|
78
|
-
let optionsJsx = ''
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
const Select = memo(({ label, value, options, fieldName, section = null, subsection = null, required = false, updateField, initial: initialValue, ...attributes }) => {
|
|
82
|
+
let optionsJsx = ''
|
|
83
|
+
if (Array.isArray(options)) {
|
|
84
|
+
//Handle basic array
|
|
85
|
+
optionsJsx = options.map(optionName => (
|
|
86
|
+
<option value={optionName} key={optionName}>
|
|
87
|
+
{optionName}
|
|
88
|
+
</option>
|
|
89
|
+
))
|
|
90
|
+
} else {
|
|
91
|
+
//Handle object with value/name pairs
|
|
92
|
+
optionsJsx = []
|
|
83
93
|
for (const [optionValue, optionName] of Object.entries(options)) {
|
|
84
|
-
optionsJsx.push(
|
|
94
|
+
optionsJsx.push(
|
|
95
|
+
<option value={optionValue} key={optionValue}>
|
|
96
|
+
{optionName}
|
|
97
|
+
</option>
|
|
98
|
+
)
|
|
85
99
|
}
|
|
86
100
|
}
|
|
87
101
|
|
|
88
|
-
if(initialValue) {
|
|
89
|
-
optionsJsx.unshift(
|
|
102
|
+
if (initialValue) {
|
|
103
|
+
optionsJsx.unshift(
|
|
104
|
+
<option value='' key='initial'>
|
|
105
|
+
{initialValue}
|
|
106
|
+
</option>
|
|
107
|
+
)
|
|
90
108
|
}
|
|
91
109
|
|
|
92
110
|
return (
|
|
93
111
|
<label>
|
|
94
|
-
<span className=
|
|
95
|
-
<select
|
|
112
|
+
<span className='edit-label'>{label}</span>
|
|
113
|
+
<select
|
|
114
|
+
className={required && !value ? 'warning' : ''}
|
|
115
|
+
name={fieldName}
|
|
116
|
+
value={value}
|
|
117
|
+
onChange={event => {
|
|
118
|
+
updateField(section, subsection, fieldName, event.target.value)
|
|
119
|
+
}}
|
|
120
|
+
{...attributes}
|
|
121
|
+
>
|
|
96
122
|
{optionsJsx}
|
|
97
123
|
</select>
|
|
98
124
|
</label>
|
|
99
125
|
)
|
|
100
126
|
})
|
|
101
127
|
|
|
102
|
-
const headerColors = ['theme-blue','theme-purple','theme-brown','theme-teal','theme-pink','theme-orange','theme-slate','theme-indigo','theme-cyan','theme-green','theme-amber']
|
|
128
|
+
const headerColors = ['theme-blue', 'theme-purple', 'theme-brown', 'theme-teal', 'theme-pink', 'theme-orange', 'theme-slate', 'theme-indigo', 'theme-cyan', 'theme-green', 'theme-amber']
|
|
103
129
|
|
|
104
130
|
const EditorPanel = memo(() => {
|
|
105
|
-
const {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
isDashboard,
|
|
112
|
-
} = useContext(Context);
|
|
113
|
-
|
|
114
|
-
const [ displayPanel, setDisplayPanel ] = useState(true);
|
|
115
|
-
const enforceRestrictions = (updatedConfig) => {
|
|
116
|
-
//If there are any dependencies between fields, etc../
|
|
117
|
-
};
|
|
131
|
+
const { config, updateConfig, loading, data, setParentConfig, isDashboard } = useContext(Context)
|
|
132
|
+
|
|
133
|
+
const [displayPanel, setDisplayPanel] = useState(true)
|
|
134
|
+
const enforceRestrictions = updatedConfig => {
|
|
135
|
+
//If there are any dependencies between fields, etc../
|
|
136
|
+
}
|
|
118
137
|
|
|
119
138
|
const updateField = (section, subsection, fieldName, newValue) => {
|
|
120
139
|
// Top level
|
|
121
|
-
if(
|
|
122
|
-
let updatedConfig = {...config, [fieldName]: newValue}
|
|
140
|
+
if (null === section && null === subsection) {
|
|
141
|
+
let updatedConfig = { ...config, [fieldName]: newValue }
|
|
123
142
|
|
|
124
|
-
if (
|
|
125
|
-
updatedConfig.filterValue = ''
|
|
143
|
+
if ('filterColumn' === fieldName) {
|
|
144
|
+
updatedConfig.filterValue = ''
|
|
126
145
|
}
|
|
127
146
|
|
|
128
|
-
enforceRestrictions(updatedConfig)
|
|
147
|
+
enforceRestrictions(updatedConfig)
|
|
129
148
|
|
|
130
|
-
updateConfig(updatedConfig)
|
|
149
|
+
updateConfig(updatedConfig)
|
|
131
150
|
return
|
|
132
151
|
}
|
|
133
152
|
|
|
134
|
-
const isArray = Array.isArray(config[section])
|
|
153
|
+
const isArray = Array.isArray(config[section])
|
|
135
154
|
|
|
136
|
-
let sectionValue = isArray ? [...config[section], newValue] : {...config[section], [fieldName]: newValue}
|
|
155
|
+
let sectionValue = isArray ? [...config[section], newValue] : { ...config[section], [fieldName]: newValue }
|
|
137
156
|
|
|
138
|
-
if(null !== subsection) {
|
|
139
|
-
if(isArray) {
|
|
157
|
+
if (null !== subsection) {
|
|
158
|
+
if (isArray) {
|
|
140
159
|
sectionValue = [...config[section]]
|
|
141
|
-
sectionValue[subsection] = {...sectionValue[subsection], [fieldName]: newValue}
|
|
142
|
-
} else if(typeof newValue ===
|
|
160
|
+
sectionValue[subsection] = { ...sectionValue[subsection], [fieldName]: newValue }
|
|
161
|
+
} else if (typeof newValue === 'string') {
|
|
143
162
|
sectionValue[subsection] = newValue
|
|
144
163
|
} else {
|
|
145
|
-
sectionValue = {...config[section], [subsection]: { ...config[section][subsection], [fieldName]: newValue}}
|
|
164
|
+
sectionValue = { ...config[section], [subsection]: { ...config[section][subsection], [fieldName]: newValue } }
|
|
146
165
|
}
|
|
147
166
|
}
|
|
148
167
|
|
|
149
|
-
let updatedConfig = {...config, [section]: sectionValue}
|
|
168
|
+
let updatedConfig = { ...config, [section]: sectionValue }
|
|
150
169
|
|
|
151
|
-
enforceRestrictions(updatedConfig)
|
|
170
|
+
enforceRestrictions(updatedConfig)
|
|
152
171
|
|
|
153
172
|
updateConfig(updatedConfig)
|
|
154
173
|
}
|
|
155
174
|
|
|
156
175
|
const missingRequiredSections = () => {
|
|
157
176
|
//Whether to show error message if something is required to show a data-bite and isn't filled in
|
|
158
|
-
return false
|
|
159
|
-
}
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
160
179
|
|
|
161
180
|
useEffect(() => {
|
|
162
181
|
// Pass up to Editor if needed
|
|
163
|
-
if(setParentConfig) {
|
|
182
|
+
if (setParentConfig) {
|
|
164
183
|
const newConfig = convertStateToConfig()
|
|
165
184
|
|
|
166
185
|
setParentConfig(newConfig)
|
|
@@ -169,36 +188,46 @@ const EditorPanel = memo(() => {
|
|
|
169
188
|
}, [config])
|
|
170
189
|
|
|
171
190
|
const onBackClick = () => {
|
|
172
|
-
setDisplayPanel(!displayPanel)
|
|
191
|
+
setDisplayPanel(!displayPanel)
|
|
173
192
|
}
|
|
174
193
|
|
|
175
194
|
const Error = () => {
|
|
176
195
|
return (
|
|
177
|
-
<section className=
|
|
178
|
-
<section className=
|
|
196
|
+
<section className='waiting'>
|
|
197
|
+
<section className='waiting-container'>
|
|
179
198
|
<h3>Error With Configuration</h3>
|
|
180
199
|
<p>{config.runtime.editorErrorMessage}</p>
|
|
181
200
|
</section>
|
|
182
201
|
</section>
|
|
183
|
-
)
|
|
202
|
+
)
|
|
184
203
|
}
|
|
185
204
|
|
|
186
205
|
const Confirm = () => {
|
|
187
206
|
return (
|
|
188
|
-
<section className=
|
|
189
|
-
<section className=
|
|
207
|
+
<section className='waiting'>
|
|
208
|
+
<section className='waiting-container'>
|
|
190
209
|
<h3>Finish Configuring</h3>
|
|
191
210
|
<p>Set all required options to the left and confirm below to display a preview of the chart.</p>
|
|
192
|
-
<button
|
|
211
|
+
<button
|
|
212
|
+
className='btn'
|
|
213
|
+
style={{ margin: '1em auto' }}
|
|
214
|
+
disabled={missingRequiredSections()}
|
|
215
|
+
onClick={e => {
|
|
216
|
+
e.preventDefault()
|
|
217
|
+
updateConfig({ ...config, newViz: false })
|
|
218
|
+
}}
|
|
219
|
+
>
|
|
220
|
+
I'm Done
|
|
221
|
+
</button>
|
|
193
222
|
</section>
|
|
194
223
|
</section>
|
|
195
|
-
)
|
|
224
|
+
)
|
|
196
225
|
}
|
|
197
226
|
|
|
198
227
|
const convertStateToConfig = () => {
|
|
199
228
|
let strippedState = JSON.parse(JSON.stringify(config))
|
|
200
229
|
//if(false === missingRequiredSections()) {
|
|
201
|
-
|
|
230
|
+
//strippedState.newViz
|
|
202
231
|
//}
|
|
203
232
|
delete strippedState.runtime
|
|
204
233
|
|
|
@@ -206,195 +235,157 @@ const EditorPanel = memo(() => {
|
|
|
206
235
|
}
|
|
207
236
|
|
|
208
237
|
// Filters -----------------------------------------------
|
|
209
|
-
const removeFilter =
|
|
210
|
-
let filters = [...config.filters]
|
|
238
|
+
const removeFilter = index => {
|
|
239
|
+
let filters = [...config.filters]
|
|
211
240
|
|
|
212
|
-
filters.splice(index, 1)
|
|
241
|
+
filters.splice(index, 1)
|
|
213
242
|
|
|
214
|
-
updateConfig({...config, filters})
|
|
243
|
+
updateConfig({ ...config, filters })
|
|
215
244
|
}
|
|
216
245
|
|
|
217
246
|
const updateFilterProp = (name, index, value) => {
|
|
218
|
-
let filters = [...config.filters]
|
|
247
|
+
let filters = [...config.filters]
|
|
219
248
|
|
|
220
|
-
filters[index][name] = value
|
|
249
|
+
filters[index][name] = value
|
|
221
250
|
|
|
222
|
-
updateConfig({...config, filters})
|
|
251
|
+
updateConfig({ ...config, filters })
|
|
223
252
|
}
|
|
224
253
|
|
|
225
254
|
const addNewFilter = () => {
|
|
226
|
-
let filters = config.filters ? [...config.filters] : []
|
|
255
|
+
let filters = config.filters ? [...config.filters] : []
|
|
227
256
|
|
|
228
|
-
filters.push({values: []})
|
|
257
|
+
filters.push({ values: [] })
|
|
229
258
|
|
|
230
|
-
updateConfig({...config, filters})
|
|
259
|
+
updateConfig({ ...config, filters })
|
|
231
260
|
}
|
|
232
261
|
|
|
233
262
|
const getColumns = (filter = true) => {
|
|
234
263
|
let columns = {}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
264
|
+
if (data.length) {
|
|
265
|
+
data.map(row => {
|
|
266
|
+
return Object.keys(row).forEach(columnName => (columns[columnName] = true))
|
|
267
|
+
})
|
|
268
|
+
}
|
|
241
269
|
|
|
242
270
|
return Object.keys(columns)
|
|
243
271
|
}
|
|
244
272
|
|
|
245
|
-
const getFilterColumnValues =
|
|
273
|
+
const getFilterColumnValues = index => {
|
|
246
274
|
let filterDataOptions = []
|
|
247
|
-
const filterColumnName = config.filters[index].columnName
|
|
275
|
+
const filterColumnName = config.filters[index].columnName
|
|
248
276
|
if (data && filterColumnName) {
|
|
249
|
-
data.forEach(function(row) {
|
|
250
|
-
if (
|
|
251
|
-
filterDataOptions.push(row[filterColumnName])
|
|
277
|
+
data.forEach(function (row) {
|
|
278
|
+
if (undefined !== row[filterColumnName] && -1 === filterDataOptions.indexOf(row[filterColumnName])) {
|
|
279
|
+
filterDataOptions.push(row[filterColumnName])
|
|
252
280
|
}
|
|
253
281
|
})
|
|
254
|
-
filterDataOptions.sort()
|
|
282
|
+
filterDataOptions.sort()
|
|
255
283
|
}
|
|
256
|
-
return filterDataOptions
|
|
284
|
+
return filterDataOptions
|
|
257
285
|
}
|
|
258
286
|
|
|
259
287
|
// Dynamic Images ----------------------------------------
|
|
260
288
|
const updateDynamicImage = (name, index, subindex = null, value) => {
|
|
261
|
-
let imageOptions = [...config.imageData.options]
|
|
262
|
-
null === subindex ? imageOptions[index][name] = value : imageOptions[index].arguments[subindex][name] = value
|
|
289
|
+
let imageOptions = [...config.imageData.options]
|
|
290
|
+
null === subindex ? (imageOptions[index][name] = value) : (imageOptions[index].arguments[subindex][name] = value)
|
|
263
291
|
|
|
264
|
-
let payload = {...config.imageData, options: imageOptions}
|
|
265
|
-
updateConfig({...config, imageData: payload})
|
|
292
|
+
let payload = { ...config.imageData, options: imageOptions }
|
|
293
|
+
updateConfig({ ...config, imageData: payload })
|
|
266
294
|
}
|
|
267
295
|
|
|
268
296
|
const setDynamicArgument = (optionIndex, name, value) => {
|
|
269
297
|
let imageArguments = [...config.imageData.options[optionIndex].arguments]
|
|
270
|
-
|
|
271
|
-
let argumentsPayload = {...config.imageData.options[optionIndex], arguments: imageArguments}
|
|
298
|
+
imageArguments[1] = { ...imageArguments[1], [name]: value }
|
|
299
|
+
let argumentsPayload = { ...config.imageData.options[optionIndex], arguments: imageArguments }
|
|
272
300
|
let optionsPayload = [...config.imageData.options]
|
|
273
|
-
|
|
274
|
-
let payload = {...config.imageData, options: optionsPayload}
|
|
275
|
-
updateConfig({...config, imageData: payload})
|
|
301
|
+
optionsPayload[optionIndex] = argumentsPayload
|
|
302
|
+
let payload = { ...config.imageData, options: optionsPayload }
|
|
303
|
+
updateConfig({ ...config, imageData: payload })
|
|
276
304
|
}
|
|
277
305
|
|
|
278
|
-
const removeDynamicArgument =
|
|
306
|
+
const removeDynamicArgument = optionIndex => {
|
|
279
307
|
if (config.imageData.options[optionIndex].arguments.length > 1) {
|
|
280
308
|
let imageArguments = [...config.imageData.options[optionIndex].arguments]
|
|
281
|
-
|
|
282
|
-
let argumentsPayload = {...config.imageData.options[optionIndex], arguments: imageArguments}
|
|
309
|
+
imageArguments.pop()
|
|
310
|
+
let argumentsPayload = { ...config.imageData.options[optionIndex], arguments: imageArguments }
|
|
283
311
|
let optionsPayload = [...config.imageData.options]
|
|
284
|
-
|
|
285
|
-
let payload = {...config.imageData, options: optionsPayload}
|
|
286
|
-
updateConfig({...config, imageData: payload})
|
|
312
|
+
optionsPayload[optionIndex] = argumentsPayload
|
|
313
|
+
let payload = { ...config.imageData, options: optionsPayload }
|
|
314
|
+
updateConfig({ ...config, imageData: payload })
|
|
287
315
|
}
|
|
288
316
|
}
|
|
289
317
|
|
|
290
318
|
const addDynamicImage = () => {
|
|
291
|
-
let imageOptions = config.imageData.options ? [
|
|
292
|
-
imageOptions.push({ source: '', arguments: [{ operator: '', threshold: ''}], alt: '', secondArgument: false })
|
|
319
|
+
let imageOptions = config.imageData.options ? [...config.imageData.options] : []
|
|
320
|
+
imageOptions.push({ source: '', arguments: [{ operator: '', threshold: '' }], alt: '', secondArgument: false })
|
|
293
321
|
|
|
294
|
-
let payload = {...config.imageData, options: imageOptions}
|
|
295
|
-
updateConfig({...config, imageData: payload})
|
|
322
|
+
let payload = { ...config.imageData, options: imageOptions }
|
|
323
|
+
updateConfig({ ...config, imageData: payload })
|
|
296
324
|
}
|
|
297
325
|
|
|
298
|
-
const removeDynamicImage =
|
|
299
|
-
let imageOptions = [...config.imageData.options]
|
|
300
|
-
imageOptions.splice(index, 1)
|
|
326
|
+
const removeDynamicImage = index => {
|
|
327
|
+
let imageOptions = [...config.imageData.options]
|
|
328
|
+
imageOptions.splice(index, 1)
|
|
301
329
|
|
|
302
|
-
let payload = {...config.imageData, options: imageOptions}
|
|
303
|
-
updateConfig({...config, imageData: payload})
|
|
330
|
+
let payload = { ...config.imageData, options: imageOptions }
|
|
331
|
+
updateConfig({ ...config, imageData: payload })
|
|
304
332
|
}
|
|
305
333
|
|
|
306
334
|
// General -----------------------------------------------
|
|
307
|
-
if(loading) {
|
|
335
|
+
if (loading) {
|
|
308
336
|
return null
|
|
309
337
|
}
|
|
310
338
|
|
|
311
339
|
return (
|
|
312
|
-
<ErrorBoundary component=
|
|
313
|
-
{!config.newViz &&
|
|
314
|
-
config.runtime &&
|
|
315
|
-
config.runtime.editorErrorMessage && <Error />}
|
|
340
|
+
<ErrorBoundary component='EditorPanel'>
|
|
341
|
+
{!config.newViz && config.runtime && config.runtime.editorErrorMessage && <Error />}
|
|
316
342
|
{(!config.dataColumn || !config.dataFunction) && <Confirm />}
|
|
317
|
-
<button
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
/>
|
|
322
|
-
<section
|
|
323
|
-
className={
|
|
324
|
-
displayPanel ? "editor-panel cove" : "hidden editor-panel cove"
|
|
325
|
-
}
|
|
326
|
-
>
|
|
327
|
-
<div className="heading-2">Configure Data Bite</div>
|
|
328
|
-
<section className="form-container">
|
|
343
|
+
<button className={displayPanel ? `editor-toggle` : `editor-toggle collapsed`} title={displayPanel ? `Collapse Editor` : `Expand Editor`} onClick={onBackClick} />
|
|
344
|
+
<section className={displayPanel ? 'editor-panel cove' : 'hidden editor-panel cove'}>
|
|
345
|
+
<div className='heading-2'>Configure Data Bite</div>
|
|
346
|
+
<section className='form-container'>
|
|
329
347
|
<form>
|
|
330
348
|
<Accordion allowZeroExpanded={true}>
|
|
331
349
|
<AccordionItem>
|
|
332
|
-
{
|
|
350
|
+
{' '}
|
|
333
351
|
{/* General */}
|
|
334
352
|
<AccordionItemHeading>
|
|
335
353
|
<AccordionItemButton>General</AccordionItemButton>
|
|
336
354
|
</AccordionItemHeading>
|
|
337
355
|
<AccordionItemPanel>
|
|
338
|
-
<Select
|
|
339
|
-
|
|
340
|
-
fieldName="biteStyle"
|
|
341
|
-
label="Data Bite Style"
|
|
342
|
-
updateField={updateField}
|
|
343
|
-
options={BITE_LOCATIONS}
|
|
344
|
-
initial="Select"
|
|
345
|
-
/>
|
|
346
|
-
<TextField
|
|
347
|
-
value={config.title}
|
|
348
|
-
fieldName="title"
|
|
349
|
-
label="Title"
|
|
350
|
-
placeholder="Data Bite Title"
|
|
351
|
-
updateField={updateField}
|
|
352
|
-
/>
|
|
356
|
+
<Select value={config.biteStyle} fieldName='biteStyle' label='Data Bite Style' updateField={updateField} options={BITE_LOCATIONS} initial='Select' />
|
|
357
|
+
<TextField value={config.title} fieldName='title' label='Title' placeholder='Data Bite Title' updateField={updateField} />
|
|
353
358
|
|
|
354
359
|
<TextField
|
|
355
|
-
type=
|
|
360
|
+
type='textarea'
|
|
356
361
|
value={config.biteBody}
|
|
357
|
-
fieldName=
|
|
358
|
-
label=
|
|
362
|
+
fieldName='biteBody'
|
|
363
|
+
label='Message'
|
|
359
364
|
updateField={updateField}
|
|
360
365
|
tooltip={
|
|
361
|
-
<Tooltip style={{ textTransform:
|
|
366
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
362
367
|
<Tooltip.Target>
|
|
363
|
-
<Icon
|
|
364
|
-
display="question"
|
|
365
|
-
style={{ marginLeft: "0.5rem" }}
|
|
366
|
-
/>
|
|
368
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
367
369
|
</Tooltip.Target>
|
|
368
370
|
<Tooltip.Content>
|
|
369
|
-
<p>
|
|
370
|
-
Enter the message text for the visualization. The
|
|
371
|
-
following HTML tags are supported: strong, em, sup,
|
|
372
|
-
and sub.
|
|
373
|
-
</p>
|
|
371
|
+
<p>Enter the message text for the visualization. The following HTML tags are supported: strong, em, sup, and sub.</p>
|
|
374
372
|
</Tooltip.Content>
|
|
375
373
|
</Tooltip>
|
|
376
374
|
}
|
|
377
375
|
/>
|
|
378
376
|
<TextField
|
|
379
377
|
value={config.subtext}
|
|
380
|
-
fieldName=
|
|
381
|
-
label=
|
|
382
|
-
placeholder=
|
|
378
|
+
fieldName='subtext'
|
|
379
|
+
label='Subtext/Citation'
|
|
380
|
+
placeholder='Data Bite Subtext or Citation'
|
|
383
381
|
updateField={updateField}
|
|
384
382
|
tooltip={
|
|
385
|
-
<Tooltip style={{ textTransform:
|
|
383
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
386
384
|
<Tooltip.Target>
|
|
387
|
-
<Icon
|
|
388
|
-
display="question"
|
|
389
|
-
style={{ marginLeft: "0.5rem" }}
|
|
390
|
-
/>
|
|
385
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
391
386
|
</Tooltip.Target>
|
|
392
387
|
<Tooltip.Content>
|
|
393
|
-
<p>
|
|
394
|
-
Enter supporting text to display below the data
|
|
395
|
-
visualization, if applicable. The following HTML
|
|
396
|
-
tags are supported: strong, em, sup, and sub.
|
|
397
|
-
</p>
|
|
388
|
+
<p>Enter supporting text to display below the data visualization, if applicable. The following HTML tags are supported: strong, em, sup, and sub.</p>
|
|
398
389
|
</Tooltip.Content>
|
|
399
390
|
</Tooltip>
|
|
400
391
|
}
|
|
@@ -403,120 +394,65 @@ const EditorPanel = memo(() => {
|
|
|
403
394
|
</AccordionItem>
|
|
404
395
|
|
|
405
396
|
<AccordionItem>
|
|
406
|
-
{
|
|
397
|
+
{' '}
|
|
407
398
|
{/*Data*/}
|
|
408
399
|
<AccordionItemHeading>
|
|
409
|
-
<AccordionItemButton>
|
|
410
|
-
Data{" "}
|
|
411
|
-
{(!config.dataColumn || !config.dataFunction) && (
|
|
412
|
-
<WarningImage width="25" className="warning-icon" />
|
|
413
|
-
)}
|
|
414
|
-
</AccordionItemButton>
|
|
400
|
+
<AccordionItemButton>Data {(!config.dataColumn || !config.dataFunction) && <WarningImage width='25' className='warning-icon' />}</AccordionItemButton>
|
|
415
401
|
</AccordionItemHeading>
|
|
416
402
|
<AccordionItemPanel>
|
|
417
|
-
<ul className=
|
|
418
|
-
<li className=
|
|
419
|
-
<Select
|
|
420
|
-
|
|
421
|
-
fieldName="dataColumn"
|
|
422
|
-
label="Data Column"
|
|
423
|
-
updateField={updateField}
|
|
424
|
-
initial="Select"
|
|
425
|
-
required={true}
|
|
426
|
-
options={getColumns()}
|
|
427
|
-
/>
|
|
428
|
-
<Select
|
|
429
|
-
value={config.dataFunction || ""}
|
|
430
|
-
fieldName="dataFunction"
|
|
431
|
-
label="Data Function"
|
|
432
|
-
updateField={updateField}
|
|
433
|
-
initial="Select"
|
|
434
|
-
required={true}
|
|
435
|
-
options={DATA_FUNCTIONS}
|
|
436
|
-
/>
|
|
403
|
+
<ul className='column-edit'>
|
|
404
|
+
<li className='two-col'>
|
|
405
|
+
<Select value={config.dataColumn || ''} fieldName='dataColumn' label='Data Column' updateField={updateField} initial='Select' required={true} options={getColumns()} />
|
|
406
|
+
<Select value={config.dataFunction || ''} fieldName='dataFunction' label='Data Function' updateField={updateField} initial='Select' required={true} options={DATA_FUNCTIONS} />
|
|
437
407
|
</li>
|
|
438
408
|
</ul>
|
|
439
|
-
<span className=
|
|
440
|
-
<ul className=
|
|
441
|
-
<li className=
|
|
442
|
-
<TextField
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
fieldName="prefix"
|
|
446
|
-
label="Prefix"
|
|
447
|
-
updateField={updateField}
|
|
448
|
-
/>
|
|
449
|
-
<TextField
|
|
450
|
-
value={config.dataFormat.suffix}
|
|
451
|
-
section="dataFormat"
|
|
452
|
-
fieldName="suffix"
|
|
453
|
-
label="Suffix"
|
|
454
|
-
updateField={updateField}
|
|
455
|
-
/>
|
|
456
|
-
<TextField
|
|
457
|
-
type="number"
|
|
458
|
-
value={config.dataFormat.roundToPlace}
|
|
459
|
-
section="dataFormat"
|
|
460
|
-
fieldName="roundToPlace"
|
|
461
|
-
label="Round"
|
|
462
|
-
updateField={updateField}
|
|
463
|
-
min="0"
|
|
464
|
-
max="99"
|
|
465
|
-
/>
|
|
409
|
+
<span className='divider-heading'>Number Formatting</span>
|
|
410
|
+
<ul className='column-edit'>
|
|
411
|
+
<li className='three-col'>
|
|
412
|
+
<TextField value={config.dataFormat.prefix} section='dataFormat' fieldName='prefix' label='Prefix' updateField={updateField} />
|
|
413
|
+
<TextField value={config.dataFormat.suffix} section='dataFormat' fieldName='suffix' label='Suffix' updateField={updateField} />
|
|
414
|
+
<TextField type='number' value={config.dataFormat.roundToPlace} section='dataFormat' fieldName='roundToPlace' label='Round' updateField={updateField} min='0' max='99' />
|
|
466
415
|
</li>
|
|
467
416
|
</ul>
|
|
468
|
-
<CheckBox value={config.dataFormat.commas} section=
|
|
469
|
-
<CheckBox value={config.dataFormat.ignoreZeros} section=
|
|
470
|
-
<hr className=
|
|
417
|
+
<CheckBox value={config.dataFormat.commas} section='dataFormat' fieldName='commas' label='Add commas' updateField={updateField} />
|
|
418
|
+
<CheckBox value={config.dataFormat.ignoreZeros} section='dataFormat' fieldName='ignoreZeros' label='Ignore Zeros' updateField={updateField} />
|
|
419
|
+
<hr className='accordion__divider' />
|
|
471
420
|
|
|
472
|
-
<label style={{ marginBottom:
|
|
473
|
-
<span className=
|
|
421
|
+
<label style={{ marginBottom: '1rem' }}>
|
|
422
|
+
<span className='edit-label'>
|
|
474
423
|
Data Point Filters
|
|
475
|
-
<Tooltip style={{ textTransform:
|
|
424
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
476
425
|
<Tooltip.Target>
|
|
477
|
-
<Icon
|
|
478
|
-
display="question"
|
|
479
|
-
style={{ marginLeft: "0.5rem" }}
|
|
480
|
-
/>
|
|
426
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
481
427
|
</Tooltip.Target>
|
|
482
428
|
<Tooltip.Content>
|
|
483
|
-
<p>
|
|
484
|
-
To refine the highlighted data point, specify one or
|
|
485
|
-
more filters (e.g., "Male" and "Female" for a column
|
|
486
|
-
called "Sex").
|
|
487
|
-
</p>
|
|
429
|
+
<p>To refine the highlighted data point, specify one or more filters (e.g., "Male" and "Female" for a column called "Sex").</p>
|
|
488
430
|
</Tooltip.Content>
|
|
489
431
|
</Tooltip>
|
|
490
432
|
</span>
|
|
491
433
|
</label>
|
|
492
434
|
{config.filters && (
|
|
493
|
-
<ul className=
|
|
435
|
+
<ul className='filters-list'>
|
|
494
436
|
{config.filters.map((filter, index) => (
|
|
495
|
-
<fieldset className=
|
|
437
|
+
<fieldset className='edit-block' key={index}>
|
|
496
438
|
<button
|
|
497
|
-
type=
|
|
498
|
-
className=
|
|
439
|
+
type='button'
|
|
440
|
+
className='remove-column'
|
|
499
441
|
onClick={() => {
|
|
500
|
-
removeFilter(index)
|
|
442
|
+
removeFilter(index)
|
|
501
443
|
}}
|
|
502
444
|
>
|
|
503
445
|
Remove
|
|
504
446
|
</button>
|
|
505
447
|
<label>
|
|
506
|
-
<span className=
|
|
507
|
-
Column
|
|
508
|
-
</span>
|
|
448
|
+
<span className='edit-label column-heading'>Column</span>
|
|
509
449
|
<select
|
|
510
|
-
value={filter.columnName ? filter.columnName :
|
|
511
|
-
onChange={
|
|
512
|
-
updateFilterProp(
|
|
513
|
-
"columnName",
|
|
514
|
-
index,
|
|
515
|
-
e.target.value
|
|
516
|
-
);
|
|
450
|
+
value={filter.columnName ? filter.columnName : ''}
|
|
451
|
+
onChange={e => {
|
|
452
|
+
updateFilterProp('columnName', index, e.target.value)
|
|
517
453
|
}}
|
|
518
454
|
>
|
|
519
|
-
<option value=
|
|
455
|
+
<option value=''>- Select Option -</option>
|
|
520
456
|
{getColumns().map((dataKey, index) => (
|
|
521
457
|
<option value={dataKey} key={index}>
|
|
522
458
|
{dataKey}
|
|
@@ -525,27 +461,19 @@ const EditorPanel = memo(() => {
|
|
|
525
461
|
</select>
|
|
526
462
|
</label>
|
|
527
463
|
<label>
|
|
528
|
-
<span className=
|
|
529
|
-
Column Value
|
|
530
|
-
</span>
|
|
464
|
+
<span className='edit-label column-heading'>Column Value</span>
|
|
531
465
|
<select
|
|
532
466
|
value={filter.columnValue}
|
|
533
|
-
onChange={
|
|
534
|
-
updateFilterProp(
|
|
535
|
-
"columnValue",
|
|
536
|
-
index,
|
|
537
|
-
e.target.value
|
|
538
|
-
);
|
|
467
|
+
onChange={e => {
|
|
468
|
+
updateFilterProp('columnValue', index, e.target.value)
|
|
539
469
|
}}
|
|
540
470
|
>
|
|
541
|
-
<option value=
|
|
542
|
-
{getFilterColumnValues(index).map(
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
)
|
|
548
|
-
)}
|
|
471
|
+
<option value=''>- Select Option -</option>
|
|
472
|
+
{getFilterColumnValues(index).map((dataKey, index) => (
|
|
473
|
+
<option value={dataKey} key={index}>
|
|
474
|
+
{dataKey}
|
|
475
|
+
</option>
|
|
476
|
+
))}
|
|
549
477
|
</select>
|
|
550
478
|
</label>
|
|
551
479
|
</fieldset>
|
|
@@ -554,98 +482,44 @@ const EditorPanel = memo(() => {
|
|
|
554
482
|
)}
|
|
555
483
|
{(!config.filters || config.filters.length === 0) && (
|
|
556
484
|
<div>
|
|
557
|
-
<fieldset className=
|
|
558
|
-
<p style={{ textAlign:
|
|
559
|
-
There are currently no filters.
|
|
560
|
-
</p>
|
|
485
|
+
<fieldset className='edit-block'>
|
|
486
|
+
<p style={{ textAlign: 'center' }}>There are currently no filters.</p>
|
|
561
487
|
</fieldset>
|
|
562
488
|
</div>
|
|
563
489
|
)}
|
|
564
|
-
<button
|
|
565
|
-
type="button"
|
|
566
|
-
onClick={addNewFilter}
|
|
567
|
-
className="btn full-width"
|
|
568
|
-
>
|
|
490
|
+
<button type='button' onClick={addNewFilter} className='btn full-width'>
|
|
569
491
|
Add Filter
|
|
570
492
|
</button>
|
|
571
493
|
</AccordionItemPanel>
|
|
572
494
|
</AccordionItem>
|
|
573
495
|
|
|
574
496
|
<AccordionItem>
|
|
575
|
-
{
|
|
497
|
+
{' '}
|
|
576
498
|
{/*Visual*/}
|
|
577
499
|
<AccordionItemHeading>
|
|
578
500
|
<AccordionItemButton>Visual</AccordionItemButton>
|
|
579
501
|
</AccordionItemHeading>
|
|
580
502
|
<AccordionItemPanel>
|
|
581
|
-
<TextField
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
fieldName=
|
|
585
|
-
label=
|
|
586
|
-
updateField={updateField}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
/>
|
|
590
|
-
<Select
|
|
591
|
-
value={config.fontSize}
|
|
592
|
-
fieldName="fontSize"
|
|
593
|
-
label="Overall Font Size"
|
|
594
|
-
updateField={updateField}
|
|
595
|
-
options={["small", "medium", "large"]}
|
|
596
|
-
/>
|
|
597
|
-
<div className="checkbox-group">
|
|
598
|
-
<CheckBox
|
|
599
|
-
value={config.visual?.border}
|
|
600
|
-
section="visual"
|
|
601
|
-
fieldName="border"
|
|
602
|
-
label="Display Border"
|
|
603
|
-
updateField={updateField}
|
|
604
|
-
/>
|
|
605
|
-
<CheckBox
|
|
606
|
-
value={config.visual?.borderColorTheme}
|
|
607
|
-
section="visual"
|
|
608
|
-
fieldName="borderColorTheme"
|
|
609
|
-
label="Use Border Color Theme"
|
|
610
|
-
updateField={updateField}
|
|
611
|
-
/>
|
|
612
|
-
<CheckBox
|
|
613
|
-
value={config.visual?.accent}
|
|
614
|
-
section="visual"
|
|
615
|
-
fieldName="accent"
|
|
616
|
-
label="Use Accent Style"
|
|
617
|
-
updateField={updateField}
|
|
618
|
-
/>
|
|
619
|
-
<CheckBox
|
|
620
|
-
value={config.visual?.background}
|
|
621
|
-
section="visual"
|
|
622
|
-
fieldName="background"
|
|
623
|
-
label="Use Theme Background Color"
|
|
624
|
-
updateField={updateField}
|
|
625
|
-
/>
|
|
626
|
-
<CheckBox
|
|
627
|
-
value={config.visual?.hideBackgroundColor}
|
|
628
|
-
section="visual"
|
|
629
|
-
fieldName="hideBackgroundColor"
|
|
630
|
-
label="Hide Background Color"
|
|
631
|
-
updateField={updateField}
|
|
632
|
-
/>
|
|
503
|
+
<TextField type='number' value={config.biteFontSize} fieldName='biteFontSize' label='Bite Font Size' updateField={updateField} min='17' max='65' />
|
|
504
|
+
<Select value={config.fontSize} fieldName='fontSize' label='Overall Font Size' updateField={updateField} options={['small', 'medium', 'large']} />
|
|
505
|
+
<div className='checkbox-group'>
|
|
506
|
+
<CheckBox value={config.visual?.border} section='visual' fieldName='border' label='Display Border' updateField={updateField} />
|
|
507
|
+
<CheckBox value={config.visual?.borderColorTheme} section='visual' fieldName='borderColorTheme' label='Use Border Color Theme' updateField={updateField} />
|
|
508
|
+
<CheckBox value={config.visual?.accent} section='visual' fieldName='accent' label='Use Accent Style' updateField={updateField} />
|
|
509
|
+
<CheckBox value={config.visual?.background} section='visual' fieldName='background' label='Use Theme Background Color' updateField={updateField} />
|
|
510
|
+
<CheckBox value={config.visual?.hideBackgroundColor} section='visual' fieldName='hideBackgroundColor' label='Hide Background Color' updateField={updateField} />
|
|
633
511
|
</div>
|
|
634
512
|
<label>
|
|
635
|
-
<span className=
|
|
636
|
-
<ul className=
|
|
637
|
-
{headerColors.map(
|
|
513
|
+
<span className='edit-label'>Theme</span>
|
|
514
|
+
<ul className='color-palette'>
|
|
515
|
+
{headerColors.map(palette => (
|
|
638
516
|
<li
|
|
639
517
|
title={palette}
|
|
640
518
|
key={palette}
|
|
641
519
|
onClick={() => {
|
|
642
|
-
updateConfig({ ...config, theme: palette })
|
|
520
|
+
updateConfig({ ...config, theme: palette })
|
|
643
521
|
}}
|
|
644
|
-
className={
|
|
645
|
-
config.theme === palette
|
|
646
|
-
? "selected " + palette
|
|
647
|
-
: palette
|
|
648
|
-
}
|
|
522
|
+
className={config.theme === palette ? 'selected ' + palette : palette}
|
|
649
523
|
/>
|
|
650
524
|
))}
|
|
651
525
|
</ul>
|
|
@@ -655,299 +529,167 @@ const EditorPanel = memo(() => {
|
|
|
655
529
|
|
|
656
530
|
{['title', 'body', 'graphic'].includes(config.biteStyle) && (
|
|
657
531
|
<AccordionItem>
|
|
658
|
-
{
|
|
532
|
+
{' '}
|
|
659
533
|
{/*Image & Dynamic Images*/}
|
|
660
534
|
<AccordionItemHeading>
|
|
661
535
|
<AccordionItemButton>
|
|
662
536
|
Image
|
|
663
|
-
{[
|
|
537
|
+
{['dynamic'].includes(config.imageData.display) && 's'}
|
|
664
538
|
</AccordionItemButton>
|
|
665
539
|
</AccordionItemHeading>
|
|
666
540
|
<AccordionItemPanel>
|
|
667
|
-
<Select
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
fieldName="display"
|
|
671
|
-
label="Image Display Type"
|
|
672
|
-
updateField={updateField}
|
|
673
|
-
options={["none", "static", "dynamic"]}
|
|
674
|
-
/>
|
|
675
|
-
<Select
|
|
676
|
-
value={config.bitePosition || ""}
|
|
677
|
-
fieldName="bitePosition"
|
|
678
|
-
label="Image/Graphic Position"
|
|
679
|
-
updateField={updateField}
|
|
680
|
-
initial="Select"
|
|
681
|
-
options={IMAGE_POSITIONS}
|
|
682
|
-
/>
|
|
683
|
-
{["static"].includes(config.imageData.display) && (
|
|
541
|
+
<Select value={config.imageData.display || ''} section='imageData' fieldName='display' label='Image Display Type' updateField={updateField} options={['none', 'static', 'dynamic']} />
|
|
542
|
+
<Select value={config.bitePosition || ''} fieldName='bitePosition' label='Image/Graphic Position' updateField={updateField} initial='Select' options={IMAGE_POSITIONS} />
|
|
543
|
+
{['static'].includes(config.imageData.display) && (
|
|
684
544
|
<>
|
|
685
|
-
<TextField
|
|
686
|
-
|
|
687
|
-
section="imageData"
|
|
688
|
-
fieldName="url"
|
|
689
|
-
label="Image URL"
|
|
690
|
-
updateField={updateField}
|
|
691
|
-
/>
|
|
692
|
-
<TextField
|
|
693
|
-
value={config.imageData.alt}
|
|
694
|
-
section="imageData"
|
|
695
|
-
fieldName="alt"
|
|
696
|
-
label="Alt Text"
|
|
697
|
-
updateField={updateField}
|
|
698
|
-
/>
|
|
545
|
+
<TextField value={config.imageData.url} section='imageData' fieldName='url' label='Image URL' updateField={updateField} />
|
|
546
|
+
<TextField value={config.imageData.alt} section='imageData' fieldName='alt' label='Alt Text' updateField={updateField} />
|
|
699
547
|
</>
|
|
700
548
|
)}
|
|
701
549
|
|
|
702
|
-
{[
|
|
550
|
+
{['dynamic'].includes(config.imageData.display) && (
|
|
703
551
|
<>
|
|
704
|
-
<TextField
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
</
|
|
746
|
-
<
|
|
747
|
-
<
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
552
|
+
<TextField value={config.imageData.url || ''} section='imageData' fieldName='url' label='Image URL (default)' updateField={updateField} />
|
|
553
|
+
<TextField value={config.imageData.alt} section='imageData' fieldName='alt' label='Alt Text (default)' updateField={updateField} />
|
|
554
|
+
|
|
555
|
+
<hr className='accordion__divider' />
|
|
556
|
+
|
|
557
|
+
{(!config.imageData.options || config.imageData.options.length === 0) && <p style={{ textAlign: 'center' }}>There are currently no dynamic images.</p>}
|
|
558
|
+
{config.imageData.options && config.imageData.options.length > 0 && (
|
|
559
|
+
<>
|
|
560
|
+
<ul>
|
|
561
|
+
{config.imageData.options.map((option, index) => (
|
|
562
|
+
<fieldset className='edit-block' key={index}>
|
|
563
|
+
<button
|
|
564
|
+
type='button'
|
|
565
|
+
className='remove-column'
|
|
566
|
+
onClick={() => {
|
|
567
|
+
removeDynamicImage(index)
|
|
568
|
+
}}
|
|
569
|
+
>
|
|
570
|
+
Remove
|
|
571
|
+
</button>
|
|
572
|
+
<label>
|
|
573
|
+
<span className='edit-label column-heading'>
|
|
574
|
+
<strong>{'Image #' + (index + 1)}</strong>
|
|
575
|
+
</span>
|
|
576
|
+
|
|
577
|
+
<div className='accordion__panel-row align-center'>
|
|
578
|
+
<div className='accordion__panel-col flex-auto'>If Value</div>
|
|
579
|
+
<div className='accordion__panel-col flex-auto'>
|
|
580
|
+
<select
|
|
581
|
+
value={option.arguments[0]?.operator || ''}
|
|
582
|
+
onChange={e => {
|
|
583
|
+
updateDynamicImage('operator', index, 0, e.target.value)
|
|
584
|
+
}}
|
|
585
|
+
>
|
|
586
|
+
<option value='' disabled />
|
|
587
|
+
{DATA_OPERATORS.map((operator, index) => (
|
|
588
|
+
<option value={operator} key={index}>
|
|
589
|
+
{operator}
|
|
590
|
+
</option>
|
|
591
|
+
))}
|
|
592
|
+
</select>
|
|
593
|
+
</div>
|
|
594
|
+
<div className='accordion__panel-col flex-grow flex-shrink'>
|
|
595
|
+
<input
|
|
596
|
+
type='number'
|
|
597
|
+
value={option.arguments[0]?.threshold || ''}
|
|
598
|
+
onChange={e => {
|
|
599
|
+
updateDynamicImage('threshold', index, 0, e.target.value)
|
|
600
|
+
}}
|
|
601
|
+
/>
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
|
|
605
|
+
<div className='accordion__panel-row mb-2 align-center'>
|
|
606
|
+
<div className='accordion__panel-col flex-grow'>
|
|
607
|
+
<select
|
|
608
|
+
className='border-dashed text-center'
|
|
609
|
+
value={option.secondArgument ? 'and' : 'then'}
|
|
610
|
+
onChange={e => {
|
|
611
|
+
if ('then' === e.target.value) {
|
|
612
|
+
updateDynamicImage('secondArgument', index, null, false)
|
|
613
|
+
removeDynamicArgument(index)
|
|
614
|
+
}
|
|
615
|
+
if ('and' === e.target.value) {
|
|
616
|
+
updateDynamicImage('secondArgument', index, null, true)
|
|
617
|
+
}
|
|
618
|
+
}}
|
|
619
|
+
>
|
|
620
|
+
<option value={'then'}>Then</option>
|
|
621
|
+
<option value={'and'}>And</option>
|
|
622
|
+
</select>
|
|
623
|
+
</div>
|
|
624
|
+
</div>
|
|
625
|
+
|
|
626
|
+
{option.secondArgument && true === option.secondArgument && (
|
|
627
|
+
<>
|
|
628
|
+
<div className='accordion__panel-row align-center'>
|
|
629
|
+
<div className='accordion__panel-col flex-auto'>If Value</div>
|
|
630
|
+
<div className='accordion__panel-col flex-auto'>
|
|
758
631
|
<select
|
|
759
|
-
value={
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
}
|
|
763
|
-
onChange={(e) => {
|
|
764
|
-
updateDynamicImage(
|
|
765
|
-
"operator",
|
|
766
|
-
index,
|
|
767
|
-
0,
|
|
768
|
-
e.target.value
|
|
769
|
-
);
|
|
632
|
+
value={option.arguments[1]?.operator || ''}
|
|
633
|
+
onChange={e => {
|
|
634
|
+
setDynamicArgument(index, 'operator', e.target.value)
|
|
770
635
|
}}
|
|
771
636
|
>
|
|
772
|
-
<option value=
|
|
773
|
-
{DATA_OPERATORS.map(
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
>
|
|
779
|
-
{operator}
|
|
780
|
-
</option>
|
|
781
|
-
)
|
|
782
|
-
)}
|
|
637
|
+
<option value='' disabled />
|
|
638
|
+
{DATA_OPERATORS.map((operator, index) => (
|
|
639
|
+
<option value={operator} key={index}>
|
|
640
|
+
{operator}
|
|
641
|
+
</option>
|
|
642
|
+
))}
|
|
783
643
|
</select>
|
|
784
644
|
</div>
|
|
785
|
-
<div className=
|
|
645
|
+
<div className='accordion__panel-col flex-grow flex-shrink'>
|
|
786
646
|
<input
|
|
787
|
-
type=
|
|
788
|
-
value={
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
}
|
|
792
|
-
onChange={(e) => {
|
|
793
|
-
updateDynamicImage(
|
|
794
|
-
"threshold",
|
|
795
|
-
index,
|
|
796
|
-
0,
|
|
797
|
-
e.target.value
|
|
798
|
-
);
|
|
647
|
+
type='number'
|
|
648
|
+
value={option.arguments[1]?.threshold || ''}
|
|
649
|
+
onChange={e => {
|
|
650
|
+
setDynamicArgument(index, 'threshold', e.target.value)
|
|
799
651
|
}}
|
|
800
652
|
/>
|
|
801
653
|
</div>
|
|
802
654
|
</div>
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
<div className="accordion__panel-col flex-grow">
|
|
806
|
-
<select
|
|
807
|
-
className="border-dashed text-center"
|
|
808
|
-
value={
|
|
809
|
-
option.secondArgument
|
|
810
|
-
? "and"
|
|
811
|
-
: "then"
|
|
812
|
-
}
|
|
813
|
-
onChange={(e) => {
|
|
814
|
-
if ("then" === e.target.value) {
|
|
815
|
-
updateDynamicImage(
|
|
816
|
-
"secondArgument",
|
|
817
|
-
index,
|
|
818
|
-
null,
|
|
819
|
-
false
|
|
820
|
-
);
|
|
821
|
-
removeDynamicArgument(index);
|
|
822
|
-
}
|
|
823
|
-
if ("and" === e.target.value) {
|
|
824
|
-
updateDynamicImage(
|
|
825
|
-
"secondArgument",
|
|
826
|
-
index,
|
|
827
|
-
null,
|
|
828
|
-
true
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
}}
|
|
832
|
-
>
|
|
833
|
-
<option value={"then"}>
|
|
834
|
-
Then
|
|
835
|
-
</option>
|
|
836
|
-
<option value={"and"}>And</option>
|
|
837
|
-
</select>
|
|
838
|
-
</div>
|
|
655
|
+
<div className='accordion__panel-row mb-2 align-center text-center text-capitalize'>
|
|
656
|
+
<div className='accordion__panel-col flex-grow'>Then</div>
|
|
839
657
|
</div>
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
<div className="accordion__panel-col flex-grow flex-shrink">
|
|
876
|
-
<input
|
|
877
|
-
type="number"
|
|
878
|
-
value={
|
|
879
|
-
option.arguments[1]
|
|
880
|
-
?.threshold || ""
|
|
881
|
-
}
|
|
882
|
-
onChange={(e) => {
|
|
883
|
-
setDynamicArgument(
|
|
884
|
-
index,
|
|
885
|
-
"threshold",
|
|
886
|
-
e.target.value
|
|
887
|
-
);
|
|
888
|
-
}}
|
|
889
|
-
/>
|
|
890
|
-
</div>
|
|
891
|
-
</div>
|
|
892
|
-
<div className="accordion__panel-row mb-2 align-center text-center text-capitalize">
|
|
893
|
-
<div className="accordion__panel-col flex-grow">
|
|
894
|
-
Then
|
|
895
|
-
</div>
|
|
896
|
-
</div>
|
|
897
|
-
</>
|
|
898
|
-
)}
|
|
899
|
-
|
|
900
|
-
<div className="accordion__panel-row mb-2 align-center">
|
|
901
|
-
<div className="accordion__panel-col flex-auto">
|
|
902
|
-
Show
|
|
903
|
-
</div>
|
|
904
|
-
<div className="accordion__panel-col flex-grow">
|
|
905
|
-
<input
|
|
906
|
-
type="text"
|
|
907
|
-
value={option.source || ""}
|
|
908
|
-
onChange={(e) => {
|
|
909
|
-
updateDynamicImage(
|
|
910
|
-
"source",
|
|
911
|
-
index,
|
|
912
|
-
null,
|
|
913
|
-
e.target.value
|
|
914
|
-
);
|
|
915
|
-
}}
|
|
916
|
-
/>
|
|
917
|
-
</div>
|
|
918
|
-
</div>
|
|
919
|
-
|
|
920
|
-
<div className="accordion__panel-row mb-2 align-center">
|
|
921
|
-
<div className="accordion__panel-col flex-auto">
|
|
922
|
-
Alt Text
|
|
923
|
-
</div>
|
|
924
|
-
<div className="accordion__panel-col flex-grow">
|
|
925
|
-
<input
|
|
926
|
-
type="text"
|
|
927
|
-
value={option.alt || ""}
|
|
928
|
-
onChange={(e) => {
|
|
929
|
-
updateDynamicImage(
|
|
930
|
-
"alt",
|
|
931
|
-
index,
|
|
932
|
-
null,
|
|
933
|
-
e.target.value
|
|
934
|
-
);
|
|
935
|
-
}}
|
|
936
|
-
/>
|
|
937
|
-
</div>
|
|
938
|
-
</div>
|
|
939
|
-
</label>
|
|
940
|
-
</fieldset>
|
|
941
|
-
)
|
|
942
|
-
)}
|
|
943
|
-
</ul>
|
|
944
|
-
</>
|
|
945
|
-
)}
|
|
946
|
-
<button
|
|
947
|
-
type="button"
|
|
948
|
-
onClick={addDynamicImage}
|
|
949
|
-
className="btn full-width"
|
|
950
|
-
>
|
|
658
|
+
</>
|
|
659
|
+
)}
|
|
660
|
+
|
|
661
|
+
<div className='accordion__panel-row mb-2 align-center'>
|
|
662
|
+
<div className='accordion__panel-col flex-auto'>Show</div>
|
|
663
|
+
<div className='accordion__panel-col flex-grow'>
|
|
664
|
+
<input
|
|
665
|
+
type='text'
|
|
666
|
+
value={option.source || ''}
|
|
667
|
+
onChange={e => {
|
|
668
|
+
updateDynamicImage('source', index, null, e.target.value)
|
|
669
|
+
}}
|
|
670
|
+
/>
|
|
671
|
+
</div>
|
|
672
|
+
</div>
|
|
673
|
+
|
|
674
|
+
<div className='accordion__panel-row mb-2 align-center'>
|
|
675
|
+
<div className='accordion__panel-col flex-auto'>Alt Text</div>
|
|
676
|
+
<div className='accordion__panel-col flex-grow'>
|
|
677
|
+
<input
|
|
678
|
+
type='text'
|
|
679
|
+
value={option.alt || ''}
|
|
680
|
+
onChange={e => {
|
|
681
|
+
updateDynamicImage('alt', index, null, e.target.value)
|
|
682
|
+
}}
|
|
683
|
+
/>
|
|
684
|
+
</div>
|
|
685
|
+
</div>
|
|
686
|
+
</label>
|
|
687
|
+
</fieldset>
|
|
688
|
+
))}
|
|
689
|
+
</ul>
|
|
690
|
+
</>
|
|
691
|
+
)}
|
|
692
|
+
<button type='button' onClick={addDynamicImage} className='btn full-width'>
|
|
951
693
|
Add Dynamic Image
|
|
952
694
|
</button>
|
|
953
695
|
</>
|
|
@@ -960,7 +702,7 @@ const EditorPanel = memo(() => {
|
|
|
960
702
|
</section>
|
|
961
703
|
</section>
|
|
962
704
|
</ErrorBoundary>
|
|
963
|
-
)
|
|
705
|
+
)
|
|
964
706
|
})
|
|
965
707
|
|
|
966
|
-
export default EditorPanel
|
|
708
|
+
export default EditorPanel
|