react-msaview 5.9.0 → 5.10.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.
Files changed (71) hide show
  1. package/bundle/index.js +76 -76
  2. package/bundle/index.js.map +3 -3
  3. package/dist/components/ConservationTrack.js +21 -16
  4. package/dist/components/ConservationTrack.js.map +1 -1
  5. package/dist/components/MSAView.js +17 -23
  6. package/dist/components/MSAView.js.map +1 -1
  7. package/dist/components/TextTrack.js +26 -21
  8. package/dist/components/TextTrack.js.map +1 -1
  9. package/dist/components/header/settingsMenuItems.js +39 -72
  10. package/dist/components/header/settingsMenuItems.js.map +1 -1
  11. package/dist/components/msa/MSACanvasBlock.js +19 -16
  12. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  13. package/dist/components/msa/MSAMouseoverCanvas.js +13 -6
  14. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  15. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +9 -6
  16. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  17. package/dist/components/msa/renderMSAMouseover.js +31 -31
  18. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  19. package/dist/components/overlayColors.d.ts +2 -0
  20. package/dist/components/overlayColors.js +5 -0
  21. package/dist/components/overlayColors.js.map +1 -1
  22. package/dist/components/tree/TreeCanvas.js +29 -24
  23. package/dist/components/tree/TreeCanvas.js.map +1 -1
  24. package/dist/components/tree/TreeCanvasBlock.js +19 -12
  25. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  26. package/dist/components/tree/renderTreeCanvas.d.ts +0 -38
  27. package/dist/components/tree/renderTreeCanvas.js +17 -17
  28. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  29. package/dist/model.js +82 -113
  30. package/dist/model.js.map +1 -1
  31. package/dist/neighborJoining.js +12 -19
  32. package/dist/neighborJoining.js.map +1 -1
  33. package/dist/parseAsn1.js +32 -43
  34. package/dist/parseAsn1.js.map +1 -1
  35. package/dist/renderToSvg.js +3 -0
  36. package/dist/renderToSvg.js.map +1 -1
  37. package/dist/seqPosToGlobalCol.d.ts +6 -19
  38. package/dist/seqPosToGlobalCol.js +6 -33
  39. package/dist/seqPosToGlobalCol.js.map +1 -1
  40. package/dist/useCanvasAutorun.d.ts +23 -1
  41. package/dist/useCanvasAutorun.js +20 -8
  42. package/dist/useCanvasAutorun.js.map +1 -1
  43. package/dist/version.d.ts +1 -1
  44. package/dist/version.js +1 -1
  45. package/dist/version.js.map +1 -1
  46. package/package.json +3 -3
  47. package/src/components/ConservationTrack.tsx +7 -5
  48. package/src/components/MSAView.tsx +24 -30
  49. package/src/components/TextTrack.tsx +7 -12
  50. package/src/components/header/settingsMenuItems.ts +40 -72
  51. package/src/components/msa/MSACanvasBlock.tsx +16 -21
  52. package/src/components/msa/MSAMouseoverCanvas.tsx +10 -6
  53. package/src/components/msa/renderBoxFeatureCanvasBlock.test.ts +51 -0
  54. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +13 -8
  55. package/src/components/msa/renderMSAMouseover.ts +37 -32
  56. package/src/components/overlayColors.ts +6 -0
  57. package/src/components/tree/TreeCanvas.tsx +6 -4
  58. package/src/components/tree/TreeCanvasBlock.tsx +11 -12
  59. package/src/components/tree/renderTreeCanvas.ts +20 -17
  60. package/src/model.ts +97 -130
  61. package/src/modelFilehandleLoaders.test.ts +137 -0
  62. package/src/neighborJoining.test.ts +12 -0
  63. package/src/neighborJoining.ts +12 -20
  64. package/src/parseAsn1.ts +32 -40
  65. package/src/{renderDomainLegendSvg.test.tsx → renderDomainsSvg.test.tsx} +44 -20
  66. package/src/renderToSvg.tsx +3 -0
  67. package/src/seqPosToGlobalCol.test.ts +39 -51
  68. package/src/seqPosToGlobalCol.ts +6 -41
  69. package/src/useCanvasAutorun.test.tsx +98 -0
  70. package/src/useCanvasAutorun.ts +30 -11
  71. package/src/version.ts +1 -1
