From 4b2274eeb4a2ba248693b87accf1288d6ea16bbc Mon Sep 17 00:00:00 2001 From: Tim <roadscape@users.noreply.github.com> Date: Thu, 20 Oct 2016 16:53:11 -0400 Subject: [PATCH] initial video embed --- app/assets/icons/video.svg | 1 + app/components/elements/Icon.jsx | 1 + app/components/elements/SlateEditor.jsx | 17 +++- app/components/elements/SlateEditor.scss | 1 + app/utils/SlateEditor/Iframe.js | 103 +++++++++++++++++++++++ app/utils/SlateEditor/Schema.js | 2 + 6 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 app/assets/icons/video.svg create mode 100644 app/utils/SlateEditor/Iframe.js diff --git a/app/assets/icons/video.svg b/app/assets/icons/video.svg new file mode 100644 index 000000000..4acd2bbfa --- /dev/null +++ b/app/assets/icons/video.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19,19H5V5H19M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M10,8V16L15,12L10,8Z" /></svg> \ No newline at end of file diff --git a/app/components/elements/Icon.jsx b/app/components/elements/Icon.jsx index a1ada9717..07ca9b7b0 100644 --- a/app/components/elements/Icon.jsx +++ b/app/components/elements/Icon.jsx @@ -30,6 +30,7 @@ const icons = [ 'reblog', 'photo', 'line', + 'video', ]; const icons_map = {}; for (const i of icons) icons_map[i] = require(`app/assets/icons/${i}.svg`); diff --git a/app/components/elements/SlateEditor.jsx b/app/components/elements/SlateEditor.jsx index 5b6d022f3..5cd6a75b0 100644 --- a/app/components/elements/SlateEditor.jsx +++ b/app/components/elements/SlateEditor.jsx @@ -367,6 +367,7 @@ console.log(JSON.stringify(Raw.serialize(state, {terse: false}), null, 2)) <Portal isOpened onOpen={this.onSidebarOpen}> <div className="SlateEditor__sidebar"> {this.renderAddBlockButton({type: 'image', label: <Icon name="photo" />, handler: this.onClickInsertImage})} + {this.renderAddBlockButton({type: 'video', label: <Icon name="video" />, handler: this.onClickInsertVideo})} {this.renderAddBlockButton({type: 'hrule', label: <Icon name="line" />, handler: this.onClickInsertHr})} </div> </Portal> @@ -383,8 +384,22 @@ console.log(JSON.stringify(Raw.serialize(state, {terse: false}), null, 2)) state = state .transform() .insertInline({type: 'image', isVoid: true, data: {src}}) - .insertBlock({type: 'paragraph', isVoid: false, nodes: [Slate.Text.create()]}) + //.insertBlock({type: 'paragraph', isVoid: false, nodes: [Slate.Text.create()]}) .focus() + .collapseToEndOfNextBlock() + .apply() + + this.setState({ state }) + } + + onClickInsertVideo = (e, type) => { + e.preventDefault() + let { state } = this.state + + state = state + .transform() + .insertBlock({type: 'embed', isVoid: true, data: {src: 'https://www.youtube.com/watch?v=7YOozVnEdFQ'}}) + .insertBlock({type: 'paragraph', isVoid: false}) .apply() this.setState({ state }) diff --git a/app/components/elements/SlateEditor.scss b/app/components/elements/SlateEditor.scss index 675f4cd94..09eb18385 100644 --- a/app/components/elements/SlateEditor.scss +++ b/app/components/elements/SlateEditor.scss @@ -4,6 +4,7 @@ position: relative; img {border: 1px dotted #00f} } + div.active, img.active {box-shadow: 0 0 2px 1px #48C;} hr.active {box-shadow: 0 0 2px 1px #48C;} diff --git a/app/utils/SlateEditor/Iframe.js b/app/utils/SlateEditor/Iframe.js new file mode 100644 index 000000000..57b1c4831 --- /dev/null +++ b/app/utils/SlateEditor/Iframe.js @@ -0,0 +1,103 @@ +import React from 'react' +import linksRe from 'app/utils/Links' + +export default class Iframe extends React.Component { + + normalizeEmbedUrl = (url) => { + // Detect youtube URLs + const match = url.match(linksRe.youTubeId) + if(match && match.length >= 2) { + return 'https://www.youtube.com/embed/' + match[1] + } + + console.log("unable to auto-detect embed url", url) + return null + } + + onChange = (e) => { + const { node, state, editor } = this.props + const value = e.target.value + + const src = this.normalizeEmbedUrl(value) || value + + const next = editor + .getState() + .transform() + .setNodeByKey(node.key, {data: {src}}) + .apply() + + editor.onChange(next) + } + + onClick = (e) => { + // stop propagation so that the void node itself isn't focused, since that would unfocus the input. + e.stopPropagation() + } + + render = () => { + const { node, state, attributes } = this.props + const isFocused = state.selection.hasEdgeIn(node) + const className = isFocused ? 'active' : null + const style = {background: 'black', color: 'white'} + + return ( + <div {...attributes} className={className} style={style}> + {this.renderFrame()} + Embed URL: {this.renderInput()} + </div> + ) + } + + renderFrame = () => { + let src = this.props.node.data.get('src') + src = this.normalizeEmbedUrl(src) || src + + const aspectStyle = { + position: 'relative', + paddingBottom: '56.2%', + height: '0' + } + const lockStyle = { + position: 'absolute', + top: '0px', + left: '0px', + width: '100%', + height: '100%', + background: 'rgba(0,0,0,0.1)' + } + const style = { + position: 'absolute', + top: '0px', + left: '0px', + width: '100%', + height: '100%' + } + + return ( + <div style={aspectStyle}> + <iframe + type="text/html" + width="640" + height="360" + src={src} + frameBorder="0" + style={style}> + </iframe> + <div style={lockStyle}></div> + </div> + ) + } + + renderInput = () => { + const src = this.props.node.data.get('src') + return ( + <input + value={src} + onChange={this.onChange} + onClick={this.onClick} + style={{ marginTop: '5px', background: 'black' }} + size="70" + /> + ) + } +} diff --git a/app/utils/SlateEditor/Schema.js b/app/utils/SlateEditor/Schema.js index 048cb1d31..2b103cfd9 100644 --- a/app/utils/SlateEditor/Schema.js +++ b/app/utils/SlateEditor/Schema.js @@ -1,6 +1,7 @@ import React from 'react' import Link from 'app/utils/SlateEditor/Link' import Image from 'app/utils/SlateEditor/Image' +import Iframe from 'app/utils/SlateEditor/Iframe' import HRule from 'app/utils/SlateEditor/HRule' @@ -211,6 +212,7 @@ export const schema = { 'hr': HRule, 'image': Image, 'link': Link, + 'embed': Iframe, }, marks: { -- GitLab