@jbrowse/plugin-alignments 1.7.7 → 1.7.10

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 (39) hide show
  1. package/dist/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js +13 -3
  2. package/dist/AlignmentsFeatureDetail/index.d.ts +28 -3
  3. package/dist/AlignmentsFeatureDetail/index.js +6 -17
  4. package/dist/BamAdapter/BamAdapter.d.ts +1 -1
  5. package/dist/BamAdapter/BamAdapter.js +3 -3
  6. package/dist/BamAdapter/MismatchParser.d.ts +2 -5
  7. package/dist/BamAdapter/MismatchParser.js +108 -46
  8. package/dist/BamAdapter/MismatchParser.test.js +6 -14
  9. package/dist/CramAdapter/CramAdapter.d.ts +10 -9
  10. package/dist/CramAdapter/CramAdapter.js +6 -6
  11. package/dist/CramAdapter/CramSlightlyLazyFeature.js +35 -30
  12. package/dist/LinearPileupDisplay/model.d.ts +6 -3
  13. package/dist/LinearPileupDisplay/model.js +132 -51
  14. package/dist/LinearSNPCoverageDisplay/components/Tooltip.js +37 -17
  15. package/dist/LinearSNPCoverageDisplay/models/model.d.ts +2 -2
  16. package/dist/LinearSNPCoverageDisplay/models/model.js +1 -1
  17. package/dist/PileupRenderer/PileupLayoutSession.d.ts +3 -0
  18. package/dist/PileupRenderer/PileupLayoutSession.js +3 -1
  19. package/dist/PileupRenderer/PileupRenderer.d.ts +66 -9
  20. package/dist/PileupRenderer/PileupRenderer.js +296 -258
  21. package/dist/PileupRenderer/configSchema.js +2 -2
  22. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +6 -6
  23. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js +95 -96
  24. package/dist/SNPCoverageRenderer/configSchema.d.ts +1 -1
  25. package/package.json +3 -3
  26. package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +14 -3
  27. package/src/AlignmentsFeatureDetail/index.ts +7 -17
  28. package/src/BamAdapter/BamAdapter.ts +3 -3
  29. package/src/BamAdapter/MismatchParser.test.ts +5 -7
  30. package/src/BamAdapter/MismatchParser.ts +72 -59
  31. package/src/CramAdapter/CramAdapter.ts +14 -10
  32. package/src/CramAdapter/CramSlightlyLazyFeature.ts +84 -91
  33. package/src/LinearPileupDisplay/model.ts +76 -24
  34. package/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +41 -20
  35. package/src/LinearSNPCoverageDisplay/models/model.ts +1 -1
  36. package/src/PileupRenderer/PileupLayoutSession.ts +6 -1
  37. package/src/PileupRenderer/PileupRenderer.tsx +413 -225
  38. package/src/PileupRenderer/configSchema.ts +2 -2
  39. package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +89 -76
@@ -20,6 +20,13 @@ interface Header {
20
20
  readGroups?: number[]
21
21
  }
22
22
 
23
+ interface FilterBy {
24
+ flagInclude: number
25
+ flagExclude: number
26
+ tagFilter: { tag: string; value: unknown }
27
+ readName: string
28
+ }
29
+
23
30
  export default class CramAdapter extends BaseFeatureDataAdapter {
24
31
  samHeader: Header = {}
25
32
 
@@ -206,12 +213,7 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
206
213
  getFeatures(
207
214
  region: Region & { originalRefName?: string },
208
215
  opts?: BaseOptions & {
209
- filterBy: {
210
- flagInclude: number
211
- flagExclude: number
212
- tagFilter: { tag: string; value: unknown }
213
- name: string
214
- }
216
+ filterBy: FilterBy
215
217
  },
216
218
  ) {
217
219
  const { signal, filterBy, statusCallback = () => {} } = opts || {}
@@ -234,7 +236,7 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
234
236
  flagInclude = 0,
235
237
  flagExclude = 0,
236
238
  tagFilter,
237
- name,
239
+ readName,
238
240
  } = filterBy || {}
239
241
 
240
242
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -251,9 +253,11 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
251
253
  })
252
254
  }
253
255
 
