diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2023-12-19 07:52:13 +0100 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2024-01-29 09:44:53 +0100 |
| commit | bf2a93dc0468d33079a4050fa3c925428c282b7d (patch) | |
| tree | 44d91cfa0d2935c570d0552c822c3bed43b9009b /gui/src/renderer | |
| parent | 3078fd30c1d8c9f3f86d9ddcfeddea3c83718f5b (diff) | |
| download | mullvadvpn-bf2a93dc0468d33079a4050fa3c925428c282b7d.tar.xz mullvadvpn-bf2a93dc0468d33079a4050fa3c925428c282b7d.zip | |
Add form component
Diffstat (limited to 'gui/src/renderer')
| -rw-r--r-- | gui/src/renderer/components/cell/SettingsForm.tsx | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/gui/src/renderer/components/cell/SettingsForm.tsx b/gui/src/renderer/components/cell/SettingsForm.tsx new file mode 100644 index 0000000000..1f2be529c1 --- /dev/null +++ b/gui/src/renderer/components/cell/SettingsForm.tsx @@ -0,0 +1,69 @@ +import React, { useCallback, useContext, useEffect, useId, useMemo, useState } from 'react'; + +interface SettingsFormContext { + formSubmittable: boolean; + reportInputSubmittable: (key: string, submittable: boolean) => void; + removeInput: (key: string) => void; +} + +// Keep track of all submittable and non submittable inputs in a form to enable e.g. buttons to +// become enabled/disabled based on input states. +const settingsFormContext = React.createContext<SettingsFormContext | undefined>(undefined); + +function useSettingsFormContext() { + return useContext(settingsFormContext); +} + +// Hook that returns whether or not the form is submittable for use in form container. +export function useSettingsFormSubmittable() { + const context = useSettingsFormContext(); + return context?.formSubmittable ?? true; +} + +// Hook that returns function that input can use to report if it's submittable or not. +export function useSettingsFormSubmittableReporter() { + const context = useSettingsFormContext(); + + // Each form needs an unique ID, this key is part of that ID. + const key = useId(); + + const reportInputSubmittable = useCallback( + (submittable: boolean) => { + context?.reportInputSubmittable(key, submittable); + }, + [context?.reportInputSubmittable], + ); + + // Remove from required fields if unmounted. + useEffect(() => () => context?.removeInput(key), []); + + return reportInputSubmittable; +} + +export function SettingsForm(props: React.PropsWithChildren) { + const [inputStatuses, setInputStatuses] = useState<Record<string, boolean>>({}); + + const reportInputSubmittable = useCallback((key: string, submittable: boolean) => { + setInputStatuses((prevInputStatuses) => ({ ...prevInputStatuses, [key]: submittable })); + }, []); + + const removeInput = useCallback((key: string) => { + setInputStatuses((prevInputStatuses) => { + const { [key]: _, ...inputStatuses } = prevInputStatuses; + return inputStatuses; + }); + }, []); + + const value = useMemo( + () => ({ + formSubmittable: Object.values(inputStatuses).every((item) => item === true), + reportInputSubmittable, + removeInput, + }), + [inputStatuses, removeInput, reportInputSubmittable], + ); + + return ( + <settingsFormContext.Provider value={value}>{props.children}</settingsFormContext.Provider> + ); +} |
