@jbrowse/plugin-linear-genome-view 2.13.0 → 2.14.0
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/dist/BaseLinearDisplay/components/LinearBlocks.js +2 -2
- package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +3 -1
- package/dist/BaseLinearDisplay/components/Tooltip.js +1 -1
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +1 -1
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +8 -5
- package/dist/BaseLinearDisplay/models/FeatureDensityMixin.js +2 -3
- package/dist/BaseLinearDisplay/models/renderSvg.d.ts +1 -1
- package/dist/BaseLinearDisplay/models/renderSvg.js +3 -1
- package/dist/BaseLinearDisplay/models/util.d.ts +1 -1
- package/dist/BaseLinearDisplay/models/util.js +3 -5
- package/dist/LaunchLinearGenomeView/index.js +12 -11
- package/dist/LinearBareDisplay/model.d.ts +1 -1
- package/dist/LinearBasicDisplay/components/AddFiltersDialog.js +9 -3
- package/dist/LinearBasicDisplay/components/SetMaxHeightDialog.js +6 -2
- package/dist/LinearBasicDisplay/model.d.ts +2 -4
- package/dist/LinearBasicDisplay/model.js +9 -3
- package/dist/LinearGenomeView/components/Cytobands.js +6 -23
- package/dist/LinearGenomeView/components/ExportSvgDialog.js +16 -6
- package/dist/LinearGenomeView/components/GetSequenceDialog.js +13 -16
- package/dist/LinearGenomeView/components/Header.js +6 -2
- package/dist/LinearGenomeView/components/Highlight.js +24 -25
- package/dist/LinearGenomeView/components/ImportForm.js +5 -2
- package/dist/LinearGenomeView/components/ImportFormRefNameAutocomplete.js +5 -1
- package/dist/LinearGenomeView/components/LinearGenomeView.d.ts +2 -3
- package/dist/LinearGenomeView/components/LinearGenomeView.js +5 -3
- package/dist/LinearGenomeView/components/MiniControls.js +6 -2
- package/dist/LinearGenomeView/components/OverviewHighlight.js +23 -30
- package/dist/LinearGenomeView/components/OverviewRubberband.js +1 -1
- package/dist/LinearGenomeView/components/OverviewScalebar.js +6 -11
- package/dist/LinearGenomeView/components/OverviewScalebarPolygon.js +1 -2
- package/dist/LinearGenomeView/components/RefNameAutocomplete/AutocompleteTextField.js +6 -5
- package/dist/LinearGenomeView/components/RefNameAutocomplete/EndAdornment.js +6 -2
- package/dist/LinearGenomeView/components/RefNameAutocomplete/HelpDialog.js +3 -1
- package/dist/LinearGenomeView/components/RefNameAutocomplete/index.js +9 -7
- package/dist/LinearGenomeView/components/RubberbandSpan.js +3 -5
- package/dist/LinearGenomeView/components/Scalebar.js +2 -1
- package/dist/LinearGenomeView/components/SearchResultsDialog.js +3 -1
- package/dist/LinearGenomeView/components/SearchResultsTable.js +2 -3
- package/dist/LinearGenomeView/components/SequenceSearchDialog.js +15 -5
- package/dist/LinearGenomeView/components/TrackContainer.js +2 -2
- package/dist/LinearGenomeView/components/TrackLabel.js +15 -5
- package/dist/LinearGenomeView/components/TrackLabelDragHandle.js +3 -1
- package/dist/LinearGenomeView/components/TracksContainer.js +1 -1
- package/dist/LinearGenomeView/components/ZoomControls.js +10 -4
- package/dist/LinearGenomeView/components/hooks.d.ts +2 -2
- package/dist/LinearGenomeView/components/hooks.js +24 -26
- package/dist/LinearGenomeView/components/util.d.ts +2 -2
- package/dist/LinearGenomeView/model.d.ts +24 -13
- package/dist/LinearGenomeView/model.js +75 -46
- package/dist/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +0 -1
- package/dist/LinearGenomeView/svgcomponents/SVGRuler.js +1 -1
- package/dist/LinearGenomeView/util.d.ts +1 -1
- package/dist/LinearGenomeView/util.js +4 -9
- package/dist/index.d.ts +3 -414
- package/dist/searchUtils.js +4 -6
- package/esm/BaseLinearDisplay/components/LinearBlocks.js +2 -2
- package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +3 -1
- package/esm/BaseLinearDisplay/components/Tooltip.js +1 -1
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +1 -1
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +8 -5
- package/esm/BaseLinearDisplay/models/FeatureDensityMixin.js +2 -3
- package/esm/BaseLinearDisplay/models/renderSvg.d.ts +1 -1
- package/esm/BaseLinearDisplay/models/renderSvg.js +3 -1
- package/esm/BaseLinearDisplay/models/util.d.ts +1 -1
- package/esm/BaseLinearDisplay/models/util.js +3 -5
- package/esm/LaunchLinearGenomeView/index.js +13 -12
- package/esm/LinearBareDisplay/model.d.ts +1 -1
- package/esm/LinearBasicDisplay/components/AddFiltersDialog.js +9 -3
- package/esm/LinearBasicDisplay/components/SetMaxHeightDialog.js +6 -2
- package/esm/LinearBasicDisplay/model.d.ts +2 -4
- package/esm/LinearBasicDisplay/model.js +9 -3
- package/esm/LinearGenomeView/components/Cytobands.js +6 -23
- package/esm/LinearGenomeView/components/ExportSvgDialog.js +16 -6
- package/esm/LinearGenomeView/components/GetSequenceDialog.js +13 -16
- package/esm/LinearGenomeView/components/Header.js +6 -2
- package/esm/LinearGenomeView/components/Highlight.js +26 -27
- package/esm/LinearGenomeView/components/ImportForm.js +5 -2
- package/esm/LinearGenomeView/components/ImportFormRefNameAutocomplete.js +5 -1
- package/esm/LinearGenomeView/components/LinearGenomeView.d.ts +2 -3
- package/esm/LinearGenomeView/components/LinearGenomeView.js +5 -3
- package/esm/LinearGenomeView/components/MiniControls.js +6 -2
- package/esm/LinearGenomeView/components/OverviewHighlight.js +24 -31
- package/esm/LinearGenomeView/components/OverviewRubberband.js +1 -1
- package/esm/LinearGenomeView/components/OverviewScalebar.js +6 -11
- package/esm/LinearGenomeView/components/OverviewScalebarPolygon.js +1 -2
- package/esm/LinearGenomeView/components/RefNameAutocomplete/AutocompleteTextField.js +6 -5
- package/esm/LinearGenomeView/components/RefNameAutocomplete/EndAdornment.js +6 -2
- package/esm/LinearGenomeView/components/RefNameAutocomplete/HelpDialog.js +3 -1
- package/esm/LinearGenomeView/components/RefNameAutocomplete/index.js +10 -8
- package/esm/LinearGenomeView/components/RubberbandSpan.js +3 -5
- package/esm/LinearGenomeView/components/Scalebar.js +2 -1
- package/esm/LinearGenomeView/components/SearchResultsDialog.js +3 -1
- package/esm/LinearGenomeView/components/SearchResultsTable.js +2 -3
- package/esm/LinearGenomeView/components/SequenceSearchDialog.js +15 -5
- package/esm/LinearGenomeView/components/TrackContainer.js +2 -2
- package/esm/LinearGenomeView/components/TrackLabel.js +15 -5
- package/esm/LinearGenomeView/components/TrackLabelDragHandle.js +3 -1
- package/esm/LinearGenomeView/components/TracksContainer.js +1 -1
- package/esm/LinearGenomeView/components/ZoomControls.js +10 -4
- package/esm/LinearGenomeView/components/hooks.d.ts +2 -2
- package/esm/LinearGenomeView/components/hooks.js +24 -26
- package/esm/LinearGenomeView/components/util.d.ts +2 -2
- package/esm/LinearGenomeView/model.d.ts +24 -13
- package/esm/LinearGenomeView/model.js +77 -48
- package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +0 -1
- package/esm/LinearGenomeView/svgcomponents/SVGRuler.js +1 -1
- package/esm/LinearGenomeView/util.d.ts +1 -1
- package/esm/LinearGenomeView/util.js +4 -9
- package/esm/index.d.ts +3 -414
- package/esm/searchUtils.js +4 -6
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { when, parseLocString
|
|
1
|
+
import { when, parseLocString } from '@jbrowse/core/util';
|
|
2
2
|
import { handleSelectedRegion } from '../searchUtils';
|
|
3
3
|
export default function LaunchLinearGenomeViewF(pluginManager) {
|
|
4
4
|
pluginManager.addToExtensionPoint('LaunchView-LinearGenomeView',
|
|
@@ -24,22 +24,23 @@ export default function LaunchLinearGenomeViewF(pluginManager) {
|
|
|
24
24
|
}
|
|
25
25
|
if (highlight !== undefined) {
|
|
26
26
|
highlight.forEach(async (h) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
const p = parseLocString(h, refName => isValidRefName(refName, assembly));
|
|
28
|
+
const { start, end } = p;
|
|
29
|
+
if (start !== undefined && end !== undefined) {
|
|
30
|
+
view.addToHighlights({
|
|
31
|
+
...p,
|
|
32
|
+
start,
|
|
33
|
+
end,
|
|
31
34
|
assemblyName: assembly,
|
|
32
|
-
};
|
|
33
|
-
if ((location === null || location === void 0 ? void 0 : location.start) !== undefined &&
|
|
34
|
-
(location === null || location === void 0 ? void 0 : location.end) !== undefined) {
|
|
35
|
-
view.addToHighlights(location);
|
|
36
|
-
}
|
|
35
|
+
});
|
|
37
36
|
}
|
|
38
37
|
});
|
|
39
38
|
}
|
|
40
39
|
await handleSelectedRegion({ input: loc, model: view, assembly: asm });
|
|
41
40
|
const idsNotFound = [];
|
|
42
|
-
tracks.forEach(track =>
|
|
41
|
+
tracks.forEach(track => {
|
|
42
|
+
tryTrack(view, track, idsNotFound);
|
|
43
|
+
});
|
|
43
44
|
if (idsNotFound.length) {
|
|
44
45
|
throw new Error(`Could not resolve identifiers: ${idsNotFound.join(',')}`);
|
|
45
46
|
}
|
|
@@ -55,7 +56,7 @@ function tryTrack(model, trackId, idsNotFound) {
|
|
|
55
56
|
model.showTrack(trackId);
|
|
56
57
|
}
|
|
57
58
|
catch (e) {
|
|
58
|
-
if (
|
|
59
|
+
if (/Could not resolve identifier/.exec(`${e}`)) {
|
|
59
60
|
idsNotFound.push(trackId);
|
|
60
61
|
}
|
|
61
62
|
else {
|
|
@@ -128,7 +128,7 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
|
|
|
128
128
|
error: unknown;
|
|
129
129
|
message: string | undefined;
|
|
130
130
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
131
|
-
onHorizontalScroll?:
|
|
131
|
+
onHorizontalScroll?: () => void;
|
|
132
132
|
blockState?: Record<string, any>;
|
|
133
133
|
}>;
|
|
134
134
|
readonly DisplayBlurb: import("react").FC<{
|
|
@@ -30,7 +30,9 @@ const AddFiltersDialog = observer(function ({ model, handleClose, }) {
|
|
|
30
30
|
.split('\n')
|
|
31
31
|
.map(line => line.trim())
|
|
32
32
|
.filter(line => !!line)
|
|
33
|
-
.map(line =>
|
|
33
|
+
.map(line => {
|
|
34
|
+
checkJexl(line.trim());
|
|
35
|
+
});
|
|
34
36
|
setError(undefined);
|
|
35
37
|
}
|
|
36
38
|
catch (e) {
|
|
@@ -54,7 +56,9 @@ const AddFiltersDialog = observer(function ({ model, handleClose, }) {
|
|
|
54
56
|
React.createElement("code", null, "jexl:get(feature,'score') > 400"),
|
|
55
57
|
" - show only features that have a score greater than 400"))),
|
|
56
58
|
error ? React.createElement("p", { className: classes.error }, `${error}`) : null,
|
|
57
|
-
React.createElement(TextField, { variant: "outlined", multiline: true, minRows: 5, maxRows: 10, className: classes.dialogContent, fullWidth: true, value: data, onChange: event =>
|
|
59
|
+
React.createElement(TextField, { variant: "outlined", multiline: true, minRows: 5, maxRows: 10, className: classes.dialogContent, fullWidth: true, value: data, onChange: event => {
|
|
60
|
+
setData(event.target.value);
|
|
61
|
+
}, InputProps: {
|
|
58
62
|
classes: {
|
|
59
63
|
input: classes.textAreaFont,
|
|
60
64
|
},
|
|
@@ -64,6 +68,8 @@ const AddFiltersDialog = observer(function ({ model, handleClose, }) {
|
|
|
64
68
|
model.setJexlFilters(data.split('\n'));
|
|
65
69
|
handleClose();
|
|
66
70
|
} }, "Submit"),
|
|
67
|
-
React.createElement(Button, { variant: "contained", color: "secondary", onClick: () =>
|
|
71
|
+
React.createElement(Button, { variant: "contained", color: "secondary", onClick: () => {
|
|
72
|
+
handleClose();
|
|
73
|
+
} }, "Cancel"))));
|
|
68
74
|
});
|
|
69
75
|
export default AddFiltersDialog;
|
|
@@ -15,12 +15,16 @@ const SetMaxHeightDialog = observer(function ({ model, handleClose, }) {
|
|
|
15
15
|
return (React.createElement(Dialog, { open: true, onClose: handleClose, title: "Set max height" },
|
|
16
16
|
React.createElement(DialogContent, { className: classes.root },
|
|
17
17
|
React.createElement(Typography, null, "Set max height for the track. For example, you can increase this if the layout says \"Max height reached\""),
|
|
18
|
-
React.createElement(TextField, { value: max, onChange: event =>
|
|
18
|
+
React.createElement(TextField, { value: max, onChange: event => {
|
|
19
|
+
setMax(event.target.value);
|
|
20
|
+
}, placeholder: "Enter max score" })),
|
|
19
21
|
React.createElement(DialogActions, null,
|
|
20
22
|
React.createElement(Button, { variant: "contained", color: "primary", type: "submit", autoFocus: true, onClick: () => {
|
|
21
23
|
model.setMaxHeight(max !== '' && !Number.isNaN(+max) ? +max : undefined);
|
|
22
24
|
handleClose();
|
|
23
25
|
} }, "Submit"),
|
|
24
|
-
React.createElement(Button, { variant: "contained", color: "secondary", onClick: () =>
|
|
26
|
+
React.createElement(Button, { variant: "contained", color: "secondary", onClick: () => {
|
|
27
|
+
handleClose();
|
|
28
|
+
} }, "Cancel"))));
|
|
25
29
|
});
|
|
26
30
|
export default SetMaxHeightDialog;
|
|
@@ -156,7 +156,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
156
156
|
error: unknown;
|
|
157
157
|
message: string | undefined;
|
|
158
158
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
159
|
-
onHorizontalScroll?:
|
|
159
|
+
onHorizontalScroll?: () => void;
|
|
160
160
|
blockState? /**
|
|
161
161
|
* #getter
|
|
162
162
|
*/: Record<string, any>;
|
|
@@ -294,9 +294,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
294
294
|
} & import("mobx-state-tree/dist/internal").NonEmptyObject & {
|
|
295
295
|
setSubschema(slotName: string, data: Record<string, unknown>): Record<string, unknown> | ({
|
|
296
296
|
[x: string]: any;
|
|
297
|
-
} & import("mobx-state-tree/dist/internal"
|
|
298
|
-
* #property
|
|
299
|
-
*/).NonEmptyObject & {
|
|
297
|
+
} & import("mobx-state-tree/dist/internal").NonEmptyObject & {
|
|
300
298
|
setSubschema(slotName: string, data: Record<string, unknown>): Record<string, unknown> | ({
|
|
301
299
|
[x: string]: any;
|
|
302
300
|
} & import("mobx-state-tree/dist/internal").NonEmptyObject & any & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>);
|
|
@@ -170,14 +170,18 @@ function stateModelFactory(configSchema) {
|
|
|
170
170
|
icon: VisibilityIcon,
|
|
171
171
|
type: 'checkbox',
|
|
172
172
|
checked: self.showLabels,
|
|
173
|
-
onClick: () =>
|
|
173
|
+
onClick: () => {
|
|
174
|
+
self.toggleShowLabels();
|
|
175
|
+
},
|
|
174
176
|
},
|
|
175
177
|
{
|
|
176
178
|
label: 'Show descriptions',
|
|
177
179
|
icon: VisibilityIcon,
|
|
178
180
|
type: 'checkbox',
|
|
179
181
|
checked: self.showDescriptions,
|
|
180
|
-
onClick: () =>
|
|
182
|
+
onClick: () => {
|
|
183
|
+
self.toggleShowDescriptions();
|
|
184
|
+
},
|
|
181
185
|
},
|
|
182
186
|
{
|
|
183
187
|
label: 'Display mode',
|
|
@@ -189,7 +193,9 @@ function stateModelFactory(configSchema) {
|
|
|
189
193
|
'collapse',
|
|
190
194
|
].map(val => ({
|
|
191
195
|
label: val,
|
|
192
|
-
onClick: () =>
|
|
196
|
+
onClick: () => {
|
|
197
|
+
self.setDisplayMode(val);
|
|
198
|
+
},
|
|
193
199
|
})),
|
|
194
200
|
},
|
|
195
201
|
{
|
|
@@ -5,26 +5,11 @@ import { HEADER_OVERVIEW_HEIGHT } from '..';
|
|
|
5
5
|
import { getCytobands } from './util';
|
|
6
6
|
import { getFillProps } from '@jbrowse/core/util';
|
|
7
7
|
// rounded rect from https://stackoverflow.com/a/45889603/2129219
|
|
8
|
-
// prettier-ignore
|
|
9
8
|
function rightRoundedRect(x, y, width, height, radius) {
|
|
10
|
-
return
|
|
11
|
-
+ "h" + (width - radius)
|
|
12
|
-
+ "a" + radius + "," + radius + " 0 0 1 " + radius + "," + radius
|
|
13
|
-
+ "v" + (height - 2 * radius)
|
|
14
|
-
+ "a" + radius + "," + radius + " 0 0 1 " + -radius + "," + radius
|
|
15
|
-
+ "h" + (radius - width)
|
|
16
|
-
+ "z";
|
|
9
|
+
return `M${x},${y}h${width - radius}a${radius},${radius} 0 0 1 ${radius},${radius}v${height - 2 * radius}a${radius},${radius} 0 0 1 ${-radius},${radius}h${radius - width}z`;
|
|
17
10
|
}
|
|
18
|
-
// prettier-ignore
|
|
19
11
|
function leftRoundedRect(x, y, width, height, radius) {
|
|
20
|
-
return
|
|
21
|
-
+ "h" + (width - radius)
|
|
22
|
-
+ "v" + height
|
|
23
|
-
+ "h" + (radius - width)
|
|
24
|
-
+ "a" + radius + "," + radius + " 0 0 1 " + (-radius) + "," + (-radius)
|
|
25
|
-
+ "v" + (2 * radius - height)
|
|
26
|
-
+ "a" + radius + "," + radius + " 0 0 1 " + radius + "," + (-radius)
|
|
27
|
-
+ "z";
|
|
12
|
+
return `M${x + radius},${y}h${width - radius}v${height}h${radius - width}a${radius},${radius} 0 0 1 ${-radius},${-radius}v${2 * radius - height}a${radius},${radius} 0 0 1 ${radius},${-radius}z`;
|
|
28
13
|
}
|
|
29
14
|
function leftTriangle(x, y, width, height) {
|
|
30
15
|
return [
|
|
@@ -71,20 +56,18 @@ const Cytobands = observer(function ({ overview, block, assembly, }) {
|
|
|
71
56
|
? rightTriangle(s - w, 0, w, h)
|
|
72
57
|
: leftTriangle(s, 0, w, h), ...getFillProps(c) }));
|
|
73
58
|
}
|
|
74
|
-
|
|
59
|
+
if (type === 'acen' && centromereSeen) {
|
|
75
60
|
return (React.createElement("polygon", { key: k, points: reversed
|
|
76
61
|
? leftTriangle(s - w, 0, w, h)
|
|
77
62
|
: rightTriangle(s, 0, w, h), ...getFillProps(c) }));
|
|
78
63
|
}
|
|
79
|
-
|
|
64
|
+
if (lcap === index) {
|
|
80
65
|
return (React.createElement("path", { key: k, d: leftRoundedRect(l, 0, w, h, 8), ...getFillProps(c) }));
|
|
81
66
|
}
|
|
82
|
-
|
|
67
|
+
if (rcap === index) {
|
|
83
68
|
return (React.createElement("path", { key: k, d: rightRoundedRect(l, 0, w, h, 8), ...getFillProps(c) }));
|
|
84
69
|
}
|
|
85
|
-
|
|
86
|
-
return (React.createElement("rect", { key: k, x: l, y: 0, width: w, height: h, ...getFillProps(c) }));
|
|
87
|
-
}
|
|
70
|
+
return (React.createElement("rect", { key: k, x: l, y: 0, width: w, height: h, ...getFillProps(c) }));
|
|
88
71
|
})));
|
|
89
72
|
});
|
|
90
73
|
export default Cytobands;
|
|
@@ -8,7 +8,7 @@ function LoadingMessage() {
|
|
|
8
8
|
React.createElement(Typography, { display: "inline" }, "Creating SVG")));
|
|
9
9
|
}
|
|
10
10
|
function useSvgLocal(key, val) {
|
|
11
|
-
return useLocalStorage(
|
|
11
|
+
return useLocalStorage(`svg-${key}`, val);
|
|
12
12
|
}
|
|
13
13
|
function TextField2({ children, ...rest }) {
|
|
14
14
|
return (React.createElement("div", null,
|
|
@@ -26,18 +26,28 @@ export default function ExportSvgDialog({ model, handleClose, }) {
|
|
|
26
26
|
return (React.createElement(Dialog, { open: true, onClose: handleClose, title: "Export SVG" },
|
|
27
27
|
React.createElement(DialogContent, null,
|
|
28
28
|
error ? (React.createElement(ErrorMessage, { error: error })) : loading ? (React.createElement(LoadingMessage, null)) : null,
|
|
29
|
-
React.createElement(TextField2, { helperText: "filename", value: filename, onChange: event =>
|
|
30
|
-
|
|
29
|
+
React.createElement(TextField2, { helperText: "filename", value: filename, onChange: event => {
|
|
30
|
+
setFilename(event.target.value);
|
|
31
|
+
} }),
|
|
32
|
+
React.createElement(TextField2, { select: true, label: "Track label positioning", variant: "outlined", style: { width: 150 }, value: trackLabels, onChange: event => {
|
|
33
|
+
setTrackLabels(event.target.value);
|
|
34
|
+
} },
|
|
31
35
|
React.createElement(MenuItem, { value: "offset" }, "Offset"),
|
|
32
36
|
React.createElement(MenuItem, { value: "overlay" }, "Overlay"),
|
|
33
37
|
React.createElement(MenuItem, { value: "left" }, "Left"),
|
|
34
38
|
React.createElement(MenuItem, { value: "none" }, "None")),
|
|
35
|
-
session.allThemes ? (React.createElement(TextField2, { select: true, label: "Theme", variant: "outlined", value: themeName, onChange: event =>
|
|
39
|
+
session.allThemes ? (React.createElement(TextField2, { select: true, label: "Theme", variant: "outlined", value: themeName, onChange: event => {
|
|
40
|
+
setThemeName(event.target.value);
|
|
41
|
+
} }, Object.entries(session.allThemes()).map(([key, val]) => (React.createElement(MenuItem, { key: key, value: key },
|
|
36
42
|
// @ts-expect-error
|
|
37
43
|
val.name || '(Unknown name)'))))) : null,
|
|
38
|
-
offscreenCanvas ? (React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { checked: rasterizeLayers, onChange: () =>
|
|
44
|
+
offscreenCanvas ? (React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { checked: rasterizeLayers, onChange: () => {
|
|
45
|
+
setRasterizeLayers(val => !val);
|
|
46
|
+
} }), label: "Rasterize canvas based tracks? File may be much larger if this is turned off" })) : (React.createElement(Typography, null, "Note: rasterizing layers not yet supported in this browser, so SVG size may be large"))),
|
|
39
47
|
React.createElement(DialogActions, null,
|
|
40
|
-
React.createElement(Button, { variant: "contained", color: "secondary", onClick: () =>
|
|
48
|
+
React.createElement(Button, { variant: "contained", color: "secondary", onClick: () => {
|
|
49
|
+
handleClose();
|
|
50
|
+
} }, "Cancel"),
|
|
41
51
|
React.createElement(Button, { variant: "contained", color: "primary", type: "submit", onClick: async () => {
|
|
42
52
|
setLoading(true);
|
|
43
53
|
setError(undefined);
|
|
@@ -48,7 +48,6 @@ async function fetchSequence(model, regions, signal) {
|
|
|
48
48
|
}
|
|
49
49
|
const GetSequenceDialog = observer(function ({ model, handleClose, }) {
|
|
50
50
|
const { classes } = useStyles();
|
|
51
|
-
const session = getSession(model);
|
|
52
51
|
const [error, setError] = useState();
|
|
53
52
|
const [sequenceChunks, setSequenceChunks] = useState();
|
|
54
53
|
const [rev, setReverse] = useState(false);
|
|
@@ -57,7 +56,6 @@ const GetSequenceDialog = observer(function ({ model, handleClose, }) {
|
|
|
57
56
|
const { leftOffset, rightOffset } = model;
|
|
58
57
|
const loading = Boolean(sequenceChunks === undefined);
|
|
59
58
|
useEffect(() => {
|
|
60
|
-
let active = true;
|
|
61
59
|
const controller = new AbortController();
|
|
62
60
|
(async () => {
|
|
63
61
|
try {
|
|
@@ -69,26 +67,19 @@ const GetSequenceDialog = observer(function ({ model, handleClose, }) {
|
|
|
69
67
|
throw new Error('Selected region is out of bounds');
|
|
70
68
|
}
|
|
71
69
|
const chunks = await fetchSequence(model, selection, controller.signal);
|
|
72
|
-
|
|
73
|
-
setSequenceChunks(chunks);
|
|
74
|
-
}
|
|
70
|
+
setSequenceChunks(chunks);
|
|
75
71
|
}
|
|
76
72
|
catch (e) {
|
|
77
73
|
console.error(e);
|
|
78
|
-
|
|
79
|
-
setError(e);
|
|
80
|
-
}
|
|
74
|
+
setError(e);
|
|
81
75
|
}
|
|
82
76
|
})();
|
|
83
77
|
return () => {
|
|
84
78
|
controller.abort();
|
|
85
|
-
active = false;
|
|
86
79
|
};
|
|
87
|
-
}, [model,
|
|
80
|
+
}, [model, leftOffset, rightOffset]);
|
|
88
81
|
const sequence = sequenceChunks
|
|
89
|
-
? formatSeqFasta(sequenceChunks
|
|
90
|
-
.filter(f => !!f)
|
|
91
|
-
.map(chunk => {
|
|
82
|
+
? formatSeqFasta(sequenceChunks.map(chunk => {
|
|
92
83
|
let chunkSeq = chunk.get('seq');
|
|
93
84
|
const chunkRefName = chunk.get('refName');
|
|
94
85
|
const chunkStart = chunk.get('start') + 1;
|
|
@@ -127,14 +118,20 @@ const GetSequenceDialog = observer(function ({ model, handleClose, }) {
|
|
|
127
118
|
},
|
|
128
119
|
} }),
|
|
129
120
|
React.createElement(FormGroup, null,
|
|
130
|
-
React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { value: rev, onChange: event =>
|
|
131
|
-
|
|
121
|
+
React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { value: rev, onChange: event => {
|
|
122
|
+
setReverse(event.target.checked);
|
|
123
|
+
} }), label: "Reverse sequence" }),
|
|
124
|
+
React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { value: comp, onChange: event => {
|
|
125
|
+
setComplement(event.target.checked);
|
|
126
|
+
} }), label: "Complement sequence" })),
|
|
132
127
|
React.createElement(Typography, { style: { margin: 10 } }, "Note: Check both boxes for the \"reverse complement\"")),
|
|
133
128
|
React.createElement(DialogActions, null,
|
|
134
129
|
React.createElement(Button, { onClick: () => {
|
|
135
130
|
copy(sequence);
|
|
136
131
|
setCopied(true);
|
|
137
|
-
setTimeout(() =>
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
setCopied(false);
|
|
134
|
+
}, 500);
|
|
138
135
|
}, disabled: loading || !!error || sequenceTooLarge, color: "primary", startIcon: React.createElement(ContentCopyIcon, null) }, copied ? 'Copied' : 'Copy to clipboard'),
|
|
139
136
|
React.createElement(Button, { onClick: () => {
|
|
140
137
|
saveAs(new Blob([sequence || ''], {
|
|
@@ -50,9 +50,13 @@ const HeaderButtons = observer(({ model }) => {
|
|
|
50
50
|
function PanControls({ model }) {
|
|
51
51
|
const { classes } = useStyles();
|
|
52
52
|
return (React.createElement(React.Fragment, null,
|
|
53
|
-
React.createElement(Button, { variant: "outlined", className: classes.panButton, onClick: () =>
|
|
53
|
+
React.createElement(Button, { variant: "outlined", className: classes.panButton, onClick: () => {
|
|
54
|
+
model.slide(-0.9);
|
|
55
|
+
} },
|
|
54
56
|
React.createElement(ArrowBackIcon, null)),
|
|
55
|
-
React.createElement(Button, { variant: "outlined", className: classes.panButton, onClick: () =>
|
|
57
|
+
React.createElement(Button, { variant: "outlined", className: classes.panButton, onClick: () => {
|
|
58
|
+
model.slide(0.9);
|
|
59
|
+
} },
|
|
56
60
|
React.createElement(ArrowForwardIcon, null))));
|
|
57
61
|
}
|
|
58
62
|
const RegionWidth = observer(({ model }) => {
|
|
@@ -2,32 +2,29 @@ import React, { useRef, useState } from 'react';
|
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
3
|
import { makeStyles } from 'tss-react/mui';
|
|
4
4
|
import { colord } from '@jbrowse/core/util/colord';
|
|
5
|
-
import { getSession
|
|
5
|
+
import { getSession } from '@jbrowse/core/util';
|
|
6
6
|
import { Menu } from '@jbrowse/core/ui';
|
|
7
|
-
import { IconButton, Tooltip
|
|
7
|
+
import { IconButton, Tooltip } from '@mui/material';
|
|
8
8
|
// icons
|
|
9
9
|
import LinkIcon from '@mui/icons-material/Link';
|
|
10
10
|
import CloseIcon from '@mui/icons-material/Close';
|
|
11
11
|
import BookmarkIcon from '@mui/icons-material/Bookmark';
|
|
12
|
-
const useStyles = makeStyles()(theme => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
24
|
-
});
|
|
12
|
+
const useStyles = makeStyles()(theme => ({
|
|
13
|
+
highlight: {
|
|
14
|
+
height: '100%',
|
|
15
|
+
position: 'absolute',
|
|
16
|
+
overflow: 'hidden',
|
|
17
|
+
background: colord(theme.palette.highlight.main).alpha(0.35).toRgbString(),
|
|
18
|
+
},
|
|
19
|
+
linkIcon: {
|
|
20
|
+
color: colord(theme.palette.highlight.main).darken(0.2).toRgbString(),
|
|
21
|
+
},
|
|
22
|
+
}));
|
|
25
23
|
const Highlight = observer(function Highlight({ model, highlight, }) {
|
|
26
|
-
var _a
|
|
24
|
+
var _a;
|
|
27
25
|
const { classes } = useStyles();
|
|
28
26
|
const [open, setOpen] = useState(false);
|
|
29
27
|
const anchorEl = useRef(null);
|
|
30
|
-
const color = (_b = (_a = useTheme().palette.highlight) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod';
|
|
31
28
|
const session = getSession(model);
|
|
32
29
|
const { assemblyManager } = session;
|
|
33
30
|
const dismissHighlight = () => {
|
|
@@ -53,20 +50,20 @@ const Highlight = observer(function Highlight({ model, highlight, }) {
|
|
|
53
50
|
}
|
|
54
51
|
: undefined;
|
|
55
52
|
};
|
|
56
|
-
const asm = assemblyManager.get(highlight
|
|
53
|
+
const asm = assemblyManager.get(highlight.assemblyName);
|
|
57
54
|
const h = mapCoords({
|
|
58
55
|
...highlight,
|
|
59
|
-
refName: (
|
|
56
|
+
refName: (_a = asm === null || asm === void 0 ? void 0 : asm.getCanonicalRefName(highlight.refName)) !== null && _a !== void 0 ? _a : highlight.refName,
|
|
60
57
|
});
|
|
61
58
|
return h ? (React.createElement("div", { className: classes.highlight, style: {
|
|
62
59
|
left: h.left,
|
|
63
60
|
width: h.width,
|
|
64
61
|
} },
|
|
65
|
-
React.createElement(Tooltip, { title:
|
|
66
|
-
React.createElement(IconButton, { ref: anchorEl, onClick: () =>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
React.createElement(Tooltip, { title: "Highlighted from URL parameter", arrow: true },
|
|
63
|
+
React.createElement(IconButton, { ref: anchorEl, onClick: () => {
|
|
64
|
+
setOpen(true);
|
|
65
|
+
}, style: { zIndex: 3 } },
|
|
66
|
+
React.createElement(LinkIcon, { fontSize: "small", className: classes.linkIcon }))),
|
|
70
67
|
React.createElement(Menu, { anchorEl: anchorEl.current, onMenuItemClick: (_event, callback) => {
|
|
71
68
|
callback(session);
|
|
72
69
|
handleClose();
|
|
@@ -74,7 +71,9 @@ const Highlight = observer(function Highlight({ model, highlight, }) {
|
|
|
74
71
|
{
|
|
75
72
|
label: 'Dismiss highlight',
|
|
76
73
|
icon: CloseIcon,
|
|
77
|
-
onClick: () =>
|
|
74
|
+
onClick: () => {
|
|
75
|
+
dismissHighlight();
|
|
76
|
+
},
|
|
78
77
|
},
|
|
79
78
|
{
|
|
80
79
|
label: 'Bookmark highlighted region',
|
|
@@ -84,7 +83,7 @@ const Highlight = observer(function Highlight({ model, highlight, }) {
|
|
|
84
83
|
if (!bookmarkWidget) {
|
|
85
84
|
bookmarkWidget = session.addWidget('GridBookmarkWidget', 'GridBookmark');
|
|
86
85
|
}
|
|
87
|
-
// @ts-
|
|
86
|
+
// @ts-expect-error
|
|
88
87
|
bookmarkWidget.addBookmark(highlight);
|
|
89
88
|
dismissHighlight();
|
|
90
89
|
},
|
|
@@ -92,6 +91,6 @@ const Highlight = observer(function Highlight({ model, highlight, }) {
|
|
|
92
91
|
] }))) : null;
|
|
93
92
|
});
|
|
94
93
|
const HighlightGroup = observer(function HighlightGroup({ model, }) {
|
|
95
|
-
return model.highlight.map((highlight, idx) => (React.createElement(Highlight, { key: JSON.stringify(highlight)
|
|
94
|
+
return model.highlight.map((highlight, idx) => (React.createElement(Highlight, { key: `${JSON.stringify(highlight)}-${idx}`, model: model, highlight: highlight })));
|
|
96
95
|
});
|
|
97
96
|
export default HighlightGroup;
|
|
@@ -35,12 +35,13 @@ const LinearGenomeViewImportForm = observer(function ({ model, }) {
|
|
|
35
35
|
const [value, setValue] = useState('');
|
|
36
36
|
const regions = assembly === null || assembly === void 0 ? void 0 : assembly.regions;
|
|
37
37
|
const assemblyLoaded = !!regions;
|
|
38
|
-
const r0 = regions ? (_a = regions[0]) === null || _a === void 0 ? void 0 : _a.refName : '';
|
|
38
|
+
const r0 = regions ? ((_a = regions[0]) === null || _a === void 0 ? void 0 : _a.refName) || '' : '';
|
|
39
39
|
// useEffect resets to an "initial state" of displaying first region from
|
|
40
40
|
// assembly after assembly change. needs to react to selectedAsm as well as
|
|
41
41
|
// r0 because changing assembly will run setValue('') and then r0 may not
|
|
42
42
|
// change if assembly names are the same across assemblies, but it still
|
|
43
43
|
// needs to be reset
|
|
44
|
+
/* biome-ignore lint/correctness/useExhaustiveDependencies: */
|
|
44
45
|
useEffect(() => {
|
|
45
46
|
setValue(r0);
|
|
46
47
|
}, [r0, selectedAsm]);
|
|
@@ -80,7 +81,9 @@ const LinearGenomeViewImportForm = observer(function ({ model, }) {
|
|
|
80
81
|
React.createElement(Grid, { container: true, spacing: 1, justifyContent: "center", alignItems: "center" },
|
|
81
82
|
React.createElement(Grid, { item: true },
|
|
82
83
|
React.createElement(FormControl, null,
|
|
83
|
-
React.createElement(AssemblySelector, { onChange: val =>
|
|
84
|
+
React.createElement(AssemblySelector, { onChange: val => {
|
|
85
|
+
setSelectedAsm(val);
|
|
86
|
+
}, localStorageKey: "lgv", session: session, selected: selectedAsm }))),
|
|
84
87
|
React.createElement(Grid, { item: true }, selectedAsm ? (assemblyError ? (React.createElement(CloseIcon, { style: { color: 'red' } })) : assemblyLoaded ? (React.createElement(FormControl, null,
|
|
85
88
|
React.createElement(ImportFormRefNameAutocomplete, { value: value, setValue: setValue, selectedAsm: selectedAsm, setOption: setOption, model: model }))) : (React.createElement(CircularProgress, { size: 20, disableShrink: true }))) : null),
|
|
86
89
|
React.createElement(Grid, { item: true },
|
|
@@ -16,7 +16,11 @@ const ImportFormRefNameAutocomplete = observer(function ({ model, selectedAsm, v
|
|
|
16
16
|
textSearchManager,
|
|
17
17
|
rankSearchResults,
|
|
18
18
|
searchScope,
|
|
19
|
-
}), model: model, assemblyName: selectedAsm, value: value, minWidth: 270, onChange: str =>
|
|
19
|
+
}), model: model, assemblyName: selectedAsm, value: value, minWidth: 270, onChange: str => {
|
|
20
|
+
setValue(str);
|
|
21
|
+
}, onSelect: val => {
|
|
22
|
+
setOption(val);
|
|
23
|
+
}, TextFieldProps: {
|
|
20
24
|
variant: 'outlined',
|
|
21
25
|
helperText: 'Enter sequence name, feature name, or location',
|
|
22
26
|
} }));
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { LinearGenomeViewModel } from '..';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
model: LGV;
|
|
3
|
+
declare const LinearGenomeView: ({ model, }: {
|
|
4
|
+
model: LinearGenomeViewModel;
|
|
6
5
|
}) => React.JSX.Element;
|
|
7
6
|
export default LinearGenomeView;
|
|
@@ -20,7 +20,7 @@ const useStyles = makeStyles()(theme => ({
|
|
|
20
20
|
zIndex: 1000,
|
|
21
21
|
},
|
|
22
22
|
}));
|
|
23
|
-
const LinearGenomeView = observer(({ model })
|
|
23
|
+
const LinearGenomeView = observer(function ({ model, }) {
|
|
24
24
|
const { tracks, error, initialized, hasDisplayedRegions } = model;
|
|
25
25
|
const ref = useRef(null);
|
|
26
26
|
const session = getSession(model);
|
|
@@ -30,7 +30,7 @@ const LinearGenomeView = observer(({ model }) => {
|
|
|
30
30
|
// necessary for subviews to be focused properly
|
|
31
31
|
function handleSelectView(e) {
|
|
32
32
|
var _a, _b;
|
|
33
|
-
if (e.target instanceof Element && ((_a = ref
|
|
33
|
+
if (e.target instanceof Element && ((_a = ref.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
|
|
34
34
|
(_b = session.setFocusedViewId) === null || _b === void 0 ? void 0 : _b.call(session, model.id);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -49,7 +49,9 @@ const LinearGenomeView = observer(({ model }) => {
|
|
|
49
49
|
}
|
|
50
50
|
const MiniControlsComponent = model.MiniControlsComponent();
|
|
51
51
|
const HeaderComponent = model.HeaderComponent();
|
|
52
|
-
return (React.createElement("div", { className: classes.rel, ref: ref, onMouseLeave: () =>
|
|
52
|
+
return (React.createElement("div", { className: classes.rel, ref: ref, onMouseLeave: () => {
|
|
53
|
+
session.setHovered(undefined);
|
|
54
|
+
}, onMouseMove: event => {
|
|
53
55
|
const c = ref.current;
|
|
54
56
|
if (!c) {
|
|
55
57
|
return;
|
|
@@ -27,9 +27,13 @@ const MiniControls = observer(function ({ model, }) {
|
|
|
27
27
|
React.createElement(Paper, { className: focusedViewId === id ? classes.focusedBackground : undefined },
|
|
28
28
|
React.createElement(CascadingMenuButton, { menuItems: model.menuItems() },
|
|
29
29
|
React.createElement(ArrowDown, { fontSize: "small" })),
|
|
30
|
-
React.createElement(IconButton, { "data-testid": "zoom_out", onClick: () =>
|
|
30
|
+
React.createElement(IconButton, { "data-testid": "zoom_out", onClick: () => {
|
|
31
|
+
model.zoom(bpPerPx * 2);
|
|
32
|
+
}, disabled: bpPerPx >= maxBpPerPx - 0.0001 || scaleFactor !== 1 },
|
|
31
33
|
React.createElement(ZoomOut, { fontSize: "small" })),
|
|
32
|
-
React.createElement(IconButton, { "data-testid": "zoom_in", onClick: () =>
|
|
34
|
+
React.createElement(IconButton, { "data-testid": "zoom_in", onClick: () => {
|
|
35
|
+
model.zoom(bpPerPx / 2);
|
|
36
|
+
}, disabled: bpPerPx <= minBpPerPx + 0.0001 || scaleFactor !== 1 },
|
|
33
37
|
React.createElement(ZoomIn, { fontSize: "small" }))))) : null;
|
|
34
38
|
});
|
|
35
39
|
export default MiniControls;
|
|
@@ -2,54 +2,47 @@ import React from 'react';
|
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
3
|
import { makeStyles } from 'tss-react/mui';
|
|
4
4
|
import { colord } from '@jbrowse/core/util/colord';
|
|
5
|
-
import { getSession, notEmpty
|
|
6
|
-
const useStyles = makeStyles()(theme => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
borderLeft: `1px solid ${(_d = (_c = theme.palette.highlight) === null || _c === void 0 ? void 0 : _c.main) !== null && _d !== void 0 ? _d : 'goldenrod'}`,
|
|
16
|
-
borderRight: `1px solid ${(_f = (_e = theme.palette.highlight) === null || _e === void 0 ? void 0 : _e.main) !== null && _f !== void 0 ? _f : 'goldenrod'}`,
|
|
17
|
-
},
|
|
18
|
-
});
|
|
19
|
-
});
|
|
5
|
+
import { getSession, notEmpty } from '@jbrowse/core/util';
|
|
6
|
+
const useStyles = makeStyles()(theme => ({
|
|
7
|
+
highlight: {
|
|
8
|
+
height: '100%',
|
|
9
|
+
position: 'absolute',
|
|
10
|
+
background: colord(theme.palette.highlight.main).alpha(0.35).toRgbString(),
|
|
11
|
+
borderLeft: `1px solid ${theme.palette.highlight.main}`,
|
|
12
|
+
borderRight: `1px solid ${theme.palette.highlight.main}`,
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
20
15
|
const OverviewHighlight = observer(function OverviewHighlight({ model, overview, }) {
|
|
21
16
|
const { classes } = useStyles();
|
|
22
|
-
const { cytobandOffset } = model;
|
|
17
|
+
const { highlight, cytobandOffset } = model;
|
|
23
18
|
const session = getSession(model);
|
|
24
19
|
const { assemblyManager } = session;
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
return highlight
|
|
21
|
+
.map(r => {
|
|
22
|
+
var _a;
|
|
23
|
+
const asm = assemblyManager.get(r.assemblyName);
|
|
24
|
+
const refName = (_a = asm === null || asm === void 0 ? void 0 : asm.getCanonicalRefName(r.refName)) !== null && _a !== void 0 ? _a : r.refName;
|
|
27
25
|
const s = overview.bpToPx({
|
|
28
26
|
...r,
|
|
29
|
-
|
|
27
|
+
refName,
|
|
28
|
+
coord: r.start,
|
|
30
29
|
});
|
|
31
30
|
const e = overview.bpToPx({
|
|
32
31
|
...r,
|
|
33
|
-
|
|
32
|
+
refName,
|
|
33
|
+
coord: r.end,
|
|
34
34
|
});
|
|
35
|
-
return s !== undefined && e
|
|
35
|
+
return s !== undefined && e !== undefined
|
|
36
36
|
? {
|
|
37
37
|
width: Math.abs(e - s),
|
|
38
38
|
left: s + cytobandOffset,
|
|
39
39
|
}
|
|
40
40
|
: undefined;
|
|
41
|
-
};
|
|
42
|
-
return model.highlight
|
|
43
|
-
.map(h => {
|
|
44
|
-
var _a;
|
|
45
|
-
const asm = assemblyManager.get(h === null || h === void 0 ? void 0 : h.assemblyName);
|
|
46
|
-
return mapCoords({
|
|
47
|
-
...h,
|
|
48
|
-
refName: (_a = asm === null || asm === void 0 ? void 0 : asm.getCanonicalRefName(h.refName)) !== null && _a !== void 0 ? _a : h.refName,
|
|
49
|
-
});
|
|
50
41
|
})
|
|
51
42
|
.filter(notEmpty)
|
|
52
|
-
.map(({ left, width }, idx) => (React.createElement("div", {
|
|
43
|
+
.map(({ left, width }, idx) => (React.createElement("div", {
|
|
44
|
+
/* biome-ignore lint/suspicious/noArrayIndexKey: */
|
|
45
|
+
key: `${left}_${width}_${idx}`, className: classes.highlight, style: {
|
|
53
46
|
width: width,
|
|
54
47
|
left: left,
|
|
55
48
|
} })));
|