254
- if (name) {
255
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
256
- filtered = filtered.filter((record: any) => record.name === name)
256
+ if (readName) {
257
+ filtered = filtered.filter(
258
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
259
+ (record: any) => record.readName === readName,
260
+ )
257
261
  }
258
262
 
259
263
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -131,7 +131,8 @@ export default class CramSlightlyLazyFeature implements Feature {
131
131
  let sublen = 0
132
132
  if (typeof this.record.readFeatures !== 'undefined') {
133
133
  // @ts-ignore
134
- this.record.readFeatures.forEach(({ code, refPos, sub, data }) => {
134
+ for (let i = 0; i < this.record.readFeatures.length; i++) {
135
+ const { code, refPos, sub, data } = this.record.readFeatures[i]
135
136
  sublen = refPos - last_pos
136
137
  seq += ref.substring(last_pos - refStart, refPos - refStart)
137
138
  last_pos = refPos
@@ -200,7 +201,7 @@ export default class CramSlightlyLazyFeature implements Feature {
200
201
  cigar += `${data}H`
201
202
  oplen = 0
202
203
  } // else q or Q
203
- })
204
+ }
204
205
  } else {
205
206
  sublen = this.record.readLength - seq.length
206
207
  }
@@ -303,95 +304,87 @@ export default class CramSlightlyLazyFeature implements Feature {
303
304
  return []
304
305
  }
305
306
  const start = this.get('start')
306
- const mismatches: Mismatch[] = []
307
- readFeatures.forEach(
308
- (args: {
309
- code: string
310
- refPos: number
311
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
312
- data: any
313
- pos: number
314
- sub: string
315
- ref: string
316
- }) => {
317
- const { code, pos, data, sub, ref } = args
318
- const refPos = args.refPos - 1 - start
319
- if (code === 'X') {
320
- // substitution
321
- mismatches.push({
322
- start: refPos,
323
- length: 1,
324
- base: sub,
325
- qual: qual?.[pos],
326
- altbase: ref,
327
- type: 'mismatch',
328
- })
329
- } else if (code === 'I') {
330
- // insertion
331
- mismatches.push({
332
- start: refPos,
333
- type: 'insertion',
334
- base: `${data.length}`,
335
- length: 0,
336
- })
337
- } else if (code === 'N') {
338
- // reference skip
339
- mismatches.push({
340
- type: 'skip',
341
- length: data,
342
- start: refPos,
343
- base: 'N',
344
- })
345
- } else if (code === 'S') {
346
- // soft clip
347
- const len = data.length
348
- mismatches.push({
349
- start: refPos,
350
- type: 'softclip',
351
- base: `S${len}`,
352
- cliplen: len,
353
- length: 1,
354
- })
355
- } else if (code === 'P') {
356
- // padding
357
- } else if (code === 'H') {
358
- // hard clip
359
- const len = data
360
- mismatches.push({
361
- start: refPos,
362
- type: 'hardclip',
363
- base: `H${len}`,
364
- cliplen: len,
365
- length: 1,
366
- })
367
- } else if (code === 'D') {
368
- // deletion
369
- mismatches.push({
370
- type: 'deletion',
371
- length: data,
372
- start: refPos,
373
- base: '*',
374
- })
375
- } else if (code === 'b') {
376
- // stretch of bases
377
- } else if (code === 'q') {
378
- // stretch of qual scores
379
- } else if (code === 'B') {
380
- // a pair of [base, qual]
381
- } else if (code === 'i') {
382
- // single-base insertion
383
- // insertion
384
- mismatches.push({
385
- start: refPos,
386
- type: 'insertion',
387
- base: data,
388
- length: 1,
389
- })
390
- } else if (code === 'Q') {
391
- // single quality value
307
+ const mismatches: Mismatch[] = new Array(readFeatures.length)
308
+ let j = 0
309
+ for (let i = 0; i < readFeatures.length; i++) {
310
+ const f = readFeatures[i]
311
+ const { code, pos, data, sub, ref } = f
312
+ const refPos = f.refPos - 1 - start
313
+ if (code === 'X') {
314
+ // substitution
315
+ mismatches[j++] = {
316
+ start: refPos,
317
+ length: 1,
318
+ base: sub,
319
+ qual: qual?.[pos],
320
+ altbase: ref,
321
+ type: 'mismatch',
392
322
  }
393
- },
394
- )
395
- return mismatches
323
+ } else if (code === 'I') {
324
+ // insertion
325
+ mismatches[j++] = {
326
+ start: refPos,
327
+ type: 'insertion',
328
+ base: `${data.length}`,
329
+ length: 0,
330
+ }
331
+ } else if (code === 'N') {
332
+ // reference skip
333
+ mismatches[j++] = {
334
+ type: 'skip',
335
+ length: data,
336
+ start: refPos,
337
+ base: 'N',
338
+ }
339
+ } else if (code === 'S') {
340
+ // soft clip
341
+ const len = data.length
342
+ mismatches[j++] = {
343
+ start: refPos,
344
+ type: 'softclip',
345
+ base: `S${len}`,
346
+ cliplen: len,
347
+ length: 1,
348
+ }
349
+ } else if (code === 'P') {
350
+ // padding
351
+ } else if (code === 'H') {
352
+ // hard clip
353
+ const len = data
354
+ mismatches[j++] = {
355
+ start: refPos,
356
+ type: 'hardclip',
357
+ base: `H${len}`,
358
+ cliplen: len,
359
+ length: 1,
360
+ }
361
+ } else if (code === 'D') {
362
+ // deletion
363
+ mismatches[j++] = {
364
+ type: 'deletion',
365
+ length: data,
366
+ start: refPos,
367
+ base: '*',
368
+ }
369
+ } else if (code === 'b') {
370
+ // stretch of bases
371
+ } else if (code === 'q') {
372
+ // stretch of qual scores
373
+ } else if (code === 'B') {
374
+ // a pair of [base, qual]
375
+ } else if (code === 'i') {
376
+ // single-base insertion
377
+ // insertion
378
+ mismatches[j++] = {
379
+ start: refPos,
380
+ type: 'insertion',
381
+ base: data,
382
+ length: 1,
383
+ }
384
+ } else if (code === 'Q') {
385
+ // single quality value
386
+ }
387
+ }
388
+ return mismatches.slice(0, j)
396
389
  }
397
390
  }
@@ -1,5 +1,9 @@
1
1
  import { lazy } from 'react'
2
+ import { autorun, observable } from 'mobx'
3
+ import { cast, types, addDisposer, getEnv, Instance } from 'mobx-state-tree'
4
+ import copy from 'copy-to-clipboard'
2
5
  import {
6
+ AnyConfigurationModel,
3
7
  ConfigurationReference,
4
8
  readConfObject,
5
9
  getConf,
@@ -13,26 +17,25 @@ import {
13
17
  Feature,
14
18
  } from '@jbrowse/core/util'
15
19
 
16
- import VisibilityIcon from '@material-ui/icons/Visibility'
17
- import { ContentCopy as ContentCopyIcon } from '@jbrowse/core/ui/Icons'
18
20
  import {
19
21
  LinearGenomeViewModel,
20
22
  BaseLinearDisplay,
21
23
  } from '@jbrowse/plugin-linear-genome-view'
22
- import { cast, types, addDisposer, getEnv, Instance } from 'mobx-state-tree'
23
- import copy from 'copy-to-clipboard'
24
+
25
+ // icons
26
+ import VisibilityIcon from '@material-ui/icons/Visibility'
27
+ import { ContentCopy as ContentCopyIcon } from '@jbrowse/core/ui/Icons'
24
28
  import MenuOpenIcon from '@material-ui/icons/MenuOpen'
25
29
  import SortIcon from '@material-ui/icons/Sort'
26
30
  import PaletteIcon from '@material-ui/icons/Palette'
27
31
  import FilterListIcon from '@material-ui/icons/ClearAll'
28
32
 
29
- import { autorun, observable } from 'mobx'
30
- import { AnyConfigurationModel } from '@jbrowse/core/configuration/configurationSchema'
33
+ // locals
31
34
  import { LinearPileupDisplayConfigModel } from './configSchema'
32
35
  import LinearPileupDisplayBlurb from './components/LinearPileupDisplayBlurb'
33
-
34
36
  import { getUniqueTagValues, getUniqueModificationValues } from '../shared'
35
37
 
38
+ // async
36
39
  const ColorByTagDlg = lazy(() => import('./components/ColorByTag'))
37
40
  const FilterByTagDlg = lazy(() => import('./components/FilterByTag'))
38
41
  const SortByTagDlg = lazy(() => import('./components/SortByTag'))
@@ -94,6 +97,7 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
94
97
  .volatile(() => ({
95
98
  colorTagMap: observable.map<string, string>({}),
96
99
  modificationTagMap: observable.map<string, string>({}),
100
+ featureUnderMouseVolatile: undefined as undefined | Feature,
97
101
  ready: false,
98
102
  }))
99
103
  .actions(self => ({
@@ -128,7 +132,9 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
128
132
  },
129
133
 
130
134
  updateColorTagMap(uniqueTag: string[]) {
131
- // pale color scheme https://cran.r-project.org/web/packages/khroma/vignettes/tol.html e.g. "tol_light"
135
+ // pale color scheme
136
+ // https://cran.r-project.org/web/packages/khroma/vignettes/tol.html
137
+ // e.g. "tol_light"
132
138
  const colorPalette = [
133
139
  '#BBCCEE',
134
140
  'pink',
@@ -150,6 +156,9 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
150
156
  }
151
157
  })
152
158
  },
159
+ setFeatureUnderMouse(feat?: Feature) {
160
+ self.featureUnderMouseVolatile = feat
161
+ },
153
162
  }))
