Skip to content

Commit

Permalink
feat: adds return promise to success and error events and better ts s…
Browse files Browse the repository at this point in the history
…upport
  • Loading branch information
fenilli committed Nov 2, 2023
1 parent bca6bf1 commit 0bdbb16
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 37 deletions.
66 changes: 38 additions & 28 deletions src/event.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,70 @@
import type { VisitOptions, GlobalEventsMap } from '@inertiajs/core';
import type { GlobalEventsMap } from '@inertiajs/core';

export type Events<A extends [...args: any]> = {
export type EventCallback<A extends [...args: any]> = {
[K in keyof Omit<GlobalEventsMap, 'navigate' | 'invalid' | 'exception'>]
: (...args: [...GlobalEventsMap[K]['parameters'], ...A]) => GlobalEventsMap[K]['result'];
: (...args: [...GlobalEventsMap[K]['parameters'], ...A])
=> K extends 'success' | 'error'
? Promise<GlobalEventsMap[K]['result']> | GlobalEventsMap[K]['result']
: GlobalEventsMap[K]['result'];
} & {
cancelToken: (...args: [{ cancel: () => void }, ...A]) => void;
};

export type EventsList<A extends [...args: any]> = {
[K in keyof Events<A>]: Events<A>[K][];
};
export type OnFunction<A extends [...args: any]> = ReturnType<typeof createEventCallbackManager<A>>['on'];
export type CombineFunction<A extends [...args: any]> = ReturnType<typeof createEventCallbackManager<A>>['combine'];
export type ExecuteFunction<A extends [...args: any]> = ReturnType<typeof createEventCallbackManager<A>>['execute'];

export const useEventsSystem = <E extends [...args: any]>() => {
const eventList: Partial<EventsList<E>> = {};
export const createEventCallbackManager = <E extends [...args: any]>() => {
const events: Partial<{
[K in keyof EventCallback<E>]: EventCallback<E>[K][];
}> = {};

const on = <T extends keyof Events<E>>(eventName: T, callback: Events<E>[T]) => {
if (typeof eventList[eventName] === 'undefined') eventList[eventName] = [];
const on = <T extends keyof EventCallback<E>>(eventName: T, callback: EventCallback<E>[T]) => {
if (typeof events[eventName] === 'undefined') events[eventName] = [];

eventList[eventName]?.push(callback);
events[eventName]?.push(callback);
};

const combine = (combineCb: (cb: typeof on) => void | ((cb: typeof on) => void)[]) => {
if (Array.isArray(combineCb)) combineCb.forEach((cb) => cb(on));
combineCb(on);
};

const execute = <T extends keyof Events<E>>(eventName: T, ...params: Parameters<Events<E>[T]>): ReturnType<Events<E>[T]> | undefined => {
const events = eventList[eventName];
if (!events) return;
const execute = <T extends keyof EventCallback<E>>(eventName: T, ...params: Parameters<EventCallback<E>[T]>): ReturnType<EventCallback<E>[T]> | undefined => {
const eventList = events[eventName];
if (!eventList) return;

if (eventName === 'before') {
for (const event of events) {
for (const event of eventList) {
const res = event(...params);

if (typeof res === 'boolean') return res as ReturnType<EventCallback<E>[T]>;
}
} else if (['success', 'error'].includes(eventName)) {
let promiseResolver = Promise.resolve();

for (const event of eventList) {
const res = event(...params);

if (typeof res === 'boolean') return res as ReturnType<Events<E>[T]>;
if (res instanceof Promise) {
promiseResolver = res;
} else {
promiseResolver = Promise.resolve(res as void);
}
}

return promiseResolver as ReturnType<EventCallback<E>[T]>;
} else {
for (const event of events) {
for (const event of eventList) {
event(...params);
}
}
};

const toVisitOptions = (...params: E): VisitOptions => ((Object.keys(eventList) as (keyof typeof eventList)[]).map((name) => ({
[`on${(name.charAt(0).toUpperCase() + name.slice(1)) as Capitalize<keyof typeof eventList>}`]: (arg: any) => {
return execute(name, ...[arg, ...params]);
}
})).reduce((p, c) => ({
...p,
...c
}), {}));

return {
events,
on,
combine,
execute,
toVisitOptions
execute
};
};
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type { Events, EventsList } from './event';
export { useEventsSystem } from './event';
export type { EventCallback, OnFunction, CombineFunction, ExecuteFunction } from './event';
export { createEventCallbackManager } from './event';

export { useForm } from './inertia';
28 changes: 21 additions & 7 deletions src/inertia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import type { Method, VisitOptions, RequestPayload } from '@inertiajs/core';
import { createMessage } from '@formkit/core';
import { router } from '@inertiajs/core';
import { reactive, toRefs, watchEffect } from 'vue';
import { useEventsSystem } from './event';
import { createEventCallbackManager } from './event';

export const useForm = <F extends RequestPayload>(initialFields?: F) => {
const event = useEventsSystem<[node: FormKitNode]>();
const eventManager = createEventCallbackManager<[node: FormKitNode]>();

let _recentlySuccessfulTimeoutId: ReturnType<typeof setTimeout> | undefined = undefined;
let _cancelToken: {
Expand All @@ -34,7 +34,7 @@ export const useForm = <F extends RequestPayload>(initialFields?: F) => {
wasSuccessful: false
});

event.combine((on) => {
eventManager.combine((on) => {
on('cancelToken', (token) => {
_cancelToken = token;
});
Expand Down Expand Up @@ -110,15 +110,29 @@ export const useForm = <F extends RequestPayload>(initialFields?: F) => {
};

const _createVisitHandler = (method: Method) => (url: URL | string, options?: Exclude<VisitOptions, 'method' | 'data'>) => (data: F, node: FormKitNode) => {
const _optionEventCallbacks: {
[key: string]: any
} = {};

const names = Object.keys(eventManager.events) as (keyof typeof eventManager.events)[];

for (const name of names) {
const _callbackName = `on${name.charAt(0).toUpperCase() + name.slice(1)}`;

_optionEventCallbacks[_callbackName] = (arg: any) => {
return eventManager.execute(name, arg, node);
};
}

if (method === 'delete') {
router.delete(url, {
...event.toVisitOptions(node),
..._optionEventCallbacks,
...options,
data
});
} else {
router[method](url, data, {
...event.toVisitOptions(node),
..._optionEventCallbacks,
...options,
});
}
Expand All @@ -136,8 +150,8 @@ export const useForm = <F extends RequestPayload>(initialFields?: F) => {

...toRefs(state),

on: event.on,
combine: event.combine,
on: eventManager.on,
combine: eventManager.combine,

plugin,
}
Expand Down

0 comments on commit 0bdbb16

Please sign in to comment.