Fix history scroll behavior and text selection

Related to #92
This commit is contained in:
Thomas Nordquist
2019-04-14 21:35:02 +02:00
parent 425bbb36e3
commit 499dfd1b68
7 changed files with 70 additions and 50 deletions

View File

@@ -1,10 +1,11 @@
import * as diff from 'diff'
import * as Prism from 'prismjs'
import * as React from 'react'
import { CodeBlockColors, CodeBlockColorsBraceMonokai } from './CodeBlockColors'
import { selectTextWithCtrlA } from '../../utils/handleTextSelectWithCtrlA'
import { Theme, withStyles } from '@material-ui/core'
import 'prismjs/components/prism-json'
import 'prismjs/themes/prism-tomorrow.css'
import { CodeBlockColors, CodeBlockColorsBraceMonokai } from './CodeBlockColors'
interface Props {
previous: string
@@ -15,6 +16,8 @@ interface Props {
}
class CodeDiff extends React.Component<Props, {}> {
private handleCtrlA = selectTextWithCtrlA({ targetSelector: 'pre' })
constructor(props: Props) {
super(props)
}
@@ -58,23 +61,6 @@ class CodeDiff extends React.Component<Props, {}> {
return this.props.classes.noChange
}
private selectText = (e: React.KeyboardEvent<HTMLDivElement>) => {
const isCtrlA = (e.metaKey || e.ctrlKey) && e.key === 'a'
if (isCtrlA && window.getSelection) {
e.persist()
e.preventDefault()
e.stopPropagation()
const selection = window.getSelection()
const range = document.createRange()
range.selectNodeContents((e.target as HTMLElement).getElementsByTagName('pre')[0])
if (selection) {
selection.removeAllRanges()
selection.addRange(range)
}
}
}
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')
@@ -102,8 +88,8 @@ class CodeDiff extends React.Component<Props, {}> {
return (
<div>
<div className={this.props.classes.gutters} tabIndex={0} onKeyDown={this.selectText}>
<pre className={`language-json ${this.props.classes.codeBlock}`} >
<div className={this.props.classes.gutters} tabIndex={0} onKeyDown={this.handleCtrlA}>
<pre className={`language-json ${this.props.classes.codeBlock}`}>
{code}
</pre>
</div>

View File

@@ -1,9 +1,10 @@
import * as React from 'react'
import { Badge, Typography } from '@material-ui/core'
import { selectTextWithCtrlA } from '../../utils/handleTextSelectWithCtrlA'
import { Theme, withStyles } from '@material-ui/core/styles'
interface HistoryItem {
key: string
title: JSX.Element | string
value: string
selected?: boolean
@@ -33,6 +34,8 @@ class HistoryDrawer extends React.Component<Props, State> {
this.setState({ collapsed: !this.state.collapsed })
}
private handleCtrlA = selectTextWithCtrlA({targetSelector: 'pre'})
public renderHistory() {
const style = (element: HistoryItem) => ({
backgroundColor: element.selected ? this.props.theme.palette.action.selected : this.props.theme.palette.action.hover,
@@ -44,9 +47,10 @@ class HistoryDrawer extends React.Component<Props, State> {
const messageStyle: React.CSSProperties = { textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden' }
const elements = this.props.items.map((element, index) => (
<div
key={index}
key={element.key}
style={style(element)}
onClick={(event: React.MouseEvent) => this.props.onClick && this.props.onClick(index, event.target)}
tabIndex={0} onKeyDown={this.handleCtrlA}
>
<div><i>{element.title}</i></div>
<div style={messageStyle}>

View File

@@ -1,12 +1,22 @@
import 'react-ace'
import 'brace/mode/json'
import 'brace/mode/text'
import 'brace/mode/xml'
import 'brace/theme/monokai'
import 'brace/theme/dawn'
import * as React from 'react'
import * as q from '../../../../../backend/src/Model'
import * as React from 'react'
import ClearAdornment from '../../helper/ClearAdornment'
import FormatAlignLeft from '@material-ui/icons/FormatAlignLeft'
import History from '../HistoryDrawer'
import Message from './Model/Message'
import Navigation from '@material-ui/icons/Navigation'
import { AppState } from '../../../reducers'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { default as AceEditor } from 'react-ace'
import { globalActions, publishActions } from '../../../actions'
import { TopicViewModel } from '../../../model/TopicViewModel'
import 'brace/mode/json'
import 'brace/theme/dawn'
import 'brace/theme/monokai'
import 'brace/mode/xml'
import 'brace/mode/text'
import 'react-ace'
import {
Button,
@@ -25,17 +35,7 @@ import {
withTheme,
} from '@material-ui/core'
import { default as AceEditor } from 'react-ace'
import { AppState } from '../../../reducers'
import History from '../History'
import Message from './Model/Message'
import Navigation from '@material-ui/icons/Navigation'
import FormatAlignLeft from '@material-ui/icons/FormatAlignLeft'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { publishActions, globalActions } from '../../../actions'
import ClearAdornment from '../../helper/ClearAdornment'
import { TopicViewModel } from '../../../model/TopicViewModel'
const sha1 = require('sha1')
interface Props {
node?: q.TreeNode<TopicViewModel>
@@ -276,10 +276,10 @@ class Publish extends React.Component<Props, State> {
private history() {
const items = this.state.history.reverse().map(message => ({
key: sha1(message.topic + message.payload),
title: message.topic,
value: message.payload || '',
}))
return <History items={items} onClick={this.didSelectHistoryEntry} />
}

View File

@@ -2,9 +2,11 @@ import * as q from '../../../../../backend/src/Model'
import * as React from 'react'
import BarChart from '@material-ui/icons/BarChart'
import DateFormatter from '../../helper/DateFormatter'
import History from '../History'
import History from '../HistoryDrawer'
import { TopicViewModel } from '../../../model/TopicViewModel'
import { Base64Message } from '../../../../../backend/src/Model/Base64Message'
import Copy from '../../helper/Copy';
import { selectTextWithCtrlA } from '../../../utils/handleTextSelectWithCtrlA';
const PlotHistory = React.lazy(() => import('./PlotHistory'))
@@ -59,10 +61,16 @@ class MessageHistory extends React.Component<Props, State> {
const history = node.messageHistory.toArray()
let previousMessage: q.Message | undefined = node.message
const historyElements = history.reverse().map((message) => {
const historyElements = history.reverse().map((message, idx) => {
const value = message.value ? Base64Message.toUnicodeString(message.value) : ''
const element = {
title: <span><DateFormatter date={message.received} /> {previousMessage ? <i>(-<DateFormatter date={message.received} intervalSince={previousMessage.received} />)</i> : null}</span>,
value: message.value ? Base64Message.toUnicodeString(message.value) : '',
value,
key: `${message.messageNumber}-${message.received}`,
title: (<span>
<DateFormatter date={message.received} />
{previousMessage ? <i>(-<DateFormatter date={message.received} intervalSince={previousMessage.received} />)</i> : null}
<div style={{ float: 'right' }}><Copy value={value} /></div>
</span>),
selected: message && message === this.props.selected,
}
previousMessage = message

View File

@@ -0,0 +1,22 @@
export const selectTextWithCtrlA = (options?: {targetSelector: string}) => (e: React.KeyboardEvent<HTMLDivElement>) => {
const isCtrlA = (e.metaKey || e.ctrlKey) && e.key === 'a'
if (isCtrlA && window.getSelection) {
e.persist()
e.preventDefault()
e.stopPropagation()
const selection = window.getSelection()
const range = document.createRange()
const eventTarget = (e.target as HTMLElement)
const target: HTMLElement | null = (options) ? eventTarget.querySelector(options.targetSelector) : eventTarget
if (!target) {
console.error('Could not find matching target for Ctrl+A Event')
}
if (selection && target) {
range.selectNodeContents(target)
selection.removeAllRanges()
selection.addRange(range)
}
}
}