208 lines
5.3 KiB
TypeScript
208 lines
5.3 KiB
TypeScript
import type { ActionState } from '../runtime/action-runner';
|
|
|
|
interface BridgeContext {
|
|
loaded: boolean;
|
|
}
|
|
|
|
export const bridgeContext: BridgeContext = import.meta.hot?.data?.editorBridgeContext ?? {
|
|
loaded: false,
|
|
};
|
|
|
|
if (import.meta.hot && import.meta.hot.data) {
|
|
import.meta.hot.data.editorBridgeContext = bridgeContext;
|
|
}
|
|
|
|
export type EventPayload = {
|
|
pageName: string;
|
|
} & Record<string, any>;
|
|
type EventHandler = (payload: EventPayload) => void;
|
|
type EventMap = Map<string, Set<EventHandler>>;
|
|
|
|
type Page = {
|
|
name: string;
|
|
title: string;
|
|
actionIds?: string[];
|
|
};
|
|
|
|
type SectionProps = {
|
|
id: string;
|
|
pageName: string;
|
|
content: string;
|
|
domId: string;
|
|
rootDomId: string;
|
|
sort?: number;
|
|
};
|
|
|
|
/**
|
|
* 构筑一个无头的 editor 的 bridge。
|
|
* 所有操作编辑器的方法,都需要经由此 bridge 进行广播。
|
|
* 其内部所保存的 pages 与 sections 均为虚拟数据,与 editor 所需的实际数据有一定差异。
|
|
*/
|
|
export class EditorBridge {
|
|
#pages: Map<string, Page> = new Map();
|
|
#sections: Map<string, SectionProps> = new Map();
|
|
#events: EventMap = new Map();
|
|
#watchHandlers: Set<(event: { type: string; payload: EventPayload }) => void> = new Set();
|
|
|
|
/**
|
|
* 监听事件
|
|
* @param event 事件名称
|
|
* @param handler 处理函数
|
|
*/
|
|
on(event: string, handler: EventHandler) {
|
|
if (!this.#events.has(event)) {
|
|
this.#events.set(event, new Set());
|
|
}
|
|
|
|
this.#events.get(event)?.add(handler);
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* 移除事件监听
|
|
* @param event 事件名称
|
|
* @param handler 处理函数
|
|
*/
|
|
off(event: string, handler?: EventHandler) {
|
|
if (!handler) {
|
|
this.#events.delete(event);
|
|
} else if (this.#events.has(event)) {
|
|
this.#events.get(event)?.delete(handler);
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* 触发事件
|
|
* @param event 事件名称
|
|
* @param payload 事件负载对象
|
|
*/
|
|
#emit(event: string, payload: EventPayload) {
|
|
this.#events.get(event)?.forEach((handler) => handler(payload));
|
|
|
|
// 同时通知所有 watch 监听器
|
|
this.#watchHandlers.forEach((handler) => handler({ type: event, payload }));
|
|
}
|
|
|
|
/**
|
|
* 创建页面
|
|
* @param pageName 页面名称
|
|
*/
|
|
async createPage(name: string, { title, actionIds }: { title?: string; actionIds?: string[] } = {}) {
|
|
const newPage: Page = { name, title: title ?? '未命名页面', actionIds: actionIds ?? [] };
|
|
this.#pages.set(name, newPage);
|
|
|
|
// 发出 add_page 事件,其他地方可通过 editorBridge.on('add_page', (payload) => {}) 监听
|
|
this.#emit('add_page', {
|
|
pageName: name,
|
|
...newPage,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 更新页面属性
|
|
*
|
|
* @param pageName
|
|
* @param param1
|
|
* @returns
|
|
*/
|
|
async updatePageAttributes(pageName: string, { title }: { title?: string } = {}) {
|
|
const page = this.#pages.get(pageName);
|
|
if (!page) {
|
|
return;
|
|
}
|
|
const actionIds = page.actionIds;
|
|
this.#emit('upsert_page', {
|
|
pageName,
|
|
title,
|
|
actionIds,
|
|
});
|
|
}
|
|
|
|
async removePage(pageName: string) {
|
|
this.#pages.delete(pageName);
|
|
|
|
// 发出 remove_page 事件,其他地方可通过 editorBridge.on('remove_page', (payload) => {}) 监听
|
|
this.#emit('remove_page', { pageName });
|
|
}
|
|
|
|
async updateSection(action: ActionState) {
|
|
this.#sections.set(action.id, action);
|
|
// 发出 add_page_section 事件,其他地方可通过 editorBridge.on('add_page_section', (payload) => {}) 监听
|
|
this.#emit('update_section', { pageName: action.pageName, id: action.id, section: action });
|
|
}
|
|
|
|
async upsertPageAction(pageName: string, pageTitle: string, actionId: string) {
|
|
const page = this.#pages.get(pageName);
|
|
const pageProps = page
|
|
? {
|
|
...page,
|
|
actionIds: [...(page.actionIds ?? []), actionId],
|
|
}
|
|
: {
|
|
name: pageName,
|
|
title: pageTitle || '未命名页面',
|
|
actionIds: [actionId],
|
|
};
|
|
pageProps.actionIds = [...new Set(pageProps.actionIds)];
|
|
|
|
this.#pages.set(pageName, pageProps);
|
|
// 发出 update_page 事件,其他地方可通过 editorBridge.on('update_page', (payload) => {}) 监听
|
|
this.#emit('upsert_page', {
|
|
pageName,
|
|
...pageProps,
|
|
});
|
|
}
|
|
|
|
// /**
|
|
// * 获取页面历史记录
|
|
// * @param pageName 页面名称
|
|
// * @returns 页面历史
|
|
// */
|
|
// async getPageHistory(pageName: string): Promise<string> {
|
|
// return this.#pages.get(pageName) || '{}';
|
|
// }
|
|
|
|
/**
|
|
* 监听所有事件
|
|
* @param handler 处理所有事件的函数
|
|
*/
|
|
watch(handler: (event: { type: string; payload: EventPayload }) => void) {
|
|
this.#watchHandlers.add(handler);
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* 移除 watch 监听
|
|
* @param handler 处理函数
|
|
*/
|
|
unwatch(handler: (event: { type: string; payload: EventPayload }) => void) {
|
|
this.#watchHandlers.delete(handler);
|
|
return this;
|
|
}
|
|
}
|
|
|
|
export let editorBridge: Promise<EditorBridge> = new Promise(() => {
|
|
// noop for ssr
|
|
});
|
|
|
|
if (!import.meta.env.SSR) {
|
|
editorBridge =
|
|
import.meta.hot?.data?.editorBridge ??
|
|
Promise.resolve()
|
|
.then(() => {
|
|
return new EditorBridge();
|
|
})
|
|
.then(async (editorBridge) => {
|
|
bridgeContext.loaded = true;
|
|
return editorBridge;
|
|
});
|
|
|
|
if (import.meta.hot && import.meta.hot.data) {
|
|
import.meta.hot.data.editorBridge = editorBridge;
|
|
}
|
|
}
|