mirror of
https://github.com/anyproto/anytype-ts.git
synced 2025-06-08 05:57:02 +09:00
youtube video
This commit is contained in:
parent
a528a72e58
commit
5fe99d7c9e
9 changed files with 199 additions and 623 deletions
27
dist/iframe.html
vendored
Normal file
27
dist/iframe.html
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
* { margin: 0px; padding: 0px; }
|
||||
html, body { height: 100%; }
|
||||
|
||||
body { background-color: #fff; }
|
||||
body.dark { background-color: #171717; }
|
||||
|
||||
iframe { width: 100% !important; height: 100% !important; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
window.addEventListener('message', event => {
|
||||
if (event.origin.match('http://localhost:') || (event.origin === null)) {
|
||||
const body = document.body;
|
||||
const { html, theme } = event.data;
|
||||
|
||||
body.innerHTML = html;
|
||||
body.className = theme;
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
12
electron.js
12
electron.js
|
@ -24,12 +24,12 @@ const Util = require('./electron/js/util.js');
|
|||
const csp = [
|
||||
`default-src 'self' 'unsafe-eval' blob: http://localhost:*`,
|
||||
`img-src 'self' http://*:* https://*:* data: blob: file://*`,
|
||||
`media-src 'self' http://*:* https://*:* data: blob: file://*`,
|
||||
`style-src 'unsafe-inline' http://localhost:* file://*`,
|
||||
`font-src data: file://* http://localhost:*`,
|
||||
`connect-src file://* http://localhost:* http://127.0.0.1:* ws://localhost:* https://*.anytype.io https://api.amplitude.com/ devtools://devtools data:`,
|
||||
`script-src-elem file: http://localhost:* https://sentry.io devtools://devtools 'unsafe-inline'`,
|
||||
`frame-src chrome-extension://react-developer-tools`,
|
||||
`media-src 'self' http://*:* https://*:* data: blob: file://* https://*.googlevideo.com`,
|
||||
`style-src 'unsafe-inline' http://localhost:* file://* https://www.youtube.com/`,
|
||||
`font-src data: file://* http://localhost:* https://www.youtube.com/`,
|
||||
`connect-src file://* http://localhost:* http://127.0.0.1:* ws://localhost:* https://*.anytype.io https://api.amplitude.com/ devtools://devtools data: https://www.youtube.com/ https://*.googlevideo.com`,
|
||||
`script-src-elem file: http://localhost:* https://sentry.io devtools://devtools 'unsafe-inline' https://www.youtube.com/`,
|
||||
`frame-src chrome-extension://react-developer-tools http://localhost:8081/iframe.html https://www.youtube.com/ https://www.vimeo.com/`,
|
||||
`worker-src 'self' 'unsafe-eval' blob: http://localhost:*`,
|
||||
];
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
background: $colorShapeTertiary; text-align: left; font-family: 'Inter'; padding: 8px; @include text-common;
|
||||
-webkit-user-modify: read-write-plaintext-only; display: none; border-radius: 4px; margin-top: 8px;
|
||||
}
|
||||
|
||||
iframe { width: 100%; height: 400px; }
|
||||
}
|
||||
|
||||
.block.blockEmbed.isLatex {
|
||||
|
|
|
@ -2,8 +2,9 @@ import * as React from 'react';
|
|||
import $ from 'jquery';
|
||||
import Prism from 'prismjs';
|
||||
import raf from 'raf';
|
||||
import mermaid from 'mermaid';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Icon } from 'Component';
|
||||
import { Icon, Label } from 'Component';
|
||||
import { I, keyboard, UtilCommon, C, focus, Renderer, translate } from 'Lib';
|
||||
import { menuStore, commonStore, blockStore } from 'Store';
|
||||
import { getRange, setRange } from 'selection-ranges';
|
||||
|
@ -12,21 +13,23 @@ import Constant from 'json/constant.json';
|
|||
const katex = require('katex');
|
||||
require('katex/dist/contrib/mhchem');
|
||||
|
||||
const BlockEmbedLatex = observer(class BlockEmbedLatex extends React.Component<I.BlockComponentEmbed> {
|
||||
const BlockEmbed = observer(class BlockEmbedIndex extends React.Component<I.BlockComponent> {
|
||||
|
||||
_isMounted = false;
|
||||
range: any = { start: 0, end: 0 };
|
||||
range = { start: 0, end: 0 };
|
||||
text = '';
|
||||
timeout = 0;
|
||||
node: any = null;
|
||||
win: any = null;
|
||||
input: any = null;
|
||||
value: any = null;
|
||||
empty: any = null;
|
||||
node = null;
|
||||
win = null;
|
||||
input = null;
|
||||
value = null;
|
||||
empty = null;
|
||||
container = null;
|
||||
|
||||
constructor (props: I.BlockComponentEmbed) {
|
||||
constructor (props: I.BlockComponent) {
|
||||
super(props);
|
||||
|
||||
this.onSelect = this.onSelect.bind(this);
|
||||
this.onKeyDownBlock = this.onKeyDownBlock.bind(this);
|
||||
this.onKeyUpBlock = this.onKeyUpBlock.bind(this);
|
||||
this.onFocusBlock = this.onFocusBlock.bind(this);
|
||||
|
@ -42,9 +45,33 @@ const BlockEmbedLatex = observer(class BlockEmbedLatex extends React.Component<I
|
|||
};
|
||||
|
||||
render () {
|
||||
const { readonly, block, onSelect } = this.props;
|
||||
const { readonly, block } = this.props;
|
||||
const { processor } = block.content;
|
||||
const cn = [ 'wrap', 'resizable', 'focusable', 'c' + block.id ];
|
||||
|
||||
let select = null;
|
||||
let empty = '';
|
||||
|
||||
switch (processor) {
|
||||
case I.EmbedProcessor.Latex: {
|
||||
select = (
|
||||
<div className="selectWrap">
|
||||
<div id="select" className="select" onClick={this.onTemplate}>
|
||||
<div className="name">{translate('blockLatexTemplate')}</div>
|
||||
<Icon className="arrow light" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
empty = translate('blockLatexEmpty');
|
||||
break;
|
||||
};
|
||||
|
||||
case I.EmbedProcessor.Mermaid: {
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={node => this.node = node}
|
||||
|
@ -54,23 +81,17 @@ const BlockEmbedLatex = observer(class BlockEmbedLatex extends React.Component<I
|
|||
onKeyUp={this.onKeyUpBlock}
|
||||
onFocus={this.onFocusBlock}
|
||||
>
|
||||
<div className="selectWrap">
|
||||
<div id="select" className="select" onClick={this.onTemplate}>
|
||||
<div className="name">{translate('blockLatexTemplate')}</div>
|
||||
<Icon className="arrow light" />
|
||||
</div>
|
||||
</div>
|
||||
{select}
|
||||
|
||||
<div id="value" onClick={this.onEdit} />
|
||||
<div id="empty" className="empty" onClick={this.onEdit}>
|
||||
Here your equation will be rendered with <Icon className="tex" />. Click to edit
|
||||
</div>
|
||||
<Label id="empty" className="empty" text={empty} onClick={this.onEdit} />
|
||||
<div id={this.getContainerId()} />
|
||||
<div
|
||||
id="input"
|
||||
contentEditable={!readonly}
|
||||
suppressContentEditableWarning={true}
|
||||
placeholder={translate('blockLatexPlaceholder')}
|
||||
onSelect={onSelect}
|
||||
onSelect={this.onSelect}
|
||||
onFocus={this.onFocusInput}
|
||||
onBlur={this.onBlurInput}
|
||||
onKeyUp={this.onKeyUpInput}
|
||||
|
@ -93,6 +114,7 @@ const BlockEmbedLatex = observer(class BlockEmbedLatex extends React.Component<I
|
|||
this.empty = node.find('#empty');
|
||||
this.value = node.find('#value');
|
||||
this.input = node.find('#input').get(0);
|
||||
this.container = node.find(`#${this.getContainerId()}`);
|
||||
|
||||
const length = this.text.length;
|
||||
|
||||
|
@ -147,6 +169,10 @@ const BlockEmbedLatex = observer(class BlockEmbedLatex extends React.Component<I
|
|||
};
|
||||
};
|
||||
|
||||
getContainerId () {
|
||||
return [ 'block', this.props.block.id, 'container' ].join('-');
|
||||
};
|
||||
|
||||
setEditing (v: boolean) {
|
||||
const node = $(this.node);
|
||||
v ? node.addClass('isEditing') : node.removeClass('isEditing');
|
||||
|
@ -212,22 +238,26 @@ const BlockEmbedLatex = observer(class BlockEmbedLatex extends React.Component<I
|
|||
return;
|
||||
};
|
||||
|
||||
const { filter } = commonStore;
|
||||
const { block } = this.props;
|
||||
const value = this.getValue();
|
||||
const range = getRange(this.input);
|
||||
const symbolBefore = value[range?.start - 1];
|
||||
const menuOpen = menuStore.isOpen('blockLatex');
|
||||
|
||||
if ((symbolBefore == '\\') && !keyboard.isSpecial(e)) {
|
||||
commonStore.filterSet(range.start, '');
|
||||
this.onMenu(e, 'input', false);
|
||||
};
|
||||
if (block.isEmbedLatex()) {
|
||||
const { filter } = commonStore;
|
||||
const range = getRange(this.input);
|
||||
const symbolBefore = value[range?.start - 1];
|
||||
const menuOpen = menuStore.isOpen('blockLatex');
|
||||
|
||||
if (menuOpen) {
|
||||
const d = range.start - filter.from;
|
||||
if (d >= 0) {
|
||||
const part = value.substring(filter.from, filter.from + d).replace(/^\\/, '');
|
||||
commonStore.filterSetText(part);
|
||||
if ((symbolBefore == '\\') && !keyboard.isSpecial(e)) {
|
||||
commonStore.filterSet(range.start, '');
|
||||
this.onMenu(e, 'input', false);
|
||||
};
|
||||
|
||||
if (menuOpen) {
|
||||
const d = range.start - filter.from;
|
||||
if (d >= 0) {
|
||||
const part = value.substring(filter.from, filter.from + d).replace(/^\\/, '');
|
||||
commonStore.filterSetText(part);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -343,7 +373,13 @@ const BlockEmbedLatex = observer(class BlockEmbedLatex extends React.Component<I
|
|||
return '';
|
||||
};
|
||||
|
||||
this.input.innerHTML = Prism.highlight(value, Prism.languages.latex, 'latex');
|
||||
const lang = this.getLang();
|
||||
|
||||
if (lang) {
|
||||
this.input.innerHTML = Prism.highlight(value, Prism.languages[lang], lang);
|
||||
} else {
|
||||
this.input.innerText = value;
|
||||
};
|
||||
this.setContent(value);
|
||||
};
|
||||
|
||||
|
@ -355,6 +391,17 @@ const BlockEmbedLatex = observer(class BlockEmbedLatex extends React.Component<I
|
|||
return String(this.input.innerText || '');
|
||||
};
|
||||
|
||||
getLang () {
|
||||
const { block } = this.props;
|
||||
const { processor } = block.content;
|
||||
|
||||
switch (processor) {
|
||||
default: break;
|
||||
case I.EmbedProcessor.Latex: return 'latex';
|
||||
case I.EmbedProcessor.Mermaid: return 'yaml';
|
||||
};
|
||||
};
|
||||
|
||||
setContent (text: string) {
|
||||
if (!this._isMounted) {
|
||||
return '';
|
||||
|
@ -362,24 +409,71 @@ const BlockEmbedLatex = observer(class BlockEmbedLatex extends React.Component<I
|
|||
|
||||
this.text = String(text || '');
|
||||
|
||||
this.value.html(this.text ? katex.renderToString(this.text, {
|
||||
displayMode: true,
|
||||
throwOnError: false,
|
||||
output: 'html',
|
||||
trust: (context: any) => [ '\\url', '\\href', '\\includegraphics' ].includes(context.command),
|
||||
}) : '');
|
||||
const { block } = this.props;
|
||||
const { processor } = block.content;
|
||||
|
||||
this.value.find('a').each((i: number, item: any) => {
|
||||
item = $(item);
|
||||
switch (processor) {
|
||||
default: {
|
||||
if (!this.text) {
|
||||
break;
|
||||
};
|
||||
|
||||
item.off('click').click((e: any) => {
|
||||
e.preventDefault();
|
||||
Renderer.send('urlOpen', item.attr('href'));
|
||||
});
|
||||
});
|
||||
const iframe = $('<iframe />', {
|
||||
src: './iframe.html',
|
||||
id: 'receiver',
|
||||
frameborder: 0,
|
||||
sandbox: 'allow-scripts allow-same-origin',
|
||||
allowtransparency: true,
|
||||
});
|
||||
|
||||
iframe.on('load', () => {
|
||||
const iw = (iframe[0] as HTMLIFrameElement).contentWindow;
|
||||
|
||||
iw.postMessage({ html: this.text, theme: commonStore.getThemeClass() }, '*');
|
||||
});
|
||||
|
||||
this.value.html('').append(iframe);
|
||||
break;
|
||||
};
|
||||
|
||||
case I.EmbedProcessor.Latex: {
|
||||
this.value.html(this.text ? katex.renderToString(this.text, {
|
||||
displayMode: true,
|
||||
throwOnError: false,
|
||||
output: 'html',
|
||||
trust: (context: any) => [ '\\url', '\\href', '\\includegraphics' ].includes(context.command),
|
||||
}) : '');
|
||||
|
||||
this.value.find('a').each((i: number, item: any) => {
|
||||
item = $(item);
|
||||
|
||||
item.off('click').click((e: any) => {
|
||||
e.preventDefault();
|
||||
Renderer.send('urlOpen', item.attr('href'));
|
||||
});
|
||||
});
|
||||
|
||||
this.updateRect();
|
||||
break;
|
||||
};
|
||||
|
||||
case I.EmbedProcessor.Mermaid: {
|
||||
if (!this.text) {
|
||||
break;
|
||||
};
|
||||
|
||||
mermaid.mermaidAPI.render(this.getContainerId(), this.text).then(res => {
|
||||
this.value.html(res.svg || this.text);
|
||||
|
||||
if (res.bindFunctions) {
|
||||
res.bindFunctions(this.value.get(0));
|
||||
};
|
||||
});
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
this.placeholderCheck(this.text);
|
||||
this.updateRect();
|
||||
};
|
||||
|
||||
placeholderCheck (value: string) {
|
||||
|
@ -419,6 +513,20 @@ const BlockEmbedLatex = observer(class BlockEmbedLatex extends React.Component<I
|
|||
this.range = range || { start: 0, end: 0 };
|
||||
};
|
||||
|
||||
onSelect () {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.setRange(getRange(this.input));
|
||||
keyboard.disableSelection(true);
|
||||
|
||||
this.win.off('mouseup.embed').on('mouseup.embed', (e: any) => {
|
||||
keyboard.disableSelection(false);
|
||||
this.win.off('mouseup.embed');
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
export default BlockEmbedLatex;
|
||||
export default BlockEmbed;
|
|
@ -1,254 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import $ from 'jquery';
|
||||
import Prism from 'prismjs';
|
||||
import { observer } from 'mobx-react';
|
||||
import { blockStore, menuStore } from 'Store';
|
||||
import { I, C, keyboard, focus } from 'Lib';
|
||||
import { getRange, setRange } from 'selection-ranges';
|
||||
|
||||
import BlockEmbedLatex from './latex';
|
||||
import BlockEmbedMermaid from './mermaid';
|
||||
|
||||
const BlockEmbedIndex = observer(class BlockEmbedIndex extends React.Component<I.BlockComponent> {
|
||||
|
||||
_isMounted = false;
|
||||
node = null;
|
||||
refChild = null;
|
||||
range = { start: 0, end: 0 };
|
||||
text = '';
|
||||
timeout = 0;
|
||||
win = null;
|
||||
input = null;
|
||||
value = null;
|
||||
empty = null;
|
||||
|
||||
constructor (props: I.BlockComponent) {
|
||||
super(props);
|
||||
|
||||
this.onSelect = this.onSelect.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { block } = this.props;
|
||||
const { processor } = block.content;
|
||||
const cn = [ 'wrap', 'resizable', 'focusable', 'c' + block.id ];
|
||||
const setRef = ref => this.refChild = ref;
|
||||
const childProps = {
|
||||
onSelect: this.onSelect,
|
||||
};
|
||||
|
||||
let content = null;
|
||||
|
||||
switch (processor) {
|
||||
case I.EmbedProcessor.Latex: {
|
||||
content = <BlockEmbedLatex ref={setRef} {...this.props} {...childProps} />;
|
||||
break;
|
||||
};
|
||||
|
||||
case I.EmbedProcessor.Mermaid: {
|
||||
content = <BlockEmbedMermaid ref={setRef} {...this.props} {...childProps} />;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={node => this.node = node}
|
||||
tabIndex={0}
|
||||
className={cn.join(' ')}
|
||||
onKeyDown={this.onKeyDownBlock}
|
||||
onKeyUp={this.onKeyUpBlock}
|
||||
onFocus={this.onFocusBlock}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this._isMounted = true;
|
||||
|
||||
const { block } = this.props;
|
||||
const node = $(this.node);
|
||||
|
||||
this.win = $(window);
|
||||
this.text = String(block.content.text || '');
|
||||
this.empty = node.find('#empty');
|
||||
this.value = node.find('#value');
|
||||
this.input = node.find('#input').get(0);
|
||||
|
||||
const length = this.text.length;
|
||||
|
||||
this.setRange({ start: length, end: length });
|
||||
this.setValue(this.text);
|
||||
};
|
||||
|
||||
componentDidUpdate () {
|
||||
const { block } = this.props;
|
||||
|
||||
this.text = String(block.content.text || '');
|
||||
this.unbind();
|
||||
this.setValue(this.text);
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
this._isMounted = false;
|
||||
this.unbind();
|
||||
};
|
||||
|
||||
rebind () {
|
||||
const { block } = this.props;
|
||||
|
||||
this.unbind();
|
||||
this.win.on(`click.c${block.id}`, (e: any) => {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
};
|
||||
|
||||
if ($(e.target).parents(`#block-${block.id}`).length > 0) {
|
||||
return;
|
||||
};
|
||||
|
||||
window.clearTimeout(this.timeout);
|
||||
|
||||
if (block.isEmbedLatex()) {
|
||||
menuStore.close('blockLatex');
|
||||
};
|
||||
|
||||
this.placeholderCheck(this.getValue());
|
||||
this.save(() => {
|
||||
this.setEditing(false);
|
||||
|
||||
if (block.isEmbedLatex()) {
|
||||
menuStore.close('previewLatex');
|
||||
};
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
unbind () {
|
||||
this.win.off(`click.c${this.props.block.id}`);
|
||||
};
|
||||
|
||||
onKeyDownBlock (e: any) {
|
||||
const { rootId, onKeyDown } = this.props;
|
||||
const cmd = keyboard.cmdKey();
|
||||
const node = $(this.node);
|
||||
const isEditing = node.hasClass('isEditing');
|
||||
|
||||
if (isEditing) {
|
||||
// Undo
|
||||
keyboard.shortcut(`${cmd}+z`, e, () => {
|
||||
e.preventDefault();
|
||||
keyboard.onUndo(rootId, 'editor');
|
||||
});
|
||||
|
||||
// Redo
|
||||
keyboard.shortcut(`${cmd}+shift+z, ${cmd}+y`, e, () => {
|
||||
e.preventDefault();
|
||||
keyboard.onRedo(rootId, 'editor');
|
||||
});
|
||||
};
|
||||
|
||||
if (onKeyDown && !isEditing) {
|
||||
onKeyDown(e, '', [], { from: 0, to: 0 }, this.props);
|
||||
};
|
||||
};
|
||||
|
||||
onKeyUpBlock (e: any) {
|
||||
const { onKeyUp } = this.props;
|
||||
const node = $(this.node);
|
||||
const isEditing = node.hasClass('isEditing');
|
||||
|
||||
if (onKeyUp && !isEditing) {
|
||||
onKeyUp(e, '', [], { from: 0, to: 0 }, this.props);
|
||||
};
|
||||
};
|
||||
|
||||
onFocusBlock () {
|
||||
focus.set(this.props.block.id, { from: 0, to: 0 });
|
||||
this.focus();
|
||||
};
|
||||
|
||||
onEdit (e: any) {
|
||||
const { readonly } = this.props;
|
||||
|
||||
if (readonly) {
|
||||
return;
|
||||
};
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
$('.block.blockEmbed .focusable.isEditing').removeClass('isEditing');
|
||||
|
||||
this.setEditing(true);
|
||||
this.focus();
|
||||
this.rebind();
|
||||
};
|
||||
|
||||
onSelect () {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.setRange(getRange(this.input));
|
||||
keyboard.disableSelection(true);
|
||||
|
||||
this.win.off('mouseup.embed').on('mouseup.embed', (e: any) => {
|
||||
keyboard.disableSelection(false);
|
||||
this.win.off('mouseup.embed');
|
||||
});
|
||||
};
|
||||
|
||||
focus () {
|
||||
if (this._isMounted && this.range) {
|
||||
setRange(this.input, this.range);
|
||||
};
|
||||
};
|
||||
|
||||
save (callBack?: (message: any) => void) {
|
||||
const { rootId, block, readonly } = this.props;
|
||||
|
||||
if (readonly) {
|
||||
return;
|
||||
};
|
||||
|
||||
const value = this.getValue();
|
||||
|
||||
blockStore.updateContent(rootId, block.id, { text: value });
|
||||
C.BlockEmbedSetText(rootId, block.id, value, callBack);
|
||||
};
|
||||
|
||||
setRange (range: any) {
|
||||
this.range = range || { start: 0, end: 0 };
|
||||
};
|
||||
|
||||
setEditing (v: boolean) {
|
||||
const node = $(this.node);
|
||||
v ? node.addClass('isEditing') : node.removeClass('isEditing');
|
||||
};
|
||||
|
||||
setValue (value: string) {
|
||||
if (!this._isMounted) {
|
||||
return '';
|
||||
};
|
||||
|
||||
this.input.innerHTML = Prism.highlight(value, Prism.languages.latex, 'latex');
|
||||
this.refChild.setContent(value);
|
||||
};
|
||||
|
||||
getValue (): string {
|
||||
if (!this._isMounted) {
|
||||
return '';
|
||||
};
|
||||
|
||||
return String(this.input.innerText || '');
|
||||
};
|
||||
|
||||
placeholderCheck (value: string) {
|
||||
value.trim().length > 0 ? this.empty.hide() : this.empty.show();
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
export default BlockEmbedIndex;
|
|
@ -1,308 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import $ from 'jquery';
|
||||
import Prism from 'prismjs';
|
||||
import mermaid from 'mermaid';
|
||||
import { observer } from 'mobx-react';
|
||||
import { I, keyboard, UtilCommon, C, focus, translate } from 'Lib';
|
||||
import { blockStore } from 'Store';
|
||||
import { getRange, setRange } from 'selection-ranges';
|
||||
|
||||
const BlockEmbedMermaid = observer(class BlockEmbedMermaid extends React.Component<I.BlockComponentEmbed> {
|
||||
|
||||
_isMounted = false;
|
||||
range: any = { start: 0, end: 0 };
|
||||
text = '';
|
||||
timeout = 0;
|
||||
node: any = null;
|
||||
win: any = null;
|
||||
input: any = null;
|
||||
value: any = null;
|
||||
empty: any = null;
|
||||
|
||||
constructor (props: I.BlockComponent) {
|
||||
super(props);
|
||||
|
||||
this.onKeyDownBlock = this.onKeyDownBlock.bind(this);
|
||||
this.onKeyUpBlock = this.onKeyUpBlock.bind(this);
|
||||
this.onFocusBlock = this.onFocusBlock.bind(this);
|
||||
this.onKeyDownInput = this.onKeyDownInput.bind(this);
|
||||
this.onKeyUpInput = this.onKeyUpInput.bind(this);
|
||||
this.onFocusInput = this.onFocusInput.bind(this);
|
||||
this.onBlurInput = this.onBlurInput.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onPaste = this.onPaste.bind(this);
|
||||
this.onEdit = this.onEdit.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { readonly, block, onSelect } = this.props;
|
||||
const cn = [ 'wrap', 'resizable', 'focusable', 'c' + block.id ];
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={node => this.node = node}
|
||||
tabIndex={0}
|
||||
className={cn.join(' ')}
|
||||
onKeyDown={this.onKeyDownBlock}
|
||||
onKeyUp={this.onKeyUpBlock}
|
||||
onFocus={this.onFocusBlock}
|
||||
>
|
||||
<div id="value" onClick={this.onEdit} />
|
||||
<div id="empty" className="empty" onClick={this.onEdit}>
|
||||
placeholder
|
||||
</div>
|
||||
<div id={[ 'block', block.id, 'container' ].join('-')} />
|
||||
<div
|
||||
id="input"
|
||||
contentEditable={!readonly}
|
||||
suppressContentEditableWarning={true}
|
||||
placeholder={translate('blockLatexPlaceholder')}
|
||||
onSelect={onSelect}
|
||||
onFocus={this.onFocusInput}
|
||||
onBlur={this.onBlurInput}
|
||||
onKeyUp={this.onKeyUpInput}
|
||||
onKeyDown={this.onKeyDownInput}
|
||||
onChange={this.onChange}
|
||||
onPaste={this.onPaste}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this._isMounted = true;
|
||||
|
||||
const { block } = this.props;
|
||||
const node = $(this.node);
|
||||
|
||||
this.win = $(window);
|
||||
this.text = String(block.content.text || '');
|
||||
this.empty = node.find('#empty');
|
||||
this.value = node.find('#value');
|
||||
this.input = node.find('#input').get(0);
|
||||
|
||||
const length = this.text.length;
|
||||
|
||||
this.setRange({ start: length, end: length });
|
||||
this.setValue(this.text);
|
||||
};
|
||||
|
||||
componentDidUpdate () {
|
||||
const { block } = this.props;
|
||||
|
||||
this.text = String(block.content.text || '');
|
||||
this.unbind();
|
||||
this.setValue(this.text);
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
this._isMounted = false;
|
||||
this.unbind();
|
||||
};
|
||||
|
||||
rebind () {
|
||||
const { block } = this.props;
|
||||
|
||||
this.unbind();
|
||||
this.win.on(`click.c${block.id}`, (e: any) => {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
};
|
||||
|
||||
if ($(e.target).parents(`#block-${block.id}`).length > 0) {
|
||||
return;
|
||||
};
|
||||
|
||||
window.clearTimeout(this.timeout);
|
||||
|
||||
this.placeholderCheck(this.getValue());
|
||||
this.save(() => {
|
||||
this.setEditing(false);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
unbind () {
|
||||
this.win.off(`click.c${this.props.block.id}`);
|
||||
};
|
||||
|
||||
focus () {
|
||||
if (this._isMounted && this.range) {
|
||||
setRange(this.input, this.range);
|
||||
};
|
||||
};
|
||||
|
||||
setEditing (v: boolean) {
|
||||
const node = $(this.node);
|
||||
v ? node.addClass('isEditing') : node.removeClass('isEditing');
|
||||
};
|
||||
|
||||
onFocusBlock () {
|
||||
focus.set(this.props.block.id, { from: 0, to: 0 });
|
||||
this.focus();
|
||||
};
|
||||
|
||||
onKeyDownBlock (e: any) {
|
||||
const { rootId, onKeyDown } = this.props;
|
||||
const cmd = keyboard.cmdKey();
|
||||
const node = $(this.node);
|
||||
const isEditing = node.hasClass('isEditing');
|
||||
|
||||
if (isEditing) {
|
||||
// Undo
|
||||
keyboard.shortcut(`${cmd}+z`, e, () => {
|
||||
e.preventDefault();
|
||||
keyboard.onUndo(rootId, 'editor');
|
||||
});
|
||||
|
||||
// Redo
|
||||
keyboard.shortcut(`${cmd}+shift+z, ${cmd}+y`, e, () => {
|
||||
e.preventDefault();
|
||||
keyboard.onRedo(rootId, 'editor');
|
||||
});
|
||||
};
|
||||
|
||||
if (onKeyDown && !isEditing) {
|
||||
onKeyDown(e, '', [], { from: 0, to: 0 }, this.props);
|
||||
};
|
||||
};
|
||||
|
||||
onKeyUpBlock (e: any) {
|
||||
const { onKeyUp } = this.props;
|
||||
const node = $(this.node);
|
||||
const isEditing = node.hasClass('isEditing');
|
||||
|
||||
if (onKeyUp && !isEditing) {
|
||||
onKeyUp(e, '', [], { from: 0, to: 0 }, this.props);
|
||||
};
|
||||
};
|
||||
|
||||
onKeyDownInput (e: any) {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
};
|
||||
};
|
||||
|
||||
onKeyUpInput (e: any) {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.setContent(this.getValue());
|
||||
this.save();
|
||||
};
|
||||
|
||||
onChange (e: any) {
|
||||
this.setValue(this.getValue());
|
||||
};
|
||||
|
||||
onPaste (e: any) {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
};
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const range = getRange(this.input);
|
||||
const cb = e.clipboardData || e.originalEvent.clipboardData;
|
||||
const text = cb.getData('text/plain');
|
||||
const to = range.end + text.length;
|
||||
|
||||
this.setValue(UtilCommon.stringInsert(this.getValue(), text, range.start, range.end));
|
||||
this.setRange({ start: to, end: to });
|
||||
this.focus();
|
||||
};
|
||||
|
||||
onFocusInput () {
|
||||
keyboard.setFocus(true);
|
||||
};
|
||||
|
||||
onBlurInput () {
|
||||
keyboard.setFocus(false);
|
||||
window.clearTimeout(this.timeout);
|
||||
|
||||
this.save();
|
||||
};
|
||||
|
||||
setValue (value: string) {
|
||||
if (!this._isMounted) {
|
||||
return '';
|
||||
};
|
||||
|
||||
this.input.innerHTML = Prism.highlight(value, Prism.languages.yaml, 'yaml');
|
||||
this.setContent(value);
|
||||
};
|
||||
|
||||
getValue (): string {
|
||||
if (!this._isMounted) {
|
||||
return '';
|
||||
};
|
||||
|
||||
return String(this.input.innerText || '');
|
||||
};
|
||||
|
||||
setContent (text: string) {
|
||||
if (!this._isMounted) {
|
||||
return '';
|
||||
};
|
||||
|
||||
const { block } = this.props;
|
||||
const id = [ 'block', block.id, 'container' ].join('-');
|
||||
const node = $(this.node);
|
||||
const value = node.find('#value');
|
||||
|
||||
this.text = String(text || '');
|
||||
|
||||
if (this.text) {
|
||||
mermaid.mermaidAPI.render(id, this.text).then(res => {
|
||||
value.html(res.svg || this.text);
|
||||
|
||||
if (res.bindFunctions) {
|
||||
res.bindFunctions(value.get(0));
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
this.placeholderCheck(this.text);
|
||||
};
|
||||
|
||||
placeholderCheck (value: string) {
|
||||
value.trim().length > 0 ? this.empty.hide() : this.empty.show();
|
||||
};
|
||||
|
||||
onEdit (e: any) {
|
||||
const { readonly } = this.props;
|
||||
|
||||
if (readonly) {
|
||||
return;
|
||||
};
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
$('.block.blockEmbed .focusable.isEditing').removeClass('isEditing');
|
||||
|
||||
this.setEditing(true);
|
||||
this.focus();
|
||||
this.rebind();
|
||||
};
|
||||
|
||||
save (callBack?: (message: any) => void) {
|
||||
const { rootId, block, readonly } = this.props;
|
||||
|
||||
if (readonly) {
|
||||
return;
|
||||
};
|
||||
|
||||
const value = this.getValue();
|
||||
|
||||
blockStore.updateContent(rootId, block.id, { text: value });
|
||||
C.BlockEmbedSetText(rootId, block.id, value, callBack);
|
||||
};
|
||||
|
||||
setRange (range: any) {
|
||||
this.range = range || { start: 0, end: 0 };
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
export default BlockEmbedMermaid;
|
|
@ -3,6 +3,9 @@ import { I } from 'Lib';
|
|||
export enum EmbedProcessor {
|
||||
Latex = 0,
|
||||
Mermaid = 1,
|
||||
Chart = 2,
|
||||
Youtube = 3,
|
||||
Vimeo = 4,
|
||||
};
|
||||
|
||||
export interface ContentEmbed {
|
||||
|
@ -10,10 +13,6 @@ export interface ContentEmbed {
|
|||
processor: EmbedProcessor;
|
||||
}
|
||||
|
||||
export interface BlockComponentEmbed extends I.BlockComponent {
|
||||
onSelect?(): void;
|
||||
};
|
||||
|
||||
export interface BlockEmbed extends I.Block {
|
||||
content: ContentEmbed;
|
||||
};
|
|
@ -67,7 +67,7 @@ import { FileType, FileState, FileStyle, ContentFile } from './block/file';
|
|||
import { BookmarkState, ContentBookmark } from './block/bookmark';
|
||||
import { DivStyle, ContentDiv } from './block/div';
|
||||
import { ContentRelation } from './block/relation';
|
||||
import { EmbedProcessor, ContentEmbed, BlockComponentEmbed } from './block/embed';
|
||||
import { EmbedProcessor, ContentEmbed } from './block/embed';
|
||||
import { BlockComponentTable, ContentTableRow } from './block/table';
|
||||
import { WidgetLayout, WidgetTreeItem, WidgetTreeDetails, ContentWidget, WidgetComponent } from './block/widget';
|
||||
|
||||
|
@ -211,7 +211,6 @@ export {
|
|||
|
||||
EmbedProcessor,
|
||||
ContentEmbed,
|
||||
BlockComponentEmbed,
|
||||
|
||||
BlockComponentTable,
|
||||
ContentTableRow,
|
||||
|
|
|
@ -51,6 +51,9 @@ class UtilMenu {
|
|||
{ type: I.BlockType.Text, id: I.TextStyle.Code, icon: 'code', lang: 'Code' },
|
||||
{ type: I.BlockType.Embed, id: I.EmbedProcessor.Latex, icon: 'latex', lang: 'Latex' },
|
||||
{ type: I.BlockType.Embed, id: I.EmbedProcessor.Mermaid, icon: 'mermaid', lang: 'Mermaid' },
|
||||
{ type: I.BlockType.Embed, id: I.EmbedProcessor.Chart, icon: 'chart', lang: 'Chart' },
|
||||
{ type: I.BlockType.Embed, id: I.EmbedProcessor.Youtube, icon: 'youtube', lang: 'Youtube' },
|
||||
{ type: I.BlockType.Embed, id: I.EmbedProcessor.Vimeo, icon: 'vimeo', lang: 'Vimeo' },
|
||||
].map(this.mapperBlock);
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue