[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-typescript-core] 01/04: input duration
From: |
gnunet |
Subject: |
[taler-typescript-core] 01/04: input duration |
Date: |
Sun, 26 Jan 2025 15:33:56 +0100 |
This is an automated email from the git hooks/post-receive script.
sebasjm pushed a commit to branch master
in repository taler-typescript-core.
commit f586af103f68c67f763c01c105fe640ce0d34aae
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Fri Jan 24 12:18:46 2025 -0300
input duration
---
packages/aml-backoffice-ui/src/pages/Dashboard.tsx | 8 +-
packages/taler-util/src/time.ts | 29 ++--
packages/web-util/src/components/index.ts | 2 +-
packages/web-util/src/components/utils.ts | 24 +++
packages/web-util/src/forms/field-types.ts | 7 +
.../src/forms/fields/InputDuration.stories.tsx | 8 +-
.../web-util/src/forms/fields/InputDuration.tsx | 176 ++++++++++++++++++---
...r.stories.tsx => InputDurationText.stories.tsx} | 10 +-
.../src/forms/fields/InputDurationText.tsx | 105 ++++++++++++
.../src/forms/fields/InputInteger.stories.tsx | 27 ++++
packages/web-util/src/forms/fields/InputLine.tsx | 16 +-
packages/web-util/src/forms/forms-types.ts | 11 ++
packages/web-util/src/forms/forms-utils.ts | 13 ++
packages/web-util/src/forms/index.stories.ts | 1 +
14 files changed, 382 insertions(+), 55 deletions(-)
diff --git a/packages/aml-backoffice-ui/src/pages/Dashboard.tsx
b/packages/aml-backoffice-ui/src/pages/Dashboard.tsx
index e87236cfc..e43934420 100644
--- a/packages/aml-backoffice-ui/src/pages/Dashboard.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Dashboard.tsx
@@ -95,7 +95,7 @@ function EventMetrics({
<div class="sm:flex sm:items-center mb-4">
<div class="sm:flex-auto">
<h1 class="text-base font-semibold leading-6 text-gray-900">
- <i18n.Translate>Events</i18n.Translate>
+ <i18n.Translate>Statistics</i18n.Translate>
</h1>
</div>
</div>
@@ -104,7 +104,7 @@ function EventMetrics({
<div class="w-full flex justify-between">
<h1 class="text-base text-gray-900 mt-5">
- {i18n.str`Trading volume from ${getDateStringForTimeframe(
+ {i18n.str`Events from ${getDateStringForTimeframe(
params.current.start,
metricType,
dateLocale,
@@ -258,13 +258,13 @@ function MetricValueNumber({
<Fragment>
<dd class="mt-1 block ">
<div class="flex justify-start text-2xl items-baseline font-semibold
text-indigo-600">
- {!current ? "-" : current}
+ {!current ? 0 : current}
</div>
<div class="flex flex-col">
<div class="flex justify-end items-baseline text-2xl font-semibold
text-indigo-600">
<small class="ml-2 text-sm font-medium text-gray-500">
<i18n.Translate>previous</i18n.Translate>{" "}
- {!previous ? "-" : previous}
+ {!previous ? 0 : previous}
</small>
</div>
{!!rate && (
diff --git a/packages/taler-util/src/time.ts b/packages/taler-util/src/time.ts
index 93e34bbaf..81774455c 100644
--- a/packages/taler-util/src/time.ts
+++ b/packages/taler-util/src/time.ts
@@ -304,25 +304,26 @@ export namespace Duration {
minutes: number;
hours: number;
days: number;
+ month: number;
+ years: number;
}
| undefined {
if (d_ms === "forever") return undefined;
- let time = d_ms;
- const millis = d_ms % SECONDS;
- time -= millis;
- const s = time % MINUTES;
- time -= s;
- const m = time % HOURS;
- time -= m;
- const h = time % DAYS;
- time -= h;
- const d = time;
+ const ms = d_ms > 0 ? d_ms : 0;
+ const Y_rest = ms % YEARS;
+ const M_rest = Y_rest % MONTHS;
+ const D_rest = M_rest % DAYS;
+ const h_rest = D_rest % HOURS;
+ const m_rest = h_rest % MINUTES;
+ const millis = m_rest % SECONDS;
return {
- seconds: s / SECONDS,
- minutes: m / MINUTES,
- hours: h / HOURS,
- days: d / DAYS,
+ years: (ms - Y_rest) / YEARS,
+ month: (Y_rest - M_rest) / MONTHS,
+ days: (M_rest - D_rest) / DAYS,
+ hours: (D_rest - h_rest) / HOURS,
+ minutes: (h_rest - m_rest) / MINUTES,
+ seconds: (m_rest - millis) / SECONDS,
};
}
diff --git a/packages/web-util/src/components/index.ts
b/packages/web-util/src/components/index.ts
index 944bed269..17d89ad1f 100644
--- a/packages/web-util/src/components/index.ts
+++ b/packages/web-util/src/components/index.ts
@@ -1,5 +1,5 @@
export * as utils from "./utils.js";
-export { onComponentUnload } from "./utils.js";
+export { onComponentUnload, preconnectAs, Preconnect } from "./utils.js";
export * from "./Attention.js";
export * from "./CopyButton.js";
export * from "./ErrorLoading.js";
diff --git a/packages/web-util/src/components/utils.ts
b/packages/web-util/src/components/utils.ts
index a9871ce85..9cbc1bbba 100644
--- a/packages/web-util/src/components/utils.ts
+++ b/packages/web-util/src/components/utils.ts
@@ -83,6 +83,30 @@ export function onComponentUnload(callback: () => void) {
}, []);
}
+const ownerDocument = typeof document === "undefined" ? null : document;
+const preconnectsSet: Set<string> = new Set();
+
+export type Preconnect = {
+ rel: "preconnect" | "dns-prefetch";
+ href: string;
+ crossOrigin: string;
+};
+
+export function preconnectAs(pre: Preconnect[]) {
+ if (ownerDocument) {
+ pre.forEach(({ rel, href, crossOrigin }) => {
+ const key = `${rel}${href}${crossOrigin}`;
+ if (preconnectsSet.has(key)) return;
+ preconnectsSet.add(key);
+ const instance = ownerDocument.createElement("link");
+ instance.setAttribute("rel", rel);
+ instance.setAttribute("crossOrigin", crossOrigin);
+ instance.setAttribute("href", href);
+ ownerDocument.head.appendChild(instance);
+ });
+ }
+}
+
/**
*
* @param obj VNode
diff --git a/packages/web-util/src/forms/field-types.ts
b/packages/web-util/src/forms/field-types.ts
index 34696b430..e65f44b1c 100644
--- a/packages/web-util/src/forms/field-types.ts
+++ b/packages/web-util/src/forms/field-types.ts
@@ -17,6 +17,7 @@ import { InputTextArea } from "./fields/InputTextArea.js";
import { InputToggle } from "./fields/InputToggle.js";
import { Group } from "./Group.js";
import { HtmlIframe } from "./HtmlIframe.js";
+import { InputDurationText } from "./fields/InputDurationText.js";
/**
* Constrain the type with the ui props
*/
@@ -39,6 +40,7 @@ type FieldType<T extends object = any, K extends keyof T =
any> = {
toggle: Parameters<typeof InputToggle<T, K>>[0];
amount: Parameters<typeof InputAmount<T, K>>[0];
duration: Parameters<typeof InputDuration<T, K>>[0];
+ durationText: Parameters<typeof InputDurationText<T, K>>[0];
};
/**
@@ -77,6 +79,10 @@ export type UIFormField =
| {
type: "duration";
properties: FieldType["duration"];
+ }
+ | {
+ type: "durationText";
+ properties: FieldType["durationText"];
};
export type FieldComponentFunction<key extends keyof FieldType> = (
@@ -118,4 +124,5 @@ export const UIFormConfiguration: UIFormFieldMap = {
//@ts-ignore
amount: InputAmount,
duration: InputDuration,
+ durationText: InputDurationText,
};
diff --git a/packages/web-util/src/forms/fields/InputDuration.stories.tsx
b/packages/web-util/src/forms/fields/InputDuration.stories.tsx
index 8c0983287..d73e5c535 100644
--- a/packages/web-util/src/forms/fields/InputDuration.stories.tsx
+++ b/packages/web-util/src/forms/fields/InputDuration.stories.tsx
@@ -33,10 +33,10 @@ type TargetObject = {
};
const initial: TargetObject = {
time: Duration.fromSpec({
- days: 1,
- hours: 2,
- minutes: 3,
- seconds: 4,
+ days: 29,
+ hours: 23,
+ minutes: 59,
+ seconds: 59,
}),
};
diff --git a/packages/web-util/src/forms/fields/InputDuration.tsx
b/packages/web-util/src/forms/fields/InputDuration.tsx
index 9c368bdf3..08364694b 100644
--- a/packages/web-util/src/forms/fields/InputDuration.tsx
+++ b/packages/web-util/src/forms/fields/InputDuration.tsx
@@ -4,7 +4,7 @@ import { UIFormProps } from "../FormProvider.js";
import { noHandlerPropsAndNoContextForField } from "./InputArray.js";
import { InputWrapper } from "./InputLine.js";
import { Duration } from "@gnu-taler/taler-util";
-import { useEffect, useState } from "preact/hooks";
+import { useEffect, useRef, useState } from "preact/hooks";
export function InputDuration<T extends object, K extends keyof T>(
props: UIFormProps<T, K>,
@@ -14,26 +14,75 @@ export function InputDuration<T extends object, K extends
keyof T>(
const { value, onChange, state, error } =
props.handler ?? noHandlerPropsAndNoContextForField(props.name);
- const sd = !value ? undefined : Duration.toSpec(value as Duration);
- const [days, setDays] = useState(sd?.days ?? 0);
- const [hours, setHours] = useState(sd?.hours ?? 0);
- const [minutes, setMinutes] = useState(sd?.minutes ?? 0);
- const [seconds, setSeconds] = useState(sd?.seconds ?? 0);
+ const specDuration = !value ? undefined : Duration.toSpec(value as Duration);
+ // const [seconds, setSeconds] = useState(sd?.seconds ?? 0);
+ // const [hours, setHours] = useState(sd?.hours ?? 0);
+ // const [minutes, setMinutes] = useState(sd?.minutes ?? 0);
+ // const [days, setDays] = useState(sd?.days ?? 0);
+ // const [months, setMonths] = useState(sd?.month ?? 0);
+ // const [years, setYears] = useState(sd?.years ?? 0);
- useEffect(() => {
- onChange(
- Duration.fromSpec({
- days,
- hours,
- minutes,
- seconds,
- }),
- );
- }, [days, hours, minutes, seconds]);
+ const secondsRef = useRef<HTMLInputElement>(null);
+ const hoursRef = useRef<HTMLInputElement>(null);
+ const minutesRef = useRef<HTMLInputElement>(null);
+ const daysRef = useRef<HTMLInputElement>(null);
+ const monthsRef = useRef<HTMLInputElement>(null);
+ const yearsRef = useRef<HTMLInputElement>(null);
+
+ // useEffect(() => {
+ // onChange(
+ // Duration.fromSpec({
+ // days,
+ // hours,
+ // minutes,
+ // seconds,
+ // months,
+ // years,
+ // }),
+ // );
+ // }, [days, hours, minutes, seconds, months, years]);
const fromString: (s: string) => any =
converter?.fromStringUI ?? defaultFromString;
const toString: (s: any) => string = converter?.toStringUI ??
defaultToString;
+ const strSeconds = toString(specDuration?.seconds ?? 0) ?? "";
+ const strHours = toString(specDuration?.hours ?? 0) ?? "";
+ const strMinutes = toString(specDuration?.minutes ?? 0) ?? "";
+ const strDays = toString(specDuration?.days ?? 0) ?? "";
+ const strMonths = toString(specDuration?.month ?? 0) ?? "";
+ const strYears = toString(specDuration?.years ?? 0) ?? "";
+
+ useEffect(() => {
+ if (!secondsRef.current) return;
+ if (secondsRef.current === document.activeElement) return;
+ secondsRef.current.value = strSeconds;
+ }, [strSeconds]);
+ useEffect(() => {
+ if (!minutesRef.current) return;
+ if (minutesRef.current === document.activeElement) return;
+ minutesRef.current.value = strMinutes;
+ }, [strMinutes]);
+ useEffect(() => {
+ if (!hoursRef.current) return;
+ if (hoursRef.current === document.activeElement) return;
+ hoursRef.current.value = strHours;
+ }, [strHours]);
+ useEffect(() => {
+ if (!daysRef.current) return;
+ if (daysRef.current === document.activeElement) return;
+ daysRef.current.value = strDays;
+ }, [strDays]);
+ useEffect(() => {
+ if (!monthsRef.current) return;
+ if (monthsRef.current === document.activeElement) return;
+ monthsRef.current.value = strMonths;
+ }, [strMonths]);
+ useEffect(() => {
+ if (!yearsRef.current) return;
+ if (yearsRef.current === document.activeElement) return;
+ yearsRef.current.value = strYears;
+ }, [strYears]);
+
if (state.hidden) return <div />;
let clazz =
@@ -86,6 +135,60 @@ export function InputDuration<T extends object, K extends
keyof T>(
error={showError ? error : undefined}
>
<div class="flex flex-col gap-1">
+ <div class="flex">
+ <span class="ml-2 inline-flex items-center rounded-l-md border
border-r-0 border-gray-300 px-3 text-gray-500 sm:text-sm">
+ <i18n.Translate>years</i18n.Translate>
+ </span>
+ <input
+ name={String(name)}
+ type="number"
+ onChange={(e) => {
+ onChange(
+ Duration.fromSpec({
+ ...specDuration,
+ years: fromString(e.currentTarget.value),
+ }),
+ );
+ }}
+ placeholder={placeholder ? placeholder : undefined}
+ ref={yearsRef}
+ // value={toString(sd?.years) ?? ""}
+ // onBlur={() => {
+ // onChange(fromString(value as any));
+ // }}
+ // defaultValue={toString(value)}
+ disabled={disabled ?? false}
+ aria-invalid={showError}
+ // aria-describedby="email-error"
+ class={clazz}
+ />
+ <span class="ml-2 inline-flex items-center rounded-l-md border
border-r-0 border-gray-300 px-3 text-gray-500 sm:text-sm">
+ <i18n.Translate>months</i18n.Translate>
+ </span>
+ <input
+ name={String(name)}
+ type="number"
+ ref={monthsRef}
+ onChange={(e) => {
+ onChange(
+ Duration.fromSpec({
+ ...specDuration,
+ months: fromString(e.currentTarget.value),
+ }),
+ );
+ }}
+ placeholder={placeholder ? placeholder : undefined}
+ // value={toString(specDuration?.month) ?? ""}
+ // onBlur={() => {
+ // onChange(fromString(value as any));
+ // }}
+ // defaultValue={toString(value)}
+ disabled={disabled ?? false}
+ aria-invalid={showError}
+ // aria-describedby="email-error"
+ class={clazz}
+ />
+ </div>
<div class="flex">
<span class="ml-2 inline-flex items-center rounded-l-md border
border-r-0 border-gray-300 px-3 text-gray-500 sm:text-sm">
<i18n.Translate>days</i18n.Translate>
@@ -93,11 +196,17 @@ export function InputDuration<T extends object, K extends
keyof T>(
<input
name={String(name)}
type="number"
+ ref={daysRef}
onChange={(e) => {
- setDays(fromString(e.currentTarget.value));
+ onChange(
+ Duration.fromSpec({
+ ...specDuration,
+ days: fromString(e.currentTarget.value),
+ }),
+ );
}}
placeholder={placeholder ? placeholder : undefined}
- value={toString(sd?.days) ?? ""}
+ // value={toString(specDuration?.days) ?? ""}
// onBlur={() => {
// onChange(fromString(value as any));
// }}
@@ -113,11 +222,17 @@ export function InputDuration<T extends object, K extends
keyof T>(
<input
name={String(name)}
type="number"
+ ref={hoursRef}
onChange={(e) => {
- setHours(fromString(e.currentTarget.value));
+ onChange(
+ Duration.fromSpec({
+ ...specDuration,
+ hours: fromString(e.currentTarget.value),
+ }),
+ );
}}
placeholder={placeholder ? placeholder : undefined}
- value={toString(sd?.hours) ?? ""}
+ // value={toString(specDuration?.hours) ?? ""}
// onBlur={() => {
// onChange(fromString(value as any));
// }}
@@ -135,11 +250,17 @@ export function InputDuration<T extends object, K extends
keyof T>(
<input
name={String(name)}
type="number"
+ ref={minutesRef}
onChange={(e) => {
- setMinutes(fromString(e.currentTarget.value));
+ onChange(
+ Duration.fromSpec({
+ ...specDuration,
+ minutes: fromString(e.currentTarget.value),
+ }),
+ );
}}
placeholder={placeholder ? placeholder : undefined}
- value={toString(sd?.minutes) ?? ""}
+ // value={toString(specDuration?.minutes) ?? ""}
// onBlur={() => {
// onChange(fromString(value as any));
// }}
@@ -155,11 +276,18 @@ export function InputDuration<T extends object, K extends
keyof T>(
<input
name={String(name)}
type="number"
+ ref={secondsRef}
onChange={(e) => {
- setSeconds(fromString(e.currentTarget.value));
+ // setSeconds(fromString(e.currentTarget.value));
+ onChange(
+ Duration.fromSpec({
+ ...specDuration,
+ seconds: fromString(e.currentTarget.value),
+ }),
+ );
}}
placeholder={placeholder ? placeholder : undefined}
- value={toString(sd?.seconds) ?? ""}
+ // value={toString(specDuration?.seconds) ?? ""}
// onBlur={() => {
// onChange(fromString(value as any));
// }}
diff --git a/packages/web-util/src/forms/fields/InputInteger.stories.tsx
b/packages/web-util/src/forms/fields/InputDurationText.stories.tsx
similarity index 88%
copy from packages/web-util/src/forms/fields/InputInteger.stories.tsx
copy to packages/web-util/src/forms/fields/InputDurationText.stories.tsx
index 0a2bcaca0..44801b0a0 100644
--- a/packages/web-util/src/forms/fields/InputInteger.stories.tsx
+++ b/packages/web-util/src/forms/fields/InputDurationText.stories.tsx
@@ -19,20 +19,20 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { TranslatedString } from "@gnu-taler/taler-util";
+import { Duration, TranslatedString } from "@gnu-taler/taler-util";
import * as tests from "../../tests/hook.js";
import { DefaultForm as TestedComponent } from "../forms-ui.js";
import { FormDesign, UIHandlerId } from "../forms-types.js";
export default {
- title: "Input Integer",
+ title: "Input Duration Text",
};
type TargetObject = {
- age: number;
+ age: Duration;
};
const initial: TargetObject = {
- age: 5,
+ age: { d_ms: 10000000 },
};
const design: FormDesign = {
@@ -42,7 +42,7 @@ const design: FormDesign = {
title: "this is a simple form" as TranslatedString,
fields: [
{
- type: "integer",
+ type: "durationText",
label: "Age" as TranslatedString,
id: "age" as UIHandlerId,
tooltip: "just numbers" as TranslatedString,
diff --git a/packages/web-util/src/forms/fields/InputDurationText.tsx
b/packages/web-util/src/forms/fields/InputDurationText.tsx
new file mode 100644
index 000000000..3a4316117
--- /dev/null
+++ b/packages/web-util/src/forms/fields/InputDurationText.tsx
@@ -0,0 +1,105 @@
+import { VNode, h } from "preact";
+import { InputLine } from "./InputLine.js";
+import { UIFormProps } from "../FormProvider.js";
+import { Duration } from "@gnu-taler/taler-util";
+
+const PATTERN = /^(?<value>[0-9]+)(?<unit>[smhDMY])$/;
+const UNIT_GROUP = "unit";
+const VALUE_GROUP = "value";
+
+type DurationUnit = "s" | "m" | "h" | "D" | "M" | "Y";
+type DurationSpec = Parameters<typeof Duration.fromSpec>[0];
+
+type DurationValue = {
+ unit: DurationUnit;
+ value: number;
+};
+
+function updateSpec(spec: DurationSpec, value: DurationValue): void {
+ switch (value.unit) {
+ case "s": {
+ spec.seconds = value.value;
+ break;
+ }
+ case "m": {
+ spec.minutes = value.value;
+ break;
+ }
+ case "h": {
+ spec.hours = value.value;
+ break;
+ }
+ case "D": {
+ spec.days = value.value;
+ break;
+ }
+ case "M": {
+ spec.months = value.value;
+ break;
+ }
+ case "Y": {
+ spec.years = value.value;
+ break;
+ }
+ }
+}
+
+function parseDurationValue(str: string): DurationValue | undefined {
+ const r = PATTERN.exec(str);
+ if (!r) return undefined;
+ const value = Number.parseInt(r.groups![VALUE_GROUP], 10);
+ const unit = r.groups![UNIT_GROUP] as DurationUnit;
+ return { value, unit };
+}
+
+export function InputDurationText<T extends object, K extends keyof T>(
+ props: UIFormProps<T, K>,
+): VNode {
+ return (
+ // <div>s</div>
+ <InputLine
+ type="text"
+ {...props}
+ converter={{
+ //@ts-ignore
+ fromStringUI: (v): Duration => {
+ if (!v) return Duration.getForever();
+ const spec = v.split(" ").reduce((prev, cur) => {
+ const v = parseDurationValue(cur);
+ if (v) {
+ updateSpec(prev, v);
+ }
+ return prev;
+ }, {} as DurationSpec);
+ return Duration.fromSpec(spec);
+ },
+ //@ts-ignore
+ toStringUI: (v?: Duration): string => {
+ if (v === undefined) return "";
+ // return v! as any;
+ const spec = Duration.toSpec(v);
+ let result = "";
+ if (spec?.years) {
+ result += `${spec.years}Y `;
+ }
+ if (spec?.month) {
+ result += `${spec.month}M `;
+ }
+ if (spec?.days) {
+ result += `${spec.days}D `;
+ }
+ if (spec?.hours) {
+ result += `${spec.hours}h `;
+ }
+ if (spec?.minutes) {
+ result += `${spec.minutes}m `;
+ }
+ if (spec?.seconds) {
+ result += `${spec.seconds}s `;
+ }
+ return result.trimEnd();
+ },
+ }}
+ />
+ );
+}
diff --git a/packages/web-util/src/forms/fields/InputInteger.stories.tsx
b/packages/web-util/src/forms/fields/InputInteger.stories.tsx
index 0a2bcaca0..b20048f70 100644
--- a/packages/web-util/src/forms/fields/InputInteger.stories.tsx
+++ b/packages/web-util/src/forms/fields/InputInteger.stories.tsx
@@ -52,7 +52,34 @@ const design: FormDesign = {
],
};
+const design2: FormDesign = {
+ type: "double-column",
+ sections: [
+ {
+ title: "this is a simple form" as TranslatedString,
+ fields: [
+ {
+ type: "integer",
+ label: "Age" as TranslatedString,
+ id: "age" as UIHandlerId,
+ tooltip: "just numbers" as TranslatedString,
+ },
+ {
+ type: "integer",
+ label: "Age" as TranslatedString,
+ id: "age" as UIHandlerId,
+ tooltip: "just numbers" as TranslatedString,
+ },
+ ],
+ },
+ ],
+};
export const SimpleComment = tests.createExample(TestedComponent, {
initial,
design,
});
+
+export const DoubleInput = tests.createExample(TestedComponent, {
+ initial,
+ design: design2,
+});
diff --git a/packages/web-util/src/forms/fields/InputLine.tsx
b/packages/web-util/src/forms/fields/InputLine.tsx
index bbbc871e0..0f981f93a 100644
--- a/packages/web-util/src/forms/fields/InputLine.tsx
+++ b/packages/web-util/src/forms/fields/InputLine.tsx
@@ -2,6 +2,7 @@ import { TranslatedString } from "@gnu-taler/taler-util";
import { ComponentChildren, Fragment, VNode, h } from "preact";
import { Addon, UIFormProps } from "../FormProvider.js";
import { noHandlerPropsAndNoContextForField } from "./InputArray.js";
+import { useEffect, useRef } from "preact/hooks";
//@ts-ignore
const TooltipIcon = (
@@ -160,6 +161,7 @@ export function InputLine<T extends object, K extends keyof
T>(
props: { type: InputType } & UIFormProps<T, K>,
): VNode {
const { name, placeholder, before, after, converter, type, disabled } =
props;
+ const input = useRef<HTMLTextAreaElement & HTMLInputElement>(null);
const { value, onChange, state, error } =
props.handler ?? noHandlerPropsAndNoContextForField(props.name);
@@ -168,6 +170,12 @@ export function InputLine<T extends object, K extends
keyof T>(
converter?.fromStringUI ?? defaultFromString;
const toString: (s: any) => string = converter?.toStringUI ??
defaultToString;
+ useEffect(() => {
+ if (!input.current) return;
+ if (input.current === document.activeElement) return;
+ input.current.value = String(value);
+ }, [value]);
+
if (state.hidden) return <div />;
let clazz =
@@ -223,12 +231,13 @@ export function InputLine<T extends object, K extends
keyof T>(
>
<textarea
rows={4}
+ ref={input}
name={String(name)}
onChange={(e) => {
onChange(fromString(e.currentTarget.value));
}}
placeholder={placeholder ? placeholder : undefined}
- value={toString(value) ?? ""}
+ // value={toString(value) ?? ""}
// defaultValue={toString(value)}
disabled={disabled ?? false}
aria-invalid={showError}
@@ -248,16 +257,17 @@ export function InputLine<T extends object, K extends
keyof T>(
>
<input
name={String(name)}
+ ref={input}
type={type}
onChange={(e) => {
onChange(fromString(e.currentTarget.value));
}}
placeholder={placeholder ? placeholder : undefined}
- value={toString(value) ?? ""}
+ // value={toString(value) ?? ""}
// onBlur={() => {
// onChange(fromString(value as any));
// }}
- // defaultValue={toString(value)}
+ defaultValue={toString(value)}
disabled={disabled ?? false}
aria-invalid={showError}
// aria-describedby="email-error"
diff --git a/packages/web-util/src/forms/forms-types.ts
b/packages/web-util/src/forms/forms-types.ts
index 0da107e34..7509a7cc8 100644
--- a/packages/web-util/src/forms/forms-types.ts
+++ b/packages/web-util/src/forms/forms-types.ts
@@ -59,6 +59,7 @@ export type UIFormElementConfig =
| UIFormFieldSecret
| UIFormFieldSelectMultiple
| UIFormFieldDuration
+ | UIFormFieldDurationText
| UIFormFieldSelectOne
| UIFormFieldText
| UIFormFieldTextArea
@@ -151,6 +152,10 @@ type UIFormFieldDuration = {
type: "duration";
} & UIFormFieldBaseConfig;
+type UIFormFieldDurationText = {
+ type: "durationText";
+} & UIFormFieldBaseConfig;
+
type UIFormFieldSelectOne = {
type: "selectOne";
choices: Array<SelectUiChoice>;
@@ -332,6 +337,11 @@ const codecForUiFormFieldDuration = ():
Codec<UIFormFieldDuration> =>
.property("type", codecForConstString("duration"))
.build("UiFormFieldDuration");
+const codecForUiFormFieldDurationText = (): Codec<UIFormFieldDurationText> =>
+ codecForUIFormFieldBaseConfigTemplate<UIFormFieldDurationText>()
+ .property("type", codecForConstString("durationText"))
+ .build("UiFormFieldDuration");
+
const codecForUiFormFieldSelectMultiple =
(): Codec<UIFormFieldSelectMultiple> =>
codecForUIFormFieldBaseConfigTemplate<UIFormFieldSelectMultiple>()
@@ -383,6 +393,7 @@ const codecForUiFormField = (): Codec<UIFormElementConfig>
=>
.alternative("secret", codecForUiFormFieldSecret())
.alternative("selectMultiple", codecForUiFormFieldSelectMultiple())
.alternative("duration", codecForUiFormFieldDuration())
+ .alternative("durationText", codecForUiFormFieldDurationText())
.alternative("selectOne", codecForUiFormFieldSelectOne())
.alternative("text", codecForUiFormFieldText())
.alternative("textArea", codecForUiFormFieldTextArea())
diff --git a/packages/web-util/src/forms/forms-utils.ts
b/packages/web-util/src/forms/forms-utils.ts
index dc7d7d22f..ebf349de0 100644
--- a/packages/web-util/src/forms/forms-utils.ts
+++ b/packages/web-util/src/forms/forms-utils.ts
@@ -258,6 +258,19 @@ export function convertFormConfigToUiField(
},
} as UIFormField;
}
+ case "durationText": {
+ return {
+ type: "durationText",
+ properties: {
+ ...converBaseFieldsProps(i18n_, config),
+ ...converInputFieldsProps(
+ form,
+ config,
+ getConverterByFieldType(config.type, config),
+ ),
+ },
+ } as UIFormField;
+ }
case "toggle": {
return {
type: "toggle",
diff --git a/packages/web-util/src/forms/index.stories.ts
b/packages/web-util/src/forms/index.stories.ts
index 5b62c512a..823e71fac 100644
--- a/packages/web-util/src/forms/index.stories.ts
+++ b/packages/web-util/src/forms/index.stories.ts
@@ -12,3 +12,4 @@ export * as a12 from "./fields/InputTextArea.stories.js";
export * as a13 from "./fields/InputToggle.stories.js";
export * as a14 from "./fields/InputSecret.stories.js";
export * as a15 from "./fields/InputDuration.stories.js";
+export * as a16 from "./fields/InputDurationText.stories.js";
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.