Skip to content

Commit

Permalink
Fix "Export SVG" feature in next 14 (#4136)
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed Dec 14, 2023
1 parent 6e061fe commit e6d30a8
Show file tree
Hide file tree
Showing 41 changed files with 205 additions and 126 deletions.
32 changes: 26 additions & 6 deletions packages/core/util/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useRef, useState } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import isObject from 'is-object'
import PluginManager from '../PluginManager'
import {
Expand Down Expand Up @@ -33,6 +33,8 @@ import { isUriLocation } from './types'
// has to be the full path and not the relative path to get the jest mock
import useMeasure from '@jbrowse/core/util/useMeasure'
import { colord } from './colord'
// eslint-disable-next-line react/no-deprecated
import { flushSync, render } from 'react-dom'
export * from './types'
export * from './aborting'
export * from './when'
Expand Down Expand Up @@ -1374,16 +1376,34 @@ export function gatherOverlaps(regions: BasicFeature[]) {
)
}

export function stripAlpha(str: string) {
const c = colord(str)
return c.alpha(1).toHex()
}

// https://react.dev/reference/react-dom/server/renderToString#removing-rendertostring-from-the-client-code
export function renderToStaticMarkup(
node: React.ReactElement,
createRootFn?: (elt: Element | DocumentFragment) => {
render: (node: React.ReactElement) => unknown
},
) {
const div = document.createElement('div')
flushSync(() => {
if (createRootFn) {
createRootFn(div).render(node)
} else {
render(node, div)
}
})
return div.innerHTML.replace('&gt;', '>').replace('&lt;', '<')
}

export {
default as SimpleFeature,
type Feature,
type SimpleFeatureSerialized,
isFeature,
} from './simpleFeature'

export function stripAlpha(str: string) {
const c = colord(str)
return c.alpha(1).toHex()
}

