@octanejs/inertia 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/package.json +64 -0
- package/src/HeadContext.ts +5 -0
- package/src/PageContext.ts +5 -0
- package/src/index.ts +26 -0
- package/src/layoutProps.ts +25 -0
- package/src/server.ts +1 -0
- package/src/useForm.ts +318 -0
- package/src/useFormState.ts +585 -0
- package/src/useHttp.ts +457 -0
- package/src/usePage.ts +15 -0
- package/src/usePoll.ts +49 -0
- package/src/usePrefetch.ts +59 -0
- package/src/useRemember.ts +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dominic Gannaway
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# `@octanejs/inertia`
|
|
2
|
+
|
|
3
|
+
Inertia.js 3 bindings for Octane.
|
|
4
|
+
|
|
5
|
+
The package reuses `@inertiajs/core` unchanged and ports the renderer-specific
|
|
6
|
+
`@inertiajs/react` adapter to Octane. It does not install or emulate React.
|
|
7
|
+
|
|
8
|
+
The initial port is pinned to `@inertiajs/react@3.6.1` and the Inertia 3.x
|
|
9
|
+
source tree at commit `68b13b662d7a6ecdd504026ee18733192b0c7d73`.
|
|
10
|
+
The exact React adapter source is retained in `upstream/` so each ported module
|
|
11
|
+
and future version update can be reviewed against its immutable baseline.
|
|
12
|
+
|
|
13
|
+
## Upstream ledger
|
|
14
|
+
|
|
15
|
+
| Contract | Upstream source | Octane entry |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| Router singleton | `packages/react/src/index.ts` | `src/index.ts` |
|
|
18
|
+
| HTTP singleton | `packages/react/src/index.ts` | `src/index.ts` |
|
|
19
|
+
| Progress singleton | `packages/react/src/index.ts` | `src/index.ts` |
|
|
20
|
+
| Server helper | `packages/react/src/server.ts` | `src/server.ts` |
|
|
21
|
+
|
|
22
|
+
These exports preserve object identity with `@inertiajs/core@3.6.1`.
|
|
23
|
+
Renderer-owned exports from `packages/react/src/index.ts` are intentionally
|
|
24
|
+
absent from this foundation unit and are added with their corresponding
|
|
25
|
+
behavioral tests. React, ReactDOM, StrictMode, `forwardRef`, and synthetic event
|
|
26
|
+
types are explicit exclusions from the Octane package.
|
|
27
|
+
|
|
28
|
+
## Status
|
|
29
|
+
|
|
30
|
+
The framework-neutral exports, package boundary, contexts, layout-property
|
|
31
|
+
store, and renderer-independent hook/form layer are present. Components, root
|
|
32
|
+
lifecycle, full SSR/hydration, and the protocol playground are implemented by
|
|
33
|
+
the dependent units in the binding plan.
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@octanejs/inertia",
|
|
3
|
+
"octane": {
|
|
4
|
+
"hookSlots": {
|
|
5
|
+
"manual": [
|
|
6
|
+
"src"
|
|
7
|
+
]
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"version": "0.0.1",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=22.22.2"
|
|
16
|
+
},
|
|
17
|
+
"description": "Inertia.js bindings for the octane renderer — reuses @inertiajs/core and ports the React adapter onto octane.",
|
|
18
|
+
"author": {
|
|
19
|
+
"name": "Dominic Gannaway",
|
|
20
|
+
"email": "dg@domgan.com"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/octanejs/octane.git",
|
|
28
|
+
"directory": "packages/inertia"
|
|
29
|
+
},
|
|
30
|
+
"main": "src/index.ts",
|
|
31
|
+
"module": "src/index.ts",
|
|
32
|
+
"types": "src/index.ts",
|
|
33
|
+
"files": [
|
|
34
|
+
"src",
|
|
35
|
+
"README.md"
|
|
36
|
+
],
|
|
37
|
+
"exports": {
|
|
38
|
+
".": "./src/index.ts",
|
|
39
|
+
"./server": "./src/server.ts",
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@inertiajs/core": "3.6.1",
|
|
44
|
+
"es-toolkit": "^1.49.0",
|
|
45
|
+
"laravel-precognition": "^2.0.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"octane": "0.1.32"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@inertiajs/react": "3.6.1",
|
|
52
|
+
"@tsrx/react": "^0.2.56",
|
|
53
|
+
"@types/react": "^19.2.17",
|
|
54
|
+
"@types/react-dom": "^19.2.3",
|
|
55
|
+
"react": "^19.2.7",
|
|
56
|
+
"react-dom": "^19.2.7",
|
|
57
|
+
"vitest": "^4.1.10",
|
|
58
|
+
"@octanejs/testing-library": "0.1.29",
|
|
59
|
+
"octane": "0.1.32"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"test": "vitest run --root ../.. --project inertia"
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// @octanejs/inertia
|
|
2
|
+
//
|
|
3
|
+
// The Inertia router, HTTP client, and progress APIs are framework-neutral.
|
|
4
|
+
// Keep their upstream identities intact while the renderer-specific adapter is
|
|
5
|
+
// implemented in this package.
|
|
6
|
+
import { config as coreConfig } from '@inertiajs/core';
|
|
7
|
+
|
|
8
|
+
export { http, progress, router } from '@inertiajs/core';
|
|
9
|
+
export { resetLayoutProps, setLayoutProps } from './layoutProps';
|
|
10
|
+
export { default as usePage } from './usePage';
|
|
11
|
+
export { default as usePoll } from './usePoll';
|
|
12
|
+
export { default as usePrefetch } from './usePrefetch';
|
|
13
|
+
export { default as useRemember } from './useRemember';
|
|
14
|
+
export {
|
|
15
|
+
type InertiaForm,
|
|
16
|
+
type InertiaFormProps,
|
|
17
|
+
type InertiaPrecognitiveFormProps,
|
|
18
|
+
type SetDataAction,
|
|
19
|
+
type SetDataByKeyValuePair,
|
|
20
|
+
type SetDataByMethod,
|
|
21
|
+
type SetDataByObject,
|
|
22
|
+
default as useForm,
|
|
23
|
+
} from './useForm';
|
|
24
|
+
export { default as useHttp } from './useHttp';
|
|
25
|
+
|
|
26
|
+
export const config = coreConfig;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createLayoutPropsStore, type LayoutProps, type NamedLayoutProps } from '@inertiajs/core';
|
|
2
|
+
|
|
3
|
+
export const store = createLayoutPropsStore();
|
|
4
|
+
|
|
5
|
+
export function setLayoutProps(props: Partial<LayoutProps>): void;
|
|
6
|
+
export function setLayoutProps<K extends keyof NamedLayoutProps>(
|
|
7
|
+
name: K,
|
|
8
|
+
props: Partial<NamedLayoutProps[K]>,
|
|
9
|
+
): void;
|
|
10
|
+
export function setLayoutProps<T = never>(props: Partial<NoInfer<T>>): void;
|
|
11
|
+
export function setLayoutProps<T = never>(name: string, props: Partial<NoInfer<T>>): void;
|
|
12
|
+
export function setLayoutProps(
|
|
13
|
+
nameOrProps: string | Record<string, unknown>,
|
|
14
|
+
props?: Record<string, unknown>,
|
|
15
|
+
): void {
|
|
16
|
+
if (typeof nameOrProps === 'string') {
|
|
17
|
+
store.setFor(nameOrProps, props!);
|
|
18
|
+
} else {
|
|
19
|
+
store.set(nameOrProps);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resetLayoutProps(): void {
|
|
24
|
+
store.reset();
|
|
25
|
+
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@inertiajs/core/server';
|
package/src/useForm.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CancelToken,
|
|
3
|
+
ErrorValue,
|
|
4
|
+
FormDataErrors,
|
|
5
|
+
FormDataKeys,
|
|
6
|
+
FormDataType,
|
|
7
|
+
FormDataValues,
|
|
8
|
+
Method,
|
|
9
|
+
OptimisticCallback,
|
|
10
|
+
Progress,
|
|
11
|
+
RequestPayload,
|
|
12
|
+
router,
|
|
13
|
+
UrlMethodPair,
|
|
14
|
+
UseFormArguments,
|
|
15
|
+
UseFormSubmitArguments,
|
|
16
|
+
UseFormSubmitOptions,
|
|
17
|
+
UseFormTransformCallback,
|
|
18
|
+
UseFormUtils,
|
|
19
|
+
UseFormWithPrecognitionArguments,
|
|
20
|
+
VisitOptions,
|
|
21
|
+
} from '@inertiajs/core';
|
|
22
|
+
import { cloneDeep } from 'es-toolkit';
|
|
23
|
+
import type {
|
|
24
|
+
NamedInputEvent,
|
|
25
|
+
PrecognitionPath,
|
|
26
|
+
ValidationConfig,
|
|
27
|
+
Validator,
|
|
28
|
+
} from 'laravel-precognition';
|
|
29
|
+
import { useCallback, useMemo, useRef } from 'octane';
|
|
30
|
+
import useFormState, {
|
|
31
|
+
SetDataAction,
|
|
32
|
+
SetDataByKeyValuePair,
|
|
33
|
+
SetDataByMethod,
|
|
34
|
+
SetDataByObject,
|
|
35
|
+
} from './useFormState';
|
|
36
|
+
import useRemember from './useRemember';
|
|
37
|
+
|
|
38
|
+
const slots = Object.fromEntries(
|
|
39
|
+
['cancelToken', 'excludeKeys', 'pendingOptimistic', 'submit', 'cancel', 'submitMethods'].map(
|
|
40
|
+
(name) => [name, Symbol(`Inertia.useForm.${name}`)],
|
|
41
|
+
),
|
|
42
|
+
) as Record<string, symbol>;
|
|
43
|
+
|
|
44
|
+
// Re-export types that were moved to useFormState
|
|
45
|
+
export { SetDataAction, SetDataByKeyValuePair, SetDataByMethod, SetDataByObject };
|
|
46
|
+
|
|
47
|
+
type PrecognitionValidationConfig<TKeys> = ValidationConfig & {
|
|
48
|
+
only?: TKeys[] | Iterable<TKeys> | ArrayLike<TKeys>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export interface InertiaFormProps<TForm extends Record<string, any>> {
|
|
52
|
+
data: TForm;
|
|
53
|
+
isDirty: boolean;
|
|
54
|
+
errors: FormDataErrors<TForm>;
|
|
55
|
+
hasErrors: boolean;
|
|
56
|
+
processing: boolean;
|
|
57
|
+
progress: Progress | null;
|
|
58
|
+
wasSuccessful: boolean;
|
|
59
|
+
recentlySuccessful: boolean;
|
|
60
|
+
setData: SetDataAction<TForm>;
|
|
61
|
+
transform: (callback: UseFormTransformCallback<TForm>) => void;
|
|
62
|
+
setDefaults: {
|
|
63
|
+
(): void;
|
|
64
|
+
<T extends FormDataKeys<TForm>>(field: T, value: FormDataValues<TForm, T>): void;
|
|
65
|
+
(fields: Partial<TForm>): void;
|
|
66
|
+
};
|
|
67
|
+
reset: <K extends FormDataKeys<TForm>>(...fields: K[]) => void;
|
|
68
|
+
clearErrors: <K extends FormDataKeys<TForm>>(...fields: K[]) => void;
|
|
69
|
+
resetAndClearErrors: <K extends FormDataKeys<TForm>>(...fields: K[]) => void;
|
|
70
|
+
setError: {
|
|
71
|
+
<K extends FormDataKeys<TForm>>(field: K, value: ErrorValue): void;
|
|
72
|
+
(errors: FormDataErrors<TForm>): void;
|
|
73
|
+
};
|
|
74
|
+
submit: (...args: UseFormSubmitArguments) => void;
|
|
75
|
+
get: (url: string, options?: UseFormSubmitOptions) => void;
|
|
76
|
+
post: (url: string, options?: UseFormSubmitOptions) => void;
|
|
77
|
+
put: (url: string, options?: UseFormSubmitOptions) => void;
|
|
78
|
+
patch: (url: string, options?: UseFormSubmitOptions) => void;
|
|
79
|
+
delete: (url: string, options?: UseFormSubmitOptions) => void;
|
|
80
|
+
cancel: () => void;
|
|
81
|
+
dontRemember: <K extends FormDataKeys<TForm>>(...fields: K[]) => InertiaFormProps<TForm>;
|
|
82
|
+
optimistic: <TProps>(callback: OptimisticCallback<TProps>) => InertiaFormProps<TForm>;
|
|
83
|
+
withPrecognition: (
|
|
84
|
+
...args: UseFormWithPrecognitionArguments
|
|
85
|
+
) => InertiaPrecognitiveFormProps<TForm>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface InertiaFormValidationProps<TForm extends Record<string, any>> {
|
|
89
|
+
invalid: <K extends FormDataKeys<TForm>>(field: K) => boolean;
|
|
90
|
+
setValidationTimeout: (duration: number) => InertiaPrecognitiveFormProps<TForm>;
|
|
91
|
+
touch: <K extends FormDataKeys<TForm>>(
|
|
92
|
+
field: K | NamedInputEvent | Array<K>,
|
|
93
|
+
...fields: K[]
|
|
94
|
+
) => InertiaPrecognitiveFormProps<TForm>;
|
|
95
|
+
touched: <K extends FormDataKeys<TForm>>(field?: K) => boolean;
|
|
96
|
+
valid: <K extends FormDataKeys<TForm>>(field: K) => boolean;
|
|
97
|
+
validate: <K extends FormDataKeys<TForm> | PrecognitionPath<TForm>>(
|
|
98
|
+
field?: K | NamedInputEvent | PrecognitionValidationConfig<K>,
|
|
99
|
+
config?: PrecognitionValidationConfig<K>,
|
|
100
|
+
) => InertiaPrecognitiveFormProps<TForm>;
|
|
101
|
+
validateFiles: () => InertiaPrecognitiveFormProps<TForm>;
|
|
102
|
+
validating: boolean;
|
|
103
|
+
validator: () => Validator;
|
|
104
|
+
withAllErrors: () => InertiaPrecognitiveFormProps<TForm>;
|
|
105
|
+
withoutFileValidation: () => InertiaPrecognitiveFormProps<TForm>;
|
|
106
|
+
// Backward compatibility for easy migration from the original Precognition libraries
|
|
107
|
+
setErrors: (errors: FormDataErrors<TForm>) => InertiaPrecognitiveFormProps<TForm>;
|
|
108
|
+
forgetError: <K extends FormDataKeys<TForm> | NamedInputEvent>(
|
|
109
|
+
field: K,
|
|
110
|
+
) => InertiaPrecognitiveFormProps<TForm>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export type InertiaForm<TForm extends Record<string, any>> = InertiaFormProps<TForm>;
|
|
114
|
+
export type InertiaPrecognitiveFormProps<TForm extends Record<string, any>> =
|
|
115
|
+
InertiaFormProps<TForm> & InertiaFormValidationProps<TForm>;
|
|
116
|
+
|
|
117
|
+
export default function useForm<TForm extends FormDataType<TForm>>(
|
|
118
|
+
method: Method | (() => Method),
|
|
119
|
+
url: string | (() => string),
|
|
120
|
+
data: TForm | (() => TForm),
|
|
121
|
+
): InertiaPrecognitiveFormProps<TForm>;
|
|
122
|
+
export default function useForm<TForm extends FormDataType<TForm>>(
|
|
123
|
+
urlMethodPair: UrlMethodPair | (() => UrlMethodPair),
|
|
124
|
+
data: TForm | (() => TForm),
|
|
125
|
+
): InertiaPrecognitiveFormProps<TForm>;
|
|
126
|
+
export default function useForm<TForm extends FormDataType<TForm>>(
|
|
127
|
+
rememberKey: string,
|
|
128
|
+
data: TForm | (() => TForm),
|
|
129
|
+
): InertiaFormProps<TForm>;
|
|
130
|
+
export default function useForm<TForm extends FormDataType<TForm>>(
|
|
131
|
+
data: TForm | (() => TForm),
|
|
132
|
+
): InertiaFormProps<TForm>;
|
|
133
|
+
export default function useForm<TForm extends FormDataType<TForm>>(): InertiaFormProps<TForm>;
|
|
134
|
+
export default function useForm<TForm extends FormDataType<TForm>>(
|
|
135
|
+
...args: UseFormArguments<TForm>
|
|
136
|
+
): InertiaFormProps<TForm> | InertiaPrecognitiveFormProps<TForm> {
|
|
137
|
+
const publicArgs =
|
|
138
|
+
typeof args[args.length - 1] === 'symbol'
|
|
139
|
+
? (args.slice(0, -1) as UseFormArguments<TForm>)
|
|
140
|
+
: args;
|
|
141
|
+
const { rememberKey, data, precognitionEndpoint } = UseFormUtils.parseUseFormArguments<TForm>(
|
|
142
|
+
...publicArgs,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Resolve initial data for remember functionality hooks
|
|
146
|
+
const initialDefaults = typeof data === 'function' ? cloneDeep(data()) : cloneDeep(data);
|
|
147
|
+
|
|
148
|
+
const cancelToken = useRef<CancelToken | null>(null, slots.cancelToken);
|
|
149
|
+
const excludeKeysRef = useRef<FormDataKeys<TForm>[]>([], slots.excludeKeys);
|
|
150
|
+
const pendingOptimisticRef = useRef<OptimisticCallback | null>(null, slots.pendingOptimistic);
|
|
151
|
+
|
|
152
|
+
// For remember functionality, we need custom state hooks
|
|
153
|
+
const useDataState = rememberKey
|
|
154
|
+
? () => useRemember<TForm>(initialDefaults, `${rememberKey}:data`, excludeKeysRef)
|
|
155
|
+
: undefined;
|
|
156
|
+
|
|
157
|
+
const useErrorsState = rememberKey
|
|
158
|
+
? () => useRemember<FormDataErrors<TForm>>({} as FormDataErrors<TForm>, `${rememberKey}:errors`)
|
|
159
|
+
: undefined;
|
|
160
|
+
|
|
161
|
+
const {
|
|
162
|
+
form: baseForm,
|
|
163
|
+
setDefaultsState,
|
|
164
|
+
transformRef,
|
|
165
|
+
precognitionEndpointRef,
|
|
166
|
+
dataRef,
|
|
167
|
+
isMounted,
|
|
168
|
+
setProcessing,
|
|
169
|
+
setProgress,
|
|
170
|
+
markAsSuccessful,
|
|
171
|
+
clearErrors,
|
|
172
|
+
setError,
|
|
173
|
+
defaultsCalledInOnSuccessRef,
|
|
174
|
+
resetBeforeSubmit,
|
|
175
|
+
finishProcessing,
|
|
176
|
+
} = useFormState<TForm>({
|
|
177
|
+
data,
|
|
178
|
+
precognitionEndpoint,
|
|
179
|
+
useDataState,
|
|
180
|
+
useErrorsState,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const submit = useCallback(
|
|
184
|
+
(...args: UseFormSubmitArguments) => {
|
|
185
|
+
const { method, url, options } = UseFormUtils.parseSubmitArguments(
|
|
186
|
+
args,
|
|
187
|
+
precognitionEndpointRef.current,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
defaultsCalledInOnSuccessRef.current = false;
|
|
191
|
+
|
|
192
|
+
const _options: VisitOptions = {
|
|
193
|
+
...options,
|
|
194
|
+
onCancelToken: (token) => {
|
|
195
|
+
cancelToken.current = token;
|
|
196
|
+
|
|
197
|
+
return options.onCancelToken?.(token);
|
|
198
|
+
},
|
|
199
|
+
onBefore: (visit) => {
|
|
200
|
+
resetBeforeSubmit();
|
|
201
|
+
|
|
202
|
+
return options.onBefore?.(visit);
|
|
203
|
+
},
|
|
204
|
+
onStart: (visit) => {
|
|
205
|
+
setProcessing(true);
|
|
206
|
+
|
|
207
|
+
return options.onStart?.(visit);
|
|
208
|
+
},
|
|
209
|
+
onProgress: (event) => {
|
|
210
|
+
setProgress(event || null);
|
|
211
|
+
|
|
212
|
+
return options.onProgress?.(event);
|
|
213
|
+
},
|
|
214
|
+
onSuccess: async (page) => {
|
|
215
|
+
if (isMounted.current) {
|
|
216
|
+
markAsSuccessful();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const onSuccess = options.onSuccess ? await options.onSuccess(page) : null;
|
|
220
|
+
|
|
221
|
+
if (isMounted.current && !defaultsCalledInOnSuccessRef.current) {
|
|
222
|
+
baseForm.setData((data: TForm) => {
|
|
223
|
+
setDefaultsState(cloneDeep(data));
|
|
224
|
+
return data;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return onSuccess;
|
|
229
|
+
},
|
|
230
|
+
onError: (errors) => {
|
|
231
|
+
if (isMounted.current) {
|
|
232
|
+
clearErrors();
|
|
233
|
+
setError(errors as FormDataErrors<TForm>);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return options.onError?.(errors);
|
|
237
|
+
},
|
|
238
|
+
onCancel: () => {
|
|
239
|
+
return options.onCancel?.();
|
|
240
|
+
},
|
|
241
|
+
onFinish: (visit) => {
|
|
242
|
+
if (isMounted.current) {
|
|
243
|
+
finishProcessing();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
cancelToken.current = null;
|
|
247
|
+
|
|
248
|
+
return options.onFinish?.(visit);
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
_options.optimistic = _options.optimistic ?? pendingOptimisticRef.current ?? undefined;
|
|
253
|
+
pendingOptimisticRef.current = null;
|
|
254
|
+
|
|
255
|
+
const transformedData = transformRef.current(dataRef.current) as RequestPayload;
|
|
256
|
+
|
|
257
|
+
if (method === 'delete') {
|
|
258
|
+
router.delete(url, { ..._options, data: transformedData });
|
|
259
|
+
} else {
|
|
260
|
+
router[method](url, transformedData, _options);
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
[clearErrors, setError, transformRef],
|
|
264
|
+
slots.submit,
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const cancel = useCallback(
|
|
268
|
+
() => {
|
|
269
|
+
if (cancelToken.current) {
|
|
270
|
+
cancelToken.current.cancel();
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
[],
|
|
274
|
+
slots.cancel,
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
const submitMethods = useMemo(
|
|
278
|
+
() => ({
|
|
279
|
+
get: (url: string, options: VisitOptions = {}) => submit('get', url, options),
|
|
280
|
+
post: (url: string, options: VisitOptions = {}) => submit('post', url, options),
|
|
281
|
+
put: (url: string, options: VisitOptions = {}) => submit('put', url, options),
|
|
282
|
+
patch: (url: string, options: VisitOptions = {}) => submit('patch', url, options),
|
|
283
|
+
delete: (url: string, options: VisitOptions = {}) => submit('delete', url, options),
|
|
284
|
+
}),
|
|
285
|
+
[submit],
|
|
286
|
+
slots.submitMethods,
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// Add useForm-specific methods to the form object
|
|
290
|
+
Object.assign(baseForm, {
|
|
291
|
+
submit,
|
|
292
|
+
...submitMethods,
|
|
293
|
+
cancel,
|
|
294
|
+
dontRemember: <K extends FormDataKeys<TForm>>(...keys: K[]) => {
|
|
295
|
+
excludeKeysRef.current = keys;
|
|
296
|
+
return form;
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
optimistic: <TProps>(callback: OptimisticCallback<TProps>) => {
|
|
300
|
+
pendingOptimisticRef.current = callback as OptimisticCallback;
|
|
301
|
+
return form;
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// Cast to the full form type (baseForm now has submit methods)
|
|
306
|
+
const form = baseForm as unknown as InertiaFormProps<TForm>;
|
|
307
|
+
|
|
308
|
+
// Wrap withPrecognition to return the correct type with submit methods
|
|
309
|
+
const originalWithPrecognition = baseForm.withPrecognition;
|
|
310
|
+
form.withPrecognition = (
|
|
311
|
+
...args: UseFormWithPrecognitionArguments
|
|
312
|
+
): InertiaPrecognitiveFormProps<TForm> => {
|
|
313
|
+
originalWithPrecognition(...args);
|
|
314
|
+
return form as InertiaPrecognitiveFormProps<TForm>;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
return precognitionEndpointRef.current ? (form as InertiaPrecognitiveFormProps<TForm>) : form;
|
|
318
|
+
}
|