@@ -265,6 +265,80 @@ defmodule ComponentsGuideWeb.ReactEditorController do
265265 render_source ( conn , source )
266266 end
267267
268+ def show ( conn , % { "id" => "form-reducer-validation" } ) do
269+ source = ~s"""
270+ function formDataFrom(element) {
271+ if (element instanceof HTMLFormElement) {
272+ return new FormData(element);
273+ }
274+
275+ const formData = new FormData();
276+ if (element instanceof HTMLInputElement) {
277+ formData.set(element.name, element.value);
278+ }
279+ return formData;
280+ }
281+
282+ function reducer(state, event) {
283+ if (event.type === "submit") {
284+ event.preventDefault();
285+ }
286+
287+ const errors = new Map(state.errors);
288+ for (const [name, value] of formDataFrom(event.target)) {
289+ // TODO: add more advanced validation here
290+ if (value.trim() === "") {
291+ errors.set(name, "Required");
292+ }
293+ }
294+
295+ return { ...state, errors };
296+ }
297+
298+ function Field({ name, label, error, type = "text" }) {
299+ const id = useId();
300+ return (
301+ <div class="flex items-center gap-2">
302+ <label for={id}>{label}</label>
303+ <input id={id} name={name} type={type} />
304+ <span class="italic">{error}</span>
305+ </div>
306+ );
307+ }
308+
309+ export default function App() {
310+ const [state, dispatch] = useReducer(reducer, { errors: new Map() });
311+
312+ return (
313+ <form onBlur={dispatch} onSubmit={dispatch} class="flex flex-col items-start gap-4">
314+ <p class="italic">Fields will individually validate on blur, or every field will validate on submit.</p>
315+ <fieldset class="flex flex-col gap-2">
316+ <Field
317+ name="firstName"
318+ label="First name"
319+ error={state.errors.get("firstName")}
320+ />
321+ <Field
322+ name="lastName"
323+ label="Last name"
324+ error={state.errors.get("lastName")}
325+ />
326+ <Field
327+ name="email"
328+ label="Email"
329+ type="email"
330+ error={state.errors.get("email")}
331+ />
332+ </fieldset>
333+ <button class="px-3 py-1 bg-blue-300 rounded">Save</button>
334+ </form>
335+ );
336+ }
337+ """
338+
339+ render_source ( conn , source )
340+ end
341+
268342 def show ( conn , % { "id" => "yieldmachine" } ) do
269343 source = ~s"""
270344 import { start, on } from "https://unpkg.com/yieldmachine@0.5.1/dist/yieldmachine.module.js"
0 commit comments