Chore: Update Slate to 0.47.8 (#19197)
* Chore: Update Slate to 0.47.8 Closes #17430
This commit is contained in:
@@ -149,7 +149,7 @@ export const getWebpackConfig: WebpackConfigurationGetter = options => {
|
||||
'emotion',
|
||||
'prismjs',
|
||||
'slate-plain-serializer',
|
||||
'slate-react',
|
||||
'@grafana/slate-react',
|
||||
'react',
|
||||
'react-dom',
|
||||
'react-redux',
|
||||
|
||||
@@ -26,10 +26,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "^6.4.0-alpha",
|
||||
"@grafana/slate-react": "0.22.9-grafana",
|
||||
"@torkelo/react-select": "2.1.1",
|
||||
"@types/react-color": "2.17.0",
|
||||
"classnames": "2.2.6",
|
||||
"d3": "5.9.1",
|
||||
"immutable": "3.8.2",
|
||||
"jquery": "3.4.1",
|
||||
"lodash": "4.17.15",
|
||||
"moment": "2.24.0",
|
||||
@@ -45,6 +47,7 @@
|
||||
"react-storybook-addon-props-combinations": "1.1.0",
|
||||
"react-transition-group": "2.6.1",
|
||||
"react-virtualized": "9.21.0",
|
||||
"slate": "0.47.8",
|
||||
"tinycolor2": "1.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -65,6 +68,8 @@
|
||||
"@types/react-custom-scrollbars": "4.0.5",
|
||||
"@types/react-test-renderer": "16.8.1",
|
||||
"@types/react-transition-group": "2.0.16",
|
||||
"@types/slate": "0.47.1",
|
||||
"@types/slate-react": "0.22.5",
|
||||
"@types/storybook__addon-actions": "3.4.2",
|
||||
"@types/storybook__addon-info": "4.1.1",
|
||||
"@types/storybook__addon-knobs": "4.0.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import sourceMaps from 'rollup-plugin-sourcemaps';
|
||||
// import sourceMaps from 'rollup-plugin-sourcemaps';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
const pkg = require('./package.json');
|
||||
@@ -47,19 +47,20 @@ const buildCjsPackage = ({ env }) => {
|
||||
],
|
||||
'../../node_modules/react-color/lib/components/common': ['Saturation', 'Hue', 'Alpha'],
|
||||
'../../node_modules/immutable/dist/immutable.js': [
|
||||
'Record',
|
||||
'Set',
|
||||
'Map',
|
||||
'List',
|
||||
'OrderedSet',
|
||||
'is',
|
||||
'Stack',
|
||||
'Record',
|
||||
],
|
||||
'node_modules/immutable/dist/immutable.js': ['Record', 'Set', 'Map', 'List', 'OrderedSet', 'is', 'Stack'],
|
||||
'../../node_modules/esrever/esrever.js': ['reverse'],
|
||||
},
|
||||
}),
|
||||
resolve(),
|
||||
sourceMaps(),
|
||||
// sourceMaps(),
|
||||
env === 'production' && terser(),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ interface DataLinkEditorProps {
|
||||
isLast: boolean;
|
||||
value: DataLink;
|
||||
suggestions: VariableSuggestion[];
|
||||
onChange: (index: number, link: DataLink) => void;
|
||||
onChange: (index: number, link: DataLink, callback?: () => void) => void;
|
||||
onRemove: (link: DataLink) => void;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ export const DataLinkEditor: React.FC<DataLinkEditorProps> = React.memo(
|
||||
const theme = useContext(ThemeContext);
|
||||
const [title, setTitle] = useState(value.title);
|
||||
|
||||
const onUrlChange = (url: string) => {
|
||||
onChange(index, { ...value, url });
|
||||
const onUrlChange = (url: string, callback?: () => void) => {
|
||||
onChange(index, { ...value, url }, callback);
|
||||
};
|
||||
const onTitleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setTitle(event.target.value);
|
||||
|
||||
@@ -1,46 +1,45 @@
|
||||
import React, { useState, useMemo, useCallback, useContext } from 'react';
|
||||
import React, { useState, useMemo, useCallback, useContext, useRef, RefObject } from 'react';
|
||||
import { VariableSuggestion, VariableOrigin, DataLinkSuggestions } from './DataLinkSuggestions';
|
||||
import { makeValue, ThemeContext, DataLinkBuiltInVars } from '../../index';
|
||||
import { ThemeContext, DataLinkBuiltInVars, makeValue } from '../../index';
|
||||
import { SelectionReference } from './SelectionReference';
|
||||
import { Portal } from '../index';
|
||||
// @ts-ignore
|
||||
import { Editor } from 'slate-react';
|
||||
// @ts-ignore
|
||||
import { Value, Change, Document } from 'slate';
|
||||
// @ts-ignore
|
||||
|
||||
import { Editor } from '@grafana/slate-react';
|
||||
import { Value, Editor as CoreEditor } from 'slate';
|
||||
import Plain from 'slate-plain-serializer';
|
||||
import { Popper as ReactPopper } from 'react-popper';
|
||||
import useDebounce from 'react-use/lib/useDebounce';
|
||||
import { css, cx } from 'emotion';
|
||||
// @ts-ignore
|
||||
import PluginPrism from 'slate-prism';
|
||||
|
||||
import { SlatePrism } from '../../slate-plugins';
|
||||
import { SCHEMA } from '../../utils/slate';
|
||||
|
||||
const modulo = (a: number, n: number) => a - n * Math.floor(a / n);
|
||||
|
||||
interface DataLinkInputProps {
|
||||
value: string;
|
||||
onChange: (url: string) => void;
|
||||
onChange: (url: string, callback?: () => void) => void;
|
||||
suggestions: VariableSuggestion[];
|
||||
}
|
||||
|
||||
const plugins = [
|
||||
PluginPrism({
|
||||
SlatePrism({
|
||||
onlyIn: (node: any) => node.type === 'code_block',
|
||||
getSyntax: () => 'links',
|
||||
}),
|
||||
];
|
||||
|
||||
export const DataLinkInput: React.FC<DataLinkInputProps> = ({ value, onChange, suggestions }) => {
|
||||
const editorRef = useRef<Editor>() as RefObject<Editor>;
|
||||
const theme = useContext(ThemeContext);
|
||||
const [showingSuggestions, setShowingSuggestions] = useState(false);
|
||||
|
||||
const [suggestionsIndex, setSuggestionsIndex] = useState(0);
|
||||
const [usedSuggestions, setUsedSuggestions] = useState(
|
||||
suggestions.filter(suggestion => {
|
||||
return value.indexOf(suggestion.value) > -1;
|
||||
})
|
||||
suggestions.filter(suggestion => value.includes(suggestion.value))
|
||||
);
|
||||
// Using any here as TS has problem pickung up `change` method existance on Value
|
||||
// According to code and documentation `change` is an instance method on Value in slate 0.33.8 that we use
|
||||
// https://github.com/ianstormtaylor/slate/blob/slate%400.33.8/docs/reference/slate/value.md#change
|
||||
const [linkUrl, setLinkUrl] = useState<any>(makeValue(value));
|
||||
|
||||
const [linkUrl, setLinkUrl] = useState<Value>(makeValue(value));
|
||||
|
||||
const getStyles = useCallback(() => {
|
||||
return {
|
||||
@@ -56,22 +55,21 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = ({ value, onChange, s
|
||||
}, [theme]);
|
||||
|
||||
const currentSuggestions = useMemo(
|
||||
() =>
|
||||
suggestions.filter(suggestion => {
|
||||
return usedSuggestions.map(s => s.value).indexOf(suggestion.value) === -1;
|
||||
}),
|
||||
() => suggestions.filter(suggestion => !usedSuggestions.map(s => s.value).includes(suggestion.value)),
|
||||
[usedSuggestions, suggestions]
|
||||
);
|
||||
|
||||
// Workaround for https://github.com/ianstormtaylor/slate/issues/2927
|
||||
const stateRef = useRef({ showingSuggestions, currentSuggestions, suggestionsIndex, linkUrl, onChange });
|
||||
stateRef.current = { showingSuggestions, currentSuggestions, suggestionsIndex, linkUrl, onChange };
|
||||
|
||||
// SelectionReference is used to position the variables suggestion relatively to current DOM selection
|
||||
const selectionRef = useMemo(() => new SelectionReference(), [setShowingSuggestions]);
|
||||
|
||||
// Keep track of variables that has been used already
|
||||
const updateUsedSuggestions = () => {
|
||||
const currentLink = Plain.serialize(linkUrl);
|
||||
const next = usedSuggestions.filter(suggestion => {
|
||||
return currentLink.indexOf(suggestion.value) > -1;
|
||||
});
|
||||
const next = usedSuggestions.filter(suggestion => currentLink.includes(suggestion.value));
|
||||
if (next.length !== usedSuggestions.length) {
|
||||
setUsedSuggestions(next);
|
||||
}
|
||||
@@ -79,75 +77,64 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = ({ value, onChange, s
|
||||
|
||||
useDebounce(updateUsedSuggestions, 250, [linkUrl]);
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Backspace' || event.key === 'Escape') {
|
||||
setShowingSuggestions(false);
|
||||
setSuggestionsIndex(0);
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
if (showingSuggestions) {
|
||||
onVariableSelect(currentSuggestions[suggestionsIndex]);
|
||||
const onKeyDown = React.useCallback((event: KeyboardEvent, next: () => any) => {
|
||||
if (!stateRef.current.showingSuggestions) {
|
||||
if (event.key === '?' || event.key === '&' || event.key === '$' || (event.keyCode === 32 && event.ctrlKey)) {
|
||||
return setShowingSuggestions(true);
|
||||
}
|
||||
return next();
|
||||
}
|
||||
|
||||
if (showingSuggestions) {
|
||||
if (event.key === 'ArrowDown') {
|
||||
switch (event.key) {
|
||||
case 'Backspace':
|
||||
case 'Escape':
|
||||
setShowingSuggestions(false);
|
||||
return setSuggestionsIndex(0);
|
||||
|
||||
case 'Enter':
|
||||
event.preventDefault();
|
||||
setSuggestionsIndex(index => {
|
||||
return (index + 1) % currentSuggestions.length;
|
||||
});
|
||||
}
|
||||
if (event.key === 'ArrowUp') {
|
||||
return onVariableSelect(stateRef.current.currentSuggestions[stateRef.current.suggestionsIndex]);
|
||||
|
||||
case 'ArrowDown':
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
setSuggestionsIndex(index => {
|
||||
const nextIndex = index - 1 < 0 ? currentSuggestions.length - 1 : (index - 1) % currentSuggestions.length;
|
||||
return nextIndex;
|
||||
});
|
||||
}
|
||||
}
|
||||
const direction = event.key === 'ArrowDown' ? 1 : -1;
|
||||
return setSuggestionsIndex(index => modulo(index + direction, stateRef.current.currentSuggestions.length));
|
||||
|
||||
if (event.key === '?' || event.key === '&' || event.key === '$' || (event.keyCode === 32 && event.ctrlKey)) {
|
||||
setShowingSuggestions(true);
|
||||
default:
|
||||
return next();
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (event.key === 'Enter' && showingSuggestions) {
|
||||
// Preventing entering a new line
|
||||
// As of https://github.com/ianstormtaylor/slate/issues/1345#issuecomment-340508289
|
||||
return false;
|
||||
} else {
|
||||
// @ts-ignore
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const onUrlChange = ({ value }: Change) => {
|
||||
const onUrlChange = React.useCallback(({ value }: { value: Value }) => {
|
||||
setLinkUrl(value);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onUrlBlur = () => {
|
||||
onChange(Plain.serialize(linkUrl));
|
||||
};
|
||||
const onUrlBlur = React.useCallback((event: Event, editor: CoreEditor, next: () => any) => {
|
||||
// Callback needed for blur to work correctly
|
||||
stateRef.current.onChange(Plain.serialize(stateRef.current.linkUrl), () => {
|
||||
editorRef.current!.blur();
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onVariableSelect = (item: VariableSuggestion) => {
|
||||
const onVariableSelect = (item: VariableSuggestion, editor = editorRef.current!) => {
|
||||
const includeDollarSign = Plain.serialize(linkUrl).slice(-1) !== '$';
|
||||
|
||||
const change = linkUrl.change();
|
||||
|
||||
if (item.origin !== VariableOrigin.Template || item.value === DataLinkBuiltInVars.includeVars) {
|
||||
change.insertText(`${includeDollarSign ? '$' : ''}\{${item.value}}`);
|
||||
editor.insertText(`${includeDollarSign ? '$' : ''}\{${item.value}}`);
|
||||
} else {
|
||||
change.insertText(`var-${item.value}=$\{${item.value}}`);
|
||||
editor.insertText(`var-${item.value}=$\{${item.value}}`);
|
||||
}
|
||||
|
||||
setLinkUrl(change.value);
|
||||
setLinkUrl(editor.value);
|
||||
setShowingSuggestions(false);
|
||||
setUsedSuggestions((previous: VariableSuggestion[]) => {
|
||||
return [...previous, item];
|
||||
});
|
||||
setSuggestionsIndex(0);
|
||||
onChange(Plain.serialize(change.value));
|
||||
onChange(Plain.serialize(editor.value));
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
@@ -186,11 +173,13 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = ({ value, onChange, s
|
||||
</Portal>
|
||||
)}
|
||||
<Editor
|
||||
schema={SCHEMA}
|
||||
ref={editorRef}
|
||||
placeholder="http://your-grafana.com/d/000000010/annotations"
|
||||
value={linkUrl}
|
||||
value={stateRef.current.linkUrl}
|
||||
onChange={onUrlChange}
|
||||
onBlur={onUrlBlur}
|
||||
onKeyDown={onKeyDown}
|
||||
onKeyDown={(event, _editor, next) => onKeyDown(event as KeyboardEvent, next)}
|
||||
plugins={plugins}
|
||||
className={getStyles().editor}
|
||||
/>
|
||||
|
||||
@@ -12,7 +12,7 @@ import { VariableSuggestion } from './DataLinkSuggestions';
|
||||
|
||||
interface DataLinksEditorProps {
|
||||
value: DataLink[];
|
||||
onChange: (links: DataLink[]) => void;
|
||||
onChange: (links: DataLink[], callback?: () => void) => void;
|
||||
suggestions: VariableSuggestion[];
|
||||
maxLinks?: number;
|
||||
}
|
||||
@@ -30,14 +30,15 @@ export const DataLinksEditor: FC<DataLinksEditorProps> = React.memo(({ value, on
|
||||
onChange(value ? [...value, { url: '', title: '' }] : [{ url: '', title: '' }]);
|
||||
};
|
||||
|
||||
const onLinkChanged = (linkIndex: number, newLink: DataLink) => {
|
||||
const onLinkChanged = (linkIndex: number, newLink: DataLink, callback?: () => void) => {
|
||||
onChange(
|
||||
value.map((item, listIndex) => {
|
||||
if (linkIndex === listIndex) {
|
||||
return newLink;
|
||||
}
|
||||
return item;
|
||||
})
|
||||
}),
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,3 +2,4 @@ export * from './components';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
export * from './themes';
|
||||
export * from './slate-plugins';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { SlatePrism } from './slate-prism';
|
||||
@@ -0,0 +1,3 @@
|
||||
const TOKEN_MARK = 'prism-token';
|
||||
|
||||
export default TOKEN_MARK;
|
||||
@@ -0,0 +1,160 @@
|
||||
import Prism from 'prismjs';
|
||||
import { Block, Text, Decoration } from 'slate';
|
||||
import { Plugin } from '@grafana/slate-react';
|
||||
import Options, { OptionsFormat } from './options';
|
||||
import TOKEN_MARK from './TOKEN_MARK';
|
||||
|
||||
/**
|
||||
* A Slate plugin to highlight code syntax.
|
||||
*/
|
||||
export function SlatePrism(optsParam: OptionsFormat = {}): Plugin {
|
||||
const opts: Options = new Options(optsParam);
|
||||
|
||||
return {
|
||||
decorateNode: (node, editor, next) => {
|
||||
if (!opts.onlyIn(node)) {
|
||||
return next();
|
||||
}
|
||||
return decorateNode(opts, Block.create(node as Block));
|
||||
},
|
||||
|
||||
renderDecoration: (props, editor, next) =>
|
||||
opts.renderDecoration(
|
||||
{
|
||||
children: props.children,
|
||||
decoration: props.decoration,
|
||||
},
|
||||
editor as any,
|
||||
next
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the decoration for a node
|
||||
*/
|
||||
function decorateNode(opts: Options, block: Block) {
|
||||
const grammarName = opts.getSyntax(block);
|
||||
const grammar = Prism.languages[grammarName];
|
||||
if (!grammar) {
|
||||
// Grammar not loaded
|
||||
return [];
|
||||
}
|
||||
|
||||
// Tokenize the whole block text
|
||||
const texts = block.getTexts();
|
||||
const blockText = texts.map(text => text && text.getText()).join('\n');
|
||||
const tokens = Prism.tokenize(blockText, grammar);
|
||||
|
||||
// The list of decorations to return
|
||||
const decorations: Decoration[] = [];
|
||||
let textStart = 0;
|
||||
let textEnd = 0;
|
||||
|
||||
texts.forEach(text => {
|
||||
textEnd = textStart + text!.getText().length;
|
||||
|
||||
let offset = 0;
|
||||
function processToken(token: string | Prism.Token, accu?: string | number) {
|
||||
if (typeof token === 'string') {
|
||||
if (accu) {
|
||||
const decoration = createDecoration({
|
||||
text: text!,
|
||||
textStart,
|
||||
textEnd,
|
||||
start: offset,
|
||||
end: offset + token.length,
|
||||
className: `prism-token token ${accu}`,
|
||||
block,
|
||||
});
|
||||
if (decoration) {
|
||||
decorations.push(decoration);
|
||||
}
|
||||
}
|
||||
offset += token.length;
|
||||
} else {
|
||||
accu = `${accu} ${token.type} ${token.alias || ''}`;
|
||||
|
||||
if (typeof token.content === 'string') {
|
||||
const decoration = createDecoration({
|
||||
text: text!,
|
||||
textStart,
|
||||
textEnd,
|
||||
start: offset,
|
||||
end: offset + token.content.length,
|
||||
className: `prism-token token ${accu}`,
|
||||
block,
|
||||
});
|
||||
if (decoration) {
|
||||
decorations.push(decoration);
|
||||
}
|
||||
|
||||
offset += token.content.length;
|
||||
} else {
|
||||
// When using token.content instead of token.matchedStr, token can be deep
|
||||
for (let i = 0; i < token.content.length; i += 1) {
|
||||
// @ts-ignore
|
||||
processToken(token.content[i], accu);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tokens.forEach(processToken);
|
||||
textStart = textEnd + 1; // account for added `\n`
|
||||
});
|
||||
|
||||
return decorations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a decoration range for the given text.
|
||||
*/
|
||||
function createDecoration({
|
||||
text,
|
||||
textStart,
|
||||
textEnd,
|
||||
start,
|
||||
end,
|
||||
className,
|
||||
block,
|
||||
}: {
|
||||
text: Text; // The text being decorated
|
||||
textStart: number; // Its start position in the whole text
|
||||
textEnd: number; // Its end position in the whole text
|
||||
start: number; // The position in the whole text where the token starts
|
||||
end: number; // The position in the whole text where the token ends
|
||||
className: string; // The prism token classname
|
||||
block: Block;
|
||||
}): Decoration | null {
|
||||
if (start >= textEnd || end <= textStart) {
|
||||
// Ignore, the token is not in the text
|
||||
return null;
|
||||
}
|
||||
|
||||
// Shrink to this text boundaries
|
||||
start = Math.max(start, textStart);
|
||||
end = Math.min(end, textEnd);
|
||||
|
||||
// Now shift offsets to be relative to this text
|
||||
start -= textStart;
|
||||
end -= textStart;
|
||||
|
||||
const myDec = block.createDecoration({
|
||||
object: 'decoration',
|
||||
anchor: {
|
||||
key: text.key,
|
||||
offset: start,
|
||||
object: 'point',
|
||||
},
|
||||
focus: {
|
||||
key: text.key,
|
||||
offset: end,
|
||||
object: 'point',
|
||||
},
|
||||
type: TOKEN_MARK,
|
||||
data: { className },
|
||||
});
|
||||
|
||||
return myDec;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import { Mark, Node, Decoration } from 'slate';
|
||||
import { Editor } from '@grafana/slate-react';
|
||||
import { Record } from 'immutable';
|
||||
|
||||
import TOKEN_MARK from './TOKEN_MARK';
|
||||
|
||||
export interface OptionsFormat {
|
||||
// Determine which node should be highlighted
|
||||
onlyIn?: (node: Node) => boolean;
|
||||
// Returns the syntax for a node that should be highlighted
|
||||
getSyntax?: (node: Node) => string;
|
||||
// Render a highlighting mark in a highlighted node
|
||||
renderMark?: ({ mark, children }: { mark: Mark; children: React.ReactNode }) => void | React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default filter for code blocks
|
||||
*/
|
||||
function defaultOnlyIn(node: Node): boolean {
|
||||
return node.object === 'block' && node.type === 'code_block';
|
||||
}
|
||||
|
||||
/**
|
||||
* Default getter for syntax
|
||||
*/
|
||||
function defaultGetSyntax(node: Node): string {
|
||||
return 'javascript';
|
||||
}
|
||||
|
||||
/**
|
||||
* Default rendering for decorations
|
||||
*/
|
||||
function defaultRenderDecoration(
|
||||
props: { children: React.ReactNode; decoration: Decoration },
|
||||
editor: Editor,
|
||||
next: () => any
|
||||
): void | React.ReactNode {
|
||||
const { decoration } = props;
|
||||
if (decoration.type !== TOKEN_MARK) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const className = decoration.data.get('className');
|
||||
return <span className={className}>{props.children}</span>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The plugin options
|
||||
*/
|
||||
class Options
|
||||
extends Record({
|
||||
onlyIn: defaultOnlyIn,
|
||||
getSyntax: defaultGetSyntax,
|
||||
renderDecoration: defaultRenderDecoration,
|
||||
})
|
||||
implements OptionsFormat {
|
||||
readonly onlyIn!: (node: Node) => boolean;
|
||||
readonly getSyntax!: (node: Node) => string;
|
||||
readonly renderDecoration!: (
|
||||
{
|
||||
decoration,
|
||||
children,
|
||||
}: {
|
||||
decoration: Decoration;
|
||||
children: React.ReactNode;
|
||||
},
|
||||
editor: Editor,
|
||||
next: () => any
|
||||
) => void | React.ReactNode;
|
||||
|
||||
constructor(props: OptionsFormat) {
|
||||
super(props);
|
||||
}
|
||||
}
|
||||
|
||||
export default Options;
|
||||
@@ -1,22 +1,22 @@
|
||||
// @ts-ignore
|
||||
import { Block, Document, Text, Value } from 'slate';
|
||||
import { Block, Document, Text, Value, SchemaProperties } from 'slate';
|
||||
|
||||
const SCHEMA = {
|
||||
blocks: {
|
||||
paragraph: 'paragraph',
|
||||
codeblock: 'code_block',
|
||||
codeline: 'code_line',
|
||||
export const SCHEMA: SchemaProperties = {
|
||||
document: {
|
||||
nodes: [
|
||||
{
|
||||
match: [{ type: 'paragraph' }, { type: 'code_block' }, { type: 'code_line' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
inlines: {},
|
||||
marks: {},
|
||||
};
|
||||
|
||||
export const makeFragment = (text: string, syntax?: string) => {
|
||||
export const makeFragment = (text: string, syntax?: string): Document => {
|
||||
const lines = text.split('\n').map(line =>
|
||||
Block.create({
|
||||
type: 'code_line',
|
||||
nodes: [Text.create(line)],
|
||||
} as any)
|
||||
})
|
||||
);
|
||||
|
||||
const block = Block.create({
|
||||
@@ -25,18 +25,17 @@ export const makeFragment = (text: string, syntax?: string) => {
|
||||
},
|
||||
type: 'code_block',
|
||||
nodes: lines,
|
||||
} as any);
|
||||
});
|
||||
|
||||
return Document.create({
|
||||
nodes: [block],
|
||||
});
|
||||
};
|
||||
|
||||
export const makeValue = (text: string, syntax?: string) => {
|
||||
export const makeValue = (text: string, syntax?: string): Value => {
|
||||
const fragment = makeFragment(text, syntax);
|
||||
|
||||
return Value.create({
|
||||
document: fragment,
|
||||
SCHEMA,
|
||||
} as any);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
"compilerOptions": {
|
||||
"rootDirs": [".", "stories"],
|
||||
"typeRoots": ["./node_modules/@types", "types"],
|
||||
"baseUrl": "./node_modules/@types",
|
||||
"paths": {
|
||||
"@grafana/slate-react": ["slate-react"]
|
||||
},
|
||||
"declarationDir": "dist",
|
||||
"outDir": "compiled"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user