@void/svelte 1.2.1 → 1.3.0
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/plugin.mjs +13 -9
- package/package.json +3 -3
- package/src/runtime/use-form.svelte.ts +51 -9
package/dist/plugin.mjs
CHANGED
|
@@ -95,7 +95,10 @@ function islandsPlugin(pagesDir, options) {
|
|
|
95
95
|
},
|
|
96
96
|
async buildStart() {
|
|
97
97
|
if (existsSync(pagesDir)) {
|
|
98
|
-
pageScan = await scanPages(root, {
|
|
98
|
+
pageScan = await scanPages(root, {
|
|
99
|
+
pagesDir,
|
|
100
|
+
environment: isDev ? "dev" : "prod"
|
|
101
|
+
});
|
|
99
102
|
const filesToScan = [
|
|
100
103
|
...pageScan.pages.map((page) => ({
|
|
101
104
|
filePath: page.componentPath,
|
|
@@ -360,7 +363,10 @@ function pagesPlugin(pagesDir, options) {
|
|
|
360
363
|
const normalizedPagesDir = normalizePath(pagesDir);
|
|
361
364
|
let pendingRescan = null;
|
|
362
365
|
const rescanPages = async () => {
|
|
363
|
-
pageScan = existsSync(pagesDir) ? await scanPages(root, {
|
|
366
|
+
pageScan = existsSync(pagesDir) ? await scanPages(root, {
|
|
367
|
+
pagesDir,
|
|
368
|
+
environment: isDev ? "dev" : "prod"
|
|
369
|
+
}) : null;
|
|
364
370
|
};
|
|
365
371
|
const runRescan = () => {
|
|
366
372
|
pendingRescan ??= rescanPages().finally(() => {
|
|
@@ -424,14 +430,12 @@ function pagesPlugin(pagesDir, options) {
|
|
|
424
430
|
viteConfig = config;
|
|
425
431
|
},
|
|
426
432
|
config(_, env) {
|
|
433
|
+
isDev = env.command === "serve";
|
|
427
434
|
if (!existsSync(pagesDir)) return;
|
|
428
|
-
if (
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
environments: { void_worker: { optimizeDeps: { exclude: ["@void/svelte"] } } }
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
+
if (isDev) return {
|
|
436
|
+
optimizeDeps: SVELTE_OPTIMIZE_DEPS,
|
|
437
|
+
environments: { void_worker: { optimizeDeps: { exclude: ["@void/svelte"] } } }
|
|
438
|
+
};
|
|
435
439
|
if (!hasNonIslandPages(pagesDir)) return;
|
|
436
440
|
return { build: {
|
|
437
441
|
manifest: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void/svelte",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Svelte pages, server rendering, forms, and navigation for Void.",
|
|
5
5
|
"homepage": "https://void.cloud",
|
|
6
6
|
"bugs": {
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
"svelte": "^5.57.2",
|
|
44
44
|
"vite": "npm:@voidzero-dev/vite-plus-core@1.3.0",
|
|
45
45
|
"vite-plus": "1.3.0",
|
|
46
|
-
"void": "1.
|
|
46
|
+
"void": "1.3.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"svelte": "^5.0.0",
|
|
50
50
|
"vite": "^8.0.0",
|
|
51
|
-
"void": "1.
|
|
51
|
+
"void": "1.3.0"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "vp pack",
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { getContext } from 'svelte';
|
|
1
|
+
import { getContext, untrack } from 'svelte';
|
|
2
2
|
import {
|
|
3
3
|
VoidActionError,
|
|
4
|
+
cloneFormValue,
|
|
4
5
|
getValidationErrors,
|
|
5
6
|
interpolateRouteParams,
|
|
6
7
|
isAbortError,
|
|
7
8
|
isEqualFormValue,
|
|
9
|
+
parseFormResetArgs,
|
|
8
10
|
submitAction,
|
|
11
|
+
type FormResetOptions,
|
|
9
12
|
type VisitOptions,
|
|
10
13
|
type VoidRouter,
|
|
11
14
|
} from 'void/pages-client';
|
|
@@ -26,7 +29,9 @@ type UntypedForm = {
|
|
|
26
29
|
put(opts?: FormSubmitOptions): Promise<void>;
|
|
27
30
|
patch(opts?: FormSubmitOptions): Promise<void>;
|
|
28
31
|
delete(opts?: FormSubmitOptions): Promise<void>;
|
|
32
|
+
reset(options: FormResetOptions, ...fields: ReadonlyArray<string>): void;
|
|
29
33
|
reset(...fields: ReadonlyArray<string>): void;
|
|
34
|
+
setError(field: string, message: string): void;
|
|
30
35
|
clearErrors(...fields: ReadonlyArray<string>): void;
|
|
31
36
|
clearError(): void;
|
|
32
37
|
};
|
|
@@ -48,7 +53,12 @@ export function useForm<U extends ActionUrl & string>(
|
|
|
48
53
|
put(opts?: FormSubmitOptions): Promise<void>;
|
|
49
54
|
patch(opts?: FormSubmitOptions): Promise<void>;
|
|
50
55
|
delete(opts?: FormSubmitOptions): Promise<void>;
|
|
56
|
+
reset(
|
|
57
|
+
options: FormResetOptions,
|
|
58
|
+
...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>
|
|
59
|
+
): void;
|
|
51
60
|
reset(...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>): void;
|
|
61
|
+
setError(field: keyof ResolveActionBody<U> & string, message: string): void;
|
|
52
62
|
clearErrors(...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>): void;
|
|
53
63
|
clearError(): void;
|
|
54
64
|
};
|
|
@@ -56,32 +66,50 @@ export function useForm<U extends ActionUrl & string>(
|
|
|
56
66
|
export function useForm(
|
|
57
67
|
url: string,
|
|
58
68
|
defaults: Record<string, unknown>,
|
|
59
|
-
options?: { params?: Record<string, string
|
|
69
|
+
options?: { params?: Record<string, string>; resetOnSuccess?: boolean },
|
|
60
70
|
): UntypedForm {
|
|
61
71
|
const router = getContext<VoidRouter>('__void_router');
|
|
62
72
|
|
|
63
|
-
|
|
73
|
+
const initialDefaults = cloneFormValue(defaults);
|
|
74
|
+
let data = $state(cloneFormValue(initialDefaults));
|
|
64
75
|
let errors: Record<string, string> = $state({});
|
|
65
76
|
let error: VoidActionError | null = $state(null);
|
|
66
77
|
let pending = $state(false);
|
|
67
78
|
let wasSuccessful = $state(false);
|
|
68
79
|
let recentlySuccessful = $state(false);
|
|
69
80
|
|
|
70
|
-
let defaults_ =
|
|
81
|
+
let defaults_ = $state.raw(initialDefaults);
|
|
71
82
|
let successTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
83
|
+
let dataSnapshot = cloneFormValue(initialDefaults);
|
|
84
|
+
|
|
85
|
+
$effect(() => {
|
|
86
|
+
// Read nested values even after an earlier edit, without subscribing to errors.
|
|
87
|
+
const current = cloneFormValue(data);
|
|
88
|
+
untrack(() => {
|
|
89
|
+
for (const key of new Set([...Object.keys(dataSnapshot), ...Object.keys(current)])) {
|
|
90
|
+
if (!isEqualFormValue(current[key], dataSnapshot[key])) {
|
|
91
|
+
delete errors[key];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
dataSnapshot = current;
|
|
95
|
+
});
|
|
96
|
+
});
|
|
72
97
|
|
|
73
98
|
const hasChanges = $derived(
|
|
74
99
|
Object.keys(defaults_).some((key) => !isEqualFormValue(data[key], defaults_[key])),
|
|
75
100
|
);
|
|
76
101
|
|
|
77
|
-
function reset(...
|
|
102
|
+
function reset(...args: ReadonlyArray<string | FormResetOptions>) {
|
|
103
|
+
const { initial, fields } = parseFormResetArgs(args);
|
|
104
|
+
const source = initial ? initialDefaults : defaults_;
|
|
78
105
|
if (fields.length === 0) {
|
|
79
|
-
data =
|
|
106
|
+
data = cloneFormValue(source);
|
|
80
107
|
} else {
|
|
81
108
|
for (const field of fields) {
|
|
82
|
-
data[field] =
|
|
109
|
+
data[field] = cloneFormValue(source[field]);
|
|
83
110
|
}
|
|
84
111
|
}
|
|
112
|
+
dataSnapshot = cloneFormValue(data);
|
|
85
113
|
}
|
|
86
114
|
|
|
87
115
|
function clearErrors(...fields: ReadonlyArray<string>) {
|
|
@@ -94,6 +122,12 @@ export function useForm(
|
|
|
94
122
|
}
|
|
95
123
|
}
|
|
96
124
|
|
|
125
|
+
function setError(field: string, message: string) {
|
|
126
|
+
// An input handler may validate its new value before the change effect runs.
|
|
127
|
+
dataSnapshot = { ...dataSnapshot, [field]: cloneFormValue(data[field]) };
|
|
128
|
+
errors = { ...errors, [field]: message };
|
|
129
|
+
}
|
|
130
|
+
|
|
97
131
|
function clearError() {
|
|
98
132
|
error = null;
|
|
99
133
|
}
|
|
@@ -120,7 +154,9 @@ export function useForm(
|
|
|
120
154
|
if (!result.ok) {
|
|
121
155
|
const validationErrors = getValidationErrors(result.error.body);
|
|
122
156
|
if (validationErrors) {
|
|
123
|
-
Object.
|
|
157
|
+
for (const [field, message] of Object.entries(validationErrors)) {
|
|
158
|
+
setError(field, message);
|
|
159
|
+
}
|
|
124
160
|
} else {
|
|
125
161
|
error = result.error;
|
|
126
162
|
}
|
|
@@ -129,7 +165,12 @@ export function useForm(
|
|
|
129
165
|
|
|
130
166
|
wasSuccessful = true;
|
|
131
167
|
recentlySuccessful = true;
|
|
132
|
-
|
|
168
|
+
if (options?.resetOnSuccess) {
|
|
169
|
+
data = cloneFormValue(initialDefaults);
|
|
170
|
+
defaults_ = cloneFormValue(initialDefaults);
|
|
171
|
+
} else {
|
|
172
|
+
defaults_ = cloneFormValue(data);
|
|
173
|
+
}
|
|
133
174
|
successTimeout = setTimeout(() => {
|
|
134
175
|
recentlySuccessful = false;
|
|
135
176
|
}, 2000);
|
|
@@ -166,6 +207,7 @@ export function useForm(
|
|
|
166
207
|
return recentlySuccessful;
|
|
167
208
|
},
|
|
168
209
|
reset,
|
|
210
|
+
setError,
|
|
169
211
|
clearErrors,
|
|
170
212
|
clearError,
|
|
171
213
|
post: (opts?: FormSubmitOptions) => submit('POST', opts),
|