154
163
  .actions(self => ({
155
164
  afterAttach() {
@@ -226,6 +235,48 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
226
235
  { delay: 1000 },
227
236
  ),
228
237
  )
238
+
239
+ // autorun synchronizes featureUnderMouse with featureIdUnderMouse
240
+ addDisposer(
241
+ self,
242
+ autorun(async () => {
243
+ const session = getSession(self)
244
+ try {
245
+ const featureId = self.featureIdUnderMouse
246
+ if (self.featureUnderMouse?.id() !== featureId) {
247
+ if (!featureId) {
248
+ self.setFeatureUnderMouse(undefined)
249
+ } else {
250
+ const sessionId = getRpcSessionId(self)
251
+ const view = getContainingView(self)
252
+ const { feature } = (await session.rpcManager.call(
253
+ sessionId,
254
+ 'CoreGetFeatureDetails',
255
+ {
256
+ featureId,
257
+ sessionId,
258
+ layoutId: view.id,
259
+ rendererType: 'PileupRenderer',
260
+ },
261
+ )) as { feature: unknown }
262
+
263
+ // check featureIdUnderMouse is still the same as the
264
+ // feature.id that was returned e.g. that the user hasn't
265
+ // moused over to a new position during the async operation
266
+ // above
267
+ // @ts-ignore
268
+ if (self.featureIdUnderMouse === feature.uniqueId) {
269
+ // @ts-ignore
270
+ self.setFeatureUnderMouse(new SimpleFeature(feature))
271
+ }
272
+ }
273
+ }
274
+ } catch (e) {
275
+ console.error(e)
276
+ session.notify(`${e}`, 'error')
277
+ }
278
+ }),
279
+ )
229
280
  },
230
281
  selectFeature(feature: Feature) {
231
282
  const session = getSession(self)
@@ -337,6 +388,9 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
337
388
  ? self.mismatchAlpha
338
389
  : readConfObject(this.rendererConfig, 'mismatchAlpha')
339
390
  },
391
+ get featureUnderMouse() {
392
+ return self.featureUnderMouseVolatile
393
+ },
340
394
  }))
