Loading...
Loading...
Compare original and translation side by side
yarn add @10play/tentap-editor react-native-webview
cd ios && pod installnpx expo install @10play/tentap-editor react-native-webviewyarn add @10play/tentap-editor react-native-webview
cd ios && pod installnpx expo install @10play/tentap-editor react-native-webviewuseEditorBridgeRichTextToolbarimport { KeyboardAvoidingView, StyleSheet } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { RichText, Toolbar, useEditorBridge } from '@10play/tentap-editor';
export const BasicEditor = () => {
const editor = useEditorBridge({
autofocus: true,
avoidIosKeyboard: true,
initialContent: '<p>Start editing!</p>',
});
return (
<SafeAreaView style={styles.fullScreen}>
<RichText editor={editor} />
<KeyboardAvoidingView
behavior="padding"
style={styles.keyboardAvoidingView}
>
<Toolbar editor={editor} />
</KeyboardAvoidingView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
fullScreen: { flex: 1 },
keyboardAvoidingView: {
position: 'absolute',
width: '100%',
bottom: 0,
},
});references/Basic.tsxuseEditorBridgeRichTextToolbarimport { KeyboardAvoidingView, StyleSheet } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { RichText, Toolbar, useEditorBridge } from '@10play/tentap-editor';
export const BasicEditor = () => {
const editor = useEditorBridge({
autofocus: true,
avoidIosKeyboard: true,
initialContent: '<p>Start editing!</p>',
});
return (
<SafeAreaView style={styles.fullScreen}>
<RichText editor={editor} />
<KeyboardAvoidingView
behavior="padding"
style={styles.keyboardAvoidingView}
>
<Toolbar editor={editor} />
</KeyboardAvoidingView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
fullScreen: { flex: 1 },
keyboardAvoidingView: {
position: 'absolute',
width: '100%',
bottom: 0,
},
});references/Basic.tsxTenTapStarterKitTenTapStarterKitbridgeExtensionsTenTapStarterKitinitialContentautofocusavoidIosKeyboarddynamicHeightthemeeditablecustomSourceonChangeDEVDEV_SERVER_URLconst editor = useEditorBridge({
autofocus: true,
avoidIosKeyboard: true,
initialContent: '<p>Hello, world!</p>',
bridgeExtensions: [...TenTapStartKit, CustomBridge],
onChange: () => {
// Handle content changes (debounce recommended)
},
});references/useEditorBridge.mdbridgeExtensionsTenTapStarterKitinitialContentautofocusavoidIosKeyboarddynamicHeightthemeeditablecustomSourceonChangeDEVDEV_SERVER_URLconst editor = useEditorBridge({
autofocus: true,
avoidIosKeyboard: true,
initialContent: '<p>Hello, world!</p>',
bridgeExtensions: [...TenTapStartKit, CustomBridge],
onChange: () => {
// 处理内容变化(建议使用防抖)
},
});references/useEditorBridge.mdgetHTML()getText()getJSON()setContent(content)focus(pos?)blur()setSelection(from, to)getEditorState()webviewRefinjectCSS(css, tag?)injectJS(js)toggleBold()toggleItalic()toggleUnderline()toggleStrikethrough()toggleHeading(level)toggleCode()toggleBlockquote()toggleBulletList()toggleOrderedList()toggleTaskList()setColor(color)unsetColor()setHighlight(color)toggleHighlight(color)unsetHighlight()setLink(url)setImage(src)lift()sink()undo()redo()setPlaceholder(text)references/EditorBridge.mdgetHTML()getText()getJSON()setContent(content)focus(pos?)blur()setSelection(from, to)getEditorState()webviewRefinjectCSS(css, tag?)injectJS(js)toggleBold()toggleItalic()toggleUnderline()toggleStrikethrough()toggleHeading(level)toggleCode()toggleBlockquote()toggleBulletList()toggleOrderedList()toggleTaskList()setColor(color)unsetColor()setHighlight(color)toggleHighlight(color)unsetHighlight()setLink(url)setImage(src)lift()sink()undo()redo()setPlaceholder(text)references/EditorBridge.mdconst editorState = useBridgeState(editor);
// Access state properties
editorState.isFocused; // Editor focus state
editorState.isEmpty; // Whether editor is empty
editorState.isBoldActive; // Bold formatting state
editorState.canToggleBold; // Whether bold can be toggled
editorState.headingLevel; // Current heading levelreferences/BridgeState.mdconst htmlContent = useEditorContent(editor, { type: 'html' });
// Alternative content types
const textContent = useEditorContent(editor, { type: 'text' });
const jsonContent = useEditorContent(editor, { type: 'json' });
// Custom debounce interval (default: 10ms)
const content = useEditorContent(editor, {
type: 'html',
debounceInterval: 100,
});references/useEditorContent.mdconst editorState = useBridgeState(editor);
// 访问状态属性
editorState.isFocused; // 编辑器聚焦状态
editorState.isEmpty; // 编辑器是否为空
editorState.isBoldActive; // 粗体格式化状态
editorState.canToggleBold; // 是否可以切换粗体
editorState.headingLevel; // 当前标题级别references/BridgeState.mdconst htmlContent = useEditorContent(editor, { type: 'html' });
// 其他内容类型
const textContent = useEditorContent(editor, { type: 'text' });
const jsonContent = useEditorContent(editor, { type: 'json' });
// 自定义防抖间隔(默认值:10ms)
const content = useEditorContent(editor, {
type: 'html',
debounceInterval: 100,
});references/useEditorContent.mdeditorexclusivelyUseCustomOnMessage<RichText editor={editor} />editorexclusivelyUseCustomOnMessage<RichText editor={editor} />editorhiddenitemsshouldHideDisabledToolbarItems<Toolbar
editor={editor}
hidden={!isKeyboardVisible}
items={DEFAULT_TOOLBAR_ITEMS}
shouldHideDisabledToolbarItems={true}
/>editorhiddenitemsshouldHideDisabledToolbarItems<Toolbar
editor={editor}
hidden={!isKeyboardVisible}
items={DEFAULT_TOOLBAR_ITEMS}
shouldHideDisabledToolbarItems={true}
/>interface ToolbarItem {
onPress: ({ editor, editorState }) => () => void;
active: ({ editor, editorState }) => boolean;
disabled: ({ editor, editorState }) => boolean;
image: ({ editor, editorState }) => any;
}references/Components.mdreferences/CustomAndStaticToolbar/interface ToolbarItem {
onPress: ({ editor, editorState }) => () => void;
active: ({ editor, editorState }) => boolean;
disabled: ({ editor, editorState }) => boolean;
image: ({ editor, editorState }) => any;
}references/Components.mdreferences/CustomAndStaticToolbar/configureExtensionconst editor = useEditorBridge({
bridgeExtensions: [
...TenTapStartKit,
PlaceholderBridge.configureExtension({
placeholder: 'Type something amazing...',
}),
LinkBridge.configureExtension({
openOnClick: false,
}),
HeadingBridge.configureExtension({
levels: [1, 2, 3],
}),
],
});configureExtensionconst editor = useEditorBridge({
bridgeExtensions: [
...TenTapStartKit,
PlaceholderBridge.configureExtension({
placeholder: 'Type something amazing...',
}),
LinkBridge.configureExtension({
openOnClick: false,
}),
HeadingBridge.configureExtension({
levels: [1, 2, 3],
}),
],
});extendExtensionCoreBridge.extendExtension({
content: 'heading block+',
});references/BridgeExtensions.mdreferences/configureExtensions.mdextendExtensionCoreBridge.extendExtension({
content: 'heading block+',
});references/BridgeExtensions.mdreferences/configureExtensions.mdthemeconst editor = useEditorBridge({
theme: {
toolbar: {
toolbarBody: {
backgroundColor: '#474747',
borderTopColor: '#C6C6C6B3',
borderBottomColor: '#C6C6C6B3',
},
},
webview: {
backgroundColor: '#1C1C1E',
},
webviewContainer: {},
},
});themeconst editor = useEditorBridge({
theme: {
toolbar: {
toolbarBody: {
backgroundColor: '#474747',
borderTopColor: '#C6C6C6B3',
borderBottomColor: '#C6C6C6B3',
},
},
webview: {
backgroundColor: '#1C1C1E',
},
webviewContainer: {},
},
});import { darkEditorTheme, darkEditorCss } from '@10play/tentap-editor';
const editor = useEditorBridge({
bridgeExtensions: [
...TenTapStartKit,
CoreBridge.configureCSS(darkEditorCss),
],
theme: darkEditorTheme,
});references/darkTheme.mdreferences/DarkEditor.tsximport { darkEditorTheme, darkEditorCss } from '@10play/tentap-editor';
const editor = useEditorBridge({
bridgeExtensions: [
...TenTapStartKit,
CoreBridge.configureCSS(darkEditorCss),
],
theme: darkEditorTheme,
});references/darkTheme.mdreferences/DarkEditor.tsxconst customCodeCSS = `
code {
background-color: #ffdede;
border-radius: 0.25em;
color: #cd4242;
padding: 0.25em;
}
`;
const editor = useEditorBridge({
bridgeExtensions: [
...TenTapStartKit,
CodeBridge.configureCSS(customCodeCSS),
],
});const customCodeCSS = `
code {
background-color: #ffdede;
border-radius: 0.25em;
color: #cd4242;
padding: 0.25em;
}
`;
const editor = useEditorBridge({
bridgeExtensions: [
...TenTapStartKit,
CodeBridge.configureCSS(customCodeCSS),
],
});injectCSS// Update bridge-specific CSS
editor.injectCSS(newCSS, CodeBridge.name);
// Add new stylesheet without overriding
editor.injectCSS(customCSS, 'custom-tag');injectCSS// 更新桥接专用CSS
editor.injectCSS(newCSS, CodeBridge.name);
// 添加新样式表而不覆盖原有样式
editor.injectCSS(customCSS, 'custom-tag');import { customFont } from './font';
const editor = useEditorBridge({
bridgeExtensions: [
...TenTapStartKit,
CoreBridge.configureCSS(customFont),
],
});references/customCss.mdreferences/CustomCss.tsximport { customFont } from './font';
const editor = useEditorBridge({
bridgeExtensions: [
...TenTapStartKit,
CoreBridge.configureCSS(customFont),
],
});references/customCss.mdreferences/CustomCss.tsxcustomSourcecustomSource// editor-web/AdvancedEditor.tsx
import { EditorContent } from '@tiptap/react';
import { useTenTap, TenTapStartKit } from '@10play/tentap-editor';
import { CounterBridge } from '../CounterBridge';
export const AdvancedEditor = () => {
const editor = useTenTap({
bridges: [...TenTapStartKit, CounterBridge],
tiptapOptions: {
extensions: [Document, Paragraph, Text],
},
});
return <EditorContent editor={editor} />;
};
// In React Native app
import { editorHtml } from './editor-web/build/editorHtml';
const editor = useEditorBridge({
customSource: editorHtml,
// ... other config
});// editor-web/AdvancedEditor.tsx
import { EditorContent } from '@tiptap/react';
import { useTenTap, TenTapStartKit } from '@10play/tentap-editor';
import { CounterBridge } from '../CounterBridge';
export const AdvancedEditor = () => {
const editor = useTenTap({
bridges: [...TenTapStartKit, CounterBridge],
tiptapOptions: {
extensions: [Document, Paragraph, Text],
},
});
return <EditorContent editor={editor} />;
};
// 在React Native应用中
import { editorHtml } from './editor-web/build/editorHtml';
const editor = useEditorBridge({
customSource: editorHtml,
// ... 其他配置
});import { BridgeExtension } from '@10play/tentap-editor';
import { CharacterCount } from '@tiptap/extension-character-count';
export const CounterBridge = new BridgeExtension<
typeof CharacterCount,
{ words: number; characters: number }
>({
tiptapExtension: CharacterCount,
extendEditorState: (editor) => ({
words: editor.storage.characterCount.words(),
characters: editor.storage.characterCount.characters(),
}),
});references/advancedSetup.mdreferences/Advanced/import { BridgeExtension } from '@10play/tentap-editor';
import { CharacterCount } from '@tiptap/extension-character-count';
export const CounterBridge = new BridgeExtension<
typeof CharacterCount,
{ words: number; characters: number }
>({
tiptapExtension: CharacterCount,
extendEditorState: (editor) => ({
words: editor.storage.characterCount.words(),
characters: editor.storage.characterCount.characters(),
}),
});references/advancedSetup.mdreferences/Advanced/KeyboardAvoidingViewimport { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Platform } from 'react-native';
const HEADER_HEIGHT = 38;
const { top } = useSafeAreaInsets();
const keyboardVerticalOffset = HEADER_HEIGHT + top;
<KeyboardAvoidingView
behavior="padding"
keyboardVerticalOffset={Platform.OS === 'ios' ? keyboardVerticalOffset : undefined}
>
<Toolbar editor={editor} />
</KeyboardAvoidingView>
// Add paddingBottom to RichText container on iOS
<View style={{ paddingBottom: Platform.OS === 'ios' ? HEADER_HEIGHT : 0 }}>
<RichText editor={editor} />
</View>references/navHeader.mdreferences/NavigationHeader.tsxKeyboardAvoidingViewimport { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Platform } from 'react-native';
const HEADER_HEIGHT = 38;
const { top } = useSafeAreaInsets();
const keyboardVerticalOffset = HEADER_HEIGHT + top;
<KeyboardAvoidingView
behavior="padding"
keyboardVerticalOffset={Platform.OS === 'ios' ? keyboardVerticalOffset : undefined}
>
<Toolbar editor={editor} />
</KeyboardAvoidingView>
// 在iOS上为RichText容器添加paddingBottom
<View style={{ paddingBottom: Platform.OS === 'ios' ? HEADER_HEIGHT : 0 }}>
<RichText editor={editor} />
</View>references/navHeader.mdreferences/NavigationHeader.tsxavoidIosKeyboardconst editor = useEditorBridge({
avoidIosKeyboard: true, // Works on both iOS and Android
});avoidIosKeyboardconst editor = useEditorBridge({
avoidIosKeyboard: true, // 同时支持iOS和Android
});references/Basic.tsxreferences/Basic.tsxreferences/DarkEditor.tsxdarkEditorThemedarkEditorCssreferences/DarkEditor.tsxdarkEditorThemedarkEditorCssreferences/ConfigureExtentions.tsxreferences/ConfigureExtentions.tsxreferences/CustomCss.tsxinjectCSSreferences/CustomCss.tsxinjectCSSreferences/NavigationHeader.tsxreferences/NavigationHeader.tsxreferences/CustomAndStaticToolbar/references/CustomAndStaticToolbar/references/EditorStickToKeyboardExample.tsxreferences/EditorStickToKeyboardExample.tsxreferences/Advanced/AdvancedRichText.tsxreferences/Advanced/AdvancedRichText.tsxreferences/Advanced/CounterBridge.tsreferences/Advanced/CounterBridge.tsreferences/Advanced/editor-web/references/Advanced/editor-web/references/intro.mdreferences/mainConcepts.mdreferences/basic.mdreferences/customTheme.mdreferences/darkTheme.mdreferences/customCss.mdreferences/configureExtensions.mdreferences/navHeader.mdreferences/advancedSetup.mdreferences/intro.mdreferences/mainConcepts.mdreferences/basic.mdreferences/customTheme.mdreferences/darkTheme.mdreferences/customCss.mdreferences/configureExtensions.mdreferences/navHeader.mdreferences/advancedSetup.mdreferences/EditorBridge.mdreferences/BridgeExtensions.mdreferences/BridgeState.mdreferences/Components.mdreferences/useEditorBridge.mdreferences/useEditorContent.mdreferences/EditorBridge.mdreferences/BridgeExtensions.mdreferences/BridgeState.mdreferences/Components.mdreferences/useEditorBridge.mdreferences/useEditorContent.mdBasic.tsxDarkEditor.tsxConfigureExtentions.tsxCustomCss.tsxNavigationHeader.tsxEditorStickToKeyboardExample.tsxAdvanced/AdvancedRichText.tsxAdvanced/CounterBridge.tsAdvanced/editor-web/index.htmlAdvancedEditor.tsxindex.tsxtsconfig.jsonvite.config.tsCustomAndStaticToolbar/CustomAndStaticToolbar.tsxCustomAndStaticToolbar/CustomRichText.tsxIcon.tsxfont.tsBasic.tsxDarkEditor.tsxConfigureExtentions.tsxCustomCss.tsxNavigationHeader.tsxEditorStickToKeyboardExample.tsxAdvanced/AdvancedRichText.tsxAdvanced/CounterBridge.tsAdvanced/editor-web/index.htmlAdvancedEditor.tsxindex.tsxtsconfig.jsonvite.config.tsCustomAndStaticToolbar/CustomAndStaticToolbar.tsxCustomAndStaticToolbar/CustomRichText.tsxIcon.tsxfont.tsuseEditorContentgetHTML()avoidIosKeyboarduseEditorContentgetHTML()avoidIosKeyboarduseBridgeStateonChangeuseBridgeStateTenTapStartKitTenTapStartKitkeyboardVerticalOffsetkeyboardVerticalOffsetreferences/EditorStickToKeyboardExample.tsxreferences/EditorStickToKeyboardExample.tsxreferences/CustomAndStaticToolbar/references/CustomAndStaticToolbar/references/DarkEditor.tsxreferences/darkTheme.mdreferences/DarkEditor.tsxreferences/darkTheme.mdreferences/Advanced/CounterBridge.tsreferences/Advanced/AdvancedRichText.tsxreferences/Advanced/CounterBridge.tsreferences/Advanced/AdvancedRichText.tsxuseEditorContentconst content = useEditorContent(editor, { type: 'html' });
useEffect(() => {
if (content) {
debouncedSave(content);
}
}, [content]);useEditorContentconst content = useEditorContent(editor, { type: 'html' });
useEffect(() => {
if (content) {
debouncedSave(content);
}
}, [content]);