@jbrowse/plugin-config 2.0.0 → 2.1.2

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.
Files changed (86) hide show
  1. package/dist/ConfigurationEditorWidget/components/CallbackEditor.js +49 -57
  2. package/dist/ConfigurationEditorWidget/components/CallbackEditor.js.map +1 -1
  3. package/dist/ConfigurationEditorWidget/components/ColorEditor.js +14 -56
  4. package/dist/ConfigurationEditorWidget/components/ColorEditor.js.map +1 -1
  5. package/dist/ConfigurationEditorWidget/components/ConfigurationEditor.js +37 -74
  6. package/dist/ConfigurationEditorWidget/components/ConfigurationEditor.js.map +1 -1
  7. package/dist/ConfigurationEditorWidget/components/JsonEditor.d.ts +9 -7
  8. package/dist/ConfigurationEditorWidget/components/JsonEditor.js +42 -43
  9. package/dist/ConfigurationEditorWidget/components/JsonEditor.js.map +1 -1
  10. package/dist/ConfigurationEditorWidget/components/SlotEditor.d.ts +7 -5
  11. package/dist/ConfigurationEditorWidget/components/SlotEditor.js +97 -142
  12. package/dist/ConfigurationEditorWidget/components/SlotEditor.js.map +1 -1
  13. package/dist/ConfigurationEditorWidget/components/StringArrayEditor.js +15 -32
  14. package/dist/ConfigurationEditorWidget/components/StringArrayEditor.js.map +1 -1
  15. package/dist/ConfigurationEditorWidget/components/TypeSelector.js +7 -8
  16. package/dist/ConfigurationEditorWidget/components/TypeSelector.js.map +1 -1
  17. package/dist/ConfigurationEditorWidget/index.d.ts +10 -3
  18. package/dist/ConfigurationEditorWidget/index.js +9 -9
  19. package/dist/ConfigurationEditorWidget/index.js.map +1 -1
  20. package/dist/ConfigurationEditorWidget/model.d.ts +3 -2
  21. package/dist/ConfigurationEditorWidget/model.js +16 -17
  22. package/dist/ConfigurationEditorWidget/model.js.map +1 -1
  23. package/dist/FromConfigAdapter/FromConfigAdapter.js +44 -158
  24. package/dist/FromConfigAdapter/FromConfigAdapter.js.map +1 -1
  25. package/dist/FromConfigAdapter/FromConfigRegionsAdapter.js +37 -144
  26. package/dist/FromConfigAdapter/FromConfigRegionsAdapter.js.map +1 -1
  27. package/dist/FromConfigAdapter/FromConfigSequenceAdapter.d.ts +2 -1
  28. package/dist/FromConfigAdapter/FromConfigSequenceAdapter.js +48 -179
  29. package/dist/FromConfigAdapter/FromConfigSequenceAdapter.js.map +1 -1
  30. package/dist/FromConfigAdapter/configSchema.js +1 -1
  31. package/dist/FromConfigAdapter/configSchema.js.map +1 -1
  32. package/dist/RefNameAliasAdapter/RefNameAliasAdapter.js +22 -108
  33. package/dist/RefNameAliasAdapter/RefNameAliasAdapter.js.map +1 -1
  34. package/dist/RefNameAliasAdapter/configSchema.js +1 -1
  35. package/dist/RefNameAliasAdapter/configSchema.js.map +1 -1
  36. package/dist/index.js +60 -94
  37. package/dist/index.js.map +1 -1
  38. package/esm/ConfigurationEditorWidget/components/CallbackEditor.js +28 -19
  39. package/esm/ConfigurationEditorWidget/components/CallbackEditor.js.map +1 -1
  40. package/esm/ConfigurationEditorWidget/components/ColorEditor.js +6 -21
  41. package/esm/ConfigurationEditorWidget/components/ColorEditor.js.map +1 -1
  42. package/esm/ConfigurationEditorWidget/components/ConfigurationEditor.js +6 -2
  43. package/esm/ConfigurationEditorWidget/components/ConfigurationEditor.js.map +1 -1
  44. package/esm/ConfigurationEditorWidget/components/JsonEditor.d.ts +9 -7
  45. package/esm/ConfigurationEditorWidget/components/JsonEditor.js +38 -22
  46. package/esm/ConfigurationEditorWidget/components/JsonEditor.js.map +1 -1
  47. package/esm/ConfigurationEditorWidget/components/SlotEditor.d.ts +7 -5
  48. package/esm/ConfigurationEditorWidget/components/SlotEditor.js +33 -27
  49. package/esm/ConfigurationEditorWidget/components/SlotEditor.js.map +1 -1
  50. package/esm/ConfigurationEditorWidget/index.d.ts +10 -3
  51. package/esm/ConfigurationEditorWidget/index.js +4 -3
  52. package/esm/ConfigurationEditorWidget/index.js.map +1 -1
  53. package/esm/ConfigurationEditorWidget/model.d.ts +3 -2
  54. package/esm/ConfigurationEditorWidget/model.js +2 -1
  55. package/esm/ConfigurationEditorWidget/model.js.map +1 -1
  56. package/esm/FromConfigAdapter/FromConfigSequenceAdapter.d.ts +2 -1
  57. package/esm/FromConfigAdapter/FromConfigSequenceAdapter.js +1 -9
  58. package/esm/FromConfigAdapter/FromConfigSequenceAdapter.js.map +1 -1
  59. package/package.json +4 -6
  60. package/src/ConfigurationEditorWidget/components/CallbackEditor.tsx +44 -45
  61. package/src/ConfigurationEditorWidget/components/ColorEditor.tsx +6 -28
  62. package/src/ConfigurationEditorWidget/components/{ConfigurationEditor.test.js → ConfigurationEditor.test.tsx} +0 -0
  63. package/src/ConfigurationEditorWidget/components/ConfigurationEditor.tsx +6 -2
  64. package/src/ConfigurationEditorWidget/components/JsonEditor.tsx +84 -0
  65. package/src/ConfigurationEditorWidget/components/SlotEditor.tsx +484 -0
  66. package/src/ConfigurationEditorWidget/components/__snapshots__/{ConfigurationEditor.test.js.snap → ConfigurationEditor.test.tsx.snap} +186 -167
  67. package/src/ConfigurationEditorWidget/index.tsx +25 -0
  68. package/src/ConfigurationEditorWidget/{model.js → model.ts} +4 -2
  69. package/src/FromConfigAdapter/FromConfigSequenceAdapter.ts +6 -10
  70. package/dist/ConfigurationEditorWidget/components/CodeEditor.d.ts +0 -5
  71. package/dist/ConfigurationEditorWidget/components/CodeEditor.js +0 -85
  72. package/dist/ConfigurationEditorWidget/components/CodeEditor.js.map +0 -1
  73. package/dist/ConfigurationEditorWidget/components/ColorPicker.d.ts +0 -7
  74. package/dist/ConfigurationEditorWidget/components/ColorPicker.js +0 -69
  75. package/dist/ConfigurationEditorWidget/components/ColorPicker.js.map +0 -1
  76. package/esm/ConfigurationEditorWidget/components/CodeEditor.d.ts +0 -5
  77. package/esm/ConfigurationEditorWidget/components/CodeEditor.js +0 -42
  78. package/esm/ConfigurationEditorWidget/components/CodeEditor.js.map +0 -1
  79. package/esm/ConfigurationEditorWidget/components/ColorPicker.d.ts +0 -7
  80. package/esm/ConfigurationEditorWidget/components/ColorPicker.js +0 -26
  81. package/esm/ConfigurationEditorWidget/components/ColorPicker.js.map +0 -1
  82. package/src/ConfigurationEditorWidget/components/CodeEditor.tsx +0 -59
  83. package/src/ConfigurationEditorWidget/components/ColorPicker.tsx +0 -43
  84. package/src/ConfigurationEditorWidget/components/JsonEditor.js +0 -52
  85. package/src/ConfigurationEditorWidget/components/SlotEditor.js +0 -376
  86. package/src/ConfigurationEditorWidget/index.js +0 -20
