@formbox/htmx 0.4.0 → 0.4.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/README.md +68 -15
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,10 +9,10 @@ React, hydrate React, or manage MobX.
|
|
|
9
9
|
The main API is built around a short-lived renderer instance:
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
|
-
import { QuestionnaireRenderer,
|
|
12
|
+
import { QuestionnaireRenderer, loadDefaultTemplates } from "@formbox/htmx";
|
|
13
13
|
|
|
14
14
|
const route = "/questionnaire";
|
|
15
|
-
const templates = await
|
|
15
|
+
const templates = await loadDefaultTemplates();
|
|
16
16
|
const renderer = new QuestionnaireRenderer({
|
|
17
17
|
token: "encounter-questionnaire",
|
|
18
18
|
templates,
|
|
@@ -55,9 +55,9 @@ field encoding and parses the submitted payload in `renderer.process()`.
|
|
|
55
55
|
## Basic Integration
|
|
56
56
|
|
|
57
57
|
```ts
|
|
58
|
-
import { QuestionnaireRenderer,
|
|
58
|
+
import { QuestionnaireRenderer, loadDefaultTemplates } from "@formbox/htmx";
|
|
59
59
|
|
|
60
|
-
const templates = await
|
|
60
|
+
const templates = await loadDefaultTemplates();
|
|
61
61
|
|
|
62
62
|
async function renderQuestionnaire(request: Request): Promise<Response> {
|
|
63
63
|
const route = "/questionnaire";
|
|
@@ -90,6 +90,59 @@ async function renderQuestionnaire(request: Request): Promise<Response> {
|
|
|
90
90
|
}
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
## HTMX Integration
|
|
94
|
+
|
|
95
|
+
The renderer does not own your page shell. If a POST should update more than
|
|
96
|
+
the form itself, such as a status label or a rendered `QuestionnaireResponse`
|
|
97
|
+
preview beside the form, target an application-owned wrapper and return that
|
|
98
|
+
same wrapper for HTMX requests:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import {
|
|
102
|
+
compileTemplates,
|
|
103
|
+
loadDefaultTemplates,
|
|
104
|
+
loadTemplates,
|
|
105
|
+
} from "@formbox/htmx";
|
|
106
|
+
|
|
107
|
+
const templates = {
|
|
108
|
+
...(await loadDefaultTemplates()),
|
|
109
|
+
...(await loadTemplates("./questionnaire-templates")),
|
|
110
|
+
...compileTemplates({
|
|
111
|
+
Form: `
|
|
112
|
+
<form{{{attrs attributes}}} hx-target="#questionnaire-app">
|
|
113
|
+
{{{fields}}}
|
|
114
|
+
</form>
|
|
115
|
+
`,
|
|
116
|
+
}),
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
async function handler(request: Request): Promise<Response> {
|
|
120
|
+
const rendered = await renderQuestionnaire(request);
|
|
121
|
+
const body =
|
|
122
|
+
request.headers.get("hx-request") === "true"
|
|
123
|
+
? questionnaireApp(rendered)
|
|
124
|
+
: layout(questionnaireApp(rendered));
|
|
125
|
+
|
|
126
|
+
return html(body);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function questionnaireApp(rendered: {
|
|
130
|
+
form: string;
|
|
131
|
+
response: unknown;
|
|
132
|
+
}): string {
|
|
133
|
+
return `
|
|
134
|
+
<div id="questionnaire-app">
|
|
135
|
+
${rendered.form}
|
|
136
|
+
<pre>${JSON.stringify(rendered.response, null, 2)}</pre>
|
|
137
|
+
</div>
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Use the default form swap when the form is the only dynamic region. Use an
|
|
143
|
+
application wrapper when the result of `renderer.process(formData)` affects
|
|
144
|
+
nearby UI outside the form.
|
|
145
|
+
|
|
93
146
|
## Lifecycle
|
|
94
147
|
|
|
95
148
|
Create one renderer instance for one request/render cycle:
|
|
@@ -129,8 +182,8 @@ const renderer = new QuestionnaireRenderer({
|
|
|
129
182
|
|
|
130
183
|
`questionnaireResponse` is optional initial state.
|
|
131
184
|
`token` is required and must be unique for each rendered form on the same page.
|
|
132
|
-
`templates` is required; call `await
|
|
133
|
-
|
|
185
|
+
`templates` is required; call `await loadDefaultTemplates()` for the package
|
|
186
|
+
defaults, or merge those templates with application overrides.
|
|
134
187
|
|
|
135
188
|
### `renderer.process(formData)`
|
|
136
189
|
|
|
@@ -182,10 +235,10 @@ Use a custom `Form` template when the application needs to add shell markup or
|
|
|
182
235
|
adjust attributes:
|
|
183
236
|
|
|
184
237
|
```ts
|
|
185
|
-
import { compileTemplates,
|
|
238
|
+
import { compileTemplates, loadDefaultTemplates } from "@formbox/htmx";
|
|
186
239
|
|
|
187
240
|
const templates = {
|
|
188
|
-
...(await
|
|
241
|
+
...(await loadDefaultTemplates()),
|
|
189
242
|
...compileTemplates({
|
|
190
243
|
Form: `
|
|
191
244
|
<form{{{attrs attributes}}}>
|
|
@@ -245,22 +298,22 @@ const templates = compileTemplates({
|
|
|
245
298
|
});
|
|
246
299
|
```
|
|
247
300
|
|
|
248
|
-
Load the
|
|
301
|
+
Load the default templates explicitly:
|
|
249
302
|
|
|
250
303
|
```ts
|
|
251
|
-
import {
|
|
304
|
+
import { loadDefaultTemplates } from "@formbox/htmx";
|
|
252
305
|
|
|
253
|
-
const templates = await
|
|
306
|
+
const templates = await loadDefaultTemplates();
|
|
254
307
|
```
|
|
255
308
|
|
|
256
309
|
If you prefer overrides as files, load a directory of `*.html.hbs` files and
|
|
257
|
-
merge the result with the
|
|
310
|
+
merge the result with the default templates:
|
|
258
311
|
|
|
259
312
|
```ts
|
|
260
|
-
import {
|
|
313
|
+
import { loadDefaultTemplates, loadTemplates } from "@formbox/htmx";
|
|
261
314
|
|
|
262
315
|
const templates = {
|
|
263
|
-
...(await
|
|
316
|
+
...(await loadDefaultTemplates()),
|
|
264
317
|
...(await loadTemplates("./questionnaire-templates")),
|
|
265
318
|
};
|
|
266
319
|
```
|
|
@@ -285,7 +338,7 @@ Available Handlebars helpers:
|
|
|
285
338
|
Use triple braces for renderer-provided HTML slots such as `fields`, `children`,
|
|
286
339
|
`label`, `errors`, and `customOptionForm`.
|
|
287
340
|
|
|
288
|
-
|
|
341
|
+
Default templates and user templates use the same data shape. The package does
|
|
289
342
|
not keep a separate JSX fallback path.
|
|
290
343
|
|
|
291
344
|
### `renderer.getQuestionnaireResponse()`
|
package/dist/index.d.ts
CHANGED
|
@@ -298,7 +298,9 @@ export declare type LinkTemplateProperties = Omit<TemplateBase<LinkProperties>,
|
|
|
298
298
|
readonly children: string;
|
|
299
299
|
};
|
|
300
300
|
|
|
301
|
-
export declare function
|
|
301
|
+
export declare function loadDefaultTemplates(): Promise<RequiredTemplates>;
|
|
302
|
+
|
|
303
|
+
export declare const loadNativeTemplates: typeof loadDefaultTemplates;
|
|
302
304
|
|
|
303
305
|
export declare function loadTemplates(directory: string | URL): Promise<Templates>;
|
|
304
306
|
|
package/dist/index.js
CHANGED
|
@@ -59781,7 +59781,7 @@ function compileTemplates(sources) {
|
|
|
59781
59781
|
})
|
|
59782
59782
|
);
|
|
59783
59783
|
}
|
|
59784
|
-
async function
|
|
59784
|
+
async function loadDefaultTemplates() {
|
|
59785
59785
|
const templates = await loadTemplates(
|
|
59786
59786
|
new URL(
|
|
59787
59787
|
/* @vite-ignore */
|
|
@@ -59791,10 +59791,11 @@ async function loadNativeTemplates() {
|
|
|
59791
59791
|
);
|
|
59792
59792
|
const missing = templateNames.filter((name) => templates[name] === void 0);
|
|
59793
59793
|
if (missing.length > 0) {
|
|
59794
|
-
throw new Error(`Missing
|
|
59794
|
+
throw new Error(`Missing default template files: ${missing.join(", ")}`);
|
|
59795
59795
|
}
|
|
59796
59796
|
return templates;
|
|
59797
59797
|
}
|
|
59798
|
+
const loadNativeTemplates = loadDefaultTemplates;
|
|
59798
59799
|
async function loadTemplates(directory) {
|
|
59799
59800
|
const sources = {};
|
|
59800
59801
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
@@ -62382,6 +62383,7 @@ export {
|
|
|
62382
62383
|
compileTemplate,
|
|
62383
62384
|
compileTemplates,
|
|
62384
62385
|
htmlAttributes,
|
|
62386
|
+
loadDefaultTemplates,
|
|
62385
62387
|
loadNativeTemplates,
|
|
62386
62388
|
loadTemplates
|
|
62387
62389
|
};
|