@void/vue 0.0.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/README.md +113 -0
- package/dist/index-C0fE29Kv.d.mts +79 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2 -0
- package/dist/plugin.d.mts +11 -0
- package/dist/plugin.mjs +729 -0
- package/dist/runtime/index.d.mts +2 -0
- package/dist/runtime/index.mjs +2 -0
- package/dist/runtime/prefetch.d.mts +2 -0
- package/dist/runtime/prefetch.mjs +2 -0
- package/dist/runtime/use-deferred.d.mts +19 -0
- package/dist/runtime/use-deferred.mjs +21 -0
- package/dist/runtime-DL_B0L_H.mjs +462 -0
- package/package.json +54 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as useIslandForm, c as useShared, i as setActionRouter, l as useRouter, n as InferProps, o as useNavigation, r as action, s as useForm, t as Deferred, u as Link } from "../index-C0fE29Kv.mjs";
|
|
2
|
+
export { Deferred, InferProps, Link, action, setActionRouter, useForm, useIslandForm, useNavigation, useRouter, useShared };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as useForm, c as Link, i as useNavigation, n as setActionRouter, o as useShared, r as useIslandForm, s as useRouter, t as action } from "../runtime-DL_B0L_H.mjs";
|
|
2
|
+
export { Link, action, setActionRouter, useForm, useIslandForm, useNavigation, useRouter, useShared };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/runtime/use-deferred.d.ts
|
|
2
|
+
type DeferredRef<T = unknown> = {
|
|
3
|
+
value: null;
|
|
4
|
+
loading: true;
|
|
5
|
+
error: null;
|
|
6
|
+
} | {
|
|
7
|
+
value: T;
|
|
8
|
+
loading: false;
|
|
9
|
+
error: null;
|
|
10
|
+
} | {
|
|
11
|
+
value: null;
|
|
12
|
+
loading: false;
|
|
13
|
+
error: Error;
|
|
14
|
+
};
|
|
15
|
+
declare function createDeferred<T = unknown>(): DeferredRef<T>;
|
|
16
|
+
declare function resolveDeferred(ref: DeferredRef, data: unknown): void;
|
|
17
|
+
declare function rejectDeferred(ref: DeferredRef, error: string): void;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { DeferredRef, createDeferred, rejectDeferred, resolveDeferred };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { reactive } from "vue";
|
|
2
|
+
//#region src/runtime/use-deferred.ts
|
|
3
|
+
function createDeferred() {
|
|
4
|
+
return reactive({
|
|
5
|
+
value: null,
|
|
6
|
+
loading: true,
|
|
7
|
+
error: null
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function resolveDeferred(ref, data) {
|
|
11
|
+
const r = ref;
|
|
12
|
+
r.value = data;
|
|
13
|
+
r.loading = false;
|
|
14
|
+
}
|
|
15
|
+
function rejectDeferred(ref, error) {
|
|
16
|
+
const r = ref;
|
|
17
|
+
r.error = new Error(error);
|
|
18
|
+
r.loading = false;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { createDeferred, rejectDeferred, resolveDeferred };
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import { computed, defineComponent, h, inject, onMounted, onUnmounted, reactive, ref, watch } from "vue";
|
|
2
|
+
import { VoidActionError, categorizeActionError, idleNavigationState, isAbortError, isEqualFormValue, ssrProxy, submitAction } from "void/pages-client";
|
|
3
|
+
//#region src/runtime/link.ts
|
|
4
|
+
function appendQueryValue(params, key, value) {
|
|
5
|
+
if (value === null || value === void 0) return false;
|
|
6
|
+
if (value instanceof Date) {
|
|
7
|
+
params.append(key, value.toISOString());
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
let appended = false;
|
|
12
|
+
for (const item of value) appended = appendQueryValue(params, key, item) || appended;
|
|
13
|
+
return appended;
|
|
14
|
+
}
|
|
15
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
16
|
+
params.append(key, String(value));
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
throw new Error(`Link: GET data only supports primitive values and arrays. Remove nested data for '${key}'.`);
|
|
20
|
+
}
|
|
21
|
+
function mergeDataIntoHref(href, data) {
|
|
22
|
+
if (!data) return href;
|
|
23
|
+
const hashIndex = href.indexOf("#");
|
|
24
|
+
const beforeHash = hashIndex === -1 ? href : href.slice(0, hashIndex);
|
|
25
|
+
const hash = hashIndex === -1 ? "" : href.slice(hashIndex);
|
|
26
|
+
const searchIndex = beforeHash.indexOf("?");
|
|
27
|
+
const path = searchIndex === -1 ? beforeHash : beforeHash.slice(0, searchIndex);
|
|
28
|
+
const search = searchIndex === -1 ? "" : beforeHash.slice(searchIndex + 1);
|
|
29
|
+
const params = new URLSearchParams(search);
|
|
30
|
+
let appended = false;
|
|
31
|
+
for (const [key, value] of Object.entries(data)) appended = appendQueryValue(params, key, value) || appended;
|
|
32
|
+
if (!appended) return href;
|
|
33
|
+
const query = params.toString();
|
|
34
|
+
return `${path}${query ? `?${query}` : ""}${hash}`;
|
|
35
|
+
}
|
|
36
|
+
function hasPrefetch(prefetch) {
|
|
37
|
+
return prefetch !== false;
|
|
38
|
+
}
|
|
39
|
+
function isModifiedEvent(e) {
|
|
40
|
+
const target = e.currentTarget.getAttribute("target");
|
|
41
|
+
return target !== null && target !== "_self" || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.button !== 0;
|
|
42
|
+
}
|
|
43
|
+
function callHandler(handler, event) {
|
|
44
|
+
if (Array.isArray(handler)) {
|
|
45
|
+
for (const fn of handler) if (typeof fn === "function") fn(event);
|
|
46
|
+
} else if (typeof handler === "function") handler(event);
|
|
47
|
+
}
|
|
48
|
+
const Link = defineComponent({
|
|
49
|
+
name: "Link",
|
|
50
|
+
props: {
|
|
51
|
+
href: {
|
|
52
|
+
type: String,
|
|
53
|
+
required: true
|
|
54
|
+
},
|
|
55
|
+
method: {
|
|
56
|
+
type: String,
|
|
57
|
+
default: "GET"
|
|
58
|
+
},
|
|
59
|
+
data: {
|
|
60
|
+
type: Object,
|
|
61
|
+
default: void 0
|
|
62
|
+
},
|
|
63
|
+
preserveScroll: {
|
|
64
|
+
type: Boolean,
|
|
65
|
+
default: false
|
|
66
|
+
},
|
|
67
|
+
preserveState: {
|
|
68
|
+
type: Boolean,
|
|
69
|
+
default: false
|
|
70
|
+
},
|
|
71
|
+
replace: {
|
|
72
|
+
type: Boolean,
|
|
73
|
+
default: false
|
|
74
|
+
},
|
|
75
|
+
reloadDocument: {
|
|
76
|
+
type: Boolean,
|
|
77
|
+
default: false
|
|
78
|
+
},
|
|
79
|
+
viewTransition: {
|
|
80
|
+
type: Boolean,
|
|
81
|
+
default: void 0
|
|
82
|
+
},
|
|
83
|
+
onNavigate: {
|
|
84
|
+
type: Function,
|
|
85
|
+
default: void 0
|
|
86
|
+
},
|
|
87
|
+
prefetch: {
|
|
88
|
+
type: [
|
|
89
|
+
Boolean,
|
|
90
|
+
String,
|
|
91
|
+
Array
|
|
92
|
+
],
|
|
93
|
+
default: false
|
|
94
|
+
},
|
|
95
|
+
cacheFor: {
|
|
96
|
+
type: [
|
|
97
|
+
Number,
|
|
98
|
+
String,
|
|
99
|
+
Array
|
|
100
|
+
],
|
|
101
|
+
default: void 0
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
setup(props, { slots, attrs }) {
|
|
105
|
+
const router = inject("__void_router", null);
|
|
106
|
+
const elRef = ref(null);
|
|
107
|
+
const normalizedMethod = computed(() => props.method.toUpperCase());
|
|
108
|
+
const isGet = computed(() => normalizedMethod.value === "GET");
|
|
109
|
+
const hrefWithData = computed(() => isGet.value ? mergeDataIntoHref(props.href, props.data) : props.href);
|
|
110
|
+
const strategies = computed(() => {
|
|
111
|
+
if (props.prefetch === false || props.reloadDocument) return [];
|
|
112
|
+
if (props.prefetch === true) return ["hover"];
|
|
113
|
+
if (typeof props.prefetch === "string") return [props.prefetch];
|
|
114
|
+
return props.prefetch;
|
|
115
|
+
});
|
|
116
|
+
function doPrefetch() {
|
|
117
|
+
if (!router?.prefetch) return;
|
|
118
|
+
const current = window.location.pathname + window.location.search;
|
|
119
|
+
if (hrefWithData.value === current) return;
|
|
120
|
+
const options = {};
|
|
121
|
+
if (props.cacheFor !== void 0) options.cacheFor = props.cacheFor;
|
|
122
|
+
if (normalizedMethod.value) options.method = normalizedMethod.value;
|
|
123
|
+
router.prefetch(hrefWithData.value, options);
|
|
124
|
+
}
|
|
125
|
+
function onClick(e) {
|
|
126
|
+
callHandler(attrs.onClick, e);
|
|
127
|
+
if (props.reloadDocument || !router || e.defaultPrevented || isModifiedEvent(e)) return;
|
|
128
|
+
if (e.currentTarget.hasAttribute("download")) return;
|
|
129
|
+
if (new URL(hrefWithData.value, window.location.origin).origin !== window.location.origin) return;
|
|
130
|
+
let navigatePrevented = false;
|
|
131
|
+
props.onNavigate?.({ preventDefault() {
|
|
132
|
+
navigatePrevented = true;
|
|
133
|
+
} });
|
|
134
|
+
if (navigatePrevented) {
|
|
135
|
+
e.preventDefault();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
e.preventDefault();
|
|
139
|
+
router.visit(hrefWithData.value, {
|
|
140
|
+
method: normalizedMethod.value,
|
|
141
|
+
data: isGet.value ? void 0 : props.data,
|
|
142
|
+
preserveScroll: props.preserveScroll,
|
|
143
|
+
preserveState: props.preserveState,
|
|
144
|
+
replace: props.replace,
|
|
145
|
+
viewTransition: props.viewTransition
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
let hoverTimer = null;
|
|
149
|
+
let observer = null;
|
|
150
|
+
function onMouseEnter(e) {
|
|
151
|
+
callHandler(attrs.onMouseenter ?? attrs.onMouseEnter, e);
|
|
152
|
+
if (e.defaultPrevented) return;
|
|
153
|
+
hoverTimer = setTimeout(doPrefetch, router?._hoverDelay ?? 75);
|
|
154
|
+
}
|
|
155
|
+
function onMouseLeave(e) {
|
|
156
|
+
callHandler(attrs.onMouseleave ?? attrs.onMouseLeave, e);
|
|
157
|
+
if (hoverTimer) {
|
|
158
|
+
clearTimeout(hoverTimer);
|
|
159
|
+
hoverTimer = null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function onFocus(e) {
|
|
163
|
+
callHandler(attrs.onFocus, e);
|
|
164
|
+
if (!e.defaultPrevented) doPrefetch();
|
|
165
|
+
}
|
|
166
|
+
function onMouseDown(e) {
|
|
167
|
+
callHandler(attrs.onMousedown ?? attrs.onMouseDown, e);
|
|
168
|
+
if (!e.defaultPrevented) doPrefetch();
|
|
169
|
+
}
|
|
170
|
+
function onTouchStart(e) {
|
|
171
|
+
callHandler(attrs.onTouchstart ?? attrs.onTouchStart, e);
|
|
172
|
+
if (!e.defaultPrevented) doPrefetch();
|
|
173
|
+
}
|
|
174
|
+
onMounted(() => {
|
|
175
|
+
if (strategies.value.includes("mount")) doPrefetch();
|
|
176
|
+
if (strategies.value.includes("visible") && elRef.value) {
|
|
177
|
+
observer = new IntersectionObserver((entries) => {
|
|
178
|
+
if (entries.some((e) => e.isIntersecting)) {
|
|
179
|
+
doPrefetch();
|
|
180
|
+
observer?.disconnect();
|
|
181
|
+
observer = null;
|
|
182
|
+
}
|
|
183
|
+
}, { rootMargin: "0px" });
|
|
184
|
+
observer.observe(elRef.value);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
onUnmounted(() => {
|
|
188
|
+
if (hoverTimer) clearTimeout(hoverTimer);
|
|
189
|
+
if (observer) {
|
|
190
|
+
observer.disconnect();
|
|
191
|
+
observer = null;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
return () => {
|
|
195
|
+
if (hasPrefetch(props.prefetch) && !isGet.value) throw new Error("Link: prefetch only supports GET links. Remove `prefetch` or use method=\"GET\".");
|
|
196
|
+
if (props.reloadDocument && !isGet.value) throw new Error("Link: reloadDocument only supports GET links.");
|
|
197
|
+
const tag = isGet.value ? "a" : "button";
|
|
198
|
+
const eventHandlers = { onClick };
|
|
199
|
+
if (strategies.value.includes("hover")) {
|
|
200
|
+
eventHandlers.onMouseenter = onMouseEnter;
|
|
201
|
+
eventHandlers.onMouseleave = onMouseLeave;
|
|
202
|
+
eventHandlers.onFocus = onFocus;
|
|
203
|
+
eventHandlers.onTouchstart = onTouchStart;
|
|
204
|
+
}
|
|
205
|
+
if (strategies.value.includes("click")) {
|
|
206
|
+
eventHandlers.onMousedown = onMouseDown;
|
|
207
|
+
eventHandlers.onTouchstart = onTouchStart;
|
|
208
|
+
}
|
|
209
|
+
return h(tag, {
|
|
210
|
+
...attrs,
|
|
211
|
+
ref: elRef,
|
|
212
|
+
...isGet.value ? { href: hrefWithData.value } : { type: "button" },
|
|
213
|
+
style: [{ touchAction: "manipulation" }, attrs.style],
|
|
214
|
+
...eventHandlers
|
|
215
|
+
}, slots.default?.());
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region src/runtime/use-router.ts
|
|
221
|
+
function useRouter() {
|
|
222
|
+
return inject("__void_router", null) ?? ssrProxy;
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/runtime/use-shared.ts
|
|
226
|
+
function useShared() {
|
|
227
|
+
const shared = inject("__void_shared");
|
|
228
|
+
if (!shared) throw new Error("useShared(): must be used inside a Void page.");
|
|
229
|
+
return shared;
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/runtime/use-form.ts
|
|
233
|
+
function getValidationErrors$1(error) {
|
|
234
|
+
const body = error.body;
|
|
235
|
+
if (body && typeof body === "object" && "errors" in body && body.errors && typeof body.errors === "object" && !Array.isArray(body.errors)) return body.errors;
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
function useForm(url, defaults, options) {
|
|
239
|
+
let resolvedUrl = url;
|
|
240
|
+
const formDefaults = defaults;
|
|
241
|
+
let actionQuery = "";
|
|
242
|
+
const qIdx = resolvedUrl.indexOf("?");
|
|
243
|
+
if (qIdx !== -1) {
|
|
244
|
+
actionQuery = resolvedUrl.slice(qIdx);
|
|
245
|
+
resolvedUrl = resolvedUrl.slice(0, qIdx);
|
|
246
|
+
}
|
|
247
|
+
if (options?.params) for (const [key, value] of Object.entries(options.params)) resolvedUrl = resolvedUrl.replace(`:${key}`, encodeURIComponent(value));
|
|
248
|
+
resolvedUrl = resolvedUrl + actionQuery;
|
|
249
|
+
const router = inject("__void_router", null);
|
|
250
|
+
const data = reactive({ ...formDefaults });
|
|
251
|
+
const errors = reactive({});
|
|
252
|
+
const error = ref(null);
|
|
253
|
+
const pending = ref(false);
|
|
254
|
+
const wasSuccessful = ref(false);
|
|
255
|
+
const recentlySuccessful = ref(false);
|
|
256
|
+
let defaults_ = { ...formDefaults };
|
|
257
|
+
let successTimeout = null;
|
|
258
|
+
const hasChanges = ref(false);
|
|
259
|
+
watch(() => ({ ...data }), (val) => {
|
|
260
|
+
hasChanges.value = Object.keys(defaults_).some((key) => !isEqualFormValue(val[key], defaults_[key]));
|
|
261
|
+
}, { deep: true });
|
|
262
|
+
function reset(...fields) {
|
|
263
|
+
if (fields.length === 0) Object.assign(data, { ...defaults_ });
|
|
264
|
+
else for (const field of fields) data[field] = defaults_[field];
|
|
265
|
+
}
|
|
266
|
+
function clearErrors(...fields) {
|
|
267
|
+
if (fields.length === 0) for (const key of Object.keys(errors)) delete errors[key];
|
|
268
|
+
else for (const field of fields) delete errors[field];
|
|
269
|
+
}
|
|
270
|
+
function clearError() {
|
|
271
|
+
error.value = null;
|
|
272
|
+
}
|
|
273
|
+
async function submit(method, opts = {}) {
|
|
274
|
+
pending.value = true;
|
|
275
|
+
wasSuccessful.value = false;
|
|
276
|
+
recentlySuccessful.value = false;
|
|
277
|
+
if (successTimeout) clearTimeout(successTimeout);
|
|
278
|
+
clearErrors();
|
|
279
|
+
clearError();
|
|
280
|
+
try {
|
|
281
|
+
if (!router) throw new Error("useForm(): requires the Void Router.");
|
|
282
|
+
const result = await submitAction(router, resolvedUrl, {
|
|
283
|
+
method,
|
|
284
|
+
data: { ...data },
|
|
285
|
+
...opts
|
|
286
|
+
});
|
|
287
|
+
if (!result.ok) {
|
|
288
|
+
const validationErrors = getValidationErrors$1(result.error);
|
|
289
|
+
if (validationErrors) Object.assign(errors, validationErrors);
|
|
290
|
+
else error.value = result.error;
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
wasSuccessful.value = true;
|
|
294
|
+
recentlySuccessful.value = true;
|
|
295
|
+
defaults_ = { ...data };
|
|
296
|
+
successTimeout = setTimeout(() => {
|
|
297
|
+
recentlySuccessful.value = false;
|
|
298
|
+
}, 2e3);
|
|
299
|
+
} catch (submitError) {
|
|
300
|
+
if (isAbortError(submitError)) return;
|
|
301
|
+
throw submitError;
|
|
302
|
+
} finally {
|
|
303
|
+
pending.value = false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return reactive({
|
|
307
|
+
data,
|
|
308
|
+
errors,
|
|
309
|
+
error,
|
|
310
|
+
pending,
|
|
311
|
+
hasChanges,
|
|
312
|
+
wasSuccessful,
|
|
313
|
+
recentlySuccessful,
|
|
314
|
+
reset,
|
|
315
|
+
clearErrors,
|
|
316
|
+
clearError,
|
|
317
|
+
post: (opts) => submit("POST", opts),
|
|
318
|
+
put: (opts) => submit("PUT", opts),
|
|
319
|
+
patch: (opts) => submit("PATCH", opts),
|
|
320
|
+
delete: (opts) => submit("DELETE", opts)
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region src/runtime/use-navigation.ts
|
|
325
|
+
function useNavigation() {
|
|
326
|
+
return inject("__void_navigation", null) ?? idleNavigationState();
|
|
327
|
+
}
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region src/runtime/use-island-form.ts
|
|
330
|
+
function getValidationErrors(body) {
|
|
331
|
+
if (body && typeof body === "object" && "errors" in body && body.errors && typeof body.errors === "object" && !Array.isArray(body.errors)) return body.errors;
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
async function readActionErrorBody(response) {
|
|
335
|
+
const contentType = response.headers.get("content-type") || "";
|
|
336
|
+
try {
|
|
337
|
+
if (contentType.includes("application/json")) return await response.json();
|
|
338
|
+
return await response.text() || null;
|
|
339
|
+
} catch {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Form composable for island pages. Uses fetch + page reload instead of the
|
|
345
|
+
* Void Router (which is not available in island mode).
|
|
346
|
+
*/
|
|
347
|
+
function useIslandForm(defaults) {
|
|
348
|
+
const data = reactive({ ...defaults });
|
|
349
|
+
const errors = reactive({});
|
|
350
|
+
const error = ref(null);
|
|
351
|
+
const pending = ref(false);
|
|
352
|
+
const wasSuccessful = ref(false);
|
|
353
|
+
const recentlySuccessful = ref(false);
|
|
354
|
+
let defaults_ = { ...defaults };
|
|
355
|
+
let successTimeout = null;
|
|
356
|
+
const hasChanges = ref(false);
|
|
357
|
+
watch(() => ({ ...data }), (val) => {
|
|
358
|
+
hasChanges.value = Object.keys(defaults_).some((key) => !isEqualFormValue(val[key], defaults_[key]));
|
|
359
|
+
}, { deep: true });
|
|
360
|
+
function reset(...fields) {
|
|
361
|
+
if (fields.length === 0) Object.assign(data, { ...defaults_ });
|
|
362
|
+
else for (const field of fields) data[field] = defaults_[field];
|
|
363
|
+
}
|
|
364
|
+
function clearErrors(...fields) {
|
|
365
|
+
if (fields.length === 0) for (const key of Object.keys(errors)) delete errors[key];
|
|
366
|
+
else for (const field of fields) delete errors[field];
|
|
367
|
+
}
|
|
368
|
+
function clearError() {
|
|
369
|
+
error.value = null;
|
|
370
|
+
}
|
|
371
|
+
async function submit(url, method) {
|
|
372
|
+
pending.value = true;
|
|
373
|
+
wasSuccessful.value = false;
|
|
374
|
+
recentlySuccessful.value = false;
|
|
375
|
+
if (successTimeout) clearTimeout(successTimeout);
|
|
376
|
+
clearErrors();
|
|
377
|
+
clearError();
|
|
378
|
+
try {
|
|
379
|
+
const response = await fetch(url, {
|
|
380
|
+
method,
|
|
381
|
+
headers: { "Content-Type": "application/json" },
|
|
382
|
+
body: JSON.stringify(data)
|
|
383
|
+
});
|
|
384
|
+
if (response.redirected) {
|
|
385
|
+
window.location.href = response.url;
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (response.ok) {
|
|
389
|
+
wasSuccessful.value = true;
|
|
390
|
+
recentlySuccessful.value = true;
|
|
391
|
+
defaults_ = { ...data };
|
|
392
|
+
successTimeout = setTimeout(() => {
|
|
393
|
+
recentlySuccessful.value = false;
|
|
394
|
+
}, 2e3);
|
|
395
|
+
window.location.reload();
|
|
396
|
+
} else if (!response.ok) {
|
|
397
|
+
const body = await readActionErrorBody(response);
|
|
398
|
+
const actionError = new VoidActionError({
|
|
399
|
+
body,
|
|
400
|
+
status: response.status,
|
|
401
|
+
statusText: response.statusText,
|
|
402
|
+
url
|
|
403
|
+
});
|
|
404
|
+
const validationErrors = response.status === 422 ? getValidationErrors(body) : null;
|
|
405
|
+
if (validationErrors) {
|
|
406
|
+
Object.assign(errors, validationErrors);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (categorizeActionError(response.status) === "boundary") throw actionError;
|
|
410
|
+
error.value = actionError;
|
|
411
|
+
}
|
|
412
|
+
} finally {
|
|
413
|
+
pending.value = false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return reactive({
|
|
417
|
+
data,
|
|
418
|
+
errors,
|
|
419
|
+
error,
|
|
420
|
+
pending,
|
|
421
|
+
hasChanges,
|
|
422
|
+
wasSuccessful,
|
|
423
|
+
recentlySuccessful,
|
|
424
|
+
submit,
|
|
425
|
+
reset,
|
|
426
|
+
clearErrors,
|
|
427
|
+
clearError,
|
|
428
|
+
post: (url) => submit(url, "POST"),
|
|
429
|
+
put: (url) => submit(url, "PUT"),
|
|
430
|
+
patch: (url) => submit(url, "PATCH"),
|
|
431
|
+
delete: (url) => submit(url, "DELETE")
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
//#endregion
|
|
435
|
+
//#region src/runtime/action.ts
|
|
436
|
+
let _router = null;
|
|
437
|
+
/** Called during app initialization to store the router instance */
|
|
438
|
+
function setActionRouter(router) {
|
|
439
|
+
_router = router;
|
|
440
|
+
}
|
|
441
|
+
/** Programmatic one-shot page action call with Inertia page update */
|
|
442
|
+
async function action(url, ...args) {
|
|
443
|
+
if (!_router) throw new Error("action(): called before router initialization.");
|
|
444
|
+
const options = args[0];
|
|
445
|
+
const data = options?.data;
|
|
446
|
+
const method = options?.method || "POST";
|
|
447
|
+
let resolvedUrl = url;
|
|
448
|
+
let actionQuery = "";
|
|
449
|
+
const qIdx = resolvedUrl.indexOf("?");
|
|
450
|
+
if (qIdx !== -1) {
|
|
451
|
+
actionQuery = resolvedUrl.slice(qIdx);
|
|
452
|
+
resolvedUrl = resolvedUrl.slice(0, qIdx);
|
|
453
|
+
}
|
|
454
|
+
if (options?.params) for (const [key, value] of Object.entries(options.params)) resolvedUrl = resolvedUrl.replace(`:${key}`, encodeURIComponent(value));
|
|
455
|
+
resolvedUrl = resolvedUrl + actionQuery;
|
|
456
|
+
return submitAction(_router, resolvedUrl, {
|
|
457
|
+
method,
|
|
458
|
+
data
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
//#endregion
|
|
462
|
+
export { useForm as a, Link as c, useNavigation as i, setActionRouter as n, useShared as o, useIslandForm as r, useRouter as s, action as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@void/vue",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.mts",
|
|
11
|
+
"import": "./dist/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"./plugin": {
|
|
14
|
+
"types": "./dist/plugin.d.mts",
|
|
15
|
+
"import": "./dist/plugin.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./runtime": {
|
|
18
|
+
"types": "./dist/runtime/index.d.mts",
|
|
19
|
+
"import": "./dist/runtime/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./deferred": {
|
|
22
|
+
"types": "./dist/runtime/use-deferred.d.mts",
|
|
23
|
+
"import": "./dist/runtime/use-deferred.mjs"
|
|
24
|
+
},
|
|
25
|
+
"./prefetch": {
|
|
26
|
+
"types": "./dist/runtime/prefetch.d.mts",
|
|
27
|
+
"import": "./dist/runtime/prefetch.mjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsdown",
|
|
35
|
+
"dev": "tsdown --watch",
|
|
36
|
+
"typecheck": "tsgo --noEmit"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@vitejs/plugin-vue": "^6.0.6"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "catalog:",
|
|
43
|
+
"pathe": "catalog:",
|
|
44
|
+
"tsdown": "catalog:",
|
|
45
|
+
"vite": "catalog:",
|
|
46
|
+
"void": "workspace:*",
|
|
47
|
+
"vue": "^3.5.33"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"vite": "catalog:",
|
|
51
|
+
"void": "workspace:*",
|
|
52
|
+
"vue": "^3.5.0"
|
|
53
|
+
}
|
|
54
|
+
}
|