341
395
  .views(self => {
342
396
  const {
@@ -395,6 +449,8 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
395
449
  colorBy,
396
450
  filterBy,
397
451
  rpcDriverName,
452
+ currBpPerPx,
453
+ ready,
398
454
  } = self
399
455
 
400
456
  const superProps = superRenderProps()
@@ -403,18 +459,18 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
403
459
  ...superProps,
404
460
  notReady:
405
461
  superProps.notReady ||
406
- !self.ready ||
407
- (sortedBy && self.currBpPerPx !== view.bpPerPx),
462
+ !ready ||
463
+ (sortedBy && currBpPerPx !== view.bpPerPx),
408
464
  rpcDriverName,
409
465
  displayModel: self,
410
466
  sortedBy,
411
467
  colorBy,
412
- filterBy,
468
+ filterBy: JSON.parse(JSON.stringify(filterBy)),
413
469
  colorTagMap: JSON.parse(JSON.stringify(colorTagMap)),
414
470
  modificationTagMap: JSON.parse(JSON.stringify(modificationTagMap)),
415
471
  showSoftClip: self.showSoftClipping,
416
472
  config: self.rendererConfig,
417
- async onFeatureClick(_: unknown, featureId: string | undefined) {
473
+ async onFeatureClick(_: unknown, featureId?: string) {
418
474
  const session = getSession(self)
419
475
  const { rpcManager } = session
420
476
  try {
@@ -444,14 +500,12 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
444
500
  session.notify(`${e}`)
445
501
  }
446
502
  },
503
+
447
504
  onClick() {
448
505
  self.clearFeatureSelection()
449
506
  },
450
507
  // similar to click but opens a menu with further options
451
- async onFeatureContextMenu(
452
- _: unknown,
453
- featureId: string | undefined,
454
- ) {
508
+ async onFeatureContextMenu(_: unknown, featureId?: string) {
455
509
  const session = getSession(self)
456
510
  const { rpcManager } = session
457
511
  try {
@@ -507,19 +561,17 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
507
561
  disabled: self.showSoftClipping,
508
562
  subMenu: [
509
563
  ...['Start location', 'Read strand', 'Base pair'].map(
510
- option => {
511
- return {
512
- label: option,
513
- onClick: () => self.setSortedBy(option),
514
- }
515
- },
564
+ option => ({
565
+ label: option,
566
+ onClick: () => self.setSortedBy(option),
567
+ }),
516
568
  ),
517
569
  {
518
570
  label: 'Sort by tag...',
519
571
  onClick: () => {
520
- getSession(self).queueDialog(doneCallback => [
572
+ getSession(self).queueDialog(handleClose => [
521
573
  SortByTagDlg,
522
- { model: self, handleClose: doneCallback },
574
+ { model: self, handleClose },
523
575
  ])
524
576
  },
525
577
  },
@@ -7,35 +7,51 @@ import { Tooltip } from '@jbrowse/plugin-wiggle'
7
7
  type Count = {
8
8
  [key: string]: {
9
9
  total: number
10
- strands: { [key: string]: number }
10
+ '-1': number
11
+ '0': number
12
+ '1': number
11
13
  }
12
14
  }
13
15
 
14
16
  type SNPInfo = {
15
- ref: Count
16
17
  cov: Count
17
18
  lowqual: Count
18
19
  noncov: Count
19
20
  delskips: Count
21
+ refbase: string
20
22
  total: number
23
+ ref: number
24
+ all: number
25
+ '-1': number
26
+ '0': number
27
+ '1': number
21
28
  }
22
29
 
23
30
  const en = (n: number) => n.toLocaleString('en-US')
31
+ const toP = (s = 0) => +(+s).toFixed(1)
32
+ const pct = (n: number, total: number) => `${toP((n / (total || 1)) * 100)}%`
24
33
 
25
34
  const TooltipContents = React.forwardRef(
26
- ({ feature }: { feature: Feature }, ref: any) => {
35
+ ({ feature }: { feature: Feature }, reactRef: any) => {
27
36
  const start = feature.get('start')
28
37
  const end = feature.get('end')
29
38
  const name = feature.get('refName')
30
- const info = feature.get('snpinfo') as SNPInfo
39
+ const {
40
+ refbase,
41
+ all,
42
+ total,
43
+ ref,
44
+ '-1': rn1,
45
+ '1': r1,
46
+ '0': r0,
47
+ ...info
48
+ } = feature.get('snpinfo') as SNPInfo
31
49
  const loc = [name, start === end ? en(start) : `${en(start)}..${en(end)}`]
32
50
  .filter(f => !!f)
33
51
  .join(':')
34
52
 
35
- const total = info?.total
36
-
37
53
  return (
38
- <div ref={ref}>
54
+ <div ref={reactRef}>
39
55
  <table>
40
56
  <caption>{loc}</caption>
41
57
  <thead>
@@ -50,33 +66,38 @@ const TooltipContents = React.forwardRef(
50
66
  <tbody>
51
67
  <tr>
52
68
  <td>Total</td>
53
- <td>{total}</td>
69
+ <td>{all}</td>
70
+ </tr>
71
+ <tr>
72
+ <td>REF {refbase ? `(${refbase.toUpperCase()})` : ''}</td>
73
+ <td>{ref}</td>
74
+ <td>{pct(ref, all)}</td>
75
+ <td>
76
+ {rn1 ? `${rn1}(-)` : ''}
77
+ {r1 ? `${r1}(+)` : ''}
78
+ </td>
54
79
  <td />
55
80
  </tr>
56
81
 
57
- {Object.entries(info).map(([key, entry]) => {
58
- return Object.entries(entry).map(([base, score]) => {
59
- const { strands } = score
60
- return (
82
+ {Object.entries(info as unknown as Record<string, Count>).map(
83
+ ([key, entry]) =>
84
+ Object.entries(entry).map(([base, score]) => (
61
85
  <tr key={base}>
62
86
  <td>{base.toUpperCase()}</td>
63
87
  <td>{score.total}</td>
64
88
  <td>
65
89
  {base === 'total' || base === 'skip'
66
90
  ? '---'
67
- : `${Math.floor(
68
- (score.total / (total || score.total || 1)) * 100,
69
- )}%`}
91
+ : pct(score.total, all)}
70
92
  </td>
71
93
  <td>
72
- {strands['-1'] ? `${strands['-1']}(-)` : ''}
73
- {strands['1'] ? `${strands['1']}(+)` : ''}
94
+ {score['-1'] ? `${score['-1']}(-)` : ''}
95
+ {score['1'] ? `${score['1']}(+)` : ''}
74
96
  </td>
75
97
  <td>{key}</td>
76
98
  </tr>
77
- )
78
- })
79
- })}
99
+ )),
100
+ )}
80
101
  </tbody>
81
102
  </table>
82
103
  </div>
@@ -143,7 +143,7 @@ const stateModelFactory = (
143
143
  // must use getSnapshot because otherwise changes to e.g. just the
144
144
  // colorBy.type are not read
145
145
  colorBy: self.colorBy ? getSnapshot(self.colorBy) : undefined,
146
- filterBy: self.filterBy,
146
+ filterBy: self.filterBy ? getSnapshot(self.filterBy) : undefined,
147
147
  }
148
148
  },
149
149
  }
@@ -10,6 +10,7 @@ export interface PileupLayoutSessionProps {
10
10
  config: AnyConfigurationModel
11
11
  bpPerPx: number
12
12
  filters: SerializableFilterChain
13
+ filterBy: unknown
13
14
  sortedBy: unknown
14
15
  showSoftClip: unknown
15
16
  }
@@ -19,6 +20,7 @@ interface CachedPileupLayout {
19
20
  layout: MyMultiLayout
20
21
  config: AnyConfigurationModel
21
22
  filters: SerializableFilterChain
23
+ filterBy: unknown
22
24
  sortedBy: unknown
23
25
  showSoftClip: boolean
24
26
  }
@@ -26,6 +28,7 @@ interface CachedPileupLayout {
26
28
  // Adds extra conditions to see if cached layout is valid
27
29
  export class PileupLayoutSession extends LayoutSession {
28
30
  sortedBy: unknown
31
+ filterBy: unknown
29
32
 
30
33
  showSoftClip = false
31
34
 
@@ -38,7 +41,8 @@ export class PileupLayoutSession extends LayoutSession {
38
41
  return (
39
42
  super.cachedLayoutIsValid(cachedLayout) &&
40
43
  this.showSoftClip === cachedLayout.showSoftClip &&
41
- deepEqual(this.sortedBy, cachedLayout.sortedBy)
44
+ deepEqual(this.sortedBy, cachedLayout.sortedBy) &&
45
+ deepEqual(this.filterBy, cachedLayout.filterBy)
42
46
  )
43
47
  }
44
48
 
@@ -50,6 +54,7 @@ export class PileupLayoutSession extends LayoutSession {
50
54
  layout: this.makeLayout(),
51
55
  config: readConfObject(this.config),
52
56
  filters: this.filters,
57
+ filterBy: this.filterBy,
53
58
  sortedBy: this.sortedBy,
54
59
  showSoftClip: this.showSoftClip,
55
60
  }