kyta-editor
v1.0.5
Published
Angular 14 block editor extracted from ewiki-gateway.
Maintainers
Readme
kyta-editor
Block editor dành cho Angular 14, hỗ trợ văn bản, heading, danh sách, checkbox, bảng, cột, code, hình ảnh, video và PDF.
Yêu cầu
- Angular
14.2.x - Angular Material/CDK
14.2.x - RxJS
7.5.x - Đã bật browser animations trong ứng dụng host
Cài đặt
npm install kyta-editorImport module trong Angular module sử dụng editor:
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { EditorModule } from 'kyta-editor';
@NgModule({
imports: [
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
EditorModule,
],
})
export class AppModule {}BrowserAnimationsModule chỉ nên được import một lần ở root module. Nếu ứng dụng đã import module này thì không cần thêm lại.
Sử dụng cơ bản
Editor là một Angular form control và hỗ trợ cả Reactive Forms lẫn Template-driven Forms.
Reactive Forms
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { EditorTreeNode } from 'kyta-editor';
@Component({
selector: 'app-document-editor',
templateUrl: './document-editor.component.html',
})
export class DocumentEditorComponent {
readonly content = new FormControl<EditorTreeNode | null>(null);
}<jhi-editor [formControl]="content"></jhi-editor>Template-driven Forms
import { EditorTreeNode } from 'kyta-editor';
content: EditorTreeNode | null = null;<jhi-editor [(ngModel)]="content" name="content"></jhi-editor>Không sử dụng Angular Forms
<jhi-editor
[debounceTime]="500"
[selectBlockId]="selectedBlockId"
(valueChange)="saveTree($event)"
(valueFlatChange)="saveBlocks($event)"
(commandChange)="handleCommands($event)"
></jhi-editor>Editor nhận dữ liệu đầu vào ở một trong hai dạng:
EditorTreeNode: cây block lồng nhau.Block[]: danh sách block phẳng;data.childrenchứa cácblockId.
Khi giá trị đầu vào là null, editor tự khởi tạo document rỗng.
Inputs và outputs
| API | Kiểu | Mô tả |
| --- | --- | --- |
| debounceTime | number | Thời gian chờ trước khi phát valueChange và cập nhật form. Mặc định 2000 ms. |
| selectBlockId | string \| null | Cuộn tới và chọn block một lần sau khi dữ liệu render. |
| valueChange | EditorTreeNode \| null | Phát document ở dạng cây. |
| valueFlatChange | Block[] | Phát document ở dạng danh sách phẳng. |
| commandChange | QueueCommand[] | Phát các command vừa được editor thực thi để ứng dụng đồng bộ lên server. |
Có thể đặt editor ở chế độ chỉ đọc bằng form control:
this.content.disable();
this.content.enable();Gọi editor bằng ViewChild
import { ViewChild } from '@angular/core';
import { Block, EditorComponent, QueueCommand } from 'kyta-editor';
@ViewChild(EditorComponent) editor?: EditorComponent;
focusBlock(blockId: string): void {
this.editor?.scrollToBlock(blockId);
}
applyServerMetadata(blocks: Block[]): void {
this.editor?.applyServerBlockMeta(blocks);
}
prepareCommands(commands: QueueCommand[]): QueueCommand[] {
return this.editor?.syncQueueCommandMeta(commands) ?? commands;
}Các method public chính:
scrollToBlock(blockId, options?): cuộn tới một block.getHeadingBlockIdsInDomOrder(): lấy danh sách heading theo thứ tự hiển thị.applyServerBlockMeta(blocks): cập nhật metadata từ server mà không render lại document.syncQueueCommandMeta(commands): gắn metadata mới nhất trước khi gửi command lên server.
Upload và tải media
Link hình ảnh, video hoặc PDF bên ngoài có thể dùng ngay. Để upload và tải lại file đã lưu trên server, ứng dụng host phải cung cấp EDITOR_MEDIA_ADAPTER.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, Observable } from 'rxjs';
import {
EditorMediaAdapterRegistration,
EditorMediaResolveRequest,
EditorMediaUploadRequest,
EditorMediaUploadResult,
EditorMediaViewerRequest,
} from 'kyta-editor';
@Injectable()
export class ApiEditorMediaAdapter implements EditorMediaAdapterRegistration {
readonly kind = 'image';
constructor(private readonly http: HttpClient) {}
upload(request: EditorMediaUploadRequest): Observable<EditorMediaUploadResult> {
const body = new FormData();
body.append('file', request.file);
body.append('kind', request.kind);
body.append('blockId', request.block.blockId);
return this.http.post<EditorMediaUploadResult>('/api/editor/media', body);
}
resolve(request: EditorMediaResolveRequest): Observable<string> {
const { blockId, documentId } = request.identity;
return this.http
.get<{ url: string }>(`/api/editor/media/${documentId}/${blockId}`)
.pipe(map(response => response.url));
}
resolveViewerUrl(request: EditorMediaViewerRequest): string {
return request.kind === 'pdf'
? 'https://your-pdf-viewer.example.com/viewer'
: '';
}
}Đăng ký adapter ở root module:
import { EDITOR_MEDIA_ADAPTER } from 'kyta-editor';
@NgModule({
providers: [
{
provide: EDITOR_MEDIA_ADAPTER,
useClass: ApiEditorMediaAdapter,
multi: true,
},
],
})
export class AppModule {}Contract của adapter:
upload(): upload file và trả về tối thiểu{ fileName }.resolve(): trả về URL có thể hiển thị hoặc tải xuống cho media đã lưu.resolveViewerUrl(): trả về URL trang viewer dùng để render PDF.
EDITOR_MEDIA_ADAPTER là multi-provider. Mỗi adapter đăng ký kind (image, video hoặc pdf), và editor chọn adapter theo đúng loại media. Nếu cùng một kind được đăng ký nhiều lần thì adapter đăng ký cuối cùng được sử dụng; loại không được đăng ký sẽ dùng default adapter.
Nếu không cung cấp adapter, upload và resolve media lưu trên server sẽ trả lỗi cấu hình rõ ràng. PDF viewer mặc định vẫn dùng endpoint Econdrive tương thích với hệ thống cũ.
