summaryrefslogtreecommitdiffhomepage
path: root/gui/src/renderer
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2023-12-19 07:52:13 +0100
committerOskar Nyberg <oskar@mullvad.net>2024-01-29 09:44:53 +0100
commitbf2a93dc0468d33079a4050fa3c925428c282b7d (patch)
tree44d91cfa0d2935c570d0552c822c3bed43b9009b /gui/src/renderer
parent3078fd30c1d8c9f3f86d9ddcfeddea3c83718f5b (diff)
downloadmullvadvpn-bf2a93dc0468d33079a4050fa3c925428c282b7d.tar.xz
mullvadvpn-bf2a93dc0468d33079a4050fa3c925428c282b7d.zip
Add form component
Diffstat (limited to 'gui/src/renderer')
-rw-r--r--gui/src/renderer/components/cell/SettingsForm.tsx69
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>
+ );
+}