diff options
Diffstat (limited to 'web/source/settings/lib/form')
-rw-r--r-- | web/source/settings/lib/form/index.ts | 3 | ||||
-rw-r--r-- | web/source/settings/lib/form/number.tsx | 104 | ||||
-rw-r--r-- | web/source/settings/lib/form/submit.ts | 23 | ||||
-rw-r--r-- | web/source/settings/lib/form/types.ts | 7 |
4 files changed, 135 insertions, 2 deletions
diff --git a/web/source/settings/lib/form/index.ts b/web/source/settings/lib/form/index.ts index 409ef0328..878b7c79b 100644 --- a/web/source/settings/lib/form/index.ts +++ b/web/source/settings/lib/form/index.ts @@ -41,6 +41,7 @@ import type { ChecklistInputHook, FieldArrayInputHook, ArrayInputHook, + NumberFormInputHook, } from "./types"; function capitalizeFirst(str: string) { @@ -102,11 +103,11 @@ function value<T>(name: string, initialValue: T) { name, Name: "", value: initialValue, - hasChanged: () => true, // always included }; } export const useTextInput = inputHook(text) as (_name: string, _opts?: HookOpts<string>) => TextFormInputHook; +export const useNumberInput = inputHook(text) as (_name: string, _opts?: HookOpts<number>) => NumberFormInputHook; export const useFileInput = inputHook(file) as (_name: string, _opts?: HookOpts<File>) => FileFormInputHook; export const useBoolInput = inputHook(bool) as (_name: string, _opts?: HookOpts<boolean>) => BoolFormInputHook; export const useRadioInput = inputHook(radio) as (_name: string, _opts?: HookOpts<string>) => RadioFormInputHook; diff --git a/web/source/settings/lib/form/number.tsx b/web/source/settings/lib/form/number.tsx new file mode 100644 index 000000000..15acf162a --- /dev/null +++ b/web/source/settings/lib/form/number.tsx @@ -0,0 +1,104 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import React, { + useState, + useRef, + useTransition, + useEffect, +} from "react"; + +import type { + CreateHookNames, + HookOpts, + NumberFormInputHook, +} from "./types"; + +const _default = 0; + +export default function useNumberInput( + { name, Name }: CreateHookNames, + { + initialValue = _default, + dontReset = false, + validator, + showValidation = true, + initValidation, + nosubmit = false, + }: HookOpts<number> +): NumberFormInputHook { + const [number, setNumber] = useState(initialValue); + const numberRef = useRef<HTMLInputElement>(null); + + const [validation, setValidation] = useState(initValidation ?? ""); + const [_isValidating, startValidation] = useTransition(); + const valid = validation == ""; + + function onChange(e: React.ChangeEvent<HTMLInputElement>) { + const input = e.target.valueAsNumber; + setNumber(input); + + if (validator) { + startValidation(() => { + setValidation(validator(input)); + }); + } + } + + function reset() { + if (!dontReset) { + setNumber(initialValue); + } + } + + useEffect(() => { + if (validator && numberRef.current) { + if (showValidation) { + numberRef.current.setCustomValidity(validation); + } else { + numberRef.current.setCustomValidity(""); + } + } + }, [validation, validator, showValidation]); + + // Array / Object hybrid, for easier access in different contexts + return Object.assign([ + onChange, + reset, + { + [name]: number, + [`${name}Ref`]: numberRef, + [`set${Name}`]: setNumber, + [`${name}Valid`]: valid, + } + ], { + onChange, + reset, + name, + Name: "", // Will be set by inputHook function. + nosubmit, + value: number, + ref: numberRef, + setter: setNumber, + valid, + validate: () => setValidation(validator ? validator(number): ""), + hasChanged: () => number != initialValue, + _default + }); +} diff --git a/web/source/settings/lib/form/submit.ts b/web/source/settings/lib/form/submit.ts index aa662086f..498481deb 100644 --- a/web/source/settings/lib/form/submit.ts +++ b/web/source/settings/lib/form/submit.ts @@ -34,8 +34,24 @@ import type { } from "./types"; interface UseFormSubmitOptions { + /** + * Include only changed fields when submitting the form. + * If no fields have been changed, submit will be a noop. + */ changedOnly: boolean; + /** + * Optional function to run when the form has been sent + * and a response has been returned from the server. + */ onFinish?: ((_res: any) => void); + /** + * Can be optionally used to modify the final mutation argument from the + * gathered mutation data before it's passed into the trigger function. + * + * Useful if the mutation trigger function takes not just a simple key/value + * object but a more complicated object. + */ + customizeMutationArgs?: (_mutationData: { [k: string]: any }) => any; } /** @@ -105,7 +121,7 @@ export default function useFormSubmit( usedAction.current = action; // Transform the hooked form into an object. - const { + let { mutationData, updatedFields, } = getFormMutations(form, { changedOnly }); @@ -117,7 +133,12 @@ export default function useFormSubmit( return; } + // Final tweaks on the mutation + // argument before triggering it. mutationData.action = action; + if (opts.customizeMutationArgs) { + mutationData = opts.customizeMutationArgs(mutationData); + } try { const res = await runMutation(mutationData); diff --git a/web/source/settings/lib/form/types.ts b/web/source/settings/lib/form/types.ts index 17fbec53a..6a4162f3a 100644 --- a/web/source/settings/lib/form/types.ts +++ b/web/source/settings/lib/form/types.ts @@ -181,6 +181,13 @@ export interface TextFormInputHook extends FormInputHook<string>, _withValidate, _withRef {} +export interface NumberFormInputHook extends FormInputHook<number>, + _withSetter<number>, + _withOnChange, + _withReset, + _withValidate, + _withRef {} + export interface RadioFormInputHook extends FormInputHook<string>, _withSetter<string>, _withOnChange, |