@@ -0,0 +1,98 @@
1
+ // @vitest-environment jsdom
2
+ //
3
+ // Assigning width/height to a canvas resets its bitmap, and React assigns them
4
+ // on every commit where they changed. A draw that ran during the mobx reaction
5
+ // -- before React commits -- is therefore wiped by the resize that follows it,
6
+ // which is what used to blank the tree on a tree-area drag and the consensus
7
+ // tracks on a vertical zoom. So the hook has to draw again after the resize.
8
+ import React, { act } from 'react'
9
+
10
+ import { observable, runInAction } from 'mobx'
11
+ import { observer } from 'mobx-react'
12
+ import { createRoot } from 'react-dom/client'
13
+ import { afterEach, beforeAll, beforeEach, expect, test } from 'vitest'
14
+
15
+ import { useCanvasAutorun } from './useCanvasAutorun.ts'
16
+
17
+ import type { Root } from 'react-dom/client'
18
+
19
+ Reflect.set(globalThis, 'IS_REACT_ACT_ENVIRONMENT', true)
20
+
21
+ beforeAll(() => {
22
+ HTMLCanvasElement.prototype.getContext = function (this: HTMLCanvasElement) {
23
+ return { canvas: this } as unknown as CanvasRenderingContext2D
24
+ } as unknown as typeof HTMLCanvasElement.prototype.getContext
25
+ })
26
+
27
+ const state = observable({ width: 100, color: 'red' })
28
+ // what each draw saw: the observables it renders from, and the size of the
29
+ // canvas it was drawing onto
30
+ let draws: { observed: number; color: string; canvas: number }[] = []
31
+
32
+ const Probe = observer(function () {
33
+ const ref = useCanvasAutorun({
34
+ draw: ctx => {
35
+ draws.push({
36
+ observed: state.width,
37
+ color: state.color,
38
+ canvas: ctx.canvas.width,
39
+ })
40
+ },
41
+ width: state.width,
42
+ height: 50,
43
+ deps: [],
44
+ })
45
+ return <canvas ref={ref} width={state.width} height={50} />
46
+ })
47
+
48
+ let container: HTMLElement
49
+ let root: Root
50
+
51
+ beforeEach(() => {
52
+ draws = []
53
+ runInAction(() => {
54
+ state.width = 100
55
+ state.color = 'red'
56
+ })
57
+ container = document.createElement('div')
58
+ document.body.appendChild(container)
59
+ root = createRoot(container)
60
+ act(() => {
61
+ root.render(<Probe />)
62
+ })
63
+ })
64
+
65
+ afterEach(() => {
66
+ act(() => {
67
+ root.unmount()
68
+ })
69
+ container.remove()
70
+ })
71
+
72
+ test('the first draw sees the canvas at its rendered size', () => {
73
+ expect(draws).toEqual([{ observed: 100, color: 'red', canvas: 100 }])
74
+ })
75
+
76
+ test('a resize is followed by a draw onto the resized canvas', () => {
77
+ act(() => {
78
+ runInAction(() => {
79
+ state.width = 300
80
+ })
81
+ })
82
+
83
+ expect(container.querySelector('canvas')!.width).toBe(300)
84
+ expect(draws.at(-1)).toEqual({ observed: 300, color: 'red', canvas: 300 })
85
+ })
86
+
87
+ test('a change that leaves the size alone draws exactly once', () => {
88
+ act(() => {
89
+ runInAction(() => {
90
+ state.color = 'blue'
91
+ })
92
+ })
93
+
94
+ expect(draws).toEqual([
95
+ { observed: 100, color: 'red', canvas: 100 },
96
+ { observed: 100, color: 'blue', canvas: 100 },
97
+ ])
98
+ })
@@ -4,18 +4,37 @@ import { autorun } from 'mobx'
4
4
 
5
5
  import type React from 'react'
6
6
 
7
- // Wires a canvas ref to a mobx autorun: grabs the 2d context once the canvas
8
- // mounts and re-runs `draw` whenever any observable it reads changes. Collapses
9
- // the getContext/null-guard/autorun-dispose boilerplate repeated by every
10
- // canvas block host. `deps` controls when the effect (and thus the context
11
- // lookup) is torn down and re-created, matching a normal useEffect dep array.
12
- export function useCanvasAutorun(
13
- draw: (ctx: CanvasRenderingContext2D) => void,
14
- deps: React.DependencyList,
15
- ) {
7
+ /**
8
+ * Wires a canvas ref to a mobx autorun: grabs the 2d context once the canvas
9
+ * mounts and re-runs `draw` whenever any observable it reads changes. Collapses
10
+ * the getContext/null-guard/autorun-dispose boilerplate repeated by every canvas
11
+ * block host.
12
+ *
13
+ * `width`/`height` are the canvas *backing store* size the caller renders onto
14
+ * the element (logical size times the high-res scale factor), and they are load
15
+ * bearing rather than informational. Assigning either resets the canvas bitmap,
16
+ * and React assigns them on every commit where they changed -- which lands
17
+ * *after* the mobx reaction that already redrew at the old size. Without a redraw
18
+ * on the far side of that commit the canvas is left blank: it is what blanked the
19
+ * tree on a tree-area resize and the consensus tracks on a vertical zoom.
20
+ *
21
+ * `deps` covers everything else the effect should re-subscribe on, matching a
22
+ * normal useEffect dep array.
23
+ */
24
+ export function useCanvasAutorun({
25
+ draw,
26
+ width,
27
+ height,
28
+ deps,
29
+ }: {
30
+ draw: (ctx: CanvasRenderingContext2D) => void
31
+ width: number
32
+ height: number
33
+ deps: React.DependencyList
34
+ }) {
16
35
  const ref = useRef<HTMLCanvasElement>(null)
17
36
  // the autorun tracks its own observable reads, so `draw` is intentionally not
18
- // a dependency; `deps` alone controls when the effect re-subscribes
37
+ // a dependency; `deps` plus the canvas size control when it re-subscribes
19
38
  useEffect(() => {
20
39
  const ctx = ref.current?.getContext('2d')
21
40
  return ctx
@@ -24,6 +43,6 @@ export function useCanvasAutorun(
24
43
  })
25
44
  : undefined
26
45
  // eslint-disable-next-line react-hooks/exhaustive-deps
27
- }, deps)
46
+ }, [...deps, width, height])
28
47
  return ref
29
48
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '5.9.0'
1
+ export const version = '5.10.0'