export { blobToDataURL } from './blobToDataURL'
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ function calc(track: Track, f: Feature) {
export interface ExportSvgOptions {
rasterizeLayers?: boolean
filename?: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Wrapper?: React.FC<any>
Wrapper?: React.FC<{ children: React.ReactNode }>
fontSize?: number
rulerHeight?: number
textHeight?: number
Expand Down
Original file line number Diff line number Diff line change
@@ -1,60 +1,23 @@
import React from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { when } from 'mobx'
import {
AbstractSessionModel,
getSession,
max,
measureText,
sum,
} from '@jbrowse/core/util'
import { getSession, renderToStaticMarkup, sum } from '@jbrowse/core/util'
import { ThemeProvider } from '@mui/material'
import { createJBrowseTheme } from '@jbrowse/core/ui'

// locals
import { getRoot } from 'mobx-state-tree'
import {
SVGTracks,
SVGRuler,
totalHeight,
LinearGenomeViewModel,
} from '@jbrowse/plugin-linear-genome-view'

// locals
import SVGBackground from './SVGBackground'
import { ExportSvgOptions, BreakpointViewModel } from '../model'
import { getTrackName } from '@jbrowse/core/util/tracks'
import Overlay from '../components/Overlay'
import { getTrackNameMaxLen, getTrackOffsets } from './util'

type BSV = BreakpointViewModel

function getTrackNameMaxLen(
views: LinearGenomeViewModel[],
fontSize: number,
session: AbstractSessionModel,
) {
return max(
views.flatMap(view =>
view.tracks.map(t =>
measureText(getTrackName(t.configuration, session), fontSize),
),
),
0,
)
}
function getTrackOffsets(
view: LinearGenomeViewModel,
textOffset: number,
extra = 0,
) {
const offsets = {} as Record<string, number>
let curr = textOffset
for (const track of view.tracks) {
offsets[track.configuration.trackId] = curr + extra
curr += track.displays[0].height + textOffset
}
return offsets
}

// render LGV to SVG
export async function renderToSvg(model: BSV, opts: ExportSvgOptions) {
const {
Expand All @@ -63,10 +26,11 @@ export async function renderToSvg(model: BSV, opts: ExportSvgOptions) {
rulerHeight = 30,
fontSize = 13,
trackLabels = 'offset',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Wrapper = ({ children }: any) => <>{children}</>,
Wrapper = ({ children }) => <>{children}</>,
themeName = 'default',
} = opts
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { createRootFn } = getRoot<any>(model)
const session = getSession(model)
const theme = session.allThemes?.()[themeName]
const { width, views } = model
Expand Down Expand Up @@ -174,5 +138,6 @@ export async function renderToSvg(model: BSV, opts: ExportSvgOptions) {
</svg>
</Wrapper>
</ThemeProvider>,
createRootFn,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { AbstractSessionModel, max, measureText } from '@jbrowse/core/util'

// locals
import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'

// locals
import { getTrackName } from '@jbrowse/core/util/tracks'

export function getTrackNameMaxLen(
views: LinearGenomeViewModel[],
fontSize: number,
session: AbstractSessionModel,
) {
return max(
views.flatMap(view =>
view.tracks.map(t =>
measureText(getTrackName(t.configuration, session), fontSize),
),
),
0,
)
}
export function getTrackOffsets(
view: LinearGenomeViewModel,
textOffset: number,
extra = 0,
) {
const offsets = {} as Record<string, number>
let curr = textOffset
for (const track of view.tracks) {
offsets[track.configuration.trackId] = curr + extra
curr += track.displays[0].height + textOffset
}
return offsets
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { renderReactionData, renderReactionEffect } from './renderReaction'
import {
CircularViewModel,
ExportSvgOptions,
} from '../../CircularView/models/CircularView'
} from '../../CircularView/models/model'
import { ThemeOptions } from '@mui/material'
import { baseChordDisplayConfig } from './configSchema'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import clone from 'clone'
import { getRpcSessionId } from '@jbrowse/core/util/tracks'
import { getSession, getContainingView } from '@jbrowse/core/util'
import { CircularViewModel } from '../../CircularView/models/CircularView'
import { CircularViewModel } from '../../CircularView/models/model'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function renderReactionData(self: any) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { makeStyles } from 'tss-react/mui'
import Ruler from './Ruler'
import Controls from './Controls'
import ImportForm from './ImportForm'
import { CircularViewModel } from '../models/CircularView'
import { CircularViewModel } from '../models/model'

const dragHandleHeight = 3

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import MoreVert from '@mui/icons-material/MoreVert'
import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons'

// locals
import { CircularViewModel } from '../models/CircularView'
import { CircularViewModel } from '../models/model'
import { getSession } from '@jbrowse/core/util'
import ExportSvgDlg from './ExportSvgDialog'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import { Dialog, ErrorMessage } from '@jbrowse/core/ui'

// locals
import { ExportSvgOptions } from '../models/CircularView'
import { ExportSvgOptions } from '../models/model'
import { getSession, useLocalStorage } from '@jbrowse/core/util'

function LoadingMessage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
SliceElidedRegion,
SliceNonElidedRegion,
} from '../models/slices'
import { CircularViewModel } from '../models/CircularView'
import { CircularViewModel } from '../models/model'

const useStyles = makeStyles()({
rulerLabel: {
Expand Down
2 changes: 1 addition & 1 deletion plugins/circular-view/src/CircularView/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { lazy } from 'react'
import PluginManager from '@jbrowse/core/PluginManager'
import ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'
import stateModelFactory from './models/CircularView'
import stateModelFactory from './models/model'

export default (pluginManager: PluginManager) => {
pluginManager.addViewType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ const ExportSvgDialog = lazy(() => import('../components/ExportSvgDialog'))
export interface ExportSvgOptions {
rasterizeLayers?: boolean
filename?: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Wrapper?: React.FC<any>
Wrapper?: React.FC<{ children: React.ReactNode }>
themeName?: string
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react'
import { ThemeProvider } from '@mui/material'
import { renderToStaticMarkup } from 'react-dom/server'
import { when } from 'mobx'
import { getSession, radToDeg } from '@jbrowse/core/util'
import { getSession, radToDeg, renderToStaticMarkup } from '@jbrowse/core/util'
import { createJBrowseTheme } from '@jbrowse/core/ui'
import { getRoot } from 'mobx-state-tree'

// locals
import { ExportSvgOptions, CircularViewModel } from '../models/CircularView'
import { ExportSvgOptions, CircularViewModel } from '../models/model'
import SVGBackground from './SVGBackground'
import Ruler from '../components/Ruler'

Expand All @@ -18,6 +18,8 @@ export async function renderToSvg(model: CGV, opts: ExportSvgOptions) {
opts
const session = getSession(model)
const theme = session.allThemes?.()[themeName]
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { createRootFn } = getRoot<any>(model)
const { width, tracks, height } = model
const shift = 50
const displayResults = await Promise.all(
Expand Down Expand Up @@ -54,5 +56,6 @@ export async function renderToSvg(model: CGV, opts: ExportSvgOptions) {
</svg>
</Wrapper>
</ThemeProvider>,
createRootFn,
)
}
2 changes: 1 addition & 1 deletion plugins/circular-view/src/LaunchCircularView/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AbstractSessionModel } from '@jbrowse/core/util'
import PluginManager from '@jbrowse/core/PluginManager'

// locals
import { CircularViewModel } from '../CircularView/models/CircularView'
import { CircularViewModel } from '../CircularView/models/model'

type CGV = CircularViewModel

Expand Down
2 changes: 1 addition & 1 deletion plugins/circular-view/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ export {
export {
type CircularViewModel,
type CircularViewStateModel,
} from './CircularView/models/CircularView'
} from './CircularView/models/model'
3 changes: 1 addition & 2 deletions plugins/dotplot-view/src/DotplotView/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ type Coord = [number, number]
export interface ExportSvgOptions {
rasterizeLayers?: boolean
filename?: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Wrapper?: React.FC<any>
Wrapper?: React.FC<{ children: React.ReactNode }>
themeName?: string
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { when } from 'mobx'
import { getSession } from '@jbrowse/core/util'
import { getSession, renderToStaticMarkup } from '@jbrowse/core/util'
import { ThemeProvider } from '@mui/material'
import { createJBrowseTheme } from '@jbrowse/core/ui'
import { getRoot } from 'mobx-state-tree'

// locals
import { DotplotViewModel, ExportSvgOptions } from '../model'
Expand All @@ -19,6 +19,8 @@ export async function renderToSvg(
await when(() => model.initialized)
const { themeName = 'default', Wrapper = ({ children }) => <>{children}</> } =
opts
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { createRootFn } = getRoot<any>(model)
const session = getSession(model)
const theme = session.allThemes?.()[themeName]
const { width, borderX, viewWidth, viewHeight, tracks, height } = model
Expand Down Expand Up @@ -64,5 +66,6 @@ export async function renderToSvg(
</svg>
</Wrapper>
</ThemeProvider>,
createRootFn,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ export interface ExportSvgOptions {
rasterizeLayers?: boolean
scale?: number
filename?: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Wrapper?: React.FC<any>
Wrapper?: React.FC<{ children: React.ReactNode }>
fontSize?: number
rulerHeight?: number
textHeight?: number
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import { ThemeProvider } from '@mui/material'
import { renderToStaticMarkup } from 'react-dom/server'
import { getRoot } from 'mobx-state-tree'
import { when } from 'mobx'
import {
getSession,
Expand All @@ -9,6 +9,7 @@ import {
measureText,
ReactRendering,
renderToAbstractCanvas,
renderToStaticMarkup,
sum,
} from '@jbrowse/core/util'
import { getTrackName } from '@jbrowse/core/util/tracks'
Expand All @@ -35,16 +36,16 @@ export async function renderToSvg(model: LSV, opts: ExportSvgOptions) {
rulerHeight = 30,
fontSize = 13,
trackLabels = 'offset',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Wrapper = ({ children }: any) => <>{children}</>,
Wrapper = ({ children }) => <>{children}</>,
themeName = 'default',
} = opts
const session = getSession(model)
const theme = session.allThemes?.()[themeName]
const { width, views, middleComparativeHeight: synH, tracks } = model
const shift = 50
const offset = headerHeight + rulerHeight

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { createRootFn } = getRoot<any>(model)
const heights = views.map(
v => totalHeight(v.tracks, textHeight, trackLabels) + offset,
)
Expand Down Expand Up @@ -172,5 +173,6 @@ export async function renderToSvg(model: LSV, opts: ExportSvgOptions) {
</svg>
</Wrapper>
</ThemeProvider>,
createRootFn,
)
}
Loading

0 comments on commit e6d30a8

Please sign in to comment.