@inertiajs/svelte 2.3.16 → 3.0.0-beta.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/dist/components/App.svelte +120 -44
- package/dist/components/App.svelte.d.ts +10 -19
- package/dist/components/Deferred.svelte +68 -9
- package/dist/components/Deferred.svelte.d.ts +9 -20
- package/dist/components/Form.svelte +358 -242
- package/dist/components/Form.svelte.d.ts +51 -96
- package/dist/components/InfiniteScroll.svelte +227 -167
- package/dist/components/InfiniteScroll.svelte.d.ts +29 -116
- package/dist/components/Link.svelte +96 -48
- package/dist/components/Link.svelte.d.ts +49 -56
- package/dist/components/Render.svelte +62 -21
- package/dist/components/Render.svelte.d.ts +9 -25
- package/dist/components/WhenVisible.svelte +83 -66
- package/dist/components/WhenVisible.svelte.d.ts +11 -26
- package/dist/components/createForm.d.ts +8 -0
- package/dist/components/createForm.js +4 -0
- package/dist/components/formContext.d.ts +3 -3
- package/dist/components/formContext.js +9 -3
- package/dist/createInertiaApp.d.ts +12 -7
- package/dist/createInertiaApp.js +63 -24
- package/dist/index.d.ts +8 -5
- package/dist/index.js +8 -5
- package/dist/layoutProps.svelte.d.ts +6 -0
- package/dist/layoutProps.svelte.js +25 -0
- package/dist/link.js +13 -2
- package/dist/page.svelte.d.ts +10 -0
- package/dist/page.svelte.js +14 -0
- package/dist/types.d.ts +11 -4
- package/dist/types.js +1 -1
- package/dist/{useForm.d.ts → useForm.svelte.d.ts} +4 -4
- package/dist/useForm.svelte.js +116 -0
- package/dist/useFormState.svelte.d.ts +84 -0
- package/dist/useFormState.svelte.js +290 -0
- package/dist/useHttp.svelte.d.ts +61 -0
- package/dist/useHttp.svelte.js +154 -0
- package/dist/usePrefetch.svelte.d.ts +7 -0
- package/dist/{usePrefetch.js → usePrefetch.svelte.js} +18 -13
- package/dist/useRemember.svelte.d.ts +1 -0
- package/dist/useRemember.svelte.js +10 -0
- package/package.json +14 -13
- package/resources/boost/guidelines/core.blade.php +3 -0
- package/resources/boost/skills/inertia-svelte-development/SKILL.blade.php +372 -0
- package/dist/page.d.ts +0 -13
- package/dist/page.js +0 -8
- package/dist/useForm.js +0 -356
- package/dist/usePrefetch.d.ts +0 -7
- package/dist/useRemember.d.ts +0 -1
- package/dist/useRemember.js +0 -11
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { hasFiles, http, HttpCancelledError, HttpResponseError, mergeDataIntoQueryString, objectToFormData, UseFormUtils, } from '@inertiajs/core';
|
|
2
|
+
import { toSimpleValidationErrors } from 'laravel-precognition';
|
|
3
|
+
import { cloneDeep } from 'lodash-es';
|
|
4
|
+
import useFormState, {} from './useFormState.svelte';
|
|
5
|
+
export default function useHttp(...args) {
|
|
6
|
+
const { rememberKey, data, precognitionEndpoint } = UseFormUtils.parseUseFormArguments(...args);
|
|
7
|
+
let abortController = null;
|
|
8
|
+
let pendingOptimisticCallback = null;
|
|
9
|
+
const { form: baseForm, setDefaults, getTransform, getPrecognitionEndpoint, setFormState, markAsSuccessful, wasDefaultsCalledInOnSuccess, resetDefaultsCalledInOnSuccess, setRememberExcludeKeys, resetBeforeSubmit, finishProcessing, withAllErrors, } = useFormState({
|
|
10
|
+
data,
|
|
11
|
+
rememberKey,
|
|
12
|
+
precognitionEndpoint,
|
|
13
|
+
});
|
|
14
|
+
const formWithPrecognition = () => baseForm;
|
|
15
|
+
setFormState('response', null);
|
|
16
|
+
const submit = async (method, url, options) => {
|
|
17
|
+
const onBefore = options.onBefore?.();
|
|
18
|
+
if (onBefore === false) {
|
|
19
|
+
return Promise.reject(new Error('Request cancelled by onBefore'));
|
|
20
|
+
}
|
|
21
|
+
resetDefaultsCalledInOnSuccess();
|
|
22
|
+
resetBeforeSubmit();
|
|
23
|
+
abortController = new AbortController();
|
|
24
|
+
const cancelToken = {
|
|
25
|
+
cancel: () => abortController?.abort(),
|
|
26
|
+
};
|
|
27
|
+
options.onCancelToken?.(cancelToken);
|
|
28
|
+
options.optimistic = options.optimistic ?? pendingOptimisticCallback ?? undefined;
|
|
29
|
+
pendingOptimisticCallback = null;
|
|
30
|
+
let snapshot;
|
|
31
|
+
if (options.optimistic) {
|
|
32
|
+
snapshot = cloneDeep(form.data());
|
|
33
|
+
const optimisticData = options.optimistic(cloneDeep(snapshot));
|
|
34
|
+
Object.keys(optimisticData).forEach((key) => {
|
|
35
|
+
;
|
|
36
|
+
baseForm[key] = optimisticData[key];
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
setFormState('processing', true);
|
|
40
|
+
options.onStart?.();
|
|
41
|
+
const transformedData = getTransform()(form.data());
|
|
42
|
+
const useFormData = hasFiles(transformedData);
|
|
43
|
+
let requestUrl = url;
|
|
44
|
+
let requestData;
|
|
45
|
+
let contentType;
|
|
46
|
+
if (method === 'get') {
|
|
47
|
+
const [urlWithParams] = mergeDataIntoQueryString(method, url, transformedData);
|
|
48
|
+
requestUrl = urlWithParams;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
if (useFormData) {
|
|
52
|
+
requestData = objectToFormData(transformedData);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
requestData = JSON.stringify(transformedData);
|
|
56
|
+
contentType = 'application/json';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const response = await http.getClient().request({
|
|
61
|
+
method,
|
|
62
|
+
url: requestUrl,
|
|
63
|
+
data: requestData,
|
|
64
|
+
headers: {
|
|
65
|
+
Accept: 'application/json',
|
|
66
|
+
...(contentType ? { 'Content-Type': contentType } : {}),
|
|
67
|
+
...options.headers,
|
|
68
|
+
},
|
|
69
|
+
signal: abortController.signal,
|
|
70
|
+
onUploadProgress: (event) => {
|
|
71
|
+
setFormState('progress', event);
|
|
72
|
+
options.onProgress?.(event);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
const responseData = JSON.parse(response.data);
|
|
76
|
+
if (response.status >= 200 && response.status < 300) {
|
|
77
|
+
markAsSuccessful();
|
|
78
|
+
setFormState('response', responseData);
|
|
79
|
+
options.onSuccess?.(responseData);
|
|
80
|
+
if (!wasDefaultsCalledInOnSuccess()) {
|
|
81
|
+
setDefaults(cloneDeep(form.data()));
|
|
82
|
+
}
|
|
83
|
+
setFormState('isDirty', false);
|
|
84
|
+
return responseData;
|
|
85
|
+
}
|
|
86
|
+
throw new HttpResponseError(`Request failed with status ${response.status}`, response, url);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (snapshot) {
|
|
90
|
+
Object.keys(snapshot).forEach((key) => {
|
|
91
|
+
;
|
|
92
|
+
baseForm[key] = snapshot[key];
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (error instanceof HttpResponseError) {
|
|
96
|
+
if (error.response.status === 422) {
|
|
97
|
+
const responseData = JSON.parse(error.response.data);
|
|
98
|
+
const validationErrors = responseData.errors || {};
|
|
99
|
+
const processedErrors = (withAllErrors.enabled() ? validationErrors : toSimpleValidationErrors(validationErrors));
|
|
100
|
+
form.clearErrors().setError(processedErrors);
|
|
101
|
+
options.onError?.(processedErrors);
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
if (error instanceof HttpCancelledError || (error instanceof Error && error.name === 'AbortError')) {
|
|
106
|
+
options.onCancel?.();
|
|
107
|
+
throw new HttpCancelledError('Request was cancelled', url);
|
|
108
|
+
}
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
finishProcessing();
|
|
113
|
+
abortController = null;
|
|
114
|
+
options.onFinish?.();
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const cancel = () => {
|
|
118
|
+
abortController?.abort();
|
|
119
|
+
};
|
|
120
|
+
const createSubmitMethod = (method) => async (url, options = {}) => {
|
|
121
|
+
return submit(method, url, options);
|
|
122
|
+
};
|
|
123
|
+
Object.assign(baseForm, {
|
|
124
|
+
submit(...args) {
|
|
125
|
+
const parsed = UseFormUtils.parseSubmitArguments(args, getPrecognitionEndpoint());
|
|
126
|
+
return submit(parsed.method, parsed.url, parsed.options);
|
|
127
|
+
},
|
|
128
|
+
get: createSubmitMethod('get'),
|
|
129
|
+
post: createSubmitMethod('post'),
|
|
130
|
+
put: createSubmitMethod('put'),
|
|
131
|
+
patch: createSubmitMethod('patch'),
|
|
132
|
+
delete: createSubmitMethod('delete'),
|
|
133
|
+
cancel,
|
|
134
|
+
dontRemember(...keys) {
|
|
135
|
+
setRememberExcludeKeys(keys);
|
|
136
|
+
return form;
|
|
137
|
+
},
|
|
138
|
+
optimistic(callback) {
|
|
139
|
+
pendingOptimisticCallback = callback;
|
|
140
|
+
return form;
|
|
141
|
+
},
|
|
142
|
+
withAllErrors() {
|
|
143
|
+
withAllErrors.enable();
|
|
144
|
+
return form;
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
const form = baseForm;
|
|
148
|
+
const originalWithPrecognition = formWithPrecognition().withPrecognition;
|
|
149
|
+
form.withPrecognition = (...args) => {
|
|
150
|
+
originalWithPrecognition(...args);
|
|
151
|
+
return form;
|
|
152
|
+
};
|
|
153
|
+
return getPrecognitionEndpoint() ? form : form;
|
|
154
|
+
}
|
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import { router } from '@inertiajs/core';
|
|
2
2
|
import { onDestroy, onMount } from 'svelte';
|
|
3
|
-
import { readonly, writable } from 'svelte/store';
|
|
4
3
|
export default function usePrefetch(options = {}) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
let isPrefetched = $state(false);
|
|
5
|
+
let isPrefetching = $state(false);
|
|
6
|
+
let lastUpdatedAt = $state(null);
|
|
8
7
|
const cached = typeof window === 'undefined' ? null : router.getCached(window.location.pathname, options);
|
|
9
8
|
const inFlight = typeof window === 'undefined' ? null : router.getPrefetching(window.location.pathname, options);
|
|
10
|
-
isPrefetched
|
|
11
|
-
isPrefetching
|
|
12
|
-
lastUpdatedAt
|
|
9
|
+
isPrefetched = cached !== null;
|
|
10
|
+
isPrefetching = inFlight !== null;
|
|
11
|
+
lastUpdatedAt = cached?.staleTimestamp || null;
|
|
13
12
|
let removePrefetchedListener;
|
|
14
13
|
let removePrefetchingListener;
|
|
15
14
|
onMount(() => {
|
|
16
15
|
removePrefetchingListener = router.on('prefetching', ({ detail }) => {
|
|
17
16
|
if (detail.visit.url.pathname === window.location.pathname) {
|
|
18
|
-
isPrefetching
|
|
17
|
+
isPrefetching = true;
|
|
19
18
|
}
|
|
20
19
|
});
|
|
21
20
|
removePrefetchedListener = router.on('prefetched', ({ detail }) => {
|
|
22
21
|
if (detail.visit.url.pathname === window.location.pathname) {
|
|
23
|
-
isPrefetched
|
|
24
|
-
isPrefetching
|
|
22
|
+
isPrefetched = true;
|
|
23
|
+
isPrefetching = false;
|
|
25
24
|
}
|
|
26
25
|
});
|
|
27
26
|
});
|
|
@@ -34,9 +33,15 @@ export default function usePrefetch(options = {}) {
|
|
|
34
33
|
}
|
|
35
34
|
});
|
|
36
35
|
return {
|
|
37
|
-
isPrefetched
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
get isPrefetched() {
|
|
37
|
+
return isPrefetched;
|
|
38
|
+
},
|
|
39
|
+
get isPrefetching() {
|
|
40
|
+
return isPrefetching;
|
|
41
|
+
},
|
|
42
|
+
get lastUpdatedAt() {
|
|
43
|
+
return lastUpdatedAt;
|
|
44
|
+
},
|
|
40
45
|
flush() {
|
|
41
46
|
router.flush(window.location.pathname, options);
|
|
42
47
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function useRemember<State extends object>(initialState: State, key?: string): State;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { router } from '@inertiajs/core';
|
|
2
|
+
import { cloneDeep } from 'lodash-es';
|
|
3
|
+
export default function useRemember(initialState, key) {
|
|
4
|
+
const restored = router.restore(key);
|
|
5
|
+
const state = $state(restored !== undefined ? cloneDeep(restored) : initialState);
|
|
6
|
+
$effect(() => {
|
|
7
|
+
router.remember(cloneDeep($state.snapshot(state)), key);
|
|
8
|
+
});
|
|
9
|
+
return state;
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inertiajs/svelte",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-beta.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "The Svelte adapter for Inertia.js",
|
|
6
6
|
"contributors": [
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"homepage": "https://inertiajs.com/",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "https://github.com/inertiajs/inertia.git",
|
|
13
|
+
"url": "git+https://github.com/inertiajs/inertia.git",
|
|
14
14
|
"directory": "packages/svelte"
|
|
15
15
|
},
|
|
16
16
|
"bugs": {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
21
|
"!dist/**/*.test.*",
|
|
22
|
-
"!dist/**/*.spec.*"
|
|
22
|
+
"!dist/**/*.spec.*",
|
|
23
|
+
"resources"
|
|
23
24
|
],
|
|
24
25
|
"type": "module",
|
|
25
26
|
"types": "./dist/index.d.ts",
|
|
@@ -35,27 +36,27 @@
|
|
|
35
36
|
}
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
|
-
"@sveltejs/adapter-auto": "^
|
|
39
|
-
"@sveltejs/kit": "^2.
|
|
39
|
+
"@sveltejs/adapter-auto": "^7.0.1",
|
|
40
|
+
"@sveltejs/kit": "^2.53.2",
|
|
40
41
|
"@sveltejs/package": "^2.5.7",
|
|
41
|
-
"@sveltejs/vite-plugin-svelte": "^
|
|
42
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
42
43
|
"axios": "^1.13.5",
|
|
43
|
-
"es-check": "
|
|
44
|
+
"es-check": "9.5.3",
|
|
44
45
|
"publint": "^0.3.17",
|
|
45
|
-
"svelte": "^
|
|
46
|
-
"svelte-check": "^4.3
|
|
46
|
+
"svelte": "^5.53.5",
|
|
47
|
+
"svelte-check": "^4.4.3",
|
|
47
48
|
"tslib": "^2.8.1",
|
|
48
49
|
"typescript": "^5.9.3",
|
|
49
|
-
"vite": "^
|
|
50
|
+
"vite": "^7.3.1"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
52
|
-
"svelte": "^
|
|
53
|
+
"svelte": "^5.0.0"
|
|
53
54
|
},
|
|
54
55
|
"dependencies": {
|
|
55
56
|
"@types/lodash-es": "^4.17.12",
|
|
56
|
-
"laravel-precognition": "
|
|
57
|
+
"laravel-precognition": "2.0.0-beta.2",
|
|
57
58
|
"lodash-es": "^4.17.23",
|
|
58
|
-
"@inertiajs/core": "
|
|
59
|
+
"@inertiajs/core": "3.0.0-beta.1"
|
|
59
60
|
},
|
|
60
61
|
"scripts": {
|
|
61
62
|
"build": "pnpm package && svelte-check --tsconfig ./tsconfig.json && publint",
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: inertia-svelte-development
|
|
3
|
+
description: "Develops Inertia.js v2 Svelte client-side applications. Activates when creating Svelte pages, forms, or navigation; using Link, Form, or router; working with deferred props, prefetching, or polling; or when user mentions Svelte with Inertia, Svelte pages, Svelte forms, or Svelte navigation."
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: laravel
|
|
7
|
+
---
|
|
8
|
+
@php
|
|
9
|
+
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
|
10
|
+
@endphp
|
|
11
|
+
# Inertia Svelte Development
|
|
12
|
+
|
|
13
|
+
## When to Apply
|
|
14
|
+
|
|
15
|
+
Activate this skill when:
|
|
16
|
+
|
|
17
|
+
- Creating or modifying Svelte page components for Inertia
|
|
18
|
+
- Working with forms in Svelte (using `<Form>` or `useForm`)
|
|
19
|
+
- Implementing client-side navigation with `<Link>` or `router`
|
|
20
|
+
- Using v2 features: deferred props, prefetching, WhenVisible, InfiniteScroll, once props, flash data, or polling
|
|
21
|
+
- Building Svelte-specific features with the Inertia protocol
|
|
22
|
+
|
|
23
|
+
## Documentation
|
|
24
|
+
|
|
25
|
+
Use `search-docs` for detailed Inertia v2 Svelte patterns and documentation.
|
|
26
|
+
|
|
27
|
+
## Basic Usage
|
|
28
|
+
|
|
29
|
+
### Page Components Location
|
|
30
|
+
|
|
31
|
+
Svelte page components should be placed in the `{{ $assist->inertia()->pagesDirectory() }}` directory.
|
|
32
|
+
|
|
33
|
+
### Page Component Structure
|
|
34
|
+
|
|
35
|
+
@boostsnippet("Basic Svelte Page Component", "svelte")
|
|
36
|
+
<script>
|
|
37
|
+
export let users
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<div>
|
|
41
|
+
<h1>Users</h1>
|
|
42
|
+
<ul>
|
|
43
|
+
{#each users as user (user.id)}
|
|
44
|
+
<li>{user.name}</li>
|
|
45
|
+
{/each}
|
|
46
|
+
</ul>
|
|
47
|
+
</div>
|
|
48
|
+
@endboostsnippet
|
|
49
|
+
|
|
50
|
+
## Client-Side Navigation
|
|
51
|
+
|
|
52
|
+
### Basic Link Component
|
|
53
|
+
|
|
54
|
+
Use `<Link>` for client-side navigation instead of traditional `<a>` tags:
|
|
55
|
+
|
|
56
|
+
@boostsnippet("Inertia Svelte Navigation", "svelte")
|
|
57
|
+
<script>
|
|
58
|
+
import { Link } from '@inertiajs/svelte'
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<Link href="/">Home</Link>
|
|
62
|
+
<Link href="/users">Users</Link>
|
|
63
|
+
<Link href={`/users/${user.id}`}>View User</Link>
|
|
64
|
+
@endboostsnippet
|
|
65
|
+
|
|
66
|
+
### Link With Method
|
|
67
|
+
|
|
68
|
+
@boostsnippet("Link With POST Method", "svelte")
|
|
69
|
+
<script>
|
|
70
|
+
import { Link } from '@inertiajs/svelte'
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<Link href="/logout" method="post">Logout</Link>
|
|
74
|
+
@endboostsnippet
|
|
75
|
+
|
|
76
|
+
### Prefetching
|
|
77
|
+
|
|
78
|
+
Prefetch pages to improve perceived performance:
|
|
79
|
+
|
|
80
|
+
@boostsnippet("Prefetch on Hover", "svelte")
|
|
81
|
+
<script>
|
|
82
|
+
import { Link } from '@inertiajs/svelte'
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<Link href="/users" prefetch>Users</Link>
|
|
86
|
+
@endboostsnippet
|
|
87
|
+
|
|
88
|
+
### Programmatic Navigation
|
|
89
|
+
|
|
90
|
+
@boostsnippet("Router Visit", "svelte")
|
|
91
|
+
<script>
|
|
92
|
+
import { router } from '@inertiajs/svelte'
|
|
93
|
+
|
|
94
|
+
function handleClick() {
|
|
95
|
+
router.visit('/users')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Or with options
|
|
99
|
+
function createUser() {
|
|
100
|
+
router.visit('/users', {
|
|
101
|
+
method: 'post',
|
|
102
|
+
data: { name: 'John' },
|
|
103
|
+
onSuccess: () => console.log('Success!'),
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
</script>
|
|
107
|
+
@endboostsnippet
|
|
108
|
+
|
|
109
|
+
## Form Handling
|
|
110
|
+
|
|
111
|
+
@if($assist->inertia()->hasFormComponent())
|
|
112
|
+
### Form Component (Recommended)
|
|
113
|
+
|
|
114
|
+
The recommended way to build forms is with the `<Form>` component:
|
|
115
|
+
|
|
116
|
+
@boostsnippet("Form Component Example", "svelte")
|
|
117
|
+
<script>
|
|
118
|
+
import { Form } from '@inertiajs/svelte'
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<Form action="/users" method="post" let:errors let:processing let:wasSuccessful>
|
|
122
|
+
<input type="text" name="name" />
|
|
123
|
+
{#if errors.name}
|
|
124
|
+
<div>{errors.name}</div>
|
|
125
|
+
{/if}
|
|
126
|
+
|
|
127
|
+
<input type="email" name="email" />
|
|
128
|
+
{#if errors.email}
|
|
129
|
+
<div>{errors.email}</div>
|
|
130
|
+
{/if}
|
|
131
|
+
|
|
132
|
+
<button type="submit" disabled={processing}>
|
|
133
|
+
{processing ? 'Creating...' : 'Create User'}
|
|
134
|
+
</button>
|
|
135
|
+
|
|
136
|
+
{#if wasSuccessful}
|
|
137
|
+
<div>User created!</div>
|
|
138
|
+
{/if}
|
|
139
|
+
</Form>
|
|
140
|
+
@endboostsnippet
|
|
141
|
+
|
|
142
|
+
### Form Component With All Props
|
|
143
|
+
|
|
144
|
+
@boostsnippet("Form Component Full Example", "svelte")
|
|
145
|
+
<script>
|
|
146
|
+
import { Form } from '@inertiajs/svelte'
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<Form
|
|
150
|
+
action="/users"
|
|
151
|
+
method="post"
|
|
152
|
+
let:errors
|
|
153
|
+
let:hasErrors
|
|
154
|
+
let:processing
|
|
155
|
+
let:progress
|
|
156
|
+
let:wasSuccessful
|
|
157
|
+
let:recentlySuccessful
|
|
158
|
+
let:clearErrors
|
|
159
|
+
let:resetAndClearErrors
|
|
160
|
+
let:defaults
|
|
161
|
+
let:isDirty
|
|
162
|
+
let:reset
|
|
163
|
+
let:submit
|
|
164
|
+
>
|
|
165
|
+
<input type="text" name="name" value={defaults.name} />
|
|
166
|
+
{#if errors.name}
|
|
167
|
+
<div>{errors.name}</div>
|
|
168
|
+
{/if}
|
|
169
|
+
|
|
170
|
+
<button type="submit" disabled={processing}>
|
|
171
|
+
{processing ? 'Saving...' : 'Save'}
|
|
172
|
+
</button>
|
|
173
|
+
|
|
174
|
+
{#if progress}
|
|
175
|
+
<progress value={progress.percentage} max="100">
|
|
176
|
+
{progress.percentage}%
|
|
177
|
+
</progress>
|
|
178
|
+
{/if}
|
|
179
|
+
|
|
180
|
+
{#if wasSuccessful}
|
|
181
|
+
<div>Saved!</div>
|
|
182
|
+
{/if}
|
|
183
|
+
</Form>
|
|
184
|
+
@endboostsnippet
|
|
185
|
+
|
|
186
|
+
@if($assist->inertia()->hasFormComponentResets())
|
|
187
|
+
### Form Component Reset Props
|
|
188
|
+
|
|
189
|
+
The `<Form>` component supports automatic resetting:
|
|
190
|
+
|
|
191
|
+
- `resetOnError` - Reset form data when the request fails
|
|
192
|
+
- `resetOnSuccess` - Reset form data when the request succeeds
|
|
193
|
+
- `setDefaultsOnSuccess` - Update default values on success
|
|
194
|
+
|
|
195
|
+
Use the `search-docs` tool with a query of `form component resetting` for detailed guidance.
|
|
196
|
+
|
|
197
|
+
@boostsnippet("Form With Reset Props", "svelte")
|
|
198
|
+
<script>
|
|
199
|
+
import { Form } from '@inertiajs/svelte'
|
|
200
|
+
</script>
|
|
201
|
+
|
|
202
|
+
<Form
|
|
203
|
+
action="/users"
|
|
204
|
+
method="post"
|
|
205
|
+
resetOnSuccess
|
|
206
|
+
setDefaultsOnSuccess
|
|
207
|
+
let:errors
|
|
208
|
+
let:processing
|
|
209
|
+
let:wasSuccessful
|
|
210
|
+
>
|
|
211
|
+
<input type="text" name="name" />
|
|
212
|
+
{#if errors.name}
|
|
213
|
+
<div>{errors.name}</div>
|
|
214
|
+
{/if}
|
|
215
|
+
|
|
216
|
+
<button type="submit" disabled={processing}>
|
|
217
|
+
Submit
|
|
218
|
+
</button>
|
|
219
|
+
</Form>
|
|
220
|
+
@endboostsnippet
|
|
221
|
+
@else
|
|
222
|
+
Note: This version of Inertia does not support `resetOnError`, `resetOnSuccess`, or `setDefaultsOnSuccess` on the `<Form>` component. Using these props will cause errors. Upgrade to Inertia v2.2.0+ to use these features.
|
|
223
|
+
@endif
|
|
224
|
+
|
|
225
|
+
Forms can also be built using the `useForm` hook for more programmatic control. Use the `search-docs` tool with a query of `useForm helper` for guidance.
|
|
226
|
+
|
|
227
|
+
@endif
|
|
228
|
+
|
|
229
|
+
### `useForm` Hook
|
|
230
|
+
|
|
231
|
+
@if($assist->inertia()->hasFormComponent() === false)
|
|
232
|
+
For Inertia v2.0.x: Build forms using the `useForm` hook as the `<Form>` component is not available until v2.1.0+.
|
|
233
|
+
@else
|
|
234
|
+
For more programmatic control or to follow existing conventions, use the `useForm` hook:
|
|
235
|
+
@endif
|
|
236
|
+
|
|
237
|
+
@boostsnippet("useForm Example", "svelte")
|
|
238
|
+
<script>
|
|
239
|
+
import { useForm } from '@inertiajs/svelte'
|
|
240
|
+
|
|
241
|
+
const form = useForm({
|
|
242
|
+
name: '',
|
|
243
|
+
email: '',
|
|
244
|
+
password: '',
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
function submit() {
|
|
248
|
+
$form.post('/users', {
|
|
249
|
+
onSuccess: () => $form.reset('password'),
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
</script>
|
|
253
|
+
|
|
254
|
+
<form on:submit|preventDefault={submit}>
|
|
255
|
+
<input type="text" bind:value={$form.name} />
|
|
256
|
+
{#if $form.errors.name}
|
|
257
|
+
<div>{$form.errors.name}</div>
|
|
258
|
+
{/if}
|
|
259
|
+
|
|
260
|
+
<input type="email" bind:value={$form.email} />
|
|
261
|
+
{#if $form.errors.email}
|
|
262
|
+
<div>{$form.errors.email}</div>
|
|
263
|
+
{/if}
|
|
264
|
+
|
|
265
|
+
<input type="password" bind:value={$form.password} />
|
|
266
|
+
{#if $form.errors.password}
|
|
267
|
+
<div>{$form.errors.password}</div>
|
|
268
|
+
{/if}
|
|
269
|
+
|
|
270
|
+
<button type="submit" disabled={$form.processing}>
|
|
271
|
+
Create User
|
|
272
|
+
</button>
|
|
273
|
+
</form>
|
|
274
|
+
@endboostsnippet
|
|
275
|
+
|
|
276
|
+
## Inertia v2 Features
|
|
277
|
+
|
|
278
|
+
### Deferred Props
|
|
279
|
+
|
|
280
|
+
Use deferred props to load data after initial page render:
|
|
281
|
+
|
|
282
|
+
@boostsnippet("Deferred Props with Empty State", "svelte")
|
|
283
|
+
<script>
|
|
284
|
+
export let users
|
|
285
|
+
</script>
|
|
286
|
+
|
|
287
|
+
<div>
|
|
288
|
+
<h1>Users</h1>
|
|
289
|
+
{#if !users}
|
|
290
|
+
<div class="animate-pulse">
|
|
291
|
+
<div class="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
|
|
292
|
+
<div class="h-4 bg-gray-200 rounded w-1/2"></div>
|
|
293
|
+
</div>
|
|
294
|
+
{:else}
|
|
295
|
+
<ul>
|
|
296
|
+
{#each users as user (user.id)}
|
|
297
|
+
<li>{user.name}</li>
|
|
298
|
+
{/each}
|
|
299
|
+
</ul>
|
|
300
|
+
{/if}
|
|
301
|
+
</div>
|
|
302
|
+
@endboostsnippet
|
|
303
|
+
|
|
304
|
+
### Polling
|
|
305
|
+
|
|
306
|
+
Automatically refresh data at intervals:
|
|
307
|
+
|
|
308
|
+
@boostsnippet("Polling Example", "svelte")
|
|
309
|
+
<script>
|
|
310
|
+
import { router } from '@inertiajs/svelte'
|
|
311
|
+
import { onMount, onDestroy } from 'svelte'
|
|
312
|
+
|
|
313
|
+
export let stats
|
|
314
|
+
|
|
315
|
+
let interval
|
|
316
|
+
|
|
317
|
+
onMount(() => {
|
|
318
|
+
interval = setInterval(() => {
|
|
319
|
+
router.reload({ only: ['stats'] })
|
|
320
|
+
}, 5000) // Poll every 5 seconds
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
onDestroy(() => {
|
|
324
|
+
clearInterval(interval)
|
|
325
|
+
})
|
|
326
|
+
</script>
|
|
327
|
+
|
|
328
|
+
<div>
|
|
329
|
+
<h1>Dashboard</h1>
|
|
330
|
+
<div>Active Users: {stats.activeUsers}</div>
|
|
331
|
+
</div>
|
|
332
|
+
@endboostsnippet
|
|
333
|
+
|
|
334
|
+
### WhenVisible
|
|
335
|
+
|
|
336
|
+
Lazy-load a prop when an element scrolls into view. Useful for deferring expensive data that sits below the fold:
|
|
337
|
+
|
|
338
|
+
@boostsnippet("WhenVisible Example", "svelte")
|
|
339
|
+
<script>
|
|
340
|
+
import { WhenVisible } from '@inertiajs/svelte'
|
|
341
|
+
|
|
342
|
+
export let stats
|
|
343
|
+
</script>
|
|
344
|
+
|
|
345
|
+
<div>
|
|
346
|
+
<h1>Dashboard</h1>
|
|
347
|
+
|
|
348
|
+
<!-- stats prop is loaded only when this section scrolls into view -->
|
|
349
|
+
<WhenVisible data="stats" buffer={200}>
|
|
350
|
+
<div>
|
|
351
|
+
<p>Total Users: {stats.total_users}</p>
|
|
352
|
+
<p>Revenue: {stats.revenue}</p>
|
|
353
|
+
</div>
|
|
354
|
+
|
|
355
|
+
<svelte:fragment slot="fallback">
|
|
356
|
+
<div class="animate-pulse">Loading stats...</div>
|
|
357
|
+
</svelte:fragment>
|
|
358
|
+
</WhenVisible>
|
|
359
|
+
</div>
|
|
360
|
+
@endboostsnippet
|
|
361
|
+
|
|
362
|
+
## Server-Side Patterns
|
|
363
|
+
|
|
364
|
+
Server-side patterns (Inertia::render, props, middleware) are covered in inertia-laravel guidelines.
|
|
365
|
+
|
|
366
|
+
## Common Pitfalls
|
|
367
|
+
|
|
368
|
+
- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
|
|
369
|
+
- Forgetting to add loading states (skeleton screens) when using deferred props
|
|
370
|
+
- Not handling the `undefined` state of deferred props before data loads
|
|
371
|
+
- Using `<form>` without preventing default submission (use `<Form>` component or `on:submit|preventDefault`)
|
|
372
|
+
- Forgetting to check if `<Form>` component is available in your Inertia version
|
package/dist/page.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { type Page, type PageProps, type SharedPageProps } from '@inertiajs/core';
|
|
2
|
-
import { type Readable } from 'svelte/store';
|
|
3
|
-
type SveltePage<TPageProps extends PageProps = PageProps> = Omit<Page<TPageProps & SharedPageProps>, 'props'> & {
|
|
4
|
-
props: Page<TPageProps & SharedPageProps>['props'] & {
|
|
5
|
-
[key: string]: any;
|
|
6
|
-
};
|
|
7
|
-
};
|
|
8
|
-
export declare const setPage: (this: void, value: SveltePage<PageProps>) => void;
|
|
9
|
-
declare const _default: {
|
|
10
|
-
subscribe: (this: void, run: import("svelte/store").Subscriber<SveltePage<PageProps>>, invalidate?: import("svelte/store").Invalidator<SveltePage<PageProps>> | undefined) => import("svelte/store").Unsubscriber;
|
|
11
|
-
};
|
|
12
|
-
export default _default;
|
|
13
|
-
export declare function usePage<TPageProps extends PageProps = PageProps>(): Readable<SveltePage<TPageProps>>;
|