feat: improve cms event types (#769)
This commit is contained in:
parent
b967b61e51
commit
4444dd791c
@ -59,6 +59,7 @@ import type {
|
|||||||
Entry,
|
Entry,
|
||||||
EntryData,
|
EntryData,
|
||||||
EntryDraft,
|
EntryDraft,
|
||||||
|
EventData,
|
||||||
FilterRule,
|
FilterRule,
|
||||||
ImplementationEntry,
|
ImplementationEntry,
|
||||||
MediaField,
|
MediaField,
|
||||||
@ -68,7 +69,6 @@ import type {
|
|||||||
UnknownField,
|
UnknownField,
|
||||||
User,
|
User,
|
||||||
} from './interface';
|
} from './interface';
|
||||||
import type { AllowedEvent } from './lib/registry';
|
|
||||||
import type { AsyncLock } from './lib/util';
|
import type { AsyncLock } from './lib/util';
|
||||||
import type { RootState } from './store';
|
import type { RootState } from './store';
|
||||||
import type AssetProxy from './valueObjects/AssetProxy';
|
import type AssetProxy from './valueObjects/AssetProxy';
|
||||||
@ -908,17 +908,19 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
|
|||||||
return slug;
|
return slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
async invokeEventWithEntry(event: AllowedEvent, entry: Entry) {
|
async getEventData(entry: Entry): Promise<EventData> {
|
||||||
const { login, name = '' } = (await this.currentUser()) as User;
|
const { login, name = '' } = (await this.currentUser()) as User;
|
||||||
return await invokeEvent({ name: event, data: { entry, author: { login, name } } });
|
return { entry, author: { login, name } };
|
||||||
}
|
}
|
||||||
|
|
||||||
async invokePreSaveEvent(entry: Entry) {
|
async invokePreSaveEvent(entry: Entry): Promise<EntryData> {
|
||||||
return await this.invokeEventWithEntry('preSave', entry);
|
const eventData = await this.getEventData(entry);
|
||||||
|
return await invokeEvent('preSave', eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
async invokePostSaveEvent(entry: Entry) {
|
async invokePostSaveEvent(entry: Entry): Promise<void> {
|
||||||
await this.invokeEventWithEntry('postSave', entry);
|
const eventData = await this.getEventData(entry);
|
||||||
|
await invokeEvent('postSave', eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
async persistMedia(config: Config, file: AssetProxy) {
|
async persistMedia(config: Config, file: AssetProxy) {
|
||||||
|
@ -232,7 +232,7 @@ const App = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
invokeEvent({ name: 'mounted' });
|
invokeEvent('mounted');
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -825,16 +825,32 @@ export interface EventData {
|
|||||||
author: { login: string | undefined; name: string };
|
author: { login: string | undefined; name: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventListenerOptions = Record<string, unknown>;
|
export type PreSaveEventHandler<O extends Record<string, unknown> = Record<string, unknown>> = (
|
||||||
|
data: EventData,
|
||||||
|
options: O,
|
||||||
|
) => EntryData | undefined | null | void | Promise<EntryData | undefined | null | void>;
|
||||||
|
|
||||||
export type EventListenerHandler = (
|
export type PostSaveEventHandler<O extends Record<string, unknown> = Record<string, unknown>> = (
|
||||||
data: EventData | undefined,
|
data: EventData,
|
||||||
options: EventListenerOptions,
|
options: O,
|
||||||
) => Promise<EntryData | undefined | null | void>;
|
) => void | Promise<void>;
|
||||||
|
|
||||||
export interface EventListener {
|
export type MountedEventHandler<O extends Record<string, unknown> = Record<string, unknown>> = (
|
||||||
name: AllowedEvent;
|
options: O,
|
||||||
handler: EventListenerHandler;
|
) => void | Promise<void>;
|
||||||
|
|
||||||
|
export type EventHandlers<O extends Record<string, unknown> = Record<string, unknown>> = {
|
||||||
|
preSave: PreSaveEventHandler<O>;
|
||||||
|
postSave: PostSaveEventHandler<O>;
|
||||||
|
mounted: MountedEventHandler<O>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface EventListener<
|
||||||
|
E extends AllowedEvent = 'mounted',
|
||||||
|
O extends Record<string, unknown> = Record<string, unknown>,
|
||||||
|
> {
|
||||||
|
name: E;
|
||||||
|
handler: EventHandlers<O>[E];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdditionalLinkOptions {
|
export interface AdditionalLinkOptions {
|
||||||
|
@ -9,11 +9,15 @@ import type {
|
|||||||
Config,
|
Config,
|
||||||
CustomIcon,
|
CustomIcon,
|
||||||
Entry,
|
Entry,
|
||||||
|
EntryData,
|
||||||
EventData,
|
EventData,
|
||||||
EventListener,
|
EventListener,
|
||||||
FieldPreviewComponent,
|
FieldPreviewComponent,
|
||||||
LocalePhrasesRoot,
|
LocalePhrasesRoot,
|
||||||
|
MountedEventHandler,
|
||||||
ObjectValue,
|
ObjectValue,
|
||||||
|
PostSaveEventHandler,
|
||||||
|
PreSaveEventHandler,
|
||||||
PreviewStyle,
|
PreviewStyle,
|
||||||
PreviewStyleOptions,
|
PreviewStyleOptions,
|
||||||
ShortcodeConfig,
|
ShortcodeConfig,
|
||||||
@ -29,10 +33,16 @@ import type {
|
|||||||
export const allowedEvents = ['mounted', 'preSave', 'postSave'] as const;
|
export const allowedEvents = ['mounted', 'preSave', 'postSave'] as const;
|
||||||
export type AllowedEvent = (typeof allowedEvents)[number];
|
export type AllowedEvent = (typeof allowedEvents)[number];
|
||||||
|
|
||||||
|
type EventHandlerRegistry = {
|
||||||
|
preSave: { handler: PreSaveEventHandler; options: Record<string, unknown> }[];
|
||||||
|
postSave: { handler: PostSaveEventHandler; options: Record<string, unknown> }[];
|
||||||
|
mounted: { handler: MountedEventHandler; options: Record<string, unknown> }[];
|
||||||
|
};
|
||||||
|
|
||||||
const eventHandlers = allowedEvents.reduce((acc, e) => {
|
const eventHandlers = allowedEvents.reduce((acc, e) => {
|
||||||
acc[e] = [];
|
acc[e] = [];
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<AllowedEvent, { handler: EventListener['handler']; options: Record<string, unknown> }[]>);
|
}, {} as EventHandlerRegistry);
|
||||||
|
|
||||||
interface Registry {
|
interface Registry {
|
||||||
backends: Record<string, BackendInitializer>;
|
backends: Record<string, BackendInitializer>;
|
||||||
@ -44,7 +54,7 @@ interface Registry {
|
|||||||
additionalLinks: Record<string, AdditionalLink>;
|
additionalLinks: Record<string, AdditionalLink>;
|
||||||
widgetValueSerializers: Record<string, WidgetValueSerializer>;
|
widgetValueSerializers: Record<string, WidgetValueSerializer>;
|
||||||
locales: Record<string, LocalePhrasesRoot>;
|
locales: Record<string, LocalePhrasesRoot>;
|
||||||
eventHandlers: typeof eventHandlers;
|
eventHandlers: EventHandlerRegistry;
|
||||||
previewStyles: PreviewStyle[];
|
previewStyles: PreviewStyle[];
|
||||||
|
|
||||||
/** Markdown editor */
|
/** Markdown editor */
|
||||||
@ -309,9 +319,8 @@ export function getBackend<EF extends BaseField = UnknownField>(
|
|||||||
/**
|
/**
|
||||||
* Event Handlers
|
* Event Handlers
|
||||||
*/
|
*/
|
||||||
function validateEventName(name: string) {
|
function validateEventName(name: AllowedEvent) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
if (!allowedEvents.includes(name)) {
|
||||||
if (!allowedEvents.includes(name as any)) {
|
|
||||||
throw new Error(`Invalid event name '${name}'`);
|
throw new Error(`Invalid event name '${name}'`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,21 +330,48 @@ export function getEventListeners(name: AllowedEvent) {
|
|||||||
return [...registry.eventHandlers[name]];
|
return [...registry.eventHandlers[name]];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerEventListener(
|
export function registerEventListener<
|
||||||
{ name, handler }: EventListener,
|
E extends AllowedEvent,
|
||||||
options: Record<string, unknown> = {},
|
O extends Record<string, unknown> = Record<string, unknown>,
|
||||||
) {
|
>({ name, handler }: EventListener<E, O>, options?: O) {
|
||||||
validateEventName(name);
|
validateEventName(name);
|
||||||
registry.eventHandlers[name].push({ handler, options });
|
registry.eventHandlers[name].push({
|
||||||
|
handler: handler as MountedEventHandler & PreSaveEventHandler & PostSaveEventHandler,
|
||||||
|
options: options ?? {},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function invokeEvent({ name, data }: { name: AllowedEvent; data?: EventData }) {
|
export async function invokeEvent(name: 'preSave', data: EventData): Promise<EntryData>;
|
||||||
|
export async function invokeEvent(name: 'postSave', data: EventData): Promise<void>;
|
||||||
|
export async function invokeEvent(name: 'mounted'): Promise<void>;
|
||||||
|
export async function invokeEvent(name: AllowedEvent, data?: EventData): Promise<void | EntryData> {
|
||||||
validateEventName(name);
|
validateEventName(name);
|
||||||
|
|
||||||
|
if (name === 'mounted') {
|
||||||
|
console.info('[StaticCMS] Firing mounted event');
|
||||||
|
const handlers = registry.eventHandlers[name];
|
||||||
|
for (const { handler, options } of handlers) {
|
||||||
|
handler(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'postSave') {
|
||||||
|
console.info(`[StaticCMS] Firing post save event`, data);
|
||||||
|
const handlers = registry.eventHandlers[name];
|
||||||
|
for (const { handler, options } of handlers) {
|
||||||
|
handler(data!, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const handlers = registry.eventHandlers[name];
|
const handlers = registry.eventHandlers[name];
|
||||||
|
|
||||||
console.info(`[StaticCMS] Firing event ${name}`, data);
|
console.info(`[StaticCMS] Firing pre save event`, data);
|
||||||
|
|
||||||
let _data = data ? { ...data } : undefined;
|
let _data = { ...data! };
|
||||||
for (const { handler, options } of handlers) {
|
for (const { handler, options } of handlers) {
|
||||||
const result = await handler(_data, options);
|
const result = await handler(_data, options);
|
||||||
if (_data !== undefined && result !== undefined) {
|
if (_data !== undefined && result !== undefined) {
|
||||||
@ -346,7 +382,8 @@ export async function invokeEvent({ name, data }: { name: AllowedEvent; data?: E
|
|||||||
_data = { ..._data, entry };
|
_data = { ..._data, entry };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _data?.entry.data;
|
|
||||||
|
return _data.entry.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeEventListener({ name, handler }: EventListener) {
|
export function removeEventListener({ name, handler }: EventListener) {
|
||||||
|
@ -17,6 +17,7 @@ For common options, see [Common widget options](/docs/widgets#common-widget-opti
|
|||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| ---------------- | ------- | ------- | -------------------------------------------------------------------------------------- |
|
| ---------------- | ------- | ------- | -------------------------------------------------------------------------------------- |
|
||||||
| allow_regenerate | boolean | `true` | _Optional_. If set to `false` the `Generate new UUID` button does not appear in the UI |
|
| allow_regenerate | boolean | `true` | _Optional_. If set to `false` the `Generate new UUID` button does not appear in the UI |
|
||||||
|
| prefix | string | | _Optional_. A value to add at the beginning of the UUID |
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user