@elia-ori/editor
v0.1.29
Published
基於 TipTap 的富文字編輯器套件
Downloads
85
Maintainers
Readme
@elia-ori/editor
基於 TipTap 的富文字編輯器。
安裝
npm install @elia-ori/editor使用方式
import { EliaEditor } from '@elia-ori/editor';
import '@elia-ori/editor/styles.css';
function MyEditor() {
const [content, setContent] = useState('');
// 必須提供圖片上傳函數
const handleImageUpload = async (
file: File,
onProgress?: (event: { progress: number }) => void
): Promise<string> => {
// 實作你的上傳邏輯,回傳圖片 URL
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const { url } = await response.json();
return url;
};
return (
<EliaEditor
content={content}
onChange={setContent}
onImageUpload={handleImageUpload}
/>
);
}儲存格式
編輯器支援 HTML 和 JSON 兩種格式:
// 儲存為 JSON 格式(推薦)
<EliaEditor
content={content}
onChange={(html, json) => {
setContent(json) // 儲存 JSON,支援多平台渲染
}}
/>
// 儲存為 HTML 格式(向後相容)
<EliaEditor
content={content}
onChange={(html) => {
setContent(html) // 只用第一個參數
}}
/>Props
| Prop | 類型 | 預設值 | 說明 |
|------|------|--------|------|
| content | string | '' | HTML 內容 |
| onChange | (html: string, json: JSONContent) => void | - | 內容變更時的回調,同時提供 HTML 和 JSON 格式 |
| placeholder | string | '開始寫作...' | 佔位文字 |
| toolbar | ToolbarItem[] | 全部 | 工具列項目 |
| onImageUpload | UploadFunction | - | 必要 - 圖片上傳函數 |
| embedded | boolean | false | 嵌入模式(高度由父容器決定) |
| card | boolean | false | 卡片樣式(邊框 + 圓角 + 陰影) |
| className | string | - | 外層 class |
| editorClassName | string | - | 編輯器 class |
| autofocus | boolean | false | 自動聚焦 |
| readOnly | boolean | false | 唯讀模式 |
Toolbar 選項
type ToolbarItem =
| 'undo-redo' // 復原/重做
| 'text' // 內文
| 'heading' // 標題 (H1-H4)
| 'list' // 列表
| 'blockquote' // 引用區塊
| 'code-block' // 程式碼區塊
| 'table' // 表格
| 'format' // 格式 (粗體、斜體、刪除線、code、底線)
| 'text-color' // 文字顏色
| 'highlight' // 螢光標記
| 'link' // 連結
| 'superscript' // 上標
| 'subscript' // 下標
| 'align' // 文字對齊
| 'image'; // 圖片上傳圖片上傳
onImageUpload 是必要的 prop。編輯器不提供預設的上傳實作,你需要根據你的後端 API 自行實作。
函數簽名
type UploadFunction = (
file: File,
onProgress?: (event: { progress: number }) => void,
abortSignal?: AbortSignal
) => Promise<string>;範例:上傳到 Cloudflare R2 (使用 Presigned URL)
const handleImageUpload = async (file, onProgress) => {
// 1. 取得 presigned URL
const { upload_url, file_url } = await getPresignedUrl(file.name, file.type);
// 2. 上傳到 R2(使用 XMLHttpRequest 以支援進度追蹤)
await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('PUT', upload_url);
xhr.setRequestHeader('Content-Type', file.type);
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
onProgress?.({ progress: Math.round((e.loaded / e.total) * 100) });
}
};
xhr.onload = () => (xhr.status >= 200 && xhr.status < 300 ? resolve() : reject(new Error('上傳失敗')));
xhr.onerror = () => reject(new Error('上傳失敗'));
xhr.send(file);
});
// 3. 回傳公開 URL
return file_url;
};注意:
fetchAPI 不支援上傳進度追蹤,需使用XMLHttpRequest。
嵌入模式
預設編輯器會佔滿整個視窗高度。如果要嵌入到現有頁面,使用 embedded prop:
<EliaEditor
embedded
card
className="min-h-96"
// ...
/>TocSidebar (目錄側邊欄)
若需要顯示文章目錄,使用 EliaEditorProvider 包裹並加入 TocSidebar:
import { EliaEditorProvider, EliaEditor, TocSidebar } from '@elia-ori/editor'
import '@elia-ori/editor/styles.css'
function MyPage() {
return (
<EliaEditorProvider>
<div style={{ display: 'flex', gap: '1rem' }}>
<TocSidebar />
<EliaEditor onChange={handleChange} />
</div>
</EliaEditorProvider>
)
}TocSidebar Props
| Prop | Type | Description | |------|------|-------------| | className | string | 自訂樣式類別 |
自訂樣式
TocSidebar 使用 CSS 變數,可透過覆蓋來自訂樣式:
.my-toc {
--toc-title-color: #1f2937;
--toc-link-color: #6b7280;
--toc-hover-color: #374151;
--toc-hover-bg: #f3f4f6;
--toc-active-color: #2563eb;
}