Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 18 additions & 90 deletions frontend/src/ts/components/modals/SimpleModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AnyFieldApi, createForm } from "@tanstack/solid-form";
import { format as dateFormat } from "date-fns/format";
import {
Accessor,
For,
Expand All @@ -10,14 +9,7 @@ import {
Switch,
untrack,
} from "solid-js";
import {
z,
ZodDate,
ZodDefault,
ZodFirstPartyTypeKind,
ZodNumber,
ZodTypeAny,
} from "zod";
import { z, ZodDefault, ZodFirstPartyTypeKind, ZodTypeAny } from "zod";

import { hideLoaderBar, showLoaderBar } from "../../states/loader-bar";
import {
Expand All @@ -38,6 +30,7 @@ import { AnimatedModal } from "../common/AnimatedModal";
import { Checkbox } from "../ui/form/Checkbox";
import { InputField } from "../ui/form/InputField";
import { SubmitButton } from "../ui/form/SubmitButton";
import { TextareaField } from "../ui/form/TextareaField";
import { fieldMandatory, fromSchema, handleResult } from "../ui/form/utils";

type SyncValidator = (opts: {
Expand Down Expand Up @@ -116,19 +109,13 @@ function FieldInput(props: {
input: GenericSimpleModalInput;
schema: z.ZodTypeAny;
}): JSXElement {
const formatDate = (date: Date | undefined) =>
date === undefined
? undefined
: dateFormat(
date,
props.input.type === "date" ? "yyyy-MM-dd" : "yyyy-MM-dd'T'HH:mm:ss",
);
return (
<Switch
fallback={
<InputField
field={props.field}
type={props.input.type}
schema={props.schema}
placeholder={props.input.placeholder}
disabled={props.input.disabled}
readOnly={
Expand All @@ -141,9 +128,15 @@ function FieldInput(props: {
? (props.input as { clickToSelect?: boolean }).clickToSelect
: undefined
}
class={props.input.class}
class={cn(
{
"w-full":
props.input.type === "date" ||
props.input.type === "datetime-local",
},
props.input.class,
)}
autocomplete="off"
{...getMinAndMax(props.schema)}
/>
}
>
Expand All @@ -156,66 +149,30 @@ function FieldInput(props: {
/>
</Match>
<Match when={props.input.type === "textarea"}>
<textarea
class={cn("w-full", props.input.class)}
<TextareaField
field={props.field}
placeholder={props.input.placeholder}
value={props.field().state.value as string}
disabled={props.input.disabled}
readOnly={(props.input as { readOnly?: boolean }).readOnly}
autocomplete="off"
onInput={(e) => {
props.field().handleChange(e.currentTarget.value);
props.input.oninput?.(e);
}}
onClick={(e) => {
if ((props.input as { clickToSelect?: boolean }).clickToSelect) {
e.currentTarget.select();
}
}}
onBlur={() => props.field().handleBlur()}
></textarea>
/>
</Match>
<Match when={props.input.type === "range"}>
<div class="flex items-center gap-2">
<input
type="range"
<InputField
field={props.field}
type={props.input.type}
schema={props.schema}
class={cn(
props.input.hidden && "hidden",
"w-full",
props.input.class,
)}
{...getMinAndMax(props.schema)}
step={(props.input as { step?: number }).step}
value={props.field().state.value as string}
disabled={props.input.disabled}
onInput={(e) => {
props.field().handleChange(e.currentTarget.value);
props.input.oninput?.(e);
}}
onBlur={() => props.field().handleBlur()}
/>
<span class="text-sub">{props.field().state.value as string}</span>
</div>
</Match>

<Match
when={
props.input.type === "datetime-local" || props.input.type === "date"
}
>
<input
type={props.input.type}
class={cn("w-full", props.input.class)}
value={formatDate(props.field().state.value as Date)}
disabled={props.input.disabled}
{...getDateMinAndMax(props.schema, formatDate)}
onInput={(e) => {
props.field().handleChange(e.currentTarget.value);
props.input.oninput?.(e);
}}
onBlur={() => props.field().handleBlur()}
/>
</Match>
</Switch>
);
}
Expand Down Expand Up @@ -443,35 +400,6 @@ export function convertFn<T>(
}
}

function getMinAndMax(schema: ZodTypeAny): {
min?: number;
max?: number;
} {
if (getZodType(schema) !== ZodFirstPartyTypeKind.ZodNumber) return {};

return {
min: (schema as ZodNumber).minValue ?? undefined,
max: (schema as ZodNumber).maxValue ?? undefined,
};
}
function getDateMinAndMax(
schema: ZodTypeAny,
format: (val: Date | undefined) => string | undefined,
): {
min?: string;
max?: string;
} {
if (getZodType(schema) !== ZodFirstPartyTypeKind.ZodDate) return {};

const applyFormat = (it: Date | null) =>
it === null ? undefined : format(it);

return {
min: applyFormat((schema as ZodDate).minDate),
max: applyFormat((schema as ZodDate).maxDate),
};
}

function getZodType(schema: ZodTypeAny): ZodFirstPartyTypeKind {
// oxlint-disable-next-line typescript/no-unsafe-assignment typescript/no-unsafe-member-access
return schema._def["typeName"] as ZodFirstPartyTypeKind;
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/ts/components/pages/settings/SettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ function AutoSetting<T extends keyof Config>(props: {
[props.key]: getConfig[props.key],
},
onSubmit: ({ value }) => {
const val = parseInt(String(value[props.key]));
const val = parseFloat(String(value[props.key]));
if (val === getConfig[props.key]) return;
savedIndicator.flash();
setConfig(props.key, val as Config[T]);
Expand All @@ -350,7 +350,7 @@ function AutoSetting<T extends keyof Config>(props: {
name={props.key}
validators={{
onChange: ({ value }) => {
const val = parseInt(String(value));
const val = parseFloat(String(value));
if (isNaN(val)) {
return "Must be a number";
}
Expand All @@ -368,6 +368,7 @@ function AutoSetting<T extends keyof Config>(props: {
<div class="relative">
<InputField
field={field}
schema={ConfigSchema.shape[props.key]}
placeholder={
configMetadata[props.key].displayString ?? props.key
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function AnimationFpsLimit(): JSXElement {
fpsLimit: "",
},
onSubmit: ({ value }) => {
const val = parseInt(String(value.fpsLimit));
const val = parseFloat(String(value.fpsLimit));
if (val === getfpsLimit()) return;
setfpsLimit(val);
savedIndicator.flash();
Expand Down Expand Up @@ -54,7 +54,7 @@ export function AnimationFpsLimit(): JSXElement {
name="fpsLimit"
validators={{
onChange: ({ value }) => {
const val = parseInt(String(value));
const val = parseFloat(String(value));
if (isNaN(val)) {
return "Must be a number";
}
Expand All @@ -72,6 +72,7 @@ export function AnimationFpsLimit(): JSXElement {
field={field}
placeholder={"custom limit"}
type="number"
schema={fpsLimitSchema}
resetToDefaultIfEmptyOnBlur
/>
<savedIndicator.component />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function MaxLineWidth(): JSXElement {
maxLineWidth: getConfig.maxLineWidth,
},
onSubmit: ({ value }) => {
const val = parseInt(String(value.maxLineWidth));
const val = parseFloat(String(value.maxLineWidth));
if (val === getConfig.maxLineWidth) return;
flash();
setConfig("maxLineWidth", val);
Expand All @@ -45,7 +45,7 @@ export function MaxLineWidth(): JSXElement {
name="maxLineWidth"
validators={{
onChange: ({ value }) => {
const val = parseInt(String(value));
const val = parseFloat(String(value));
if (isNaN(val)) {
return "Must be a number";
}
Expand All @@ -61,6 +61,7 @@ export function MaxLineWidth(): JSXElement {
<div class="relative">
<InputField
field={field}
schema={MaxLineWidthSchema}
placeholder={
configMetadata.maxLineWidth.displayString ??
"max line width"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function MinAcc(): JSXElement {
minAccCustom: getConfig.minAccCustom,
},
onSubmit: ({ value }) => {
const val = parseInt(String(value.minAccCustom));
const val = parseFloat(String(value.minAccCustom));
if (val === getConfig.minAccCustom) return;
if (getConfig.minAcc === "custom") {
//
Expand Down Expand Up @@ -51,7 +51,7 @@ export function MinAcc(): JSXElement {
name="minAccCustom"
validators={{
onChange: ({ value }) => {
const val = parseInt(String(value));
const val = parseFloat(String(value));
if (isNaN(val)) {
return "Must be a number";
}
Expand All @@ -67,6 +67,7 @@ export function MinAcc(): JSXElement {
<div class="relative">
<InputField
field={field}
schema={MinimumAccuracyCustomSchema}
placeholder={
configMetadata.minAcc.displayString ?? "min accuracy"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function MinBurst(): JSXElement {
minBurstCustomSpeed: getConfig.minBurstCustomSpeed,
},
onSubmit: ({ value }) => {
const val = parseInt(String(value.minBurstCustomSpeed));
const val = parseFloat(String(value.minBurstCustomSpeed));
if (val === getConfig.minBurstCustomSpeed) return;
if (getConfig.minBurst !== "off") {
//
Expand Down Expand Up @@ -51,7 +51,7 @@ export function MinBurst(): JSXElement {
name="minBurstCustomSpeed"
validators={{
onChange: ({ value }) => {
const val = parseInt(String(value));
const val = parseFloat(String(value));
if (isNaN(val)) {
return "Must be a number";
}
Expand All @@ -67,6 +67,7 @@ export function MinBurst(): JSXElement {
<div class="relative">
<InputField
field={field}
schema={MinimumBurstCustomSpeedSchema}
placeholder={
configMetadata.minBurst.displayString ?? "min burst"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function MinSpeed(): JSXElement {
minWpmCustomSpeed: getConfig.minWpmCustomSpeed,
},
onSubmit: ({ value }) => {
const val = parseInt(String(value.minWpmCustomSpeed));
const val = parseFloat(String(value.minWpmCustomSpeed));
if (val === getConfig.minWpmCustomSpeed) return;
if (getConfig.minWpm === "custom") {
//
Expand Down Expand Up @@ -51,7 +51,7 @@ export function MinSpeed(): JSXElement {
name="minWpmCustomSpeed"
validators={{
onChange: ({ value }) => {
const val = parseInt(String(value));
const val = parseFloat(String(value));
if (isNaN(val)) {
return "Must be a number";
}
Expand All @@ -65,6 +65,7 @@ export function MinSpeed(): JSXElement {
<div class="relative">
<InputField
field={field}
schema={MinWpmCustomSpeedSchema}
placeholder={
configMetadata.minWpm.displayString ?? "min speed"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function PaceCaret(): JSXElement {
paceCaretCustomSpeed: getConfig.paceCaretCustomSpeed,
},
onSubmit: ({ value }) => {
const val = parseInt(String(value.paceCaretCustomSpeed));
const val = parseFloat(String(value.paceCaretCustomSpeed));
if (val === getConfig.paceCaretCustomSpeed) return;
if (getConfig.paceCaret !== "off") {
//
Expand Down Expand Up @@ -54,7 +54,7 @@ export function PaceCaret(): JSXElement {
name="paceCaretCustomSpeed"
validators={{
onChange: ({ value }) => {
const val = parseInt(String(value));
const val = parseFloat(String(value));
if (isNaN(val)) {
return "Must be a number";
}
Expand All @@ -70,6 +70,7 @@ export function PaceCaret(): JSXElement {
<div class="relative">
<InputField
field={field}
schema={PaceCaretCustomSpeedSchema}
placeholder={"pace caret speed"}
type="number"
resetToDefaultIfEmptyOnBlur
Expand Down
Loading
Loading