diff options
Diffstat (limited to 'packages/web-util/src/forms/InputChoiceStacked.tsx')
-rw-r--r-- | packages/web-util/src/forms/InputChoiceStacked.tsx | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/packages/web-util/src/forms/InputChoiceStacked.tsx b/packages/web-util/src/forms/InputChoiceStacked.tsx new file mode 100644 index 000000000..c37984368 --- /dev/null +++ b/packages/web-util/src/forms/InputChoiceStacked.tsx @@ -0,0 +1,111 @@ +import { TranslatedString } from "@gnu-taler/taler-util"; +import { Fragment, VNode, h } from "preact"; +import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js"; +import { useField } from "./useField.js"; + +export interface Choice<V> { + label: TranslatedString; + description?: TranslatedString; + value: V; +} + +export function InputChoiceStacked<T extends object, K extends keyof T>( + props: { + choices: Choice<T[K]>[]; + } & UIFormProps<T, K>, +): VNode { + const { + choices, + name, + label, + tooltip, + help, + placeholder, + required, + before, + after, + converter, + } = props; + const { value, onChange, state, isDirty } = useField<T, K>(name); + if (state.hidden) { + return <Fragment />; + } + + return ( + <div class="sm:col-span-6"> + <LabelWithTooltipMaybeRequired + label={label} + required={required} + tooltip={tooltip} + /> + <fieldset class="mt-2"> + <div class="space-y-4"> + {choices.map((choice) => { + // const currentValue = !converter + // ? choice.value + // : converter.fromStringUI(choice.value) ?? ""; + + let clazz = + "border relative block cursor-pointer rounded-lg bg-white px-6 py-4 shadow-sm focus:outline-none sm:flex sm:justify-between"; + if (choice.value === value) { + clazz += + " border-transparent border-indigo-600 ring-2 ring-indigo-600"; + } else { + clazz += " border-gray-300"; + } + + return ( + <label class={clazz}> + <input + type="radio" + name="server-size" + // defaultValue={choice.value} + value={ + (!converter + ? (choice.value as string) + : converter?.toStringUI(choice.value)) ?? "" + } + onClick={(e) => { + onChange( + (value === choice.value + ? undefined + : choice.value) as T[K], + ); + }} + class="sr-only" + aria-labelledby="server-size-0-label" + aria-describedby="server-size-0-description-0 server-size-0-description-1" + /> + <span class="flex items-center"> + <span class="flex flex-col text-sm"> + <span + id="server-size-0-label" + class="font-medium text-gray-900" + > + {choice.label} + </span> + {choice.description !== undefined && ( + <span + id="server-size-0-description-0" + class="text-gray-500" + > + <span class="block sm:inline"> + {choice.description} + </span> + </span> + )} + </span> + </span> + </label> + ); + })} + </div> + </fieldset> + {help && ( + <p class="mt-2 text-sm text-gray-500" id="email-description"> + {help} + </p> + )} + </div> + ); +} |