@hyperspan/framework 0.3.4 → 0.3.5
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/package.json +1 -1
- package/src/actions.ts +23 -15
- package/src/clientjs/hyperspan-client.ts +24 -15
package/package.json
CHANGED
package/src/actions.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { html, HSHtml } from '@hyperspan/html';
|
|
2
2
|
import * as z from 'zod/v4';
|
|
3
3
|
import { HTTPException } from 'hono/http-exception';
|
|
4
|
-
|
|
4
|
+
import { assetHash } from './assets';
|
|
5
5
|
import { IS_PROD, returnHTMLResponse, type THSResponseTypes } from './server';
|
|
6
6
|
import type { Context, MiddlewareHandler } from 'hono';
|
|
7
7
|
import type { HandlerResponse, Next, TypedResponse } from 'hono/types';
|
|
8
|
-
import { assetHash } from './assets';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Actions = Form + route handler
|
|
@@ -20,13 +19,19 @@ import { assetHash } from './assets';
|
|
|
20
19
|
* 5. Replaces form content in place with HTML response content from server via the Idiomorph library
|
|
21
20
|
* 6. Handles any Exception thrown on server as error displayed back to user on the page
|
|
22
21
|
*/
|
|
23
|
-
type TActionResponse =
|
|
22
|
+
export type TActionResponse =
|
|
23
|
+
| THSResponseTypes
|
|
24
|
+
| HandlerResponse<any>
|
|
25
|
+
| TypedResponse<any, any, any>;
|
|
24
26
|
export interface HSAction<T extends z.ZodTypeAny> {
|
|
25
27
|
_kind: string;
|
|
26
28
|
_route: string;
|
|
27
29
|
_form: Parameters<HSAction<T>['form']>[0];
|
|
28
30
|
form(
|
|
29
|
-
renderForm: (
|
|
31
|
+
renderForm: (
|
|
32
|
+
c: Context<any, any, {}>,
|
|
33
|
+
{ data, error }: { data?: z.infer<T>; error?: z.ZodError | Error }
|
|
34
|
+
) => HSHtml | void | null | Promise<HSHtml | void | null>
|
|
30
35
|
): HSAction<T>;
|
|
31
36
|
post(
|
|
32
37
|
handler: (
|
|
@@ -40,12 +45,17 @@ export interface HSAction<T extends z.ZodTypeAny> {
|
|
|
40
45
|
{ data, error }: { data?: z.infer<T>; error?: z.ZodError | Error }
|
|
41
46
|
) => TActionResponse
|
|
42
47
|
): HSAction<T>;
|
|
43
|
-
render(
|
|
48
|
+
render(
|
|
49
|
+
c: Context<any, any, {}>,
|
|
50
|
+
props?: { data?: z.infer<T>; error?: z.ZodError | Error }
|
|
51
|
+
): TActionResponse;
|
|
44
52
|
run(c: Context<any, any, {}>): TActionResponse | Promise<TActionResponse>;
|
|
45
53
|
middleware: (
|
|
46
54
|
middleware: Array<
|
|
47
55
|
| MiddlewareHandler
|
|
48
|
-
| ((
|
|
56
|
+
| ((
|
|
57
|
+
context: Context<any, string, {}>
|
|
58
|
+
) => TActionResponse | Promise<TActionResponse> | void | Promise<void>)
|
|
49
59
|
>
|
|
50
60
|
) => HSAction<T>;
|
|
51
61
|
_getRouteHandlers: () => Array<
|
|
@@ -103,8 +113,11 @@ export function unstable__createAction<T extends z.ZodTypeAny>(
|
|
|
103
113
|
/**
|
|
104
114
|
* Get form renderer method
|
|
105
115
|
*/
|
|
106
|
-
render(
|
|
107
|
-
|
|
116
|
+
render(
|
|
117
|
+
c: Context<any, any, {}>,
|
|
118
|
+
formState?: { data?: z.infer<T>; error?: z.ZodError | Error }
|
|
119
|
+
) {
|
|
120
|
+
const form = _form ? _form(c, formState || {}) : null;
|
|
108
121
|
return form ? html`<hs-action url="${this._route}">${form}</hs-action>` : null;
|
|
109
122
|
},
|
|
110
123
|
|
|
@@ -136,7 +149,7 @@ export function unstable__createAction<T extends z.ZodTypeAny>(
|
|
|
136
149
|
const method = c.req.method;
|
|
137
150
|
|
|
138
151
|
if (method === 'GET') {
|
|
139
|
-
return await api.render();
|
|
152
|
+
return await api.render(c);
|
|
140
153
|
}
|
|
141
154
|
|
|
142
155
|
if (method !== 'POST') {
|
|
@@ -171,18 +184,13 @@ export function unstable__createAction<T extends z.ZodTypeAny>(
|
|
|
171
184
|
});
|
|
172
185
|
}
|
|
173
186
|
|
|
174
|
-
return await returnHTMLResponse(c, () => api.render({ data, error }), { status: 400 });
|
|
187
|
+
return await returnHTMLResponse(c, () => api.render(c, { data, error }), { status: 400 });
|
|
175
188
|
},
|
|
176
189
|
};
|
|
177
190
|
|
|
178
191
|
return api;
|
|
179
192
|
}
|
|
180
193
|
|
|
181
|
-
/**
|
|
182
|
-
* Form route handler helper
|
|
183
|
-
*/
|
|
184
|
-
export type THSHandlerResponse = (context: Context) => THSResponseTypes | Promise<THSResponseTypes>;
|
|
185
|
-
|
|
186
194
|
/**
|
|
187
195
|
* Return JSON data structure for a given FormData object
|
|
188
196
|
* Accounts for array fields (e.g. name="options[]" or <select multiple>)
|
|
@@ -73,24 +73,33 @@ class HSAction extends HTMLElement {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
connectedCallback() {
|
|
76
|
-
|
|
77
|
-
setTimeout(() => {
|
|
78
|
-
const form = this.querySelector('form');
|
|
79
|
-
|
|
80
|
-
if (form) {
|
|
81
|
-
form.setAttribute('action', this.getAttribute('url') || '');
|
|
82
|
-
const submitHandler = (e: Event) => {
|
|
83
|
-
formSubmitToRoute(e, form as HTMLFormElement, {
|
|
84
|
-
afterResponse: () => this.connectedCallback(),
|
|
85
|
-
});
|
|
86
|
-
form.removeEventListener('submit', submitHandler);
|
|
87
|
-
};
|
|
88
|
-
form.addEventListener('submit', submitHandler);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
76
|
+
actionFormObserver.observe(this, { childList: true, subtree: true });
|
|
91
77
|
}
|
|
92
78
|
}
|
|
93
79
|
window.customElements.define('hs-action', HSAction);
|
|
80
|
+
const actionFormObserver = new MutationObserver((list) => {
|
|
81
|
+
list.forEach((mutation) => {
|
|
82
|
+
mutation.addedNodes.forEach((node) => {
|
|
83
|
+
if (node instanceof HTMLFormElement) {
|
|
84
|
+
bindHSActionForm(node.closest('hs-action') as HSAction, node);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Bind the form inside an hs-action element to the action URL and submit handler
|
|
92
|
+
*/
|
|
93
|
+
function bindHSActionForm(hsActionElement: HSAction, form: HTMLFormElement) {
|
|
94
|
+
form.setAttribute('action', hsActionElement.getAttribute('url') || '');
|
|
95
|
+
const submitHandler = (e: Event) => {
|
|
96
|
+
formSubmitToRoute(e, form as HTMLFormElement, {
|
|
97
|
+
afterResponse: () => bindHSActionForm(hsActionElement, form),
|
|
98
|
+
});
|
|
99
|
+
form.removeEventListener('submit', submitHandler);
|
|
100
|
+
};
|
|
101
|
+
form.addEventListener('submit', submitHandler);
|
|
102
|
+
}
|
|
94
103
|
|
|
95
104
|
/**
|
|
96
105
|
* Submit form data to route and replace contents with response
|