@@ -0,0 +1,484 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import { getPropertyMembers, getEnv, IAnyType } from 'mobx-state-tree'
4
+ import { FileSelector, SanitizedHTML } from '@jbrowse/core/ui'
5
+ import {
6
+ getPropertyType,
7
+ getSubType,
8
+ getUnionSubTypes,
9
+ } from '@jbrowse/core/util/mst-reflection'
10
+ import {
11
+ Card,
12
+ CardContent,
13
+ CardHeader,
14
+ Checkbox,
15
+ FormControl,
16
+ FormControlLabel,
17
+ FormHelperText,
18
+ IconButton,
19
+ InputAdornment,
20
+ InputLabel,
21
+ MenuItem,
22
+ Paper,
23
+ SvgIcon,
24
+ TextField,
25
+ TextFieldProps,
26
+ } from '@mui/material'
27
+ import { makeStyles } from 'tss-react/mui'
28
+
29
+ // icons
30
+ import DeleteIcon from '@mui/icons-material/Delete'
31
+ import AddIcon from '@mui/icons-material/Add'
32
+ import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked'
33
+
34
+ // locals
35
+ import StringArrayEditor from './StringArrayEditor'
36
+ import CallbackEditor from './CallbackEditor'
37
+ import ColorEditor from './ColorEditor'
38
+ import JsonEditor from './JsonEditor'
39
+ import { FileLocation } from '@jbrowse/core/util'
40
+
41
+ // adds ability to have html in helperText. note that FormHelperTextProps is
42
+ // div because the default is p which does not like div children
43
+ function MyTextField(props: { helperText?: string } & TextFieldProps) {
44
+ const { helperText } = props
45
+ return (
46
+ <TextField
47
+ {...props}
48
+ helperText={<SanitizedHTML html={helperText || ''} />}
49
+ FormHelperTextProps={{
50
+ // @ts-ignore
51
+ component: 'div',
52
+ }}
53
+ fullWidth
54
+ />
55
+ )
56
+ }
57
+
58
+ const StringEditor = observer(
59
+ ({
60
+ slot,
61
+ }: {
62
+ slot: {
63
+ name: string
64
+ description: string
65
+ value: string
66
+ set: (arg: string) => void
67
+ }
68
+ }) => (
69
+ <MyTextField
70
+ label={slot.name}
71
+ helperText={slot.description}
72
+ value={slot.value}
73
+ onChange={evt => slot.set(evt.target.value)}
74
+ />
75
+ ),
76
+ )
77
+
78
+ const TextEditor = observer(
79
+ ({
80
+ slot,
81
+ }: {
82
+ slot: {
83
+ name: string
84
+ description: string
85
+ value: string
86
+ set: (arg: string) => void
87
+ }
88
+ }) => (
89
+ <TextField
90
+ label={slot.name}
91
+ helperText={slot.description}
92
+ multiline
93
+ value={slot.value}
94
+ onChange={evt => slot.set(evt.target.value)}
95
+ />
96
+ ),
97
+ )
98
+
99
+ // checked checkbox, looks like a styled (x)
100
+ const SvgCheckbox = () => (
101
+ <SvgIcon>
102
+ <path d="M20.41,3C21.8,5.71 22.35,8.84 22,12C21.8,15.16 20.7,18.29 18.83,21L17.3,20C18.91,17.57 19.85,14.8 20,12C20.34,9.2 19.89,6.43 18.7,4L20.41,3M5.17,3L6.7,4C5.09,6.43 4.15,9.2 4,12C3.66,14.8 4.12,17.57 5.3,20L3.61,21C2.21,18.29 1.65,15.17 2,12C2.2,8.84 3.3,5.71 5.17,3M12.08,10.68L14.4,7.45H16.93L13.15,12.45L15.35,17.37H13.09L11.71,14L9.28,17.33H6.76L10.66,12.21L8.53,7.45H10.8L12.08,10.68Z" />
103
+ </SvgIcon>
104
+ )
105
+
106
+ const useMapEditorStyles = makeStyles()(theme => ({
107
+ card: {
108
+ marginTop: theme.spacing(1),
109
+ },
110
+ }))
111
+
112
+ const StringArrayMapEditor = observer(
113
+ ({
114
+ slot,
115
+ }: {
116
+ slot: {
117
+ name: string
118
+ value: Map<string, string[]>
119
+ remove: (key: string) => void
120
+ add: (key: string, val: string[]) => void
121
+ description: string
122
+ setAtKeyIndex: (key: string, idx: number, val: string) => void
123
+ removeAtKeyIndex: (key: string, idx: number) => void
124
+ addToKey: (key: string, val: string) => void
125
+ }
126
+ }) => {
127
+ const { classes } = useMapEditorStyles()
128
+ const [value, setValue] = useState('')
129
+ return (
130
+ <>
131
+ <InputLabel>{slot.name}</InputLabel>
132
+ {Array.from(slot.value, ([key, val]) => (
133
+ <Card raised key={key} className={classes.card}>
134
+ <CardHeader
135
+ title={key}
136
+ action={
137
+ <IconButton color="secondary" onClick={() => slot.remove(key)}>
138
+ <DeleteIcon />
139
+ </IconButton>
140
+ }
141
+ />
142
+ <CardContent>
143
+ <StringArrayEditor
144
+ slot={{
145
+ name: slot.name,
146
+ value: val,
147
+ description: `Values associated with entry ${key}`,
148
+ setAtIndex: (idx: number, val: string) =>
149
+ slot.setAtKeyIndex(key, idx, val),
150
+ removeAtIndex: (idx: number) =>
151
+ slot.removeAtKeyIndex(key, idx),
152
+ add: (val: string) => slot.addToKey(key, val),
153
+ }}
154
+ />
155
+ </CardContent>
156
+ </Card>
157
+ ))}
158
+ <Card raised className={classes.card}>
159
+ <CardHeader
160
+ disableTypography
161
+ title={
162
+ <TextField
163
+ fullWidth
164
+ value={value}
165
+ placeholder="add new"
166
+ onChange={event => setValue(event.target.value)}
167
+ InputProps={{
168
+ endAdornment: (
169
+ <InputAdornment position="end">
170
+ <IconButton
171
+ disabled={value === ''}
172
+ onClick={() => {
173
+ slot.add(value, [])
174
+ setValue('')
175
+ }}
176
+ color="secondary"
177
+ >
178
+ <AddIcon />
179
+ </IconButton>
180
+ </InputAdornment>
181
+ ),
182
+ }}
183
+ />
184
+ }
185
+ />
186
+ </Card>
187
+ <FormHelperText>{slot.description}</FormHelperText>
188
+ </>
189
+ )
190
+ },
191
+ )
192
+
193
+ const NumberMapEditor = observer(
194
+ ({
195
+ slot,
196
+ }: {
197
+ slot: {
198
+ name: string
199
+ value: Map<string, string>
200
+ remove: (key: string) => void
201
+ add: (key: string, val: number) => void
202
+ description: string
203
+ }
204
+ }) => {
205
+ const { classes } = useMapEditorStyles()
206
+ const [value, setValue] = useState('')
207
+ return (
208
+ <>
209
+ <InputLabel>{slot.name}</InputLabel>
210
+ {Array.from(slot.value, ([key, val]) => (
211
+ <Card raised key={key} className={classes.card}>
212
+ <CardHeader
213
+ title={key}
214
+ action={
215
+ <IconButton color="secondary" onClick={() => slot.remove(key)}>
216
+ <DeleteIcon />
217
+ </IconButton>
218
+ }
219
+ />
220
+ <CardContent>
221
+ <NumberEditor
222
+ slot={{
223
+ value: val,
224
+ set: (val: number) => slot.add(key, val),
225
+ }}
226
+ />
227
+ </CardContent>
228
+ </Card>
229
+ ))}
230
+ <Card raised className={classes.card}>
231
+ <CardHeader
232
+ disableTypography
233
+ title={
234
+ <TextField
235
+ fullWidth
236
+ value={value}
237
+ placeholder="add new"
238
+ onChange={event => setValue(event.target.value)}
239
+ InputProps={{
240
+ endAdornment: (
241
+ <InputAdornment position="end">
242
+ <IconButton
243
+ disabled={value === ''}
244
+ onClick={() => {
245
+ slot.add(value, 0)
246
+ setValue('')
247
+ }}
248
+ color="secondary"
249
+ >
250
+ <AddIcon />
251
+ </IconButton>
252
+ </InputAdornment>
253
+ ),
254
+ }}
255
+ />
256
+ }
257
+ />
258
+ </Card>
259
+ <FormHelperText>{slot.description}</FormHelperText>
260
+ </>
261
+ )
262
+ },
263
+ )
264
+
265
+ const NumberEditor = observer(
266
+ ({
267
+ slot,
268
+ }: {
269
+ slot: {
270
+ name?: string
271
+ value: string
272
+ description?: string
273
+ set: (val: number) => void
274
+ reset?: () => void
275
+ }
276
+ }) => {
277
+ const [val, setVal] = useState(slot.value)
278
+ useEffect(() => {
279
+ const num = parseFloat(val)
280
+ if (!Number.isNaN(num)) {
281
+ slot.set(num)
282
+ } else {
283
+ slot.reset?.()
284
+ }
285
+ }, [slot, val])
286
+ return (
287
+ <MyTextField
288
+ label={slot.name}
289
+ helperText={slot.description}
290
+ value={val}
291
+ type="number"
292
+ onChange={evt => setVal(evt.target.value)}
293
+ />
294
+ )
295
+ },
296
+ )
297
+
298
+ const IntegerEditor = observer(
299
+ ({
300
+ slot,
301
+ }: {
302
+ slot: {
303
+ name: string
304
+ value: string
305
+ description: string
306
+ set: (num: number) => void
307
+ }
308
+ }) => {
309
+ const [val, setVal] = useState(slot.value)
310
+ useEffect(() => {
311
+ const num = parseInt(val, 10)
312
+ if (!Number.isNaN(num)) {
313
+ slot.set(num)
314
+ }
315
+ }, [slot, val])
316
+ return (
317
+ <MyTextField
318
+ label={slot.name}
319
+ helperText={slot.description}
320
+ value={val}
321
+ type="number"
322
+ onChange={evt => setVal(evt.target.value)}
323
+ />
324
+ )
325
+ },
326
+ )
327
+
328
+ const BooleanEditor = observer(
329
+ ({
330
+ slot,
331
+ }: {
332
+ slot: {
333
+ name: string
334
+ value: boolean
335
+ set: (arg: boolean) => void
336
+ description: string
337
+ }
338
+ }) => (
339
+ <FormControl>
340
+ <FormControlLabel
341
+ label={slot.name}
342
+ control={
343
+ <Checkbox
344
+ checked={slot.value}
345
+ onChange={evt => slot.set(evt.target.checked)}
346
+ />
347
+ }
348
+ />
349
+ <FormHelperText>{slot.description}</FormHelperText>
350
+ </FormControl>
351
+ ),
352
+ )
353
+
354
+ const StringEnumEditor = observer(
355
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
356
+ ({ slot, slotSchema }: { slot: any; slotSchema: IAnyType }) => {
357
+ const p = getPropertyMembers(getSubType(slotSchema))
358
+ const choices = getUnionSubTypes(
359
+ getUnionSubTypes(getSubType(getPropertyType(p, 'value')))[1],
360
+ ).map(t => t.value)
361
+
362
+ return (
363
+ <MyTextField
364
+ value={slot.value}
365
+ label={slot.name}
366
+ select
367
+ helperText={slot.description}
368
+ onChange={evt => slot.set(evt.target.value)}
369
+ >
370
+ {choices.map(str => (
371
+ <MenuItem key={str} value={str}>
372
+ {str}
373
+ </MenuItem>
374
+ ))}
375
+ </MyTextField>
376
+ )
377
+ },
378
+ )
379
+
380
+ const FileSelectorWrapper = observer(
381
+ ({
382
+ slot,
383
+ }: {
384
+ slot: {
385
+ name: string
386
+ value: FileLocation
387
+ set: (arg: FileLocation) => void
388
+ description: string
389
+ }
390
+ }) => {
391
+ return (
392
+ <FileSelector
393
+ location={slot.value}
394
+ setLocation={location => slot.set(location)}
395
+ name={slot.name}
396
+ description={slot.description}
397
+ rootModel={getEnv(slot).pluginManager?.rootModel}
398
+ />
399
+ )
400
+ },
401
+ )
402
+
403
+ const valueComponents = {
404
+ string: StringEditor,
405
+ text: TextEditor,
406
+ fileLocation: FileSelectorWrapper,
407
+ stringArray: StringArrayEditor,
408
+ stringArrayMap: StringArrayMapEditor,
409
+ numberMap: NumberMapEditor,
410
+ number: NumberEditor,
411
+ integer: IntegerEditor,
412
+ color: ColorEditor,
413
+ stringEnum: StringEnumEditor,
414
+ boolean: BooleanEditor,
415
+ frozen: JsonEditor,
416
+ configRelationships: JsonEditor,
417
+ }
418
+
419
+ export const useSlotEditorStyles = makeStyles()(theme => ({
420
+ paper: {
421
+ display: 'flex',
422
+ marginBottom: theme.spacing(2),
423
+ position: 'relative',
424
+ },
425
+ paperContent: {
426
+ width: '100%',
427
+ },
428
+ slotModeSwitch: {
429
+ width: 24,
430
+ background: theme.palette.secondary.light,
431
+ display: 'flex',
432
+ justifyContent: 'center',
433
+ alignItems: 'center',
434
+ },
435
+ }))
436
+
437
+ const SlotEditor = observer(
438
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
439
+ ({ slot, slotSchema }: { slot: any; slotSchema: IAnyType }) => {
440
+ const { classes } = useSlotEditorStyles()
441
+ const { type } = slot
442
+ let ValueComponent = slot.isCallback
443
+ ? CallbackEditor
444
+ : // @ts-ignore
445
+ valueComponents[type]
446
+ if (!ValueComponent) {
447
+ console.warn(`no slot editor defined for ${type}, editing as string`)
448
+ ValueComponent = StringEditor
449
+ }
450
+ if (!(type in valueComponents)) {
451
+ console.warn(`SlotEditor needs to implement ${type}`)
452
+ }
453
+ return (
454
+ <Paper className={classes.paper}>
455
+ <div className={classes.paperContent}>
456
+ <ValueComponent slot={slot} slotSchema={slotSchema} />
457
+ </div>
458
+ <div className={classes.slotModeSwitch}>
459
+ {slot.contextVariable.length ? (
460
+ <IconButton
461
+ onClick={() =>
462
+ slot.isCallback
463
+ ? slot.convertToValue()
464
+ : slot.convertToCallback()
465
+ }
466
+ title={`convert to ${
467
+ slot.isCallback ? 'regular value' : 'callback'
468
+ }`}
469
+ color="secondary"
470
+ >
471
+ {!slot.isCallback ? (
472
+ <RadioButtonUncheckedIcon />
473
+ ) : (
474
+ <SvgCheckbox />
475
+ )}
476
+ </IconButton>
477
+ ) : null}
478
+ </div>
479
+ </Paper>
480
+ )
481
+ },
482
+ )
483
+
484
+ export default SlotEditor