feat: add Slate component (#272)
* feat: add Slate component * feat: add slate Read Pretty * fix: remove unused code * fix: demo * feat: rich text field interface * fix: editor normalize * fix: modify slate style * fix: remove debug info Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
a4e943ace7
commit
3f3e9b5373
@ -25,6 +25,7 @@ import {
|
|||||||
SchemaTemplateShortcut,
|
SchemaTemplateShortcut,
|
||||||
SigninPage,
|
SigninPage,
|
||||||
SignupPage,
|
SignupPage,
|
||||||
|
Slate,
|
||||||
SystemSettingsProvider,
|
SystemSettingsProvider,
|
||||||
SystemSettingsShortcut,
|
SystemSettingsShortcut,
|
||||||
useRoutes,
|
useRoutes,
|
||||||
@ -86,7 +87,7 @@ const providers = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[SchemaComponentProvider, { components: { Link, NavLink } }],
|
[SchemaComponentProvider, { components: { Slate, Link, NavLink } }],
|
||||||
// RemoteCollectionManagerProvider,
|
// RemoteCollectionManagerProvider,
|
||||||
[
|
[
|
||||||
SchemaInitializerProvider,
|
SchemaInitializerProvider,
|
||||||
|
@ -38,6 +38,9 @@
|
|||||||
"react-i18next": "^11.15.1",
|
"react-i18next": "^11.15.1",
|
||||||
"react-image-lightbox": "^5.1.4",
|
"react-image-lightbox": "^5.1.4",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
|
"slate": "^0.76.1",
|
||||||
|
"slate-history": "^0.66.0",
|
||||||
|
"slate-react": "^0.76.1",
|
||||||
"use-deep-compare-effect": "^1.8.1"
|
"use-deep-compare-effect": "^1.8.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
@ -16,6 +16,7 @@ export * from './password';
|
|||||||
export * from './percent';
|
export * from './percent';
|
||||||
export * from './phone';
|
export * from './phone';
|
||||||
export * from './radioGroup';
|
export * from './radioGroup';
|
||||||
|
export * from './richText';
|
||||||
export * from './select';
|
export * from './select';
|
||||||
export * from './subTable';
|
export * from './subTable';
|
||||||
export * from './textarea';
|
export * from './textarea';
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
import type { ISchema } from '@formily/react';
|
||||||
|
import { defaultProps } from './properties';
|
||||||
|
import { IField } from './types';
|
||||||
|
|
||||||
|
export const richText: IField = {
|
||||||
|
name: 'richText',
|
||||||
|
type: 'object',
|
||||||
|
group: 'media',
|
||||||
|
order: 2,
|
||||||
|
title: '{{t("Rich Text")}}',
|
||||||
|
default: {
|
||||||
|
interface: 'richText',
|
||||||
|
type: 'json',
|
||||||
|
// name,
|
||||||
|
uiSchema: {
|
||||||
|
type: 'array',
|
||||||
|
'x-component': 'Slate.RichText',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
...defaultProps,
|
||||||
|
},
|
||||||
|
schemaInitialize(schema: ISchema, { block }) {
|
||||||
|
if (['Table', 'Kanban'].includes(block)) {
|
||||||
|
schema['x-component-props'] = schema['x-component-props'] || {};
|
||||||
|
schema['x-component-props']['ellipsis'] = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
@ -21,7 +21,7 @@ export * from './schema-initializer';
|
|||||||
export * from './schema-settings';
|
export * from './schema-settings';
|
||||||
export * from './schema-templates';
|
export * from './schema-templates';
|
||||||
export * from './settings-form';
|
export * from './settings-form';
|
||||||
|
export * from './slate';
|
||||||
export * from './system-settings';
|
export * from './system-settings';
|
||||||
export * from './user';
|
export * from './user';
|
||||||
export * from './workflow';
|
export * from './workflow';
|
||||||
|
|
||||||
|
264
packages/client/src/slate/RichText.tsx
Normal file
264
packages/client/src/slate/RichText.tsx
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
import { css, cx } from '@emotion/css';
|
||||||
|
import isHotkey from 'is-hotkey';
|
||||||
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
import { createEditor, Editor, Element as SlateElement, Node, Transforms } from 'slate';
|
||||||
|
import { withHistory } from 'slate-history';
|
||||||
|
import type { ReactEditor } from 'slate-react';
|
||||||
|
import { Editable, Slate, useSlate, withReact } from 'slate-react';
|
||||||
|
import { EllipsisWithTooltip } from '../schema-component/antd/input/EllipsisWithTooltip';
|
||||||
|
import { Button, Icon, Toolbar } from './components';
|
||||||
|
|
||||||
|
const HOTKEYS = {
|
||||||
|
'mod+b': 'bold',
|
||||||
|
'mod+i': 'italic',
|
||||||
|
'mod+u': 'underline',
|
||||||
|
'mod+`': 'code',
|
||||||
|
};
|
||||||
|
|
||||||
|
const LIST_TYPES = ['numbered-list', 'bulleted-list'];
|
||||||
|
const TEXT_ALIGN_TYPES = ['left', 'center', 'right', 'justify'];
|
||||||
|
const DEFAULT_VALUE = [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: '' }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const RichText = (props: any) => {
|
||||||
|
const { value = DEFAULT_VALUE, placeholder = '', className, readOnly, autop = true, ellipsis, onChange } = props;
|
||||||
|
const renderElement = useCallback((props) => <Element {...props} />, []);
|
||||||
|
const renderLeaf = useCallback((props) => <Leaf {...props} />, []);
|
||||||
|
const editor = useMemo(() => withHistory(withReact(createEditor() as ReactEditor)), []);
|
||||||
|
const slateValue = useMemo(() => {
|
||||||
|
editor.children = JSON.parse(JSON.stringify(value));
|
||||||
|
Editor.normalize(editor, { force: true });
|
||||||
|
return editor.children;
|
||||||
|
}, [editor, value]);
|
||||||
|
if (readOnly) {
|
||||||
|
const slateContent = (
|
||||||
|
<Slate editor={editor} value={slateValue}>
|
||||||
|
<Editable renderElement={renderElement} renderLeaf={renderLeaf} spellCheck autoFocus readOnly={readOnly} />
|
||||||
|
</Slate>
|
||||||
|
);
|
||||||
|
const slatePlainText = serialize(slateValue);
|
||||||
|
const content = (
|
||||||
|
<EllipsisWithTooltip ellipsis={ellipsis} popoverContent={autop ? slateContent : slatePlainText}>
|
||||||
|
{ellipsis ? slatePlainText : slateContent}
|
||||||
|
</EllipsisWithTooltip>
|
||||||
|
);
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
className,
|
||||||
|
'slate',
|
||||||
|
css`
|
||||||
|
border: solid 1px #d2d2d2;
|
||||||
|
border-radius: 2px;
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Slate editor={editor} value={slateValue} onChange={onChange}>
|
||||||
|
<Toolbar>
|
||||||
|
<MarkButton format="bold" icon="format_bold" />
|
||||||
|
<MarkButton format="italic" icon="format_italic" />
|
||||||
|
<MarkButton format="underline" icon="format_underlined" />
|
||||||
|
<MarkButton format="code" icon="code" />
|
||||||
|
<BlockButton format="heading-one" icon="looks_one" />
|
||||||
|
<BlockButton format="heading-two" icon="looks_two" />
|
||||||
|
<BlockButton format="block-quote" icon="format_quote" />
|
||||||
|
<BlockButton format="numbered-list" icon="format_list_numbered" />
|
||||||
|
<BlockButton format="bulleted-list" icon="format_list_bulleted" />
|
||||||
|
<BlockButton format="left" icon="format_align_left" />
|
||||||
|
<BlockButton format="center" icon="format_align_center" />
|
||||||
|
<BlockButton format="right" icon="format_align_right" />
|
||||||
|
<BlockButton format="justify" icon="format_align_justify" />
|
||||||
|
</Toolbar>
|
||||||
|
<div
|
||||||
|
className={css`
|
||||||
|
padding: 5px 11px;
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<Editable
|
||||||
|
renderElement={renderElement}
|
||||||
|
renderLeaf={renderLeaf}
|
||||||
|
placeholder={placeholder}
|
||||||
|
spellCheck
|
||||||
|
autoFocus
|
||||||
|
readOnly={readOnly}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
for (const hotkey in HOTKEYS) {
|
||||||
|
if (isHotkey(hotkey, event as any)) {
|
||||||
|
event.preventDefault();
|
||||||
|
const mark = HOTKEYS[hotkey];
|
||||||
|
toggleMark(editor, mark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Slate>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const serialize = (nodes) => {
|
||||||
|
return nodes.map((n) => Node.string(n)).join('\n');
|
||||||
|
};
|
||||||
|
const toggleBlock = (editor, format) => {
|
||||||
|
const isActive = isBlockActive(editor, format, TEXT_ALIGN_TYPES.includes(format) ? 'align' : 'type');
|
||||||
|
const isList = LIST_TYPES.includes(format);
|
||||||
|
|
||||||
|
Transforms.unwrapNodes(editor, {
|
||||||
|
match: (n: Node) =>
|
||||||
|
!Editor.isEditor(n) &&
|
||||||
|
SlateElement.isElement(n) &&
|
||||||
|
LIST_TYPES.includes((n as any).type) &&
|
||||||
|
!TEXT_ALIGN_TYPES.includes(format),
|
||||||
|
split: true,
|
||||||
|
});
|
||||||
|
let newProperties: Partial<SlateElement & { align: any; type: string }>;
|
||||||
|
if (TEXT_ALIGN_TYPES.includes(format)) {
|
||||||
|
newProperties = {
|
||||||
|
align: isActive ? undefined : format,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
newProperties = {
|
||||||
|
type: isActive ? 'paragraph' : isList ? 'list-item' : format,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Transforms.setNodes<SlateElement>(editor, newProperties);
|
||||||
|
|
||||||
|
if (!isActive && isList) {
|
||||||
|
const block = { type: format, children: [] };
|
||||||
|
Transforms.wrapNodes(editor, block);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleMark = (editor, format) => {
|
||||||
|
const isActive = isMarkActive(editor, format);
|
||||||
|
|
||||||
|
if (isActive) {
|
||||||
|
Editor.removeMark(editor, format);
|
||||||
|
} else {
|
||||||
|
Editor.addMark(editor, format, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isBlockActive = (editor, format, blockType = 'type') => {
|
||||||
|
const { selection } = editor;
|
||||||
|
if (!selection) return false;
|
||||||
|
|
||||||
|
const [match] = Array.from(
|
||||||
|
Editor.nodes(editor, {
|
||||||
|
at: Editor.unhangRange(editor, selection),
|
||||||
|
match: (n) => !Editor.isEditor(n) && SlateElement.isElement(n) && n[blockType] === format,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return !!match;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMarkActive = (editor, format) => {
|
||||||
|
const marks = Editor.marks(editor);
|
||||||
|
return marks ? marks[format] === true : false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Element = ({ attributes, children, element }) => {
|
||||||
|
const style = { textAlign: element.align };
|
||||||
|
switch (element.type) {
|
||||||
|
case 'block-quote':
|
||||||
|
return (
|
||||||
|
<blockquote style={style} {...attributes}>
|
||||||
|
{children}
|
||||||
|
</blockquote>
|
||||||
|
);
|
||||||
|
case 'bulleted-list':
|
||||||
|
return (
|
||||||
|
<ul style={style} {...attributes}>
|
||||||
|
{children}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
case 'heading-one':
|
||||||
|
return (
|
||||||
|
<h1 style={style} {...attributes}>
|
||||||
|
{children}
|
||||||
|
</h1>
|
||||||
|
);
|
||||||
|
case 'heading-two':
|
||||||
|
return (
|
||||||
|
<h2 style={style} {...attributes}>
|
||||||
|
{children}
|
||||||
|
</h2>
|
||||||
|
);
|
||||||
|
case 'list-item':
|
||||||
|
return (
|
||||||
|
<li style={style} {...attributes}>
|
||||||
|
{children}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
case 'numbered-list':
|
||||||
|
return (
|
||||||
|
<ol style={style} {...attributes}>
|
||||||
|
{children}
|
||||||
|
</ol>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<p style={style} {...attributes}>
|
||||||
|
{children}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Leaf = ({ attributes, children, leaf }) => {
|
||||||
|
if (leaf.bold) {
|
||||||
|
children = <strong>{children}</strong>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leaf.code) {
|
||||||
|
children = <code>{children}</code>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leaf.italic) {
|
||||||
|
children = <em>{children}</em>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leaf.underline) {
|
||||||
|
children = <u>{children}</u>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <span {...attributes}>{children}</span>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BlockButton = ({ format, icon }) => {
|
||||||
|
const editor = useSlate();
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
active={isBlockActive(editor, format, TEXT_ALIGN_TYPES.includes(format) ? 'align' : 'type')}
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
toggleBlock(editor, format);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon>{icon}</Icon>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MarkButton = ({ format, icon }) => {
|
||||||
|
const editor = useSlate();
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
active={isMarkActive(editor, format)}
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
toggleMark(editor, format);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon>{icon}</Icon>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
32
packages/client/src/slate/Slate.tsx
Normal file
32
packages/client/src/slate/Slate.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Field } from '@formily/core';
|
||||||
|
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { Node } from 'slate';
|
||||||
|
import './index.less';
|
||||||
|
import { RichText } from './RichText';
|
||||||
|
|
||||||
|
export const Slate = () => null;
|
||||||
|
|
||||||
|
const serialize = (nodes) => {
|
||||||
|
return nodes?.map?.((n) => Node.string(n)).join('');
|
||||||
|
};
|
||||||
|
const DEFAULT_VALUE = [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ text: '' }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
Slate.RichText = connect(
|
||||||
|
RichText,
|
||||||
|
mapProps((props, field: Field) => {
|
||||||
|
const fieldValue = serialize(field.value)?.trim();
|
||||||
|
if (!fieldValue) {
|
||||||
|
field.value = undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
mapReadPretty((props: any) => <RichText {...props} readOnly />),
|
||||||
|
);
|
172
packages/client/src/slate/components.tsx
Normal file
172
packages/client/src/slate/components.tsx
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import { css, cx } from '@emotion/css';
|
||||||
|
import React, { PropsWithChildren, Ref } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
interface BaseProps {
|
||||||
|
className: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
type OrNull<T> = T | null;
|
||||||
|
|
||||||
|
export const Button = React.forwardRef(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
className,
|
||||||
|
active,
|
||||||
|
reversed,
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<
|
||||||
|
{
|
||||||
|
active: boolean;
|
||||||
|
reversed: boolean;
|
||||||
|
} & BaseProps
|
||||||
|
>,
|
||||||
|
ref: Ref<OrNull<HTMLSpanElement>>,
|
||||||
|
) => (
|
||||||
|
<span
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
className={cx(
|
||||||
|
className,
|
||||||
|
css`
|
||||||
|
cursor: pointer;
|
||||||
|
color: ${reversed ? (active ? 'white' : '#aaa') : active ? 'black' : '#ccc'};
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const EditorValue = React.forwardRef(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
className,
|
||||||
|
value,
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<
|
||||||
|
{
|
||||||
|
value: any;
|
||||||
|
} & BaseProps
|
||||||
|
>,
|
||||||
|
ref: Ref<OrNull<null>>,
|
||||||
|
) => {
|
||||||
|
const textLines = value.document.nodes
|
||||||
|
.map((node) => node.text)
|
||||||
|
.toArray()
|
||||||
|
.join('\n');
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
className={cx(
|
||||||
|
className,
|
||||||
|
css`
|
||||||
|
margin: 30px -20px 0;
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={css`
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 5px 20px;
|
||||||
|
color: #404040;
|
||||||
|
border-top: 2px solid #eeeeee;
|
||||||
|
background: #f8f8f8;
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
Slate's value as text
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={css`
|
||||||
|
color: #404040;
|
||||||
|
font: 12px monospace;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
padding: 10px 20px;
|
||||||
|
div {
|
||||||
|
margin: 0 0 0.5em;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{textLines}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Icon = React.forwardRef(
|
||||||
|
({ className, ...props }: PropsWithChildren<BaseProps>, ref: Ref<OrNull<HTMLSpanElement>>) => (
|
||||||
|
<span
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
className={cx(
|
||||||
|
'material-icons',
|
||||||
|
className,
|
||||||
|
css`
|
||||||
|
font-size: 18px;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Instruction = React.forwardRef(
|
||||||
|
({ className, ...props }: PropsWithChildren<BaseProps>, ref: Ref<OrNull<HTMLDivElement>>) => (
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
className={cx(
|
||||||
|
className,
|
||||||
|
css`
|
||||||
|
white-space: pre-wrap;
|
||||||
|
margin: 0 -20px 10px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
background: #f8f8e8;
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Menu = React.forwardRef(
|
||||||
|
({ className, ...props }: PropsWithChildren<BaseProps>, ref: Ref<OrNull<HTMLDivElement>>) => (
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
className={cx(
|
||||||
|
className,
|
||||||
|
css`
|
||||||
|
& > * {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
& > * + * {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Portal = ({ children }) => {
|
||||||
|
return typeof document === 'object' ? ReactDOM.createPortal(children, document.body) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Toolbar = React.forwardRef(
|
||||||
|
({ className, ...props }: PropsWithChildren<BaseProps>, ref: Ref<OrNull<HTMLDivElement>>) => (
|
||||||
|
<Menu
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
className={cx(
|
||||||
|
className,
|
||||||
|
css`
|
||||||
|
position: relative;
|
||||||
|
padding: 5px 11px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
);
|
27
packages/client/src/slate/demos/demo1.tsx
Normal file
27
packages/client/src/slate/demos/demo1.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { FormItem } from '@formily/antd';
|
||||||
|
import { SchemaComponent, SchemaComponentProvider, Slate } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
slate: {
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Slate.RichText',
|
||||||
|
'x-component-props': {
|
||||||
|
onChange: (value) => {
|
||||||
|
console.log(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ Slate, FormItem }}>
|
||||||
|
<SchemaComponent schema={schema} />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
79
packages/client/src/slate/demos/demo2.tsx
Normal file
79
packages/client/src/slate/demos/demo2.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { FormItem } from '@formily/antd';
|
||||||
|
import { SchemaComponent, SchemaComponentProvider, Slate } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
let value = [
|
||||||
|
{
|
||||||
|
type: 'heading-one',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: 'Slate Rich Text Editor',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'block-quote',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: 'this is based in Slate',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'bulleted-list',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'list-item',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: 'The editor\'s "schema" was hardcoded and hard to customize. ',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'list-item',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: 'Transforming the documents programmatically was very convoluted. ',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'list-item',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: 'Serializing to HTML, Markdown, etc. seemed like an afterthought. ',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'list-item',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: 'Re-inventing the view layer seemed inefficient and limiting.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
read: {
|
||||||
|
type: 'string',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Slate.RichText',
|
||||||
|
default: value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ Slate, FormItem }}>
|
||||||
|
<SchemaComponent schema={schema} />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
44
packages/client/src/slate/demos/demo3.tsx
Normal file
44
packages/client/src/slate/demos/demo3.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* title: Input
|
||||||
|
*/
|
||||||
|
import { FormItem } from '@formily/antd';
|
||||||
|
import { SchemaComponent, SchemaComponentProvider, Slate } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
input: {
|
||||||
|
title: `Editable1`,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Slate.RichText',
|
||||||
|
'x-reactions': {
|
||||||
|
target: '*(input2,read)',
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
value: '{{$self.value}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input2: {
|
||||||
|
title: `Editable2`,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Slate.RichText',
|
||||||
|
},
|
||||||
|
read: {
|
||||||
|
title: `Read pretty`,
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Slate.RichText',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ Slate, FormItem }}>
|
||||||
|
<SchemaComponent schema={schema} />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
31
packages/client/src/slate/index.less
Normal file
31
packages/client/src/slate/index.less
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url(./materialicons.woff2) format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
word-wrap: normal;
|
||||||
|
direction: ltr;
|
||||||
|
-webkit-font-feature-settings: 'liga';
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
.ant-formily-item-error .slate {
|
||||||
|
border-color: #ff4d4f !important;
|
||||||
|
}
|
||||||
|
.slate:focus-within {
|
||||||
|
border-color: #40a9ff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||||
|
border-right-width: 1px;
|
||||||
|
outline: 0;
|
||||||
|
}
|
22
packages/client/src/slate/index.md
Normal file
22
packages/client/src/slate/index.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
nav:
|
||||||
|
path: /client
|
||||||
|
group:
|
||||||
|
path: /client
|
||||||
|
---
|
||||||
|
|
||||||
|
# Slate
|
||||||
|
|
||||||
|
基于 [slate](https://github.com/ianstormtaylor/slate) 封装。
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Slate Rich Text Editor
|
||||||
|
|
||||||
|
<code src="./demos/demo1.tsx" />
|
||||||
|
|
||||||
|
### Slate Rich Text Editor is Read Pretty
|
||||||
|
|
||||||
|
<code src="./demos/demo2.tsx" />
|
||||||
|
|
||||||
|
<code src="./demos/demo3.tsx" />
|
1
packages/client/src/slate/index.ts
Normal file
1
packages/client/src/slate/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './Slate';
|
BIN
packages/client/src/slate/materialicons.woff2
Normal file
BIN
packages/client/src/slate/materialicons.woff2
Normal file
Binary file not shown.
69
yarn.lock
69
yarn.lock
@ -3124,6 +3124,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.1.tgz#e81ad28a60bee0328c6d2384e029aec626f1ae67"
|
resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.1.tgz#e81ad28a60bee0328c6d2384e029aec626f1ae67"
|
||||||
integrity sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q==
|
integrity sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q==
|
||||||
|
|
||||||
|
"@types/is-hotkey@^0.1.1":
|
||||||
|
version "0.1.7"
|
||||||
|
resolved "https://registry.npmmirror.com/@types/is-hotkey/-/is-hotkey-0.1.7.tgz#30ec6d4234895230b576728ef77e70a52962f3b3"
|
||||||
|
integrity sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ==
|
||||||
|
|
||||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
|
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
|
resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
|
||||||
@ -3216,6 +3221,11 @@
|
|||||||
"@types/koa-compose" "*"
|
"@types/koa-compose" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/lodash@^4.14.149":
|
||||||
|
version "4.14.181"
|
||||||
|
resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.181.tgz#d1d3740c379fda17ab175165ba04e2d03389385d"
|
||||||
|
integrity sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==
|
||||||
|
|
||||||
"@types/lodash@^4.14.177":
|
"@types/lodash@^4.14.177":
|
||||||
version "4.14.177"
|
version "4.14.177"
|
||||||
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578"
|
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578"
|
||||||
@ -6355,6 +6365,11 @@ dir-glob@^3.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
path-type "^4.0.0"
|
path-type "^4.0.0"
|
||||||
|
|
||||||
|
direction@^1.0.3:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.npmmirror.com/direction/-/direction-1.0.4.tgz#2b86fb686967e987088caf8b89059370d4837442"
|
||||||
|
integrity sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==
|
||||||
|
|
||||||
doctrine@^2.1.0:
|
doctrine@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
|
resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
|
||||||
@ -8429,6 +8444,11 @@ ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8:
|
|||||||
resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb"
|
resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb"
|
||||||
integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==
|
integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==
|
||||||
|
|
||||||
|
immer@^9.0.6:
|
||||||
|
version "9.0.12"
|
||||||
|
resolved "https://registry.npmmirror.com/immer/-/immer-9.0.12.tgz#2d33ddf3ee1d247deab9d707ca472c8c942a0f20"
|
||||||
|
integrity sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==
|
||||||
|
|
||||||
import-cwd@^2.0.0:
|
import-cwd@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
||||||
@ -8857,6 +8877,11 @@ is-hexadecimal@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7"
|
resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7"
|
||||||
integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==
|
integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==
|
||||||
|
|
||||||
|
is-hotkey@^0.1.6:
|
||||||
|
version "0.1.8"
|
||||||
|
resolved "https://registry.npmmirror.com/is-hotkey/-/is-hotkey-0.1.8.tgz#6b1f4b2d0e5639934e20c05ed24d623a21d36d25"
|
||||||
|
integrity sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==
|
||||||
|
|
||||||
is-installed-globally@^0.1.0:
|
is-installed-globally@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
|
resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
|
||||||
@ -10415,7 +10440,7 @@ lodash.truncate@^4.4.2:
|
|||||||
resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
|
resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
|
||||||
integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
|
integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
|
||||||
|
|
||||||
lodash@4.x, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
|
lodash@4.x, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
@ -14859,6 +14884,13 @@ screenfull@^5.0.0:
|
|||||||
resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba"
|
resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba"
|
||||||
integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==
|
integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==
|
||||||
|
|
||||||
|
scroll-into-view-if-needed@^2.2.20:
|
||||||
|
version "2.2.29"
|
||||||
|
resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885"
|
||||||
|
integrity sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==
|
||||||
|
dependencies:
|
||||||
|
compute-scroll-into-view "^1.0.17"
|
||||||
|
|
||||||
scroll-into-view-if-needed@^2.2.25:
|
scroll-into-view-if-needed@^2.2.25:
|
||||||
version "2.2.28"
|
version "2.2.28"
|
||||||
resolved "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.28.tgz#5a15b2f58a52642c88c8eca584644e01703d645a"
|
resolved "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.28.tgz#5a15b2f58a52642c88c8eca584644e01703d645a"
|
||||||
@ -15080,6 +15112,36 @@ slash@^3.0.0:
|
|||||||
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
||||||
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
||||||
|
|
||||||
|
slate-history@^0.66.0:
|
||||||
|
version "0.66.0"
|
||||||
|
resolved "https://registry.npmmirror.com/slate-history/-/slate-history-0.66.0.tgz#ac63fddb903098ceb4c944433e3f75fe63acf940"
|
||||||
|
integrity sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==
|
||||||
|
dependencies:
|
||||||
|
is-plain-object "^5.0.0"
|
||||||
|
|
||||||
|
slate-react@^0.76.1:
|
||||||
|
version "0.76.1"
|
||||||
|
resolved "https://registry.npmmirror.com/slate-react/-/slate-react-0.76.1.tgz#a92915b204d84d59cb42d7818dcf48891d072016"
|
||||||
|
integrity sha512-deIhlGp8jUd5eZ8fodJ+Os3Eznk2bGJ4T6FPxzX04gFDCFVgXaYLfy9pObkQHW5XGnHwV3rniAyAtHBxNzrm9Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/is-hotkey" "^0.1.1"
|
||||||
|
"@types/lodash" "^4.14.149"
|
||||||
|
direction "^1.0.3"
|
||||||
|
is-hotkey "^0.1.6"
|
||||||
|
is-plain-object "^5.0.0"
|
||||||
|
lodash "^4.17.4"
|
||||||
|
scroll-into-view-if-needed "^2.2.20"
|
||||||
|
tiny-invariant "1.0.6"
|
||||||
|
|
||||||
|
slate@^0.76.1:
|
||||||
|
version "0.76.1"
|
||||||
|
resolved "https://registry.npmmirror.com/slate/-/slate-0.76.1.tgz#6bc46cb923f3b26fdddb80b2a28142c2d7aa9a0b"
|
||||||
|
integrity sha512-ALsAInU8vUEL65IGWttc1xOzq7KmNtn4XIDxylJnbGXWziKZxxRpL4nL8QHZX553Q1EPmi0w+kWOG+hnLFfcgw==
|
||||||
|
dependencies:
|
||||||
|
immer "^9.0.6"
|
||||||
|
is-plain-object "^5.0.0"
|
||||||
|
tiny-warning "^1.0.3"
|
||||||
|
|
||||||
slice-ansi@^3.0.0:
|
slice-ansi@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
|
resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
|
||||||
@ -15933,6 +15995,11 @@ timers-browserify@^2.0.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
setimmediate "^1.0.4"
|
setimmediate "^1.0.4"
|
||||||
|
|
||||||
|
tiny-invariant@1.0.6:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73"
|
||||||
|
integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==
|
||||||
|
|
||||||
tiny-invariant@^1.0.2, tiny-invariant@^1.0.6:
|
tiny-invariant@^1.0.2, tiny-invariant@^1.0.6:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9"
|
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9"
|
||||||
|
Loading…
Reference in New Issue
Block a user