diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2020-06-08 00:53:10 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2020-06-08 00:53:10 +0200 |
| commit | 734c19347e5ff307c2412289248f95dc66db94f1 (patch) | |
| tree | 11dfa40e49275f54154bc46212d5078df843209f | |
| parent | 084333904ab589788a121b44461f242006545594 (diff) | |
slider component
| -rw-r--r-- | frontend/common/index.js | 1 | ||||
| -rw-r--r-- | frontend/common/slider.component.js | 98 | ||||
| -rw-r--r-- | frontend/views/graph/components/graph.editor.js | 28 |
3 files changed, 114 insertions, 13 deletions
diff --git a/frontend/common/index.js b/frontend/common/index.js index 6f9747a..3647203 100644 --- a/frontend/common/index.js +++ b/frontend/common/index.js @@ -19,6 +19,7 @@ export { default as CopyToClipboardButton } from './copyToClipboardButton.compon export { default as ImageCrop } from './imageCrop.component' export { Modal } from './modal.component' export { default as UploadImage } from './uploadImage.component' +export { default as Slider } from './slider.component' import './fonts.css' import './app.css' diff --git a/frontend/common/slider.component.js b/frontend/common/slider.component.js new file mode 100644 index 0000000..70a4abd --- /dev/null +++ b/frontend/common/slider.component.js @@ -0,0 +1,98 @@ +import { React, Component } from 'preact' + +const SLIDER_THROTTLE_TIME = 100 + +export default class Slider extends Component { + constructor(props){ + super(props) + this.timeout = 0 + this.state = { + value: props.value + } + this.handleInput = this.handleInput.bind(this) + this.handleRange = this.handleRange.bind(this) + } + UNSAFE_componentWillReceiveProps(nextProps) { + let next_value = nextProps.value || nextProps.opt[nextProps.name] + if (next_value !== this.state.value) { + if (this.props.type === 'int') { + next_value = parseInt(next_value) + } + this.setState({ value: next_value }) + } + } + handleInput(e){ + let { name, opt } = this.props + let old_value = opt[name] + let new_value = e.target.value + if (new_value === '') { + new_value = this.props.defaultValue || (this.props.max - this.props.min) / 2 + } + else if (this.props.type === 'int') { + new_value = parseInt(new_value) + } + else if (this.props.type === 'odd') { + new_value = parseInt(Math.floor(new_value / 2) * 2 + 1) + } + if (old_value !== new_value) { + this.setState({ value: new_value }) + this.props.onChange && this.props.onChange(new_value) + } + clearTimeout(this.timeout) + } + handleRange(e){ + clearTimeout(this.timeout) + let new_value = e.target.value + if (this.props.type === 'int') { + new_value = parseInt(new_value) + } + if (this.props.type === 'odd') { + new_value = parseInt(Math.floor(new_value / 2) * 2 + 1) + } + if (this.props.type === 'list') { + new_value = this.props.options[new_value] || this.props.options[0] + } + this.setState({ value: new_value }) + this.timeout = setTimeout(() => { + this.props.onChange && this.props.onChange(new_value) + }, SLIDER_THROTTLE_TIME) + } + render(){ + let { name, title } = this.props + let value = this.state.value + if (typeof value === 'undefined') { + value = this.props.min + } + let text_value = value + let step; + let min = this.props.min || 0 + let max = this.props.max || 0 + if (this.props.type === 'int') { + step = 1 + } else if (this.props.type === 'list') { + min = 0 + max = this.props.options.length-1 + step = 1 + value = this.props.options.indexOf(value) + } else { + step = (this.props.max - this.props.min) / 100 + text_value = parseFloat(value).toFixed(2) + } + return ( + <div className='slider param'> + <label> + <span>{title || name.replace(/_/g, ' ')}</span> + <input type='text' value={text_value} onBlur={this.handleInput} /> + </label> + <input + type='range' + min={min} + max={max} + step={step} + value={value} + onInput={this.handleRange} + /> + </div> + ) + } +} diff --git a/frontend/views/graph/components/graph.editor.js b/frontend/views/graph/components/graph.editor.js index f8b45f9..87109bf 100644 --- a/frontend/views/graph/components/graph.editor.js +++ b/frontend/views/graph/components/graph.editor.js @@ -182,6 +182,10 @@ class GraphEditor extends Component { } const DEFAULT_MEASUREMENT = { width: 16, height: 16 } +const BACKLINK_SPACING = 10 +const ARROWHEAD_LENGTH = 10 +const GRAPH_LINK_COLOR = "#ff88ff" +const GRAPH_BACKLINK_COLOR = "#88ffff" class GraphCanvas extends Component { constructor(props) { @@ -207,7 +211,6 @@ class GraphCanvas extends Component { canvas.width = width canvas.height = height } - console.log(measurements) const ctx = canvas.getContext('2d') ctx.clearRect(0, 0, width, height) ctx.lineWidth = 2 @@ -234,11 +237,11 @@ class GraphCanvas extends Component { const targetCoord = coordsLookup[tile.page_id] let tile_measurement = measurements[tile.page_id] || DEFAULT_MEASUREMENT let target_measurement = measurements[tile.target_page_id] || DEFAULT_MEASUREMENT - let x1_offset = tile_measurement.width / 2 + let theta = angle(targetCoord.x, targetCoord.y, sourceCoord.x, sourceCoord.y) + let x1_offset = tile_measurement.width / 2 // * (0.5 - Math.cos(theta)) let y1_offset = tile_measurement.height / 2 - let x2_offset = target_measurement.width / 2 + let x2_offset = target_measurement.width / 2 // (0.5 - Math.cos(theta)) let y2_offset = target_measurement.height / 2 - let theta = angle(targetCoord.x, targetCoord.y, sourceCoord.x, sourceCoord.y) // skip duplicate links if (sourceCoord.backlinks.has(tile.page_id)) { return @@ -246,13 +249,13 @@ class GraphCanvas extends Component { // if this is the first time encountering this link... if (!targetCoord.backlinks.has(tile.target_page_id)) { sourceCoord.backlinks.add(tile.page_id) - ctx.strokeStyle = "#ff88ff" + ctx.strokeStyle = GRAPH_LINK_COLOR } else { // otherwise this is a two-way link - x1_offset += 10 * Math.cos(theta + Math.PI /2) - y1_offset += 10 * Math.sin(theta + Math.PI /2) - x2_offset += 10 * Math.cos(theta + Math.PI /2) - y2_offset += 10 * Math.sin(theta + Math.PI /2) - ctx.strokeStyle = "#88ffff" + x1_offset += BACKLINK_SPACING * Math.cos(theta + Math.PI /2) + y1_offset += BACKLINK_SPACING * Math.sin(theta + Math.PI /2) + x2_offset += BACKLINK_SPACING * Math.cos(theta + Math.PI /2) + y2_offset += BACKLINK_SPACING * Math.sin(theta + Math.PI /2) + ctx.strokeStyle = GRAPH_BACKLINK_COLOR } ctx.beginPath() const x1 = targetCoord.x * width + x1_offset @@ -266,7 +269,6 @@ class GraphCanvas extends Component { } arrow(ctx, x1, y1, x2, y2) { - const headlen = 10 // length of head in pixels const farOffset = 20 const endOffset = 1 const theta = angle(x1, y1, x2, y2) @@ -281,9 +283,9 @@ class GraphCanvas extends Component { ctx.moveTo(x1, y1) ctx.lineTo(xEnd, yEnd) ctx.moveTo(x2, y2) - ctx.lineTo(x2 - headlen * Math.cos(leftAngle), y2 - headlen * Math.sin(leftAngle)) + ctx.lineTo(x2 - ARROWHEAD_LENGTH * Math.cos(leftAngle), y2 - ARROWHEAD_LENGTH * Math.sin(leftAngle)) ctx.moveTo(x2, y2) - ctx.lineTo(x2 - headlen * Math.cos(rightAngle), y2 - headlen * Math.sin(rightAngle)) + ctx.lineTo(x2 - ARROWHEAD_LENGTH * Math.cos(rightAngle), y2 - ARROWHEAD_LENGTH * Math.sin(rightAngle)) } render() { |
