Skip to content

Commit

Permalink
Make save button be enabled properly for system settings
Browse files Browse the repository at this point in the history
  • Loading branch information
mairas committed Feb 1, 2024
1 parent 654d9ad commit 6b73e0d
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 23 deletions.
11 changes: 11 additions & 0 deletions web/src/common/InputDirtyContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createContext } from "preact";

export interface InputDirtyContextType {
isInputDirty: boolean;
setInputDirty: (value: boolean) => void;
}

export const InputDirtyContext = createContext<InputDirtyContextType>({
isInputDirty: false,
setInputDirty: (v) => {},
});
64 changes: 58 additions & 6 deletions web/src/components/Form.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import {
InputDirtyContext,
InputDirtyContextType,
} from "common/InputDirtyContext";
import { type JSX } from "preact";
import { useContext } from "preact/hooks";

interface FormInputProps {
id: string;
Expand All @@ -15,16 +20,26 @@ interface FormInputProps {
}

export function FormInput(props: FormInputProps): JSX.Element {
const { isInputDirty, setInputDirty } =
useContext<InputDirtyContextType>(InputDirtyContext);
return (
<div>
<label className="form-label" htmlFor={props.id}>
{props.label}
</label>

{props.as === "textarea" ? (
<textarea className="form-control" {...props} />
<textarea
className="form-control"
onChange={() => setInputDirty(true)}
{...props}
/>
) : (
<input className="form-control" {...props} />
<input
className="form-control"
onChange={() => setInputDirty(true)}
{...props}
/>
)}
</div>
);
Expand All @@ -36,9 +51,16 @@ interface FormFloatInputProps {
}

export function FormFloatInput(props: FormFloatInputProps): JSX.Element {
const { isInputDirty, setInputDirty } =
useContext<InputDirtyContextType>(InputDirtyContext);

return (
<div className="form-floating mb-3">
<input className="form-control" {...props} />
<input
className="form-control"
onChange={() => setInputDirty(true)}
{...props}
/>
<label className="form-label" htmlFor={props.id}>
{props.label}
</label>
Expand All @@ -53,12 +75,19 @@ interface FormSelectProps {
}

export function FormSelect(props: FormSelectProps): JSX.Element {
const { isInputDirty, setInputDirty } =
useContext<InputDirtyContextType>(InputDirtyContext);

return (
<div className="mb-3">
<label className="form-label" htmlFor={props.id}>
{props.label}
</label>
<select className="form-select" {...props} />
<select
className="form-select"
onChange={() => setInputDirty(true)}
{...props}
/>
{props.children}
</div>
);
Expand All @@ -74,9 +103,16 @@ interface FormCheckProps {
}

export function FormCheck(props: FormCheckProps): JSX.Element {
const { isInputDirty, setInputDirty } =
useContext<InputDirtyContextType>(InputDirtyContext);

return (
<div className="form-check">
<input className="form-check-input" {...props} />
<input
className="form-check-input"
onChange={() => setInputDirty(true)}
{...props}
/>
<label className="form-check-label" htmlFor={props.id}>
{props.label}
</label>
Expand All @@ -87,9 +123,25 @@ export function FormCheck(props: FormCheckProps): JSX.Element {
export function FormSwitch(
props: JSX.IntrinsicAttributes & JSX.HTMLAttributes<HTMLInputElement>,
): JSX.Element {
const { isInputDirty, setInputDirty } =
useContext<InputDirtyContextType>(InputDirtyContext);

// Extend props.onChange (if it exists) to set the dirty flag
if (props.onChange) {
const onChange = props.onChange;
props.onChange = (e) => {
setInputDirty(true);
onChange(e);
};
}

return (
<div className="form-check form-switch mb-3">
<input className="form-check-input" role="switch" {...props} />
<input
className="form-check-input"
role="switch"
{...props}
/>
<label className="form-check-label" htmlFor={props.id}>
{props.label}
</label>
Expand Down
39 changes: 22 additions & 17 deletions web/src/pages/System/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { InputDirtyContext } from "common/InputDirtyContext";
import { ButtonCard } from "components/Card";
import { FormInput, FormSwitch } from "components/Form";
import { AppPage } from "pages/AppPage";
Expand Down Expand Up @@ -35,7 +36,7 @@ function SystemCards(): JSX.Element {
<SystemSettingsCard title={<h5 className="card-title">Device Name</h5>}>
<p className="card-text">
The device name is used to identify this device on the network. It is
used both as a hostname (e.g. &quot;my-device.local&quot;) and as an
used both as a hostname (e.g. <code>my-device.local</code>) and as an
identifying name in the Signal K network.
</p>
<FormInput
Expand All @@ -58,8 +59,8 @@ function AuthCard(): JSX.Element {
<SystemSettingsCard title={<h5 className="card-title">Authentication</h5>}>
<p className="card-text">
Authentication is used to restrict access to the configuration
interface. You should disable authentication only if
you are using this device on a trusted private network.
interface. You should disable authentication only if you are using this
device on a trusted private network.
</p>
<div>
<FormSwitch
Expand Down Expand Up @@ -113,20 +114,24 @@ function SystemSettingsCard({ title, children }: SystemCardProps): JSX.Element {
}

return (
<SystemCard>
<div className="card-header">{title}</div>
<div className="card-body">
{children}
<button
type="button"
className="btn btn-primary mt-3"
disabled={!isDirty}
onClick={handleSave}
>
Save
</button>
</div>
</SystemCard>
<InputDirtyContext.Provider
value={{ isInputDirty: isDirty, setInputDirty: setIsDirty }}
>
<SystemCard>
<div className="card-header">{title}</div>
<div className="card-body">
{children}
<button
type="button"
className="btn btn-primary mt-3"
disabled={!isDirty}
onClick={handleSave}
>
Save
</button>
</div>
</SystemCard>
</InputDirtyContext.Provider>
);
}

Expand Down

0 comments on commit 6b73e0d

Please sign in to comment.