@assistant-ui/react-generative-ui 0.0.6 → 0.0.8
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/JSONGenerativeUI.client.d.ts +4 -2
- package/dist/JSONGenerativeUI.client.d.ts.map +1 -1
- package/dist/JSONGenerativeUI.client.js +9 -2
- package/dist/JSONGenerativeUI.client.js.map +1 -1
- package/dist/JSONGenerativeUI.server.d.ts +0 -1
- package/dist/JSONGenerativeUI.server.d.ts.map +1 -1
- package/dist/JSONGenerativeUI.shared.d.ts +8 -1
- package/dist/JSONGenerativeUI.shared.d.ts.map +1 -1
- package/dist/JSONGenerativeUI.shared.js.map +1 -1
- package/dist/actionRegistry.d.ts +32 -0
- package/dist/actionRegistry.d.ts.map +1 -0
- package/dist/actionRegistry.js +41 -0
- package/dist/actionRegistry.js.map +1 -0
- package/dist/buildPresentParameters.d.ts +5 -5
- package/dist/buildPresentParameters.d.ts.map +1 -1
- package/dist/buildPresentParameters.js +20 -6
- package/dist/buildPresentParameters.js.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/defineGenerativeComponents.d.ts +0 -1
- package/dist/defineGenerativeComponents.d.ts.map +1 -1
- package/dist/generativeUIToJSX.d.ts +15 -10
- package/dist/generativeUIToJSX.d.ts.map +1 -1
- package/dist/generativeUIToJSX.js +47 -17
- package/dist/generativeUIToJSX.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.js +5 -2
- package/dist/index.server.d.ts +13 -0
- package/dist/index.server.js +10 -0
- package/dist/index.shared.d.ts +11 -0
- package/dist/index.shared.js +9 -0
- package/dist/ir.d.ts +115 -0
- package/dist/ir.d.ts.map +1 -0
- package/dist/ir.js +175 -0
- package/dist/ir.js.map +1 -0
- package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts +3 -3
- package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts.map +1 -1
- package/dist/renderGenerativeUI.d.ts +5 -5
- package/dist/renderGenerativeUI.d.ts.map +1 -1
- package/dist/renderGenerativeUI.js +18 -45
- package/dist/renderGenerativeUI.js.map +1 -1
- package/dist/slack/constants.d.ts +111 -0
- package/dist/slack/constants.d.ts.map +1 -0
- package/dist/slack/constants.js +168 -0
- package/dist/slack/constants.js.map +1 -0
- package/dist/slack/decodeBlockAction.d.ts +11 -0
- package/dist/slack/decodeBlockAction.d.ts.map +1 -0
- package/dist/slack/decodeBlockAction.js +40 -0
- package/dist/slack/decodeBlockAction.js.map +1 -0
- package/dist/slack/fromSlackBlocks.d.ts +30 -0
- package/dist/slack/fromSlackBlocks.d.ts.map +1 -0
- package/dist/slack/fromSlackBlocks.js +447 -0
- package/dist/slack/fromSlackBlocks.js.map +1 -0
- package/dist/slack/toSlackBlocks.d.ts +7 -0
- package/dist/slack/toSlackBlocks.d.ts.map +1 -0
- package/dist/slack/toSlackBlocks.js +649 -0
- package/dist/slack/toSlackBlocks.js.map +1 -0
- package/dist/slack/types.d.ts +178 -0
- package/dist/slack/types.d.ts.map +1 -0
- package/dist/slack/types.js +0 -0
- package/dist/slack.d.ts +5 -0
- package/dist/slack.js +4 -0
- package/dist/teams/boundSpec.d.ts +20 -0
- package/dist/teams/boundSpec.d.ts.map +1 -0
- package/dist/teams/boundSpec.js +67 -0
- package/dist/teams/boundSpec.js.map +1 -0
- package/dist/teams/constants.d.ts +57 -0
- package/dist/teams/constants.d.ts.map +1 -0
- package/dist/teams/constants.js +92 -0
- package/dist/teams/constants.js.map +1 -0
- package/dist/teams/decodeSubmitData.d.ts +25 -0
- package/dist/teams/decodeSubmitData.d.ts.map +1 -0
- package/dist/teams/decodeSubmitData.js +44 -0
- package/dist/teams/decodeSubmitData.js.map +1 -0
- package/dist/teams/toAdaptiveCard.d.ts +39 -0
- package/dist/teams/toAdaptiveCard.d.ts.map +1 -0
- package/dist/teams/toAdaptiveCard.js +496 -0
- package/dist/teams/toAdaptiveCard.js.map +1 -0
- package/dist/teams/toTeamsAttachments.d.ts +13 -0
- package/dist/teams/toTeamsAttachments.d.ts.map +1 -0
- package/dist/teams/toTeamsAttachments.js +95 -0
- package/dist/teams/toTeamsAttachments.js.map +1 -0
- package/dist/teams/types.d.ts +196 -0
- package/dist/teams/types.d.ts.map +1 -0
- package/dist/teams/types.js +0 -0
- package/dist/teams.d.ts +5 -0
- package/dist/teams.js +4 -0
- package/dist/types.d.ts +41 -34
- package/dist/types.d.ts.map +1 -1
- package/dist/vocabulary/alert.d.ts +28 -0
- package/dist/vocabulary/alert.d.ts.map +1 -0
- package/dist/vocabulary/alert.js +58 -0
- package/dist/vocabulary/alert.js.map +1 -0
- package/dist/vocabulary/collectFormValues.d.ts +23 -0
- package/dist/vocabulary/collectFormValues.d.ts.map +1 -0
- package/dist/vocabulary/collectFormValues.js +32 -0
- package/dist/vocabulary/collectFormValues.js.map +1 -0
- package/dist/vocabulary/data.d.ts +52 -0
- package/dist/vocabulary/data.d.ts.map +1 -0
- package/dist/vocabulary/data.js +294 -0
- package/dist/vocabulary/data.js.map +1 -0
- package/dist/vocabulary/dispatch.d.ts +11 -0
- package/dist/vocabulary/dispatch.d.ts.map +1 -0
- package/dist/vocabulary/dispatch.js +21 -0
- package/dist/vocabulary/dispatch.js.map +1 -0
- package/dist/vocabulary/fact.d.ts +15 -0
- package/dist/vocabulary/fact.d.ts.map +1 -0
- package/dist/vocabulary/fact.js +24 -0
- package/dist/vocabulary/fact.js.map +1 -0
- package/dist/vocabulary/form.d.ts +14 -0
- package/dist/vocabulary/form.d.ts.map +1 -0
- package/dist/vocabulary/form.js +23 -0
- package/dist/vocabulary/form.js.map +1 -0
- package/dist/vocabulary/icon.d.ts +44 -0
- package/dist/vocabulary/icon.d.ts.map +1 -0
- package/dist/vocabulary/icon.js +272 -0
- package/dist/vocabulary/icon.js.map +1 -0
- package/dist/vocabulary/index.d.ts +15 -0
- package/dist/vocabulary/index.d.ts.map +1 -0
- package/dist/vocabulary/index.js +36 -0
- package/dist/vocabulary/index.js.map +1 -0
- package/dist/vocabulary/interactive.d.ts +93 -0
- package/dist/vocabulary/interactive.d.ts.map +1 -0
- package/dist/vocabulary/interactive.js +172 -0
- package/dist/vocabulary/interactive.js.map +1 -0
- package/dist/vocabulary/layout.d.ts +82 -0
- package/dist/vocabulary/layout.d.ts.map +1 -0
- package/dist/vocabulary/layout.js +136 -0
- package/dist/vocabulary/layout.js.map +1 -0
- package/dist/vocabulary/list.d.ts +17 -0
- package/dist/vocabulary/list.d.ts.map +1 -0
- package/dist/vocabulary/list.js +48 -0
- package/dist/vocabulary/list.js.map +1 -0
- package/dist/vocabulary/media.d.ts +28 -0
- package/dist/vocabulary/media.d.ts.map +1 -0
- package/dist/vocabulary/media.js +42 -0
- package/dist/vocabulary/media.js.map +1 -0
- package/dist/vocabulary/text.d.ts +60 -0
- package/dist/vocabulary/text.d.ts.map +1 -0
- package/dist/vocabulary/text.js +48 -0
- package/dist/vocabulary/text.js.map +1 -0
- package/package.json +21 -13
- package/src/JSONGenerativeUI.client.tsx +10 -2
- package/src/JSONGenerativeUI.shared.ts +8 -0
- package/src/actionRegistry.test.ts +90 -0
- package/src/actionRegistry.ts +88 -0
- package/src/buildPresentParameters.ts +38 -16
- package/src/generativeUIToJSX.test.ts +186 -0
- package/src/generativeUIToJSX.ts +112 -21
- package/src/index.server.ts +2 -0
- package/src/index.shared.ts +61 -0
- package/src/index.ts +2 -26
- package/src/ir.test.ts +203 -0
- package/src/ir.ts +279 -0
- package/src/renderGenerativeUI.test.tsx +69 -2
- package/src/renderGenerativeUI.tsx +49 -82
- package/src/slack/constants.ts +217 -0
- package/src/slack/decodeBlockAction.test.ts +135 -0
- package/src/slack/decodeBlockAction.ts +66 -0
- package/src/slack/fromSlackBlocks.test.ts +1145 -0
- package/src/slack/fromSlackBlocks.ts +709 -0
- package/src/slack/toSlackBlocks.test.ts +1936 -0
- package/src/slack/toSlackBlocks.ts +1319 -0
- package/src/slack/types.ts +230 -0
- package/src/slack.ts +37 -0
- package/src/teams/boundSpec.ts +102 -0
- package/src/teams/constants.ts +127 -0
- package/src/teams/decodeSubmitData.test.ts +148 -0
- package/src/teams/decodeSubmitData.ts +57 -0
- package/src/teams/toAdaptiveCard.test.ts +1410 -0
- package/src/teams/toAdaptiveCard.ts +762 -0
- package/src/teams/toTeamsAttachments.test.ts +29 -0
- package/src/teams/toTeamsAttachments.ts +137 -0
- package/src/teams/types.ts +237 -0
- package/src/teams.ts +34 -0
- package/src/types.ts +57 -42
- package/src/vocabulary/alert.test.tsx +136 -0
- package/src/vocabulary/alert.tsx +66 -0
- package/src/vocabulary/collectFormValues.test.ts +127 -0
- package/src/vocabulary/collectFormValues.ts +58 -0
- package/src/vocabulary/data.test.tsx +567 -0
- package/src/vocabulary/data.tsx +494 -0
- package/src/vocabulary/dispatch.ts +22 -0
- package/src/vocabulary/fact.test.tsx +15 -0
- package/src/vocabulary/fact.tsx +22 -0
- package/src/vocabulary/form.test.tsx +112 -0
- package/src/vocabulary/form.tsx +34 -0
- package/src/vocabulary/icon.test.tsx +64 -0
- package/src/vocabulary/icon.tsx +174 -0
- package/src/vocabulary/index.test.ts +63 -0
- package/src/vocabulary/index.ts +33 -0
- package/src/vocabulary/interactive.dispatch.test.tsx +352 -0
- package/src/vocabulary/interactive.test.tsx +248 -0
- package/src/vocabulary/interactive.tsx +269 -0
- package/src/vocabulary/layout.test.tsx +289 -0
- package/src/vocabulary/layout.tsx +229 -0
- package/src/vocabulary/list.test.tsx +195 -0
- package/src/vocabulary/list.tsx +48 -0
- package/src/vocabulary/media.test.tsx +85 -0
- package/src/vocabulary/media.tsx +56 -0
- package/src/vocabulary/text.test.tsx +73 -0
- package/src/vocabulary/text.tsx +59 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Children } from "react";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { GenerativeUILibrary } from "../types";
|
|
4
|
+
import { ALERT_TONES } from "../ir";
|
|
5
|
+
|
|
6
|
+
const MAX_CARDS = 10;
|
|
7
|
+
|
|
8
|
+
export const alertVocabulary = {
|
|
9
|
+
Alert: {
|
|
10
|
+
description:
|
|
11
|
+
"A highlighted message conveying urgency. `tone` drives the severity.",
|
|
12
|
+
properties: z.object({
|
|
13
|
+
title: z.string().optional().describe("Alert title."),
|
|
14
|
+
description: z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Supporting description text."),
|
|
18
|
+
tone: z
|
|
19
|
+
.enum(ALERT_TONES)
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Severity tone; defaults to `info`."),
|
|
22
|
+
}),
|
|
23
|
+
render: ({ title, description, tone, children }) => (
|
|
24
|
+
<div data-aui="alert" data-aui-tone={tone ?? "info"} role="alert">
|
|
25
|
+
{title ? <header data-aui="alert-title">{title}</header> : null}
|
|
26
|
+
{description ? <p data-aui="alert-desc">{description}</p> : null}
|
|
27
|
+
{children}
|
|
28
|
+
</div>
|
|
29
|
+
),
|
|
30
|
+
},
|
|
31
|
+
Carousel: {
|
|
32
|
+
description:
|
|
33
|
+
"A horizontally scrollable group of `Card` children (max 10). Set `label` for an accessible name.",
|
|
34
|
+
properties: z.object({
|
|
35
|
+
label: z
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Accessible label for the carousel."),
|
|
39
|
+
}),
|
|
40
|
+
render: ({ label, children }) => {
|
|
41
|
+
const cards = Children.toArray(children).slice(0, MAX_CARDS);
|
|
42
|
+
const n = cards.length;
|
|
43
|
+
return (
|
|
44
|
+
<div
|
|
45
|
+
data-aui="carousel"
|
|
46
|
+
role={label ? "region" : "group"}
|
|
47
|
+
aria-roledescription="carousel"
|
|
48
|
+
aria-label={label}
|
|
49
|
+
tabIndex={0}
|
|
50
|
+
>
|
|
51
|
+
{cards.map((card, i) => (
|
|
52
|
+
<div
|
|
53
|
+
key={i}
|
|
54
|
+
data-aui="carousel-slide"
|
|
55
|
+
role="group"
|
|
56
|
+
aria-roledescription="slide"
|
|
57
|
+
aria-label={`${i + 1} of ${n}`}
|
|
58
|
+
>
|
|
59
|
+
{card}
|
|
60
|
+
</div>
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
} satisfies GenerativeUILibrary;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
collectFormValues,
|
|
4
|
+
type FormControlElementLike,
|
|
5
|
+
} from "./collectFormValues";
|
|
6
|
+
|
|
7
|
+
const el = (
|
|
8
|
+
partial: Partial<FormControlElementLike>,
|
|
9
|
+
): FormControlElementLike => ({
|
|
10
|
+
name: "",
|
|
11
|
+
type: "text",
|
|
12
|
+
value: "",
|
|
13
|
+
disabled: false,
|
|
14
|
+
...partial,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("collectFormValues", () => {
|
|
18
|
+
it("resolves a checkbox to its checked boolean", () => {
|
|
19
|
+
expect(
|
|
20
|
+
collectFormValues([
|
|
21
|
+
el({ name: "agree", type: "checkbox", checked: true }),
|
|
22
|
+
]),
|
|
23
|
+
).toEqual({ agree: true });
|
|
24
|
+
expect(
|
|
25
|
+
collectFormValues([
|
|
26
|
+
el({ name: "agree", type: "checkbox", checked: false }),
|
|
27
|
+
]),
|
|
28
|
+
).toEqual({ agree: false });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("resolves a radio set to the checked option's value", () => {
|
|
32
|
+
expect(
|
|
33
|
+
collectFormValues([
|
|
34
|
+
el({ name: "size", type: "radio", value: "sm", checked: false }),
|
|
35
|
+
el({ name: "size", type: "radio", value: "md", checked: true }),
|
|
36
|
+
el({ name: "size", type: "radio", value: "lg", checked: false }),
|
|
37
|
+
]),
|
|
38
|
+
).toEqual({ size: "md" });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("resolves a radio set with no checked option to undefined", () => {
|
|
42
|
+
const values = collectFormValues([
|
|
43
|
+
el({ name: "size", type: "radio", value: "sm", checked: false }),
|
|
44
|
+
el({ name: "size", type: "radio", value: "md", checked: false }),
|
|
45
|
+
]);
|
|
46
|
+
expect("size" in values).toBe(true);
|
|
47
|
+
expect(values.size).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("resolves select/input/textarea/date controls to their string value", () => {
|
|
51
|
+
expect(
|
|
52
|
+
collectFormValues([
|
|
53
|
+
el({ name: "role", type: "select-one", value: "admin" }),
|
|
54
|
+
el({ name: "email", type: "email", value: "a@example.com" }),
|
|
55
|
+
el({ name: "bio", type: "textarea", value: "hi" }),
|
|
56
|
+
el({ name: "dob", type: "date", value: "2026-01-01" }),
|
|
57
|
+
]),
|
|
58
|
+
).toEqual({
|
|
59
|
+
role: "admin",
|
|
60
|
+
email: "a@example.com",
|
|
61
|
+
bio: "hi",
|
|
62
|
+
dob: "2026-01-01",
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("collects repeated non-radio names into an array in document order", () => {
|
|
67
|
+
expect(
|
|
68
|
+
collectFormValues([
|
|
69
|
+
el({ name: "tag", type: "text", value: "a" }),
|
|
70
|
+
el({ name: "tag", type: "text", value: "b" }),
|
|
71
|
+
el({ name: "tag", type: "text", value: "c" }),
|
|
72
|
+
]),
|
|
73
|
+
).toEqual({ tag: ["a", "b", "c"] });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("skips controls without a name", () => {
|
|
77
|
+
expect(
|
|
78
|
+
collectFormValues([
|
|
79
|
+
el({ name: "", type: "text", value: "unnamed" }),
|
|
80
|
+
el({ name: "kept", type: "text", value: "yes" }),
|
|
81
|
+
]),
|
|
82
|
+
).toEqual({ kept: "yes" });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("skips disabled controls", () => {
|
|
86
|
+
expect(
|
|
87
|
+
collectFormValues([
|
|
88
|
+
el({ name: "off", type: "text", value: "nope", disabled: true }),
|
|
89
|
+
el({ name: "on", type: "text", value: "yep" }),
|
|
90
|
+
]),
|
|
91
|
+
).toEqual({ on: "yep" });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("skips a disabled control inside an otherwise-checked radio set", () => {
|
|
95
|
+
expect(
|
|
96
|
+
collectFormValues([
|
|
97
|
+
el({
|
|
98
|
+
name: "size",
|
|
99
|
+
type: "radio",
|
|
100
|
+
value: "sm",
|
|
101
|
+
checked: true,
|
|
102
|
+
disabled: true,
|
|
103
|
+
}),
|
|
104
|
+
el({ name: "size", type: "radio", value: "md", checked: false }),
|
|
105
|
+
]),
|
|
106
|
+
).toEqual({ size: undefined });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("returns an empty object for no elements", () => {
|
|
110
|
+
expect(collectFormValues([])).toEqual({});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("a field named __proto__ round-trips as an own data key", () => {
|
|
114
|
+
const values = collectFormValues([
|
|
115
|
+
el({ name: "__proto__", type: "text", value: "x" }),
|
|
116
|
+
]);
|
|
117
|
+
expect(Object.hasOwn(values, "__proto__")).toBe(true);
|
|
118
|
+
expect(values["__proto__"]).toBe("x");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("a field named constructor collects as its submitted string, not an array seeded with the inherited function", () => {
|
|
122
|
+
const values = collectFormValues([
|
|
123
|
+
el({ name: "constructor", type: "text", value: "hi" }),
|
|
124
|
+
]);
|
|
125
|
+
expect(values["constructor"]).toBe("hi");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { FormEvent } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The subset of `HTMLInputElement`/`HTMLSelectElement`/`HTMLTextAreaElement` that {@link collectFormValues} reads. A structural type rather than the DOM interfaces themselves, so a plain object can stand in for a form control in tests.
|
|
5
|
+
*/
|
|
6
|
+
export type FormControlElementLike = {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly type: string;
|
|
9
|
+
readonly value: string;
|
|
10
|
+
readonly checked?: boolean;
|
|
11
|
+
readonly disabled: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Collects a submitted form's named control values into a plain object, keyed by `name`, in document order. Reads each control's live DOM state rather than `FormData`, so a checkbox resolves to its `checked` boolean instead of an on/off string. A radio group resolves to its checked option's `value`, or `undefined` if none is checked. Any other repeated `name` resolves to an array of its controls' values, in document order. Controls without a `name`, or that are `disabled`, are skipped entirely.
|
|
16
|
+
*/
|
|
17
|
+
export function collectFormValues(
|
|
18
|
+
elements: ArrayLike<FormControlElementLike>,
|
|
19
|
+
): Record<string, unknown> {
|
|
20
|
+
const values: Record<string, unknown> = Object.create(null);
|
|
21
|
+
|
|
22
|
+
for (const element of Array.from(elements)) {
|
|
23
|
+
const { name, disabled } = element;
|
|
24
|
+
if (!name || disabled) continue;
|
|
25
|
+
|
|
26
|
+
if (element.type === "radio") {
|
|
27
|
+
if (element.checked) values[name] = element.value;
|
|
28
|
+
else if (!Object.hasOwn(values, name)) values[name] = undefined;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const value: string | boolean =
|
|
33
|
+
element.type === "checkbox" ? (element.checked ?? false) : element.value;
|
|
34
|
+
|
|
35
|
+
if (Object.hasOwn(values, name)) {
|
|
36
|
+
const existing = values[name];
|
|
37
|
+
values[name] = Array.isArray(existing)
|
|
38
|
+
? [...existing, value]
|
|
39
|
+
: [existing, value];
|
|
40
|
+
} else {
|
|
41
|
+
values[name] = value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return values;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Convenience wrapper around {@link collectFormValues} for a native form submit event; the cast from `HTMLFormControlsCollection` to `ArrayLike<FormControlElementLike>` lives here once instead of at every call site.
|
|
50
|
+
*/
|
|
51
|
+
export function collectFormValuesFromEvent(
|
|
52
|
+
event: FormEvent<HTMLFormElement>,
|
|
53
|
+
): Record<string, unknown> {
|
|
54
|
+
return collectFormValues(
|
|
55
|
+
event.currentTarget
|
|
56
|
+
.elements as unknown as ArrayLike<FormControlElementLike>,
|
|
57
|
+
);
|
|
58
|
+
}
|