Remove old Select, change Switch for Checkbox

This commit is contained in:
Sacha Weatherstone
2021-11-09 22:55:15 +11:00
parent a839839d2d
commit 38c27da898
19 changed files with 105 additions and 255 deletions

View File

@@ -12,7 +12,7 @@
},
"dependencies": {
"@headlessui/react": "^1.4.2",
"@meshtastic/meshtasticjs": "^0.6.24",
"@meshtastic/meshtasticjs": "^0.6.25",
"@reduxjs/toolkit": "^1.6.2",
"apexcharts": "^3.29.0",
"boring-avatars": "^1.5.8",
@@ -23,7 +23,6 @@
"react-apexcharts": "^1.3.9",
"react-dom": "^17.0.2",
"react-file-icon": "^1.1.0",
"react-flags-select": "^2.1.2",
"react-hook-form": "^7.19.1",
"react-i18next": "^11.13.0",
"react-icons": "^4.3.1",

25
pnpm-lock.yaml generated
View File

@@ -2,7 +2,7 @@ lockfileVersion: 5.3
specifiers:
'@headlessui/react': ^1.4.2
'@meshtastic/meshtasticjs': ^0.6.24
'@meshtastic/meshtasticjs': ^0.6.25
'@reduxjs/toolkit': ^1.6.2
'@snowpack/plugin-dotenv': ^2.2.0
'@snowpack/plugin-postcss': ^1.4.3
@@ -39,7 +39,6 @@ specifiers:
react-apexcharts: ^1.3.9
react-dom: ^17.0.2
react-file-icon: ^1.1.0
react-flags-select: ^2.1.2
react-hook-form: ^7.19.1
react-i18next: ^11.13.0
react-icons: ^4.3.1
@@ -54,7 +53,7 @@ specifiers:
dependencies:
'@headlessui/react': 1.4.2_react-dom@17.0.2+react@17.0.2
'@meshtastic/meshtasticjs': 0.6.24
'@meshtastic/meshtasticjs': 0.6.25
'@reduxjs/toolkit': 1.6.2_react-redux@7.2.6+react@17.0.2
apexcharts: 3.29.0
boring-avatars: 1.5.8
@@ -65,7 +64,6 @@ dependencies:
react-apexcharts: 1.3.9_apexcharts@3.29.0+react@17.0.2
react-dom: 17.0.2_react@17.0.2
react-file-icon: 1.1.0_react-dom@17.0.2+react@17.0.2
react-flags-select: 2.1.2_react-dom@17.0.2+react@17.0.2
react-hook-form: 7.19.1_react@17.0.2
react-i18next: 11.13.0_i18next@21.4.1+react@17.0.2
react-icons: 4.3.1_react@17.0.2
@@ -426,8 +424,8 @@ packages:
resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==}
dev: true
/@meshtastic/meshtasticjs/0.6.24:
resolution: {integrity: sha512-zdIxyIL6a+UUgZdZvxeRhK/xVhLYB0ZeB5OuA9peRFVE/ICHmAhkmNb58G1hDr0Lq3mPtmlsnh0DiPkI5O4TnQ==}
/@meshtastic/meshtasticjs/0.6.25:
resolution: {integrity: sha512-/FVmKIFpzSo2xguJ0+HfnpN9/6TS3ReMOXL29U7CsPnqGwePC3JJd3THNn2+ofOa//Iy7xGTYah0nF+LH1znJQ==}
dependencies:
'@protobuf-ts/runtime': 2.0.7
node-fetch: 3.1.0
@@ -1582,10 +1580,6 @@ packages:
resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==}
dev: true
/classnames/2.3.1:
resolution: {integrity: sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==}
dev: false
/clean-stack/2.2.0:
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
engines: {node: '>=6'}
@@ -4466,17 +4460,6 @@ packages:
tinycolor2: 1.4.2
dev: false
/react-flags-select/2.1.2_react-dom@17.0.2+react@17.0.2:
resolution: {integrity: sha512-nx/6mY/nKVJB9sVZOylJoSI6idTYZfu0dtUQ0N1L+cD8VAPNl5c/lxL7yyi9vtn66hDRFy6Sr16GzsBj3aoZfQ==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
classnames: 2.3.1
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
dev: false
/react-hook-form/7.19.1_react@17.0.2:
resolution: {integrity: sha512-e0Oii07qNAa72JeGUT5czVCMwdAFPxmxYvd1Y9oPy2KVD6ZGblN6DG1G7AwL9Bz2lOPFZu15SRNnn0Vpx/eGdg==}
peerDependencies:

View File

@@ -1,3 +1,5 @@
//@ts-check
/** @type {import("snowpack").SnowpackUserConfig } */
export default {
mount: {

View File

@@ -6,8 +6,8 @@ import { FiEdit3, FiSave } from 'react-icons/fi';
import { Protobuf } from '@meshtastic/meshtasticjs';
import { connection } from '../core/connection.js';
import { EnumSelect } from './generic/form/EnumSelect.jsx';
import { Input } from './generic/form/Input.jsx';
import { Select } from './generic/form/Select.jsx';
import { IconButton } from './generic/IconButton.jsx';
export interface ChannelProps {
@@ -77,7 +77,7 @@ export const Channel = ({ channel }: ChannelProps): JSX.Element => {
<div className="my-auto space-x-2">
<form>
<div className="flex space-x-2">
<EnumSelect
<Select
label="Channel Type"
optionsEnum={Protobuf.Channel_Role}
{...register('role', { valueAsNumber: true })}

View File

@@ -8,7 +8,7 @@ import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
import { Input } from '@components/generic/form/Input';
import { connection } from '@core/connection';
import { EnumSelect } from '../generic/form/EnumSelect.jsx';
import { Select } from '../generic/form/Select.jsx';
import { IconButton } from '../generic/IconButton.jsx';
export const MessageBar = (): JSX.Element => {
@@ -40,7 +40,7 @@ export const MessageBar = (): JSX.Element => {
sendMessage();
}}
>
<EnumSelect
<Select
onChange={(e): void => {
setDestinationNode(parseInt(e.target.value));
}}

View File

@@ -1,84 +0,0 @@
import React from 'react';
import { FiChevronDown } from 'react-icons/fi';
import { Listbox } from '@headlessui/react';
export interface SelectProps {
label: string;
options: {
name: string;
value: string;
icon: JSX.Element;
}[];
id: string;
active: {
name: string;
value: string;
icon: JSX.Element;
};
onChange: (value: string) => void;
}
export const Select = ({
label,
options,
id,
active,
onChange,
}: SelectProps): JSX.Element => {
return (
<div className="w-full">
<label
htmlFor={id}
className="block text-sm font-medium text-black dark:text-white"
>
{label}
</label>
<Listbox value={active.value} onChange={onChange}>
<div className="relative mt-1">
<Listbox.Button className="flex w-full text-left bg-white border rounded-md shadow-sm h-11 focus:outline-none focus:border-primary dark:focus:border-primary dark:bg-secondaryDark dark:border-gray-600 dark:text-white">
<div className="mx-2 my-auto">{active.icon}</div>
<span className="block my-auto truncate">{active.name}</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<FiChevronDown
className="w-5 h-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Listbox.Options className="absolute w-full bg-white border rounded-md shadow-sm focus:outline-none dark:bg-secondaryDark dark:border-gray-600 dark:text-white">
{options.map((option) => (
<Listbox.Option
key={option.value}
className={({ active, selected }): string =>
`cursor-default select-none relative py-2 pr-4 first:rounded-t-md last:rounded-b-md dark:text-white ${
active || selected
? 'bg-gray-200 dark:bg-primaryDark'
: 'text-gray-900'
}`
}
value={option.value}
>
{({ selected }): JSX.Element => (
<>
<span
className={`flex truncate ${
selected ? 'font-medium' : 'font-normal'
}`}
>
<div className="mx-4 my-auto">{option.icon}</div>
{option.name}
</span>
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</div>
</Listbox>
</div>
);
};

View File

@@ -1,73 +0,0 @@
import React from 'react';
import { Switch } from '@headlessui/react';
import { Label } from './form/Label.jsx';
type DefaultButtonProps = JSX.IntrinsicElements['button'];
interface ToggleProps extends DefaultButtonProps {
action?: (enabled: boolean) => void;
label: string;
valid?: boolean;
validationMessage?: string;
checked?: boolean;
}
export const Toggle = ({
action,
label,
valid,
validationMessage,
checked,
id,
...props
}: ToggleProps): JSX.Element => {
const [enabled, setEnabled] = React.useState(false);
React.useEffect(() => {
if (checked !== undefined) {
setEnabled(checked);
}
}, [checked]);
const handleToggle = (enabled: boolean) => {
setEnabled(enabled);
if (action) {
action(enabled);
}
};
return (
<div className="flex flex-col w-full">
<Label label={label} />
{/* <label
htmlFor={id}
className="block text-sm font-medium text-black dark:text-white"
>
{label}
</label> */}
<div className="ml-auto">
<Switch
id={id}
{...props}
checked={enabled}
onChange={handleToggle}
className={`${
enabled ? 'bg-primary' : 'bg-gray-200 dark:bg-secondaryDark'
}
relative inline-flex flex-shrink-0 h-[38px] w-[74px] border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75`}
>
<span className="sr-only">Use setting</span>
<span
aria-hidden="true"
className={`${enabled ? 'translate-x-9' : 'translate-x-0'}
pointer-events-none inline-block h-[34px] w-[34px] rounded-full bg-white shadow-lg transform ring-0 transition ease-in-out duration-200`}
/>
</Switch>
</div>
{!valid && (
<div className="text-sm text-gray-600">{validationMessage}</div>
)}
</div>
);
};

View File

@@ -0,0 +1,49 @@
import React from 'react';
import { Label } from './Label.jsx';
type DefaultInputProps = JSX.IntrinsicElements['input'];
interface CheckboxProps extends DefaultInputProps {
action?: (enabled: boolean) => void;
label: string;
valid?: boolean;
validationMessage?: string;
error?: boolean;
}
export const Checkbox = ({
label,
valid,
validationMessage,
id,
error,
...props
}: CheckboxProps): JSX.Element => {
return (
<div className="flex flex-col w-full">
<Label label={label} />
<div className="ml-auto">
<input
type="checkbox"
id={id}
className={`appearance-none w-8 h-8 border rounded-md focus:outline-none checked:bg-primary checked:border-transparent ${
props.disabled
? 'bg-gray-200 text-gray-500 dark:bg-gray-800 dark:text-gray-400 border-gray-400'
: ''
} ${
error
? 'border-red-500'
: props.disabled
? 'border-gray-200'
: ' focus-within:border-primary hover:border-primary'
}`}
{...props}
/>
</div>
{!valid && (
<div className="text-sm text-gray-600">{validationMessage}</div>
)}
</div>
);
};

View File

@@ -19,7 +19,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
<InputWrapper error={error} disabled={props.disabled}>
<input
ref={ref}
className="w-full h-10 px-3 py-2 bg-transparent focus:outline-none focus:border-primary"
className="w-full h-10 px-3 py-2 bg-transparent focus:outline-none"
{...props}
/>
{action && <div className="flex mr-1">{action}</div>}

View File

@@ -6,8 +6,9 @@ export interface LabelProps {
}
export const Label = ({ label, error }: LabelProps): JSX.Element => (
<label className="text-xs font-semibold text-gray-500">
<label className="flex py-1 text-xs font-semibold text-gray-500">
{label}
{error && <span className="ml-2 text-red-500">{error}</span>}
<div className="flex-grow h-0.5 my-auto ml-2 dark:bg-gray-600 bg-gray-500" />
</label>
);

View File

@@ -16,7 +16,7 @@ interface SelectProps extends DefaultSelectProps {
small?: boolean;
}
export const EnumSelect = React.forwardRef<HTMLSelectElement, SelectProps>(
export const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
({ options, optionsEnum, label, error, small, ...props }, ref) => {
const optionsEnumValues = optionsEnum
? Object.entries(optionsEnum).filter(
@@ -29,7 +29,7 @@ export const EnumSelect = React.forwardRef<HTMLSelectElement, SelectProps>(
<InputWrapper>
<select
ref={ref}
className={`w-full bg-transparent focus:outline-none focus:border-primary ${
className={`w-full bg-white dark:bg-transparent focus:outline-none focus:border-primary ${
small ? 'py-1 mx-1' : 'h-10 mx-2'
}`}
disabled={

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { FiHash } from 'react-icons/fi';
import { EnumSelect } from '@app/components/generic/form/EnumSelect.jsx';
import { Select } from '@app/components/generic/form/Select.jsx';
import { Message } from '@components/chat/Message';
import { MessageBar } from '@components/chat/MessageBar';
import { Protobuf } from '@meshtastic/meshtasticjs';
@@ -19,7 +19,7 @@ export const Messages = (): JSX.Element => {
<div className="flex justify-between w-full px-2 border-b dark:border-gray-600 dark:text-gray-300">
<div className="flex py-2 my-auto text-sm">
<FiHash className="w-4 h-4 my-auto mr-1" />
<EnumSelect
<Select
options={channels
.filter(
(channel) =>

View File

@@ -5,9 +5,9 @@ import { FiMenu, FiTerminal } from 'react-icons/fi';
import { Card } from '@app/components/generic/Card';
import { Chart } from '@app/components/generic/Chart';
import { Checkbox } from '@app/components/generic/form/Checkbox';
import { Input } from '@app/components/generic/form/Input';
import { IconButton } from '@app/components/generic/IconButton.jsx';
import { Toggle } from '@app/components/generic/Toggle';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
import type { Protobuf } from '@meshtastic/meshtasticjs';
@@ -167,7 +167,7 @@ export const Node = ({ navOpen, setNavOpen, node }: NodeProps): JSX.Element => {
<form className="space-y-4">
<Input label={'Device Name'} />
<Input label={'Short Name'} maxLength={3} />
<Toggle label="Licenced Operator?" />
<Checkbox label="Licenced Operator?" />
</form>
</div>
</Card>

View File

@@ -5,9 +5,9 @@ import { useTranslation } from 'react-i18next';
import { FiMenu, FiSave } from 'react-icons/fi';
import { Card } from '@app/components/generic/Card';
import { Checkbox } from '@app/components/generic/form/Checkbox';
import { Input } from '@app/components/generic/form/Input.jsx';
import { IconButton } from '@app/components/generic/IconButton.jsx';
import { Toggle } from '@app/components/generic/Toggle';
import { connection } from '@app/core/connection.js';
import { useAppSelector } from '@app/hooks/redux';
import { Button } from '@components/generic/Button';
@@ -70,31 +70,13 @@ export const RangeTest = ({
<Card title="Range Test" description="Settings">
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form onSubmit={onSubmit}>
<Toggle
<Checkbox
label="Range Test Plugin Enabled?"
checked={preferences.rangeTestPluginEnabled}
action={(checked): void => {
void connection.setPreferences({
...preferences,
rangeTestPluginEnabled: checked,
});
}}
{...register('rangeTestPluginEnabled')}
/>
<Toggle
<Checkbox
label="Range Test Plugin Save?"
checked={preferences.rangeTestPluginEnabled}
action={(checked): void => {
void connection.setPreferences({
...preferences,
rangeTestPluginSave: checked,
});
}}
/>
<Toggle
label="Range Test Plugin Save?"
{...register('rangeTestPluginEnabled', {
valueAsNumber: true,
})}
{...register('rangeTestPluginSave')}
/>
<Input
type="number"

View File

@@ -6,6 +6,7 @@ import { FiMenu, FiSave } from 'react-icons/fi';
import { Channel } from '@app/components/Channel.jsx';
import { Card } from '@app/components/generic/Card';
import { IconButton } from '@app/components/generic/IconButton.jsx';
import { connection } from '@app/core/connection.js';
import { useAppSelector } from '@app/hooks/redux.js';
import { Button } from '@components/generic/Button';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
@@ -67,7 +68,12 @@ export const Channels = ({
))}
<div className="flex space-x-52">
<div className="text-sm font-thin text-gray-400 dark:text-gray-300">
<div
onClick={(): Promise<void> => {
return connection.confirmSetChannel();
}}
className="text-sm font-thin text-gray-400 dark:text-gray-300"
>
Please ensure any changes are working before confirming
</div>
<Button active>Confirm</Button>

View File

@@ -5,10 +5,10 @@ import { useTranslation } from 'react-i18next';
import { FiCheck, FiMenu, FiSave } from 'react-icons/fi';
import { Card } from '@app/components/generic/Card';
import { EnumSelect } from '@app/components/generic/form/EnumSelect';
import { Checkbox } from '@app/components/generic/form/Checkbox';
import { Input } from '@app/components/generic/form/Input';
import { Select } from '@app/components/generic/form/Select';
import { IconButton } from '@app/components/generic/IconButton';
import { Toggle } from '@app/components/generic/Toggle';
import { connection, setConnection } from '@app/core/connection';
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
import { Button } from '@components/generic/Button';
@@ -147,7 +147,7 @@ export const Connection = ({
>
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form className="space-y-2" onSubmit={onSubmit}>
<EnumSelect
<Select
label="Method"
optionsEnum={connType}
value={selectedConnType}
@@ -157,7 +157,7 @@ export const Connection = ({
/>
{selectedConnType === connType.HTTP && (
<>
<EnumSelect
<Select
label="Host Source"
options={[
{
@@ -179,7 +179,7 @@ export const Connection = ({
) : (
<Input label="Host" />
)}
<Toggle label="Use TLS?" />
<Checkbox label="Use TLS?" />
</>
)}
{selectedConnType === connType.BLE && (

View File

@@ -5,8 +5,8 @@ import { useTranslation } from 'react-i18next';
import { FiMenu, FiSave } from 'react-icons/fi';
import { Card } from '@app/components/generic/Card';
import { Checkbox } from '@app/components/generic/form/Checkbox';
import { IconButton } from '@app/components/generic/IconButton.jsx';
import { Toggle } from '@app/components/generic/Toggle';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Button } from '@components/generic/Button';
@@ -81,7 +81,7 @@ export const Device = ({ navOpen, setNavOpen }: DeviceProps): JSX.Element => {
maxLength={3}
{...register('shortName')}
/>
<Toggle label="Licenced Operator?" {...register('isLicensed')} />
<Checkbox label="Licenced Operator?" {...register('isLicensed')} />
</form>
</div>
</Card>

View File

@@ -1,11 +1,10 @@
import React from 'react';
import { Jp, Pt, Us } from 'react-flags-select';
import { useTranslation } from 'react-i18next';
import { FiMenu, FiSave } from 'react-icons/fi';
import { Card } from '@app/components/generic/Card';
import { Select } from '@app/components/generic/Select';
import { Select } from '@app/components/generic/form/Select';
import i18n from '@app/core/translation';
import { Button } from '@components/generic/Button';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
@@ -52,40 +51,23 @@ export const Interface = ({
<div className="w-full max-w-3xl p-10 space-y-2 md:max-w-xl">
<Select
label="Language"
active={
i18n.language === 'en'
? {
name: 'English',
value: 'en',
icon: <Us />,
}
: i18n.language === 'pt'
? { name: 'Português', value: 'pt', icon: <Pt /> }
: i18n.language === 'jp'
? { name: '日本', value: 'jp', icon: <Jp /> }
: { name: 'English', value: 'en', icon: <Us /> }
}
onChange={(value): void => {
void i18n.changeLanguage(value);
}}
id="aaa"
options={[
{
name: 'English',
value: 'en',
icon: <Us className="w-6" />,
},
{
name: 'Português',
value: 'pt',
icon: <Pt className="w-6" />,
},
{
name: '日本',
value: 'jp',
icon: <Jp className="w-6" />,
},
{
name: 'Português',
value: 'pt',
},
]}
onChange={(e): void => {
void i18n.changeLanguage(e.target.value);
}}
/>
</div>
</Card>

View File

@@ -5,9 +5,9 @@ import { useTranslation } from 'react-i18next';
import { FiMenu, FiSave, FiXCircle } from 'react-icons/fi';
import { Card } from '@app/components/generic/Card';
import { EnumSelect } from '@app/components/generic/form/EnumSelect.jsx';
import { Checkbox } from '@app/components/generic/form/Checkbox';
import { Select } from '@app/components/generic/form/Select.jsx';
import { IconButton } from '@app/components/generic/IconButton.jsx';
import { Toggle } from '@app/components/generic/Toggle.jsx';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Button } from '@components/generic/Button';
@@ -89,18 +89,21 @@ export const Radio = ({ navOpen, setNavOpen }: RadioProps): JSX.Element => {
label={'Broadcast Interval (seconds)'}
{...register('positionBroadcastSecs', { valueAsNumber: true })}
/>
<EnumSelect
<Select
label="Position Type"
optionsEnum={Protobuf.PositionFlags}
{...register('positionFlags', { valueAsNumber: true })}
/>
<Toggle label="Use Fixed Position" {...register('fixedPosition')} />
<EnumSelect
<Checkbox
label="Use Fixed Position"
{...register('fixedPosition')}
/>
<Select
label="Location Sharing"
optionsEnum={Protobuf.LocationSharing}
{...register('locationShare', { valueAsNumber: true })}
/>
<EnumSelect
<Select
label="GPS Mode"
optionsEnum={Protobuf.GpsOperation}
{...register('gpsOperation', { valueAsNumber: true })}