Prevent unneccessary re-renders
This commit is contained in:
@@ -5,11 +5,16 @@ import * as React from 'react'
|
||||
import DiffCount from './DiffCount'
|
||||
import Gutters from './Gutters'
|
||||
import TopicPlot from '../TopicPlot'
|
||||
import { CodeBlockColors, CodeBlockColorsBraceMonokai } from '../CodeBlockColors'
|
||||
import {
|
||||
Fade,
|
||||
Paper,
|
||||
Popper,
|
||||
withStyles
|
||||
} from '@material-ui/core'
|
||||
import { isPlottable, lineChangeStyle, trimNewlineRight } from './util'
|
||||
import { JsonPropertyLocation, literalsMappedByLines } from '../../../../../backend/src/JsonAstParser'
|
||||
import { Theme, withStyles, Popper, Paper, Fade, Zoom } from '@material-ui/core'
|
||||
import { selectTextWithCtrlA } from '../../../utils/handleTextSelectWithCtrlA'
|
||||
import { style } from './style'
|
||||
import 'prismjs/components/prism-json'
|
||||
const throttle = require('lodash.throttle')
|
||||
|
||||
@@ -27,8 +32,8 @@ interface State {
|
||||
}
|
||||
|
||||
interface DiagramOptions {
|
||||
dotPath?: string
|
||||
anchorEl?: EventTarget
|
||||
dotPath: string
|
||||
anchorEl: React.Ref<HTMLElement>
|
||||
}
|
||||
|
||||
class CodeDiff extends React.Component<Props, State> {
|
||||
@@ -43,33 +48,35 @@ class CodeDiff extends React.Component<Props, State> {
|
||||
this.state = {}
|
||||
}
|
||||
|
||||
private showDiagram(dotPath: string, target: EventTarget) {
|
||||
private showDiagram = (dotPath: string, target: React.Ref<Element>) => {
|
||||
this.updateDiagram({
|
||||
dotPath,
|
||||
anchorEl: target,
|
||||
})
|
||||
}
|
||||
|
||||
private hideDiagram() {
|
||||
private hideDiagram = () => {
|
||||
this.updateDiagram(undefined)
|
||||
}
|
||||
|
||||
public render() {
|
||||
const changes = diff.diffLines(this.props.previous, this.props.current)
|
||||
const styledLines = Prism.highlight(this.props.current, Prism.languages.json, 'json').split('\n')
|
||||
const literalPositions = (
|
||||
(literalsMappedByLines(this.props.current) || [])
|
||||
.map((l: JsonPropertyLocation) => isPlottable(l.value) ? l : undefined)
|
||||
) as Array<JsonPropertyLocation>
|
||||
private plottableLiteralsIndexedWithLineNumbers() {
|
||||
const allLiterals = this.isValidJson(this.props.current) ? (literalsMappedByLines(this.props.current) || []) : []
|
||||
|
||||
return allLiterals
|
||||
.map((l: JsonPropertyLocation) => isPlottable(l.value) ? l : undefined) as Array<JsonPropertyLocation>
|
||||
}
|
||||
|
||||
private renderLines(changes: Array<Diff.Change>) {
|
||||
const styledLines = Prism.highlight(this.props.current, Prism.languages.json, 'json').split('\n')
|
||||
let lineNumber = 0
|
||||
const code = changes.map((change, key) => {
|
||||
|
||||
return changes.map((change, key) => {
|
||||
const hasStyledCode = change.removed !== true
|
||||
const changedLines = change.count || 0
|
||||
if (hasStyledCode && this.props.language === 'json') {
|
||||
const currentLines = styledLines.slice(lineNumber, lineNumber + changedLines)
|
||||
const lines = currentLines.map((html: string, idx: number) => {
|
||||
return <div key={`${key}-${idx}`} style={lineChangeStyle(change)} className={`${this.props.classes.line}`}><span dangerouslySetInnerHTML={{ __html: html }} /></div>
|
||||
return <div key={`${key}-${idx}`} style={lineChangeStyle(change)} className={this.props.classes.line}><span dangerouslySetInnerHTML={{ __html: html }} /></div>
|
||||
})
|
||||
lineNumber += changedLines
|
||||
|
||||
@@ -82,23 +89,31 @@ class CodeDiff extends React.Component<Props, State> {
|
||||
return <div key={`${key}-${idx}`} style={lineChangeStyle(change)} className={this.props.classes.line}><span>{line}</span></div>
|
||||
})
|
||||
}).reduce((a, b) => a.concat(b), [])
|
||||
}
|
||||
|
||||
public render() {
|
||||
const changes = diff.diffLines(this.props.previous, this.props.current)
|
||||
const literalPositions = this.plottableLiteralsIndexedWithLineNumbers()
|
||||
|
||||
const code = this.renderLines(changes)
|
||||
|
||||
const { diagram } = this.state
|
||||
|
||||
const hasEnoughDataToDisplayDiagrams = this.props.messageHistory.count() > 1
|
||||
return (
|
||||
<div>
|
||||
<div tabIndex={0} onKeyDown={this.handleCtrlA} className={this.props.classes.codeWrapper}>
|
||||
<Gutters
|
||||
showDiagram={(dotPath, target) => this.showDiagram(dotPath, target)}
|
||||
hideDiagram={() => this.hideDiagram()}
|
||||
showDiagram={this.showDiagram}
|
||||
hideDiagram={this.hideDiagram}
|
||||
className={this.props.classes.gutters}
|
||||
hasEnoughDataToDisplayDiagrams={hasEnoughDataToDisplayDiagrams}
|
||||
changes={changes}
|
||||
literalPositions={literalPositions} />
|
||||
<pre className={this.props.classes.codeBlock}>{code}</pre>
|
||||
</div>
|
||||
<Popper
|
||||
open={Boolean(this.state.diagram)}
|
||||
anchorEl={diagram && diagram.anchorEl as any}
|
||||
open={Boolean(this.state.diagram) && hasEnoughDataToDisplayDiagrams}
|
||||
anchorEl={diagram && (diagram.anchorEl as any).current}
|
||||
placement="left-end"
|
||||
>
|
||||
<Fade in={Boolean(this.state.diagram)} timeout={300}>
|
||||
@@ -111,67 +126,14 @@ class CodeDiff extends React.Component<Props, State> {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const style = (theme: Theme) => {
|
||||
const codeBlockColors = theme.palette.type === 'light' ? CodeBlockColors : CodeBlockColorsBraceMonokai
|
||||
|
||||
const codeBaseStyle = {
|
||||
font: "12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace",
|
||||
display: 'inline-grid' as 'inline-grid',
|
||||
margin: '0',
|
||||
padding: '1px 0 0 0',
|
||||
}
|
||||
|
||||
return {
|
||||
line: {
|
||||
lineHeight: 'normal' as 'normal',
|
||||
paddingLeft: '4px',
|
||||
width: '100%',
|
||||
height: '16px',
|
||||
},
|
||||
codeWrapper: {
|
||||
display: 'flex',
|
||||
maxHeight: '15em',
|
||||
overflow: 'auto',
|
||||
backgroundColor: `${codeBlockColors.background}`,
|
||||
margin: '8px 0 0 0',
|
||||
},
|
||||
gutters: {
|
||||
...codeBaseStyle,
|
||||
width: '33px',
|
||||
backgroundColor: codeBlockColors.gutters,
|
||||
userSelect: 'none' as 'none',
|
||||
},
|
||||
codeBlock: {
|
||||
...codeBaseStyle,
|
||||
width: 'calc(100% - 33px)',
|
||||
backgroundColor: 'inherit !important',
|
||||
'& span': {
|
||||
color: codeBlockColors.text,
|
||||
},
|
||||
'& .token.number': {
|
||||
color: codeBlockColors.numeric,
|
||||
},
|
||||
'& .token.boolean': {
|
||||
color: codeBlockColors.numeric,
|
||||
},
|
||||
'& .token.property': {
|
||||
color: codeBlockColors.variable,
|
||||
},
|
||||
'& .token.string': {
|
||||
color: codeBlockColors.string,
|
||||
},
|
||||
'& .token': {
|
||||
color: codeBlockColors.text,
|
||||
},
|
||||
'& .token.operator': {
|
||||
color: codeBlockColors.text,
|
||||
},
|
||||
'& .token.punctuation': {
|
||||
color: codeBlockColors.text,
|
||||
},
|
||||
},
|
||||
private isValidJson(str: string) {
|
||||
try {
|
||||
JSON.parse(str)
|
||||
return true
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user