aboutsummaryrefslogtreecommitdiff
path: root/packages/web-util/src/forms/InputLine.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/web-util/src/forms/InputLine.tsx')
-rw-r--r--packages/web-util/src/forms/InputLine.tsx80
1 files changed, 33 insertions, 47 deletions
diff --git a/packages/web-util/src/forms/InputLine.tsx b/packages/web-util/src/forms/InputLine.tsx
index 9448ef5e4..8c44b1ca5 100644
--- a/packages/web-util/src/forms/InputLine.tsx
+++ b/packages/web-util/src/forms/InputLine.tsx
@@ -1,42 +1,8 @@
import { TranslatedString } from "@gnu-taler/taler-util";
import { ComponentChildren, Fragment, VNode, h } from "preact";
import { useField } from "./useField.js";
-
-export interface IconAddon {
- type: "icon";
- icon: VNode;
-}
-interface ButtonAddon {
- type: "button";
- onClick: () => void;
- children: ComponentChildren;
-}
-interface TextAddon {
- type: "text";
- text: TranslatedString;
-}
-type Addon = IconAddon | ButtonAddon | TextAddon;
-
-interface StringConverter<T> {
- toStringUI: (v?: T) => string;
- fromStringUI: (v?: string) => T;
-}
-
-export interface UIFormProps<T extends object, K extends keyof T> {
- name: K;
- label: TranslatedString;
- placeholder?: TranslatedString;
- tooltip?: TranslatedString;
- help?: TranslatedString;
- before?: Addon;
- after?: Addon;
- required?: boolean;
- converter?: StringConverter<T[K]>;
-}
-
-export type FormErrors<T> = {
- [P in keyof T]?: string | FormErrors<T[P]>;
-};
+import { useEffect, useState } from "preact/hooks";
+import { UIFormProps } from "./FormProvider.js";
//@ts-ignore
const TooltipIcon = (
@@ -80,11 +46,11 @@ export function LabelWithTooltipMaybeRequired({
{Label}
<span class="relative flex items-center group pl-2">
{TooltipIcon}
- <div class="absolute bottom-0 flex flex-col items-center hidden mb-6 group-hover:flex">
- <span class="relative z-10 p-2 text-xs leading-none text-white whitespace-no-wrap bg-black shadow-lg">
+ <div class="absolute bottom-0 -ml-10 hidden flex-col items-center mb-6 group-hover:flex w-28">
+ <div class="relative z-10 p-2 text-xs leading-none text-white whitespace-no-wrap bg-black shadow-lg">
{tooltip}
- </span>
- <div class="w-3 h-3 -mt-2 rotate-45 bg-black"></div>
+ </div>
+ <div class="w-3 h-3 -mt-2 rotate-45 bg-black"></div>
</div>
</span>
</div>
@@ -110,8 +76,9 @@ function InputWrapper<T extends object, K extends keyof T>({
after,
help,
error,
+ disabled,
required,
-}: { error?: string; children: ComponentChildren } & UIFormProps<T, K>): VNode {
+}: { error?: string; disabled: boolean, children: ComponentChildren } & UIFormProps<T, K>): VNode {
return (
<div class="sm:col-span-6">
<LabelWithTooltipMaybeRequired
@@ -132,6 +99,7 @@ function InputWrapper<T extends object, K extends keyof T>({
) : before.type === "button" ? (
<button
type="button"
+ disabled={disabled}
onClick={before.onClick}
class="relative -ml-px inline-flex items-center gap-x-1.5 rounded-l-md px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
>
@@ -153,6 +121,7 @@ function InputWrapper<T extends object, K extends keyof T>({
) : after.type === "button" ? (
<button
type="button"
+ disabled={disabled}
onClick={after.onClick}
class="relative -ml-px inline-flex items-center gap-x-1.5 rounded-r-md px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
>
@@ -189,6 +158,18 @@ export function InputLine<T extends object, K extends keyof T>(
const { name, placeholder, before, after, converter, type } = props;
const { value, onChange, state, isDirty } = useField<T, K>(name);
+ const [text, setText] = useState("")
+ const fromString: (s: string) => any =
+ converter?.fromStringUI ?? defaultFromString;
+ const toString: (s: any) => string = converter?.toStringUI ?? defaultToString;
+
+ useEffect(() => {
+ const newValue = toString(value)
+ if (newValue) {
+ setText(newValue)
+ }
+ }, [value])
+
if (state.hidden) return <div />;
let clazz =
@@ -233,14 +214,13 @@ export function InputLine<T extends object, K extends keyof T>(
clazz +=
" text-gray-900 ring-gray-300 placeholder:text-gray-400 focus:ring-indigo-600";
}
- const fromString: (s: string) => any =
- converter?.fromStringUI ?? defaultFromString;
- const toString: (s: any) => string = converter?.toStringUI ?? defaultToString;
if (type === "text-area") {
return (
<InputWrapper<T, K>
{...props}
+ help={props.help ?? state.help}
+ disabled={state.disabled ?? false}
error={showError ? state.error : undefined}
>
<textarea
@@ -262,15 +242,21 @@ export function InputLine<T extends object, K extends keyof T>(
}
return (
- <InputWrapper<T, K> {...props} error={showError ? state.error : undefined}>
+ <InputWrapper<T, K> {...props}
+ help={props.help ?? state.help}
+ disabled={state.disabled ?? false} error={showError ? state.error : undefined}
+ >
<input
name={String(name)}
type={type}
onChange={(e) => {
- onChange(fromString(e.currentTarget.value));
+ setText(e.currentTarget.value)
}}
placeholder={placeholder ? placeholder : undefined}
- value={toString(value) ?? ""}
+ value={text}
+ onBlur={() => {
+ onChange(fromString(text));
+ }}
// defaultValue={toString(value)}
disabled={state.disabled}
aria-invalid={showError}