react-msaview 5.6.1 → 5.6.3
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 +95 -95
- package/bundle/index.js.map +4 -4
- package/dist/clustalX.d.ts +4 -3
- package/dist/clustalX.js +25 -4
- package/dist/clustalX.js.map +1 -1
- package/dist/columnCounts.d.ts +37 -0
- package/dist/columnCounts.js +148 -0
- package/dist/columnCounts.js.map +1 -0
- package/dist/components/MSAView.js +1 -8
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/getVisibleLeaves.d.ts +6 -0
- package/dist/components/getVisibleLeaves.js.map +1 -0
- package/dist/components/msa/MSACanvasBlock.js +17 -54
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +2 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +21 -19
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +97 -96
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +9 -32
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/msa/useMsaBlockMouse.d.ts +23 -0
- package/dist/components/msa/useMsaBlockMouse.js +45 -0
- package/dist/components/msa/useMsaBlockMouse.js.map +1 -0
- package/dist/components/tree/TreeCanvas.js +14 -19
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +40 -111
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.d.ts +2 -0
- package/dist/components/tree/renderTreeCanvas.js +15 -3
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/components/tree/useTreeHover.d.ts +24 -0
- package/dist/components/tree/useTreeHover.js +44 -0
- package/dist/components/tree/useTreeHover.js.map +1 -0
- package/dist/hierarchy.js +15 -9
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/model/msaModel.js +4 -3
- package/dist/model/msaModel.js.map +1 -1
- package/dist/model/treeModel.js +9 -8
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +52 -10
- package/dist/model.js +208 -93
- package/dist/model.js.map +1 -1
- package/dist/propertyConservation.d.ts +2 -1
- package/dist/propertyConservation.js +36 -25
- package/dist/propertyConservation.js.map +1 -1
- package/dist/seqPosToGlobalCol.d.ts +7 -0
- package/dist/seqPosToGlobalCol.js +17 -0
- package/dist/seqPosToGlobalCol.js.map +1 -1
- package/dist/stripDefault.d.ts +21 -0
- package/dist/stripDefault.js +26 -0
- package/dist/stripDefault.js.map +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/clustalX.test.ts +9 -11
- package/src/clustalX.ts +27 -28
- package/src/columnCounts.test.ts +58 -0
- package/src/columnCounts.ts +170 -0
- package/src/components/MSAView.tsx +1 -9
- package/src/components/{msa/getVisibleLeaves.ts → getVisibleLeaves.ts} +1 -1
- package/src/components/msa/MSACanvasBlock.tsx +42 -110
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +25 -18
- package/src/components/msa/renderMSABlock.ts +113 -125
- package/src/components/msa/renderMSAMouseover.ts +11 -34
- package/src/components/msa/useMsaBlockMouse.ts +81 -0
- package/src/components/tree/TreeCanvas.tsx +16 -23
- package/src/components/tree/TreeCanvasBlock.tsx +62 -143
- package/src/components/tree/renderTreeCanvas.test.ts +100 -190
- package/src/components/tree/renderTreeCanvas.ts +14 -3
- package/src/components/tree/useTreeHover.ts +69 -0
- package/src/domainBands.test.ts +98 -0
- package/src/hierarchy.ts +15 -9
- package/src/index.ts +7 -0
- package/src/model/msaModel.ts +4 -6
- package/src/model/treeModel.ts +9 -14
- package/src/model.ts +241 -112
- package/src/propertyConservation.test.ts +2 -13
- package/src/propertyConservation.ts +40 -31
- package/src/rowSpace.test.ts +46 -0
- package/src/seqPosToGlobalCol.ts +18 -0
- package/src/stripDefault.test.ts +48 -0
- package/src/stripDefault.ts +35 -0
- package/src/types.ts +23 -0
- package/src/version.ts +1 -1
- package/dist/components/msa/getVisibleLeaves.d.ts +0 -6
- package/dist/components/msa/getVisibleLeaves.js.map +0 -1
- package/dist/useDevicePixelRatio.d.ts +0 -1
- package/dist/useDevicePixelRatio.js +0 -27
- package/dist/useDevicePixelRatio.js.map +0 -1
- package/src/useDevicePixelRatio.ts +0 -29
- /package/dist/components/{msa/getVisibleLeaves.js → getVisibleLeaves.js} +0 -0
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { numResidueSlots, residueSlotOfLetter } from './columnCounts.ts'
|
|
2
|
+
|
|
3
|
+
import type { ColumnCounts } from './columnCounts.ts'
|
|
4
|
+
|
|
1
5
|
// Disjoint physicochemical classes for the 20 standard amino acids. Property
|
|
2
6
|
// conservation asks a different question than identity conservation: a column
|
|
3
7
|
// can vary in exact residue yet stay within one property class (a conservative
|
|
@@ -29,8 +33,15 @@ const aminoPropertyClass: Record<string, string> = {
|
|
|
29
33
|
P: 'special',
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
const
|
|
33
|
-
const maxPropertyEntropy = Math.log2(
|
|
36
|
+
const propertyClasses = [...new Set(Object.values(aminoPropertyClass))]
|
|
37
|
+
const maxPropertyEntropy = Math.log2(propertyClasses.length)
|
|
38
|
+
|
|
39
|
+
// residue slot -> index into propertyClasses (-1 for residues with no class,
|
|
40
|
+
// e.g. X or '*'), so the per-column tally needs no string lookups
|
|
41
|
+
const classOfSlot = new Int8Array(numResidueSlots).fill(-1)
|
|
42
|
+
for (const [letter, cls] of Object.entries(aminoPropertyClass)) {
|
|
43
|
+
classOfSlot[residueSlotOfLetter(letter)] = propertyClasses.indexOf(cls)
|
|
44
|
+
}
|
|
34
45
|
|
|
35
46
|
/**
|
|
36
47
|
* Per-column property conservation using Shannon entropy over physicochemical
|
|
@@ -38,36 +49,34 @@ const maxPropertyEntropy = Math.log2(numPropertyClasses)
|
|
|
38
49
|
* residue shares one property class. Mirrors the identity-conservation formula
|
|
39
50
|
* so the two tracks are directly comparable: (1 - H/Hmax) * (1 - gapFraction).
|
|
40
51
|
*/
|
|
41
|
-
export function calculatePropertyConservation(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
export function calculatePropertyConservation(counts: ColumnCounts) {
|
|
53
|
+
const classCounts = new Float64Array(propertyClasses.length)
|
|
54
|
+
return Array.from({ length: counts.numColumns }, (_, col) => {
|
|
55
|
+
const total = counts.total(col)
|
|
56
|
+
let score = 0
|
|
57
|
+
if (total) {
|
|
58
|
+
classCounts.fill(0)
|
|
59
|
+
let nonGapTotal = 0
|
|
60
|
+
counts.forEachResidue(col, (slot, count) => {
|
|
61
|
+
const cls = classOfSlot[slot]!
|
|
62
|
+
if (cls >= 0) {
|
|
63
|
+
classCounts[cls]! += count
|
|
64
|
+
nonGapTotal += count
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
if (nonGapTotal > 0) {
|
|
68
|
+
let entropy = 0
|
|
69
|
+
for (const count of classCounts) {
|
|
70
|
+
if (count > 0) {
|
|
71
|
+
const freq = count / nonGapTotal
|
|
72
|
+
entropy -= freq * Math.log2(freq)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const gapFraction = (total - nonGapTotal) / total
|
|
76
|
+
score =
|
|
77
|
+
Math.max(0, 1 - entropy / maxPropertyEntropy) * (1 - gapFraction)
|
|
58
78
|
}
|
|
59
79
|
}
|
|
60
|
-
|
|
61
|
-
return 0
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let entropy = 0
|
|
65
|
-
for (const count of Object.values(classCounts)) {
|
|
66
|
-
const freq = count / nonGapTotal
|
|
67
|
-
entropy -= freq * Math.log2(freq)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const gapFraction = (total - nonGapTotal) / total
|
|
71
|
-
return Math.max(0, 1 - entropy / maxPropertyEntropy) * (1 - gapFraction)
|
|
80
|
+
return score
|
|
72
81
|
})
|
|
73
82
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { expect, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import MSAModelF from './model.ts'
|
|
5
|
+
|
|
6
|
+
// the tree names a tip the alignment has no sequence for. It still occupies a
|
|
7
|
+
// row of vertical space (drawn blank), so anything that maps y-pixels to rows has
|
|
8
|
+
// to count it, or the last row becomes unhoverable and fit-to-height overshoots.
|
|
9
|
+
const msa = `>a
|
|
10
|
+
ACGT
|
|
11
|
+
>b
|
|
12
|
+
ACGT`
|
|
13
|
+
const tree = '((a:1,b:1):1,ghost:1);'
|
|
14
|
+
|
|
15
|
+
function makeModel() {
|
|
16
|
+
const model = MSAModelF().create({
|
|
17
|
+
id: 'row-space-test',
|
|
18
|
+
type: 'MsaView',
|
|
19
|
+
msaFormat: 'fasta',
|
|
20
|
+
height: 400,
|
|
21
|
+
data: { msa, tree },
|
|
22
|
+
})
|
|
23
|
+
model.setWidth(800)
|
|
24
|
+
return model
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test('row space counts tree leaves, not just rows with sequence', () => {
|
|
28
|
+
const model = makeModel()
|
|
29
|
+
expect(model.rowNames).toEqual(['a', 'b', 'ghost'])
|
|
30
|
+
expect(model.rows.map(r => r[0])).toEqual(['a', 'b'])
|
|
31
|
+
expect(model.numRows).toBe(3)
|
|
32
|
+
expect(model.totalHeight).toBe(3 * model.rowHeight)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('the last row is addressable', () => {
|
|
36
|
+
const model = makeModel()
|
|
37
|
+
const lastRow = model.numRows - 1
|
|
38
|
+
model.setMousePos(0, lastRow)
|
|
39
|
+
expect(model.mouseOverRowName).toBe('ghost')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('fit vertically leaves every row on screen', () => {
|
|
43
|
+
const model = makeModel()
|
|
44
|
+
model.fitVertically()
|
|
45
|
+
expect(model.totalHeight).toBeLessThanOrEqual(model.msaAreaHeight)
|
|
46
|
+
})
|
package/src/seqPosToGlobalCol.ts
CHANGED
|
@@ -15,6 +15,24 @@ import { isBlank } from './util.ts'
|
|
|
15
15
|
* seqPosToGlobalCol({ row: "A-TG-C", seqPos: 2 }) // → 3 (G)
|
|
16
16
|
* seqPosToGlobalCol({ row: "A-TG-C", seqPos: 3 }) // → 5 (C)
|
|
17
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* The global column of every ungapped sequence position in a row, i.e.
|
|
20
|
+
* index[seqPos] is the column holding the seqPos-th non-gap character. Amortizes
|
|
21
|
+
* repeated seqPosToGlobalCol lookups (the domain overlay does one per feature
|
|
22
|
+
* per row) into a single pass per row.
|
|
23
|
+
*/
|
|
24
|
+
export function buildSeqPosIndex(row: string) {
|
|
25
|
+
const index = new Int32Array(row.length)
|
|
26
|
+
let n = 0
|
|
27
|
+
for (let col = 0; col < row.length; col++) {
|
|
28
|
+
// bit trick: (code - 45) >>> 0 <= 1 checks for '-' (45) or '.' (46)
|
|
29
|
+
if (!((row.charCodeAt(col) - 45) >>> 0 <= 1)) {
|
|
30
|
+
index[n++] = col
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return index.subarray(0, n)
|
|
34
|
+
}
|
|
35
|
+
|
|
18
36
|
export function seqPosToGlobalCol({
|
|
19
37
|
row,
|
|
20
38
|
seqPos,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getSnapshot, types } from '@jbrowse/mobx-state-tree'
|
|
2
|
+
import { afterEach, expect, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { stripDefault } from './stripDefault.ts'
|
|
5
|
+
|
|
6
|
+
const real = types.stripDefault
|
|
7
|
+
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
types.stripDefault = real
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test('strips defaults from the snapshot when the host mst supports it', () => {
|
|
13
|
+
const model = types
|
|
14
|
+
.model({ a: stripDefault(types.boolean, false) })
|
|
15
|
+
.create({})
|
|
16
|
+
expect(model.a).toBe(false)
|
|
17
|
+
expect(getSnapshot(model).a).toBeUndefined()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// The path that actually runs inside every RELEASED jbrowse-web: the host
|
|
21
|
+
// supplies its own mobx-state-tree, and stripDefault only exists in the copy
|
|
22
|
+
// that ships with unreleased @jbrowse/core. Calling it there used to throw while
|
|
23
|
+
// the model was built, which error-paged the whole app. dev has the API, so
|
|
24
|
+
// without stubbing it away this path is never exercised.
|
|
25
|
+
test('falls back to types.optional when the host mst lacks stripDefault', () => {
|
|
26
|
+
// @ts-expect-error deleting it is the point: simulate an older host
|
|
27
|
+
delete types.stripDefault
|
|
28
|
+
const model = types
|
|
29
|
+
.model({ a: stripDefault(types.boolean, false) })
|
|
30
|
+
.create({})
|
|
31
|
+
expect(model.a).toBe(false)
|
|
32
|
+
// the only difference: the default is serialized rather than omitted
|
|
33
|
+
expect(getSnapshot(model).a).toBe(false)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('non-default values survive both paths', () => {
|
|
37
|
+
const withApi = types
|
|
38
|
+
.model({ a: stripDefault(types.number, 1) })
|
|
39
|
+
.create({ a: 5 })
|
|
40
|
+
expect(getSnapshot(withApi).a).toBe(5)
|
|
41
|
+
|
|
42
|
+
// @ts-expect-error see above
|
|
43
|
+
delete types.stripDefault
|
|
44
|
+
const without = types
|
|
45
|
+
.model({ a: stripDefault(types.number, 1) })
|
|
46
|
+
.create({ a: 5 })
|
|
47
|
+
expect(getSnapshot(without).a).toBe(5)
|
|
48
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { types } from '@jbrowse/mobx-state-tree'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
IAnyType,
|
|
5
|
+
IOptionalIType,
|
|
6
|
+
OptionalDefaultValueOrFunction,
|
|
7
|
+
} from '@jbrowse/mobx-state-tree'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* `types.stripDefault`, degrading to `types.optional` on hosts that lack it.
|
|
11
|
+
*
|
|
12
|
+
* stripDefault keeps defaulted properties out of snapshots, but it exists only
|
|
13
|
+
* in the mobx-state-tree that ships with unreleased @jbrowse/core. This package
|
|
14
|
+
* declares `@jbrowse/mobx-state-tree: ^5.0.0`, and inside jbrowse-web the host
|
|
15
|
+
* supplies its own copy at runtime -- the plugin bundle externalizes it -- so we
|
|
16
|
+
* genuinely do not know which version we are calling until we are running.
|
|
17
|
+
*
|
|
18
|
+
* Calling it where it is absent throws while the model is being constructed,
|
|
19
|
+
* which in a plugin bundle means PluginLoader rejects and the entire app renders
|
|
20
|
+
* its error page. Losing minimal snapshots is a far better failure than losing
|
|
21
|
+
* the session, so this degrades instead.
|
|
22
|
+
*
|
|
23
|
+
* The two signatures are identical, so behaviour differs only in that defaults
|
|
24
|
+
* reappear in snapshots on older hosts (bigger session snapshots and shared
|
|
25
|
+
* URLs, nothing incorrect). stripDefaultSnapshot.test.ts still pins the stripped
|
|
26
|
+
* behaviour, since dev runs against a version that has it.
|
|
27
|
+
*/
|
|
28
|
+
export function stripDefault<IT extends IAnyType>(
|
|
29
|
+
type: IT,
|
|
30
|
+
defaultValue: OptionalDefaultValueOrFunction<IT>,
|
|
31
|
+
): IOptionalIType<IT, [undefined]> {
|
|
32
|
+
return typeof types.stripDefault === 'function'
|
|
33
|
+
? types.stripDefault(type, defaultValue)
|
|
34
|
+
: types.optional(type, defaultValue)
|
|
35
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -26,6 +26,29 @@ export interface BasicTrack {
|
|
|
26
26
|
model: TextTrackModel & BarTrackModel
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
// one InterProScan/GFF annotation location flattened onto a single row, as
|
|
30
|
+
// produced by the model's tidyInterProAnnotations
|
|
31
|
+
export interface TidyDomainAnnotation {
|
|
32
|
+
id: string
|
|
33
|
+
name: string
|
|
34
|
+
accession: string
|
|
35
|
+
description: string
|
|
36
|
+
featureType?: string
|
|
37
|
+
start: number
|
|
38
|
+
end: number
|
|
39
|
+
strand?: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// a tidy annotation resolved to the visible column span it is drawn across.
|
|
43
|
+
// stackIndex is its position within its row's annotation list, which the
|
|
44
|
+
// sub-row layout uses to stack boxes.
|
|
45
|
+
export interface DomainBand {
|
|
46
|
+
annotation: TidyDomainAnnotation
|
|
47
|
+
startCol: number
|
|
48
|
+
endCol: number
|
|
49
|
+
stackIndex: number
|
|
50
|
+
}
|
|
51
|
+
|
|
29
52
|
export interface Node {
|
|
30
53
|
children?: Node[]
|
|
31
54
|
name?: string
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '5.6.
|
|
1
|
+
export const version = '5.6.3'
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { MsaViewModel } from '../../model.ts';
|
|
2
|
-
export declare function getVisibleLeaves({ model, offsetY, blockSizeY, }: {
|
|
3
|
-
model: MsaViewModel;
|
|
4
|
-
offsetY: number;
|
|
5
|
-
blockSizeY: number;
|
|
6
|
-
}): import("../../hierarchy.ts").HierarchyNode<import("../../types.ts").NodeWithIdsAndLength>[];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getVisibleLeaves.js","sourceRoot":"","sources":["../../../src/components/msa/getVisibleLeaves.ts"],"names":[],"mappings":"AAEA,2EAA2E;AAC3E,2DAA2D;AAC3D,MAAM,UAAU,gBAAgB,CAAC,EAC/B,KAAK,EACL,OAAO,EACP,UAAU,GAKX;IACC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;IACnC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC,CAAA;IACzE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CACnB,CAAC,EACD,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,CAC1D,CAAA;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;AACnC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function useDevicePixelRatio(): number;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { useSyncExternalStore } from 'react';
|
|
2
|
-
// Subscribes to devicePixelRatio changes (moving the window between monitors,
|
|
3
|
-
// browser zoom). The matchMedia query is pinned to the current ratio, so on each
|
|
4
|
-
// change we re-register against the new ratio to keep tracking subsequent moves.
|
|
5
|
-
function subscribe(callback) {
|
|
6
|
-
if (typeof window === 'undefined') {
|
|
7
|
-
return () => { };
|
|
8
|
-
}
|
|
9
|
-
let mql = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
|
|
10
|
-
function onChange() {
|
|
11
|
-
callback();
|
|
12
|
-
mql.removeEventListener('change', onChange);
|
|
13
|
-
mql = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
|
|
14
|
-
mql.addEventListener('change', onChange);
|
|
15
|
-
}
|
|
16
|
-
mql.addEventListener('change', onChange);
|
|
17
|
-
return () => {
|
|
18
|
-
mql.removeEventListener('change', onChange);
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
function getSnapshot() {
|
|
22
|
-
return typeof window === 'undefined' ? 1 : window.devicePixelRatio;
|
|
23
|
-
}
|
|
24
|
-
export function useDevicePixelRatio() {
|
|
25
|
-
return useSyncExternalStore(subscribe, getSnapshot, () => 1);
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=useDevicePixelRatio.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useDevicePixelRatio.js","sourceRoot":"","sources":["../src/useDevicePixelRatio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAE5C,8EAA8E;AAC9E,iFAAiF;AACjF,iFAAiF;AACjF,SAAS,SAAS,CAAC,QAAoB;IACrC,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IACjB,CAAC;IACD,IAAI,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,gBAAgB,MAAM,CAAC,gBAAgB,OAAO,CAAC,CAAA;IAC3E,SAAS,QAAQ;QACf,QAAQ,EAAE,CAAA;QACV,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAC3C,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,gBAAgB,MAAM,CAAC,gBAAgB,OAAO,CAAC,CAAA;QACvE,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC1C,CAAC;IACD,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACxC,OAAO,GAAG,EAAE;QACV,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC7C,CAAC,CAAA;AACH,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAA;AACpE,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAA;AAC9D,CAAC"}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { useSyncExternalStore } from 'react'
|
|
2
|
-
|
|
3
|
-
// Subscribes to devicePixelRatio changes (moving the window between monitors,
|
|
4
|
-
// browser zoom). The matchMedia query is pinned to the current ratio, so on each
|
|
5
|
-
// change we re-register against the new ratio to keep tracking subsequent moves.
|
|
6
|
-
function subscribe(callback: () => void) {
|
|
7
|
-
if (typeof window === 'undefined') {
|
|
8
|
-
return () => {}
|
|
9
|
-
}
|
|
10
|
-
let mql = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`)
|
|
11
|
-
function onChange() {
|
|
12
|
-
callback()
|
|
13
|
-
mql.removeEventListener('change', onChange)
|
|
14
|
-
mql = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`)
|
|
15
|
-
mql.addEventListener('change', onChange)
|
|
16
|
-
}
|
|
17
|
-
mql.addEventListener('change', onChange)
|
|
18
|
-
return () => {
|
|
19
|
-
mql.removeEventListener('change', onChange)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function getSnapshot() {
|
|
24
|
-
return typeof window === 'undefined' ? 1 : window.devicePixelRatio
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function useDevicePixelRatio() {
|
|
28
|
-
return useSyncExternalStore(subscribe, getSnapshot, () => 1)
|
|
29
|
-
}
|
|
File without changes
|