Skip to content

Commit e28badb

Browse files
committed
refactor: changes internal use of useForm
1 parent 96cf888 commit e28badb

File tree

9 files changed

+225
-194
lines changed

9 files changed

+225
-194
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
},
5656
"peerDependencies": {
5757
"@formkit/vue": ">= 1.2.0",
58-
"@inertiajs/core": ">= 1.0.0",
58+
"@inertiajs/vue3": ">= 1.0.0",
5959
"vue": ">= 3.0.0"
6060
}
6161
}

pnpm-lock.yaml

Lines changed: 25 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/addons/formkit.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import type { FormKitNode } from '@formkit/core';
2+
import type { RequestPayload } from '@inertiajs/core';
3+
import type { AddonExtension } from '../inertia';
4+
5+
import { reactive, watchEffect } from 'vue';
6+
import { createMessage } from '@formkit/core';
7+
8+
export default <F extends RequestPayload>(initialFields?: F) => {
9+
const state = reactive({
10+
node: null as null | FormKitNode,
11+
dirty: false as boolean | null,
12+
errors: false as boolean | null,
13+
valid: false as boolean | null,
14+
});
15+
16+
return {
17+
state,
18+
19+
addon: ((on) => {
20+
on('start', (_, node) => {
21+
node.store.set(createMessage({
22+
key: 'loading',
23+
visible: false,
24+
value: true
25+
}));
26+
27+
if (node.props.submitBehavior !== 'live') node.props.disabled = true;
28+
});
29+
30+
on('error', (errors, node) => {
31+
node.setErrors(node.name in errors ? errors[node.name] : [], errors);
32+
});
33+
34+
on('finish', (_, node) => {
35+
node.store.remove('loading');
36+
37+
if (node.props.submitBehavior !== 'live') node.props.disabled = false;
38+
});
39+
}) as AddonExtension,
40+
plugin: (node: FormKitNode) => {
41+
if (node.props.type !== 'form') return;
42+
43+
state.node = node;
44+
node.input(initialFields);
45+
46+
node.on('created', () => {
47+
watchEffect(() => {
48+
state.dirty = node.context!.state.dirty;
49+
state.valid = node.context!.state.valid;
50+
state.errors = node.context!.state.errors;
51+
});
52+
});
53+
54+
return false;
55+
}
56+
}
57+
};

src/addons/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default as createFormkitAddon } from './formkit';
2+
export { default as createStateAddon } from './state';

src/addons/state.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { AddonExtension } from '../inertia';
2+
3+
import { reactive } from 'vue';
4+
5+
export default (recentlySuccessfulTimeoutTime = 2000) => {
6+
let _recentlySuccessfulTimeoutId: ReturnType<typeof setTimeout> | undefined = undefined;
7+
8+
const state = reactive({
9+
processing: false,
10+
progress: 0,
11+
recentlySuccessful: false,
12+
wasSuccessful: false,
13+
});
14+
15+
return {
16+
state,
17+
18+
addon: ((on) => {
19+
on('before', () => {
20+
state.processing = false;
21+
state.progress = 0;
22+
state.recentlySuccessful = false;
23+
state.wasSuccessful = false;
24+
25+
clearInterval(_recentlySuccessfulTimeoutId);
26+
});
27+
28+
on('start', () => {
29+
state.processing = true;
30+
});
31+
32+
on('progress', (progress) => {
33+
state.progress = progress?.percentage || 0;
34+
});
35+
36+
on('success', () => {
37+
state.recentlySuccessful = true;
38+
state.wasSuccessful = true;
39+
40+
_recentlySuccessfulTimeoutId = setTimeout(() => {
41+
state.recentlySuccessful = false;
42+
}, recentlySuccessfulTimeoutTime);
43+
});
44+
45+
on('finish', () => {
46+
state.processing = false;
47+
state.progress = 0;
48+
});
49+
}) as AddonExtension
50+
};
51+
}

src/event.ts

Lines changed: 0 additions & 70 deletions
This file was deleted.

src/eventManager.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { FormKitNode } from '@formkit/core';
2+
import type { GlobalEventsMap } from '@inertiajs/core';
3+
4+
export type EventCallback = {
5+
[K in keyof Omit<GlobalEventsMap, 'navigate' | 'invalid' | 'exception'>]
6+
: (...args: [...GlobalEventsMap[K]['parameters'], ...[node: FormKitNode]])
7+
=> K extends 'success' | 'error'
8+
? Promise<GlobalEventsMap[K]['result']> | GlobalEventsMap[K]['result']
9+
: GlobalEventsMap[K]['result'];
10+
} & {
11+
cancelToken: (...args: [{ cancel: () => void }, ...[node: FormKitNode]]) => void;
12+
};
13+
14+
export const createEventManager = () => {
15+
const events: Partial<{
16+
[K in keyof EventCallback]: EventCallback[K][];
17+
}> = {};
18+
19+
const on = <T extends keyof EventCallback>(name: T, cb: EventCallback[T]) => {
20+
if (typeof events[name] === 'undefined') events[name] = [];
21+
22+
events[name]?.push(cb);
23+
};
24+
25+
const run = (name: keyof EventCallback, ...args: any): Promise<void> | void | boolean => {
26+
let promiseResolver = Promise.resolve();
27+
28+
for (const event of events[name] || []) {
29+
const res = event(...args);
30+
31+
if (name === 'before' && typeof res === 'boolean') return res;
32+
else if (name === 'success' || name === 'finish') {
33+
if (res instanceof Promise) {
34+
promiseResolver = res;
35+
} else {
36+
promiseResolver = Promise.resolve(res);
37+
}
38+
}
39+
}
40+
41+
if (name === 'success' || name === 'finish') return promiseResolver;
42+
};
43+
44+
return {
45+
events,
46+
on,
47+
run
48+
}
49+
}

src/index.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
export type { EventCallback, OnFunction, CombineFunction, ExecuteFunction } from './event';
2-
export { createEventCallbackManager } from './event';
3-
4-
export { useForm } from './inertia';
1+
export { useForm, type AddonExtension } from './inertia';

0 commit comments

Comments
 (0)