react-msaview 9.1.1 → 9.3.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/bundle/index.js +79 -79
- package/bundle/index.js.map +4 -4
- package/dist/components/DragHandle.js +2 -1
- package/dist/components/DragHandle.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +31 -19
- package/dist/components/MSAViewer.js +2 -1
- package/dist/components/MSAViewer.js.map +1 -1
- package/dist/components/Track.js +2 -5
- package/dist/components/Track.js.map +1 -1
- package/dist/components/VerticalScrollbar.js +1 -1
- package/dist/components/VerticalScrollbar.js.map +1 -1
- package/dist/components/header/Header.js +2 -1
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/minimap/Minimap.js +1 -1
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/msa/MSACanvas.js +16 -27
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/rowpanels/RowPanels.js +8 -6
- package/dist/components/rowpanels/RowPanels.js.map +1 -1
- package/dist/components/settings/SettingsPanel.js +2 -1
- package/dist/components/settings/SettingsPanel.js.map +1 -1
- package/dist/components/settings/TrackList.js +8 -5
- package/dist/components/settings/TrackList.js.map +1 -1
- package/dist/components/tracks/TrackTooltipContent.js +2 -1
- package/dist/components/tracks/TrackTooltipContent.js.map +1 -1
- package/dist/components/tracks/drawTracks.js +13 -6
- package/dist/components/tracks/drawTracks.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js +8 -6
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/element.d.ts +11 -7
- package/dist/element.js +46 -8
- package/dist/element.js.map +1 -1
- package/dist/featurePanels.d.ts +0 -7
- package/dist/featurePanels.js +1 -16
- package/dist/featurePanels.js.map +1 -1
- package/dist/ggplotPalettes.js +12 -1
- package/dist/ggplotPalettes.js.map +1 -1
- package/dist/groupKeys.d.ts +6 -0
- package/dist/groupKeys.js +52 -0
- package/dist/groupKeys.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/model.d.ts +40 -17
- package/dist/model.js +123 -54
- package/dist/model.js.map +1 -1
- package/dist/scales.d.ts +114 -13
- package/dist/scales.js +154 -19
- package/dist/scales.js.map +1 -1
- package/dist/svgTestUtil.d.ts +22 -12
- package/dist/types.d.ts +30 -28
- package/dist/useDragScroll.d.ts +4 -3
- package/dist/useDragScroll.js +9 -6
- package/dist/useDragScroll.js.map +1 -1
- package/dist/useWheelScroll.d.ts +26 -8
- package/dist/useWheelScroll.js +108 -71
- package/dist/useWheelScroll.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/viewerSnapshot.js +2 -1
- package/dist/viewerSnapshot.js.map +1 -1
- package/package.json +3 -3
- package/src/components/DragHandle.tsx +2 -1
- package/src/components/MSAViewer.tsx +11 -8
- package/src/components/Track.tsx +2 -8
- package/src/components/VerticalScrollbar.tsx +1 -1
- package/src/components/header/Header.tsx +2 -1
- package/src/components/minimap/Minimap.tsx +1 -1
- package/src/components/msa/MSACanvas.tsx +14 -35
- package/src/components/rowpanels/RowPanels.tsx +8 -10
- package/src/components/settings/SettingsPanel.tsx +2 -1
- package/src/components/settings/TrackList.tsx +9 -4
- package/src/components/tracks/TrackTooltipContent.tsx +2 -1
- package/src/components/tracks/drawTracks.ts +15 -7
- package/src/components/tree/TreeCanvas.tsx +8 -10
- package/src/constants.ts +2 -0
- package/src/element.ts +53 -8
- package/src/featurePanels.ts +1 -25
- package/src/ggplotPalettes.ts +13 -1
- package/src/groupKeys.ts +54 -0
- package/src/index.ts +13 -2
- package/src/model.ts +174 -72
- package/src/scales.ts +268 -25
- package/src/types.ts +29 -26
- package/src/useDragScroll.ts +11 -8
- package/src/useWheelScroll.ts +124 -84
- package/src/version.ts +1 -1
- package/src/viewerSnapshot.ts +3 -1
package/src/useWheelScroll.ts
CHANGED
|
@@ -2,24 +2,71 @@ import { useEffect, useRef, useState } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { WheelZoomSmoother } from './wheelZoom.ts'
|
|
4
4
|
|
|
5
|
+
import type { MsaViewModel } from './model.ts'
|
|
6
|
+
|
|
5
7
|
const zoomSettleMs = 150
|
|
6
8
|
|
|
9
|
+
// a wheel event this soon after one the view took belongs to the same gesture,
|
|
10
|
+
// so the view keeps it at an edge rather than handing the momentum to the page
|
|
11
|
+
const wheelLatchMs = 150
|
|
12
|
+
|
|
13
|
+
type Axes = 'x' | 'y' | 'xy'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Whether a vertical wheel `delta` moves a view at `scrollY`. At the top or
|
|
17
|
+
* bottom, or when every row fits, the page scrolls instead, so an embedded
|
|
18
|
+
* viewer never traps the reader scrolling past it.
|
|
19
|
+
*/
|
|
20
|
+
export function canScrollY(model: MsaViewModel, delta: number) {
|
|
21
|
+
const { scrollY, maxScrollY } = model
|
|
22
|
+
return delta > 0 ? scrollY > maxScrollY : delta < 0 && scrollY < 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Safari reads a touch-action holding pinch-zoom as invalid and falls back to
|
|
26
|
+
// auto, which hands every swipe to the page
|
|
27
|
+
const pinchZoom =
|
|
28
|
+
typeof CSS !== 'undefined' && CSS.supports('touch-action', 'pinch-zoom')
|
|
29
|
+
? 'pinch-zoom'
|
|
30
|
+
: undefined
|
|
31
|
+
|
|
32
|
+
function withPinch(pan: string) {
|
|
33
|
+
return pinchZoom ? `${pan} ${pinchZoom}` : pan
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The CSS touch-action for a pannable area: the browser keeps a touch pan
|
|
38
|
+
* along every axis the view cannot scroll, and pinch zoom on the page where it
|
|
39
|
+
* supports that value.
|
|
40
|
+
*/
|
|
41
|
+
export function panTouchAction(model: MsaViewModel, axes: Axes) {
|
|
42
|
+
const x = axes.includes('x') && model.maxScrollX < 0
|
|
43
|
+
const y = axes.includes('y') && model.maxScrollY < 0
|
|
44
|
+
return x && y
|
|
45
|
+
? (pinchZoom ?? 'none')
|
|
46
|
+
: x
|
|
47
|
+
? withPinch('pan-y')
|
|
48
|
+
: y
|
|
49
|
+
? withPinch('pan-x')
|
|
50
|
+
: 'auto'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Wheel scrolling, wheel zoom and pointer-drag panning of `model` over `ref`,
|
|
55
|
+
* along `axes`. The returned `onPointerDown` starts a pan with a mouse, a pen
|
|
56
|
+
* or a finger; put `touchAction` on the same element.
|
|
57
|
+
*/
|
|
7
58
|
export function useWheelScroll({
|
|
8
59
|
ref,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
onZoomSettle,
|
|
60
|
+
model,
|
|
61
|
+
axes,
|
|
62
|
+
zoom,
|
|
13
63
|
onWheelScroll,
|
|
14
|
-
scrollZoom,
|
|
15
64
|
}: {
|
|
16
65
|
ref: React.RefObject<HTMLDivElement | null>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
onZoomSettle?: () => void
|
|
66
|
+
model: MsaViewModel
|
|
67
|
+
axes: Axes
|
|
68
|
+
zoom?: boolean
|
|
21
69
|
onWheelScroll?: (event: WheelEvent) => void
|
|
22
|
-
scrollZoom?: boolean
|
|
23
70
|
}) {
|
|
24
71
|
const scheduled = useRef(false)
|
|
25
72
|
const zoomFrame = useRef<number | undefined>(undefined)
|
|
@@ -28,13 +75,18 @@ export function useWheelScroll({
|
|
|
28
75
|
)
|
|
29
76
|
const deltaX = useRef(0)
|
|
30
77
|
const deltaY = useRef(0)
|
|
78
|
+
const lastTaken = useRef(Number.NEGATIVE_INFINITY)
|
|
31
79
|
const zoomSmoother = useRef(new WheelZoomSmoother())
|
|
32
80
|
const zoomX = useRef(0)
|
|
33
81
|
const zoomY = useRef(0)
|
|
34
82
|
const lastZoomTime = useRef<number | null>(null)
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
83
|
+
const prev = useRef({ x: 0, y: 0 })
|
|
84
|
+
const latest = useRef({ x: 0, y: 0 })
|
|
85
|
+
const panFrame = useRef(false)
|
|
86
|
+
// the pointer that started the pan; a second finger neither moves nor ends it
|
|
87
|
+
const [panPointer, setPanPointer] = useState<number>()
|
|
88
|
+
const x = axes.includes('x')
|
|
89
|
+
const y = axes.includes('y')
|
|
38
90
|
|
|
39
91
|
useEffect(() => {
|
|
40
92
|
const curr = ref.current
|
|
@@ -47,11 +99,10 @@ export function useWheelScroll({
|
|
|
47
99
|
// shift is the escape hatch to pan instead.
|
|
48
100
|
const isCtrlZoom = event.ctrlKey || event.metaKey
|
|
49
101
|
const isScrollZoom =
|
|
50
|
-
scrollZoom &&
|
|
102
|
+
model.scrollZoom &&
|
|
51
103
|
!event.shiftKey &&
|
|
52
104
|
Math.abs(event.deltaY) >= Math.abs(event.deltaX)
|
|
53
|
-
if (
|
|
54
|
-
const zoom = onZoom
|
|
105
|
+
if (zoom && (isCtrlZoom || isScrollZoom)) {
|
|
55
106
|
const rect = curr!.getBoundingClientRect()
|
|
56
107
|
zoomSmoother.current.add(event.deltaY, event.deltaMode, isCtrlZoom)
|
|
57
108
|
zoomX.current = event.clientX - rect.left
|
|
@@ -62,14 +113,14 @@ export function useWheelScroll({
|
|
|
62
113
|
const elapsed =
|
|
63
114
|
lastZoomTime.current === null ? 16.67 : now - lastZoomTime.current
|
|
64
115
|
lastZoomTime.current = now
|
|
65
|
-
|
|
116
|
+
model.wheelZoomToPos(
|
|
66
117
|
zoomSmoother.current.step(Math.min(100, elapsed)),
|
|
67
118
|
zoomX.current,
|
|
68
119
|
zoomY.current,
|
|
69
120
|
)
|
|
70
121
|
clearTimeout(zoomSettle.current)
|
|
71
122
|
zoomSettle.current = setTimeout(() => {
|
|
72
|
-
|
|
123
|
+
model.endWheelZoom()
|
|
73
124
|
}, zoomSettleMs)
|
|
74
125
|
zoomFrame.current = zoomSmoother.current.idle
|
|
75
126
|
? undefined
|
|
@@ -81,30 +132,30 @@ export function useWheelScroll({
|
|
|
81
132
|
event.stopPropagation()
|
|
82
133
|
return
|
|
83
134
|
}
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
135
|
+
// the dominant axis decides: a trackpad's vertical swipe carries a pixel
|
|
136
|
+
// or two of deltaX, which must not hold the page at the bottom row. A
|
|
137
|
+
// horizontal wheel stays taken at the edges, where the page would read
|
|
138
|
+
// it as a swipe back through history.
|
|
139
|
+
const { deltaX: dx, deltaY: dy } = event
|
|
140
|
+
const vertical = Math.abs(dy) >= Math.abs(dx)
|
|
141
|
+
const latched = event.timeStamp - lastTaken.current < wheelLatchMs
|
|
142
|
+
const takeY =
|
|
143
|
+
y && dy !== 0 && (latched || canScrollY(model, dy) || !vertical)
|
|
144
|
+
const takeX = x && dx !== 0 && (!vertical || takeY)
|
|
145
|
+
if (takeX || takeY) {
|
|
146
|
+
if (takeY && vertical) {
|
|
147
|
+
lastTaken.current = event.timeStamp
|
|
92
148
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
149
|
+
onWheelScroll?.(event)
|
|
150
|
+
deltaX.current += takeX ? event.deltaX : 0
|
|
151
|
+
deltaY.current += takeY ? event.deltaY : 0
|
|
97
152
|
if (!scheduled.current) {
|
|
98
153
|
scheduled.current = true
|
|
99
154
|
requestAnimationFrame(() => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (onScrollY) {
|
|
105
|
-
onScrollY(-deltaY.current)
|
|
106
|
-
deltaY.current = 0
|
|
107
|
-
}
|
|
155
|
+
model.doScrollX(-deltaX.current)
|
|
156
|
+
model.doScrollY(-deltaY.current)
|
|
157
|
+
deltaX.current = 0
|
|
158
|
+
deltaY.current = 0
|
|
108
159
|
scheduled.current = false
|
|
109
160
|
})
|
|
110
161
|
}
|
|
@@ -121,64 +172,53 @@ export function useWheelScroll({
|
|
|
121
172
|
}
|
|
122
173
|
clearTimeout(zoomSettle.current)
|
|
123
174
|
}
|
|
124
|
-
}, [
|
|
125
|
-
ref,
|
|
126
|
-
onScrollX,
|
|
127
|
-
onScrollY,
|
|
128
|
-
onZoom,
|
|
129
|
-
onZoomSettle,
|
|
130
|
-
onWheelScroll,
|
|
131
|
-
scrollZoom,
|
|
132
|
-
])
|
|
175
|
+
}, [ref, model, x, y, zoom, onWheelScroll])
|
|
133
176
|
|
|
134
177
|
useEffect(() => {
|
|
135
|
-
if (
|
|
178
|
+
if (panPointer === undefined) {
|
|
136
179
|
return
|
|
137
180
|
}
|
|
138
|
-
|
|
181
|
+
// a frame applies the distance from the last applied position to the
|
|
182
|
+
// newest one, so a move landing while a frame is pending is not dropped
|
|
183
|
+
function onMove(event: PointerEvent) {
|
|
184
|
+
if (event.pointerId !== panPointer) {
|
|
185
|
+
return
|
|
186
|
+
}
|
|
139
187
|
event.preventDefault()
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
prevY.current = event.clientY
|
|
151
|
-
})
|
|
152
|
-
}
|
|
188
|
+
latest.current = { x: event.clientX, y: event.clientY }
|
|
189
|
+
if (!panFrame.current) {
|
|
190
|
+
panFrame.current = true
|
|
191
|
+
requestAnimationFrame(() => {
|
|
192
|
+
const to = latest.current
|
|
193
|
+
model.doScrollX(x ? to.x - prev.current.x : 0)
|
|
194
|
+
model.doScrollY(y ? to.y - prev.current.y : 0)
|
|
195
|
+
prev.current = to
|
|
196
|
+
panFrame.current = false
|
|
197
|
+
})
|
|
153
198
|
}
|
|
154
199
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
setMouseDragging(false)
|
|
200
|
+
function onUp(event: PointerEvent) {
|
|
201
|
+
if (event.pointerId === panPointer) {
|
|
202
|
+
setPanPointer(undefined)
|
|
203
|
+
}
|
|
160
204
|
}
|
|
161
|
-
|
|
162
|
-
window.addEventListener('
|
|
163
|
-
window.addEventListener('
|
|
205
|
+
window.addEventListener('pointermove', onMove, true)
|
|
206
|
+
window.addEventListener('pointerup', onUp, true)
|
|
207
|
+
window.addEventListener('pointercancel', onUp, true)
|
|
164
208
|
return () => {
|
|
165
|
-
window.removeEventListener('
|
|
166
|
-
window.removeEventListener('
|
|
209
|
+
window.removeEventListener('pointermove', onMove, true)
|
|
210
|
+
window.removeEventListener('pointerup', onUp, true)
|
|
211
|
+
window.removeEventListener('pointercancel', onUp, true)
|
|
167
212
|
}
|
|
168
|
-
}, [
|
|
213
|
+
}, [panPointer, model, x, y])
|
|
169
214
|
|
|
170
|
-
function
|
|
171
|
-
if (event.button === 0) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
215
|
+
function onPointerDown(event: React.PointerEvent) {
|
|
216
|
+
if (event.button === 0 && event.isPrimary && panPointer === undefined) {
|
|
217
|
+
prev.current = { x: event.clientX, y: event.clientY }
|
|
218
|
+
latest.current = prev.current
|
|
219
|
+
setPanPointer(event.pointerId)
|
|
175
220
|
}
|
|
176
221
|
}
|
|
177
222
|
|
|
178
|
-
|
|
179
|
-
event.preventDefault()
|
|
180
|
-
setMouseDragging(false)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return { onMouseDown, onMouseUp }
|
|
223
|
+
return { onPointerDown, touchAction: panTouchAction(model, axes) }
|
|
184
224
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '9.
|
|
1
|
+
export const version = '9.3.0'
|
package/src/viewerSnapshot.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createJBrowseTheme } from '@jbrowse/core/ui/theme'
|
|
2
2
|
|
|
3
|
+
import { letterColors } from './scales.ts'
|
|
4
|
+
|
|
3
5
|
import type { MSAViewerProps } from './components/MSAViewer.tsx'
|
|
4
6
|
import type { ColorScheme, Highlight } from './types.ts'
|
|
5
7
|
|
|
@@ -7,7 +9,7 @@ export function colorSchemeSnapshot(scheme: ColorScheme | undefined) {
|
|
|
7
9
|
return typeof scheme === 'string'
|
|
8
10
|
? { colorSchemeName: scheme }
|
|
9
11
|
: scheme
|
|
10
|
-
? { customColorScheme: scheme
|
|
12
|
+
? { customColorScheme: letterColors(scheme) }
|
|
11
13
|
: {}
|
|
12
14
|
}
|
|
13
15
|
|