@marktiderman/genesis-forms-web 0.1.0 → 0.2.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 +25 -5
- package/dist/index.d.ts +50 -21
- package/dist/index.js +646 -10
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@ The web renderer for [Genesis Forms](../forms/README.md), built on Genesis UI co
|
|
|
6
6
|
import { Form } from "@marktiderman/genesis-forms-web";
|
|
7
7
|
|
|
8
8
|
<Form
|
|
9
|
-
form={
|
|
9
|
+
form={eventFeedback} // a defineForm result or a canonical definition
|
|
10
10
|
layout="focus" // one question at a time; or "classic"
|
|
11
|
-
options={{ hidden: {
|
|
11
|
+
options={{ hidden: { owner: "Sam" } }}
|
|
12
12
|
onChange={(state) => saveDraft(state)}
|
|
13
13
|
onSubmit={(engine) => submit(engine.getState().events)}
|
|
14
14
|
/>
|
|
@@ -41,13 +41,13 @@ import { Form } from "@marktiderman/genesis-forms-web";
|
|
|
41
41
|
`prefers-reduced-motion`). The ending is centred and large. In classic layout, page mode is one
|
|
42
42
|
scrolling page, Google Forms style: a header card with the title and description, each question
|
|
43
43
|
in its own card (red-edged while invalid), Submit at the end.
|
|
44
|
-
`brand={{ name: "
|
|
44
|
+
`brand={{ name: "Acme", logo }}` shows an organisation's or business's logo (an image URL or an
|
|
45
45
|
element; its initials without one) and name small at the top of every step and on the ending.
|
|
46
46
|
Custom slots and renderers can match with `useFormPresentation()`, which returns
|
|
47
47
|
`{ presentation, layout }`.
|
|
48
48
|
|
|
49
49
|
```tsx
|
|
50
|
-
<Form form={
|
|
50
|
+
<Form form={eventFeedback} presentation="page" brand={{ name: "Acme" }} onSubmit={send} />
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
- **Accessible by default:** group questions are a `fieldset` with a `legend`, and rating, NPS and
|
|
@@ -74,7 +74,27 @@ import { Form } from "@marktiderman/genesis-forms-web";
|
|
|
74
74
|
`defaultRegistry.register(entry)` mutates the shared registry in place, for every form in the app.
|
|
75
75
|
|
|
76
76
|
Renderers: short and long text, email, phone (`type="tel"`), rating, NPS, single choice, multiple
|
|
77
|
-
choice, yes/no
|
|
77
|
+
choice, yes/no, and the structured types:
|
|
78
|
+
|
|
79
|
+
- **`list`** (goals, action items, three wins) and **`table`** (an action plan with What / Who /
|
|
80
|
+
By when, pros and cons) add, remove and reorder entries (SortableList: drag, keyboard and a Move
|
|
81
|
+
menu).
|
|
82
|
+
- **`media-notes`** (lecture notes, reviewing a recorded call) takes a typed time (`m:ss`,
|
|
83
|
+
`h:mm:ss` or seconds). Wrap the form in `MediaTimeContext` to link it to your video or audio
|
|
84
|
+
player: `currentTime` prefills "Add note at 1:23" and `seek` plays from a note.
|
|
85
|
+
- **`text-highlights`** (annotating an article, a policy or a contract) takes highlights by
|
|
86
|
+
selecting words in the passage or typing them. The passage is the field's `text` prop, or the
|
|
87
|
+
value of `SourceTextContext` around the form.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
<MediaTimeContext.Provider value={{ currentTime: () => player.currentTime, seek: (s) => player.seek(s) }}>
|
|
91
|
+
<SourceTextContext.Provider value={article.body}>
|
|
92
|
+
<Form form={videoNotes} onSubmit={send} />
|
|
93
|
+
</SourceTextContext.Provider>
|
|
94
|
+
</MediaTimeContext.Provider>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`useMediaTime()` and `useSourceText()` read them in a custom renderer.
|
|
78
98
|
|
|
79
99
|
## License
|
|
80
100
|
|
package/dist/index.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ interface HeaderSlotProps {
|
|
|
28
28
|
className?: string;
|
|
29
29
|
}
|
|
30
30
|
interface BrandSlotProps {
|
|
31
|
-
/** The
|
|
31
|
+
/** The organisation or business name, shown beside the logo. */
|
|
32
32
|
name: string;
|
|
33
33
|
/** An image URL or your own element. Without one, the name's initials are shown. */
|
|
34
34
|
logo?: ReactNode;
|
|
@@ -90,7 +90,7 @@ interface FormSlotMap {
|
|
|
90
90
|
interface FormBrand {
|
|
91
91
|
name: string;
|
|
92
92
|
/** An image URL or your own element (decorative: the name is the text). Without one, the name's initials are shown. */
|
|
93
|
-
logo?: ReactNode;
|
|
93
|
+
logo?: ReactNode | undefined;
|
|
94
94
|
}
|
|
95
95
|
interface FormProps {
|
|
96
96
|
/** A `defineForm` result or a canonical definition. */
|
|
@@ -98,10 +98,10 @@ interface FormProps {
|
|
|
98
98
|
definition: FormDefinition;
|
|
99
99
|
};
|
|
100
100
|
/** Bring your own engine (from `useForm`) to read state outside the form. `form` and `options` are then unused. */
|
|
101
|
-
engine?: FormEngine;
|
|
102
|
-
options?: CreateFormOptions;
|
|
101
|
+
engine?: FormEngine | undefined;
|
|
102
|
+
options?: CreateFormOptions | undefined;
|
|
103
103
|
/** Overrides the definition's layout. */
|
|
104
|
-
layout?: "focus" | "classic";
|
|
104
|
+
layout?: "focus" | "classic" | undefined;
|
|
105
105
|
/**
|
|
106
106
|
* `"embedded"` (default): a block inside your own page. `"page"`: the
|
|
107
107
|
* respondent's whole screen, for a QR code or a shared link. It fills the
|
|
@@ -109,36 +109,39 @@ interface FormProps {
|
|
|
109
109
|
* under a thin progress bar with large type and tap targets, and in classic
|
|
110
110
|
* layout the questions are sections on one scrolling page.
|
|
111
111
|
*/
|
|
112
|
-
presentation?: FormPresentation;
|
|
113
|
-
/**
|
|
114
|
-
brand?: FormBrand;
|
|
115
|
-
registry?: RendererRegistry;
|
|
112
|
+
presentation?: FormPresentation | undefined;
|
|
113
|
+
/** An organisation's or business's name and logo, shown small at the top of every step and on the ending. */
|
|
114
|
+
brand?: FormBrand | undefined;
|
|
115
|
+
registry?: RendererRegistry | undefined;
|
|
116
116
|
/**
|
|
117
117
|
* Called once the form completes. The ending shows only after it resolves.
|
|
118
118
|
* If it rejects, the answers stay on screen, read-only (a completed response
|
|
119
119
|
* is final), with an error and a "Try again" button that sends them again.
|
|
120
120
|
*/
|
|
121
|
-
onSubmit?: (engine: FormEngine) => void | Promise<void
|
|
122
|
-
/**
|
|
123
|
-
|
|
121
|
+
onSubmit?: ((engine: FormEngine) => void | Promise<void>) | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* Called on every state change: an answer, a step forward or back, a
|
|
124
|
+
* submit attempt (debounce it to save drafts).
|
|
125
|
+
*/
|
|
126
|
+
onChange?: ((state: FormState, engine: FormEngine) => void) | undefined;
|
|
124
127
|
/**
|
|
125
128
|
* In focus layout, move on after a single pick (rating, yes/no, one choice).
|
|
126
129
|
* Arrow keys only move through the options; a click, tap or Space picks.
|
|
127
130
|
* Never moves on from the last question. Default true.
|
|
128
131
|
*/
|
|
129
|
-
autoAdvance?: boolean;
|
|
130
|
-
submitLabel?: string;
|
|
131
|
-
nextLabel?: string;
|
|
132
|
+
autoAdvance?: boolean | undefined;
|
|
133
|
+
submitLabel?: string | undefined;
|
|
134
|
+
nextLabel?: string | undefined;
|
|
132
135
|
/** The submit button's label after `onSubmit` failed. Default "Try again". */
|
|
133
|
-
retryLabel?: string;
|
|
136
|
+
retryLabel?: string | undefined;
|
|
134
137
|
slots?: {
|
|
135
138
|
[K in keyof FormSlotMap]?: SlotOverride<FormSlotMap[K]>;
|
|
136
|
-
};
|
|
139
|
+
} | undefined;
|
|
137
140
|
slotProps?: {
|
|
138
141
|
[K in keyof FormSlotMap]?: Partial<FormSlotMap[K]>;
|
|
139
|
-
};
|
|
140
|
-
className?: string;
|
|
141
|
-
testID?: string;
|
|
142
|
+
} | undefined;
|
|
143
|
+
className?: string | undefined;
|
|
144
|
+
testID?: string | undefined;
|
|
142
145
|
}
|
|
143
146
|
declare function Form(props: FormProps): react.JSX.Element;
|
|
144
147
|
|
|
@@ -161,6 +164,7 @@ interface FieldChromeProps {
|
|
|
161
164
|
*/
|
|
162
165
|
declare function FieldChrome({ props, children, className }: FieldChromeProps): react.JSX.Element;
|
|
163
166
|
declare function describedBy(props: FieldRendererProps): string | undefined;
|
|
167
|
+
|
|
164
168
|
declare const builtInRenderers: RendererEntry[];
|
|
165
169
|
/**
|
|
166
170
|
* The built-in web renderers, shared by every `<Form>` that gets no
|
|
@@ -169,4 +173,29 @@ declare const builtInRenderers: RendererEntry[];
|
|
|
169
173
|
*/
|
|
170
174
|
declare const defaultRegistry: _marktiderman_genesis_forms_react.RendererRegistry;
|
|
171
175
|
|
|
172
|
-
|
|
176
|
+
/**
|
|
177
|
+
* The video or audio a form is about, as the host app plays it. `media-notes`
|
|
178
|
+
* fields read it: with `currentTime` the add row starts at the current moment
|
|
179
|
+
* ("Add note at 1:23"), with `seek` a note's time plays the media from there.
|
|
180
|
+
* Both are in seconds from the start of the full recording.
|
|
181
|
+
*/
|
|
182
|
+
interface MediaTime {
|
|
183
|
+
/** The playhead now, or undefined when it isn't known yet. */
|
|
184
|
+
currentTime?: (() => number | undefined) | undefined;
|
|
185
|
+
/** Move the playhead. */
|
|
186
|
+
seek?: ((seconds: number) => void) | undefined;
|
|
187
|
+
}
|
|
188
|
+
/** Provide it around `<Form>` to link `media-notes` fields to a media player. */
|
|
189
|
+
declare const MediaTimeContext: react.Context<MediaTime | null>;
|
|
190
|
+
/** The enclosing `MediaTimeContext` value, or null. */
|
|
191
|
+
declare function useMediaTime(): MediaTime | null;
|
|
192
|
+
/**
|
|
193
|
+
* The text a form is about (an article, a policy, a contract). `text-highlights`
|
|
194
|
+
* fields highlight passages of it; a field's own `props.text` is used when no
|
|
195
|
+
* host provides one. Offsets are UTF-16 indices into this exact string.
|
|
196
|
+
*/
|
|
197
|
+
declare const SourceTextContext: react.Context<string | null>;
|
|
198
|
+
/** The enclosing `SourceTextContext` value, or null. */
|
|
199
|
+
declare function useSourceText(): string | null;
|
|
200
|
+
|
|
201
|
+
export { type BrandSlotProps, type EndingSlotProps, type ErrorSummaryItem, type ErrorSummarySlotProps, FieldChrome, type FieldChromeProps, Form, type FormBrand, type FormPresentation, type FormPresentationValue, type FormProps, type FormSlotMap, type HeaderSlotProps, type MediaTime, MediaTimeContext, type NavigationSlotProps, type ProgressSlotProps, SourceTextContext, builtInRenderers, defaultRegistry, describedBy, useFormPresentation, useMediaTime, useSourceText };
|