45 changed files with 323 additions and 602 deletions
@ -5,6 +5,8 @@ specifiers: |
|||||
'@hookform/error-message': ^2.0.1 |
'@hookform/error-message': ^2.0.1 |
||||
'@hookform/resolvers': ^2.9.11 |
'@hookform/resolvers': ^2.9.11 |
||||
'@meshtastic/meshtasticjs': 2.0.20-1 |
'@meshtastic/meshtasticjs': 2.0.20-1 |
||||
|
'@radix-ui/react-accordion': ^1.1.0 |
||||
|
'@radix-ui/react-checkbox': ^1.0.1 |
||||
'@radix-ui/react-dialog': ^1.0.2 |
'@radix-ui/react-dialog': ^1.0.2 |
||||
'@radix-ui/react-label': ^2.0.0 |
'@radix-ui/react-label': ^2.0.0 |
||||
'@radix-ui/react-menubar': ^1.0.0 |
'@radix-ui/react-menubar': ^1.0.0 |
||||
@ -73,7 +75,9 @@ dependencies: |
|||||
'@emeraldpay/hashicon-react': 0.5.2 |
'@emeraldpay/hashicon-react': 0.5.2 |
||||
'@hookform/error-message': 2.0.1_zf7ga3u4zrffjlingb6kh5ipva |
'@hookform/error-message': 2.0.1_zf7ga3u4zrffjlingb6kh5ipva |
||||
'@hookform/resolvers': 2.9[email protected] |
'@hookform/resolvers': 2.9[email protected] |
||||
'@meshtastic/meshtasticjs': 2.0.20-1 |
'@meshtastic/meshtasticjs': link:../js |
||||
|
'@radix-ui/react-accordion': 1.1.0_biqbaboplfbrettd7655fr4n2y |
||||
|
'@radix-ui/react-checkbox': 1.0.1_biqbaboplfbrettd7655fr4n2y |
||||
'@radix-ui/react-dialog': 1.0.2_zula6vjvt3wdocc4mwcxqa6nzi |
'@radix-ui/react-dialog': 1.0.2_zula6vjvt3wdocc4mwcxqa6nzi |
||||
'@radix-ui/react-label': 2.0.0_biqbaboplfbrettd7655fr4n2y |
'@radix-ui/react-label': 2.0.0_biqbaboplfbrettd7655fr4n2y |
||||
'@radix-ui/react-menubar': 1.0.0_zula6vjvt3wdocc4mwcxqa6nzi |
'@radix-ui/react-menubar': 1.0.0_zula6vjvt3wdocc4mwcxqa6nzi |
||||
@ -1326,18 +1330,6 @@ packages: |
|||||
to-fast-properties: 2.0.0 |
to-fast-properties: 2.0.0 |
||||
dev: true |
dev: true |
||||
|
|
||||
/@buf/meshtastic_protobufs.bufbuild_es/1.0.0-20230211031647-2cce48659fb1.1_@[email protected]: |
|
||||
resolution: {registry: https://buf.build/gen/npm/v1, tarball: https://buf.build/gen/npm/v1/@buf/meshtastic_protobufs.bufbuild_es/1.0.0-20230211031647-2cce48659fb1.1/tarball} |
|
||||
peerDependencies: |
|
||||
'@bufbuild/protobuf': ^1.0.0 |
|
||||
dependencies: |
|
||||
'@bufbuild/protobuf': 1.0.0 |
|
||||
dev: false |
|
||||
|
|
||||
/@bufbuild/protobuf/1.0.0: |
|
||||
resolution: {integrity: sha512-oH3jHBrZ6to8Qf4zLg7O8KqSY42kQZNBRXJRMp5uSi0mqE4L8NbyMnZHeOsbXmTb0xpptRyH11LfS+KeVhXzAA==} |
|
||||
dev: false |
|
||||
|
|
||||
/@emeraldpay/hashicon-react/0.5.2: |
/@emeraldpay/hashicon-react/0.5.2: |
||||
resolution: {integrity: sha512-XCoYKpq8QQOniiSZf5ouzdvXbKfG6q4ICHRqCO/GNofiF0Ra+LR/7+tomHlXVcLPBS9sDAoZQQw/Sr24KRAbJg==} |
resolution: {integrity: sha512-XCoYKpq8QQOniiSZf5ouzdvXbKfG6q4ICHRqCO/GNofiF0Ra+LR/7+tomHlXVcLPBS9sDAoZQQw/Sr24KRAbJg==} |
||||
engines: {node: '>=8'} |
engines: {node: '>=8'} |
||||
@ -1725,18 +1717,6 @@ packages: |
|||||
engines: {node: '>=6.0.0'} |
engines: {node: '>=6.0.0'} |
||||
dev: false |
dev: false |
||||
|
|
||||
/@meshtastic/meshtasticjs/2.0.20-1: |
|
||||
resolution: {integrity: sha512-ShYcQ0/T4reWHcfgMIGhqjBQIIz4GrTZOJ9p+O9ChXxND+sWdmFe4U3e5sCMnEZW33xbI6bgQhq7De2sjqAzcQ==} |
|
||||
dependencies: |
|
||||
'@buf/meshtastic_protobufs.bufbuild_es': 1.0.0-20230211031647-2cce48659fb1.1_@[email protected] |
|
||||
'@bufbuild/protobuf': 1.0.0 |
|
||||
crc: 4.3.2 |
|
||||
sub-events: 1.9.0 |
|
||||
tslog: 4.7.2 |
|
||||
transitivePeerDependencies: |
|
||||
- buffer |
|
||||
dev: false |
|
||||
|
|
||||
/@nodelib/fs.scandir/2.1.5: |
/@nodelib/fs.scandir/2.1.5: |
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} |
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} |
||||
engines: {node: '>= 8'} |
engines: {node: '>= 8'} |
||||
@ -1779,6 +1759,26 @@ packages: |
|||||
'@babel/runtime': 7.20.13 |
'@babel/runtime': 7.20.13 |
||||
dev: false |
dev: false |
||||
|
|
||||
|
/@radix-ui/react-accordion/1.1.0_biqbaboplfbrettd7655fr4n2y: |
||||
|
resolution: {integrity: sha512-CNN9ZBgCK4i4SX7gFk5s8095j55DUWi85vwRNfkfBLs0QdAG5Tb4ku6sBeugCAiLvsmxw481GyNl+C3stoJVBQ==} |
||||
|
peerDependencies: |
||||
|
react: ^16.8 || ^17.0 || ^18.0 |
||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 |
||||
|
dependencies: |
||||
|
'@babel/runtime': 7.20.13 |
||||
|
'@radix-ui/primitive': 1.0.0 |
||||
|
'@radix-ui/react-collapsible': 1.0.1_biqbaboplfbrettd7655fr4n2y |
||||
|
'@radix-ui/react-collection': 1.0.1_biqbaboplfbrettd7655fr4n2y |
||||
|
'@radix-ui/react-compose-refs': 1.0[email protected] |
||||
|
'@radix-ui/react-context': 1.0[email protected] |
||||
|
'@radix-ui/react-direction': 1.0[email protected] |
||||
|
'@radix-ui/react-id': 1.0[email protected] |
||||
|
'@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y |
||||
|
'@radix-ui/react-use-controllable-state': 1.0[email protected] |
||||
|
react: 18.2.0 |
||||
|
react-dom: 18.2[email protected] |
||||
|
dev: false |
||||
|
|
||||
/@radix-ui/react-arrow/1.0.1_biqbaboplfbrettd7655fr4n2y: |
/@radix-ui/react-arrow/1.0.1_biqbaboplfbrettd7655fr4n2y: |
||||
resolution: {integrity: sha512-1yientwXqXcErDHEv8av9ZVNEBldH8L9scVR3is20lL+jOCfcJyMFZFEY5cgIrgexsq1qggSXqiEL/d/4f+QXA==} |
resolution: {integrity: sha512-1yientwXqXcErDHEv8av9ZVNEBldH8L9scVR3is20lL+jOCfcJyMFZFEY5cgIrgexsq1qggSXqiEL/d/4f+QXA==} |
||||
peerDependencies: |
peerDependencies: |
||||
@ -1791,6 +1791,44 @@ packages: |
|||||
react-dom: 18.2[email protected] |
react-dom: 18.2[email protected] |
||||
dev: false |
dev: false |
||||
|
|
||||
|
/@radix-ui/react-checkbox/1.0.1_biqbaboplfbrettd7655fr4n2y: |
||||
|
resolution: {integrity: sha512-TisH0B8hWmYP3ONRduYCyN04rR9yLPIw/Rwyn1RoC1suSoGCa8Wn+YPdSSSarSszeIbcg3p2lBkDp2XXit4sZw==} |
||||
|
peerDependencies: |
||||
|
react: ^16.8 || ^17.0 || ^18.0 |
||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 |
||||
|
dependencies: |
||||
|
'@babel/runtime': 7.20.13 |
||||
|
'@radix-ui/primitive': 1.0.0 |
||||
|
'@radix-ui/react-compose-refs': 1.0[email protected] |
||||
|
'@radix-ui/react-context': 1.0[email protected] |
||||
|
'@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y |
||||
|
'@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y |
||||
|
'@radix-ui/react-use-controllable-state': 1.0[email protected] |
||||
|
'@radix-ui/react-use-previous': 1.0[email protected] |
||||
|
'@radix-ui/react-use-size': 1.0[email protected] |
||||
|
react: 18.2.0 |
||||
|
react-dom: 18.2[email protected] |
||||
|
dev: false |
||||
|
|
||||
|
/@radix-ui/react-collapsible/1.0.1_biqbaboplfbrettd7655fr4n2y: |
||||
|
resolution: {integrity: sha512-0maX4q91iYa4gjt3PsNf7dq/yqSR+HGAE8I5p54dQ6gnveS+ETWlMoijxrhmgV1k8svxpm34mQAtqIrJt4XZmA==} |
||||
|
peerDependencies: |
||||
|
react: ^16.8 || ^17.0 || ^18.0 |
||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 |
||||
|
dependencies: |
||||
|
'@babel/runtime': 7.20.13 |
||||
|
'@radix-ui/primitive': 1.0.0 |
||||
|
'@radix-ui/react-compose-refs': 1.0[email protected] |
||||
|
'@radix-ui/react-context': 1.0[email protected] |
||||
|
'@radix-ui/react-id': 1.0[email protected] |
||||
|
'@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y |
||||
|
'@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y |
||||
|
'@radix-ui/react-use-controllable-state': 1.0[email protected] |
||||
|
'@radix-ui/react-use-layout-effect': 1.0[email protected] |
||||
|
react: 18.2.0 |
||||
|
react-dom: 18.2[email protected] |
||||
|
dev: false |
||||
|
|
||||
/@radix-ui/react-collection/1.0.1_biqbaboplfbrettd7655fr4n2y: |
/@radix-ui/react-collection/1.0.1_biqbaboplfbrettd7655fr4n2y: |
||||
resolution: {integrity: sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==} |
resolution: {integrity: sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==} |
||||
peerDependencies: |
peerDependencies: |
||||
@ -4273,16 +4311,6 @@ packages: |
|||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} |
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} |
||||
dev: true |
dev: true |
||||
|
|
||||
/crc/4.3.2: |
|
||||
resolution: {integrity: sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==} |
|
||||
engines: {node: '>=12'} |
|
||||
peerDependencies: |
|
||||
buffer: '>=6.0.3' |
|
||||
peerDependenciesMeta: |
|
||||
buffer: |
|
||||
optional: true |
|
||||
dev: false |
|
||||
|
|
||||
/cross-spawn/7.0.3: |
/cross-spawn/7.0.3: |
||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} |
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} |
||||
engines: {node: '>= 8'} |
engines: {node: '>= 8'} |
||||
@ -6753,11 +6781,6 @@ packages: |
|||||
engines: {node: '>=8'} |
engines: {node: '>=8'} |
||||
dev: true |
dev: true |
||||
|
|
||||
/sub-events/1.9.0: |
|
||||
resolution: {integrity: sha512-dnFBayilG9Ku0k/lNs1Y7WV4kv91+ovCoeBV3uIYrY49DylvBb6z9d9ED2ctcrvX2YlReFalpCgJNtSgmrOaJg==} |
|
||||
engines: {node: '>=10.0.0'} |
|
||||
dev: false |
|
||||
|
|
||||
/supercluster/7.1.5: |
/supercluster/7.1.5: |
||||
resolution: {integrity: sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==} |
resolution: {integrity: sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==} |
||||
dependencies: |
dependencies: |
||||
@ -6974,11 +6997,6 @@ packages: |
|||||
/tslib/2.5.0: |
/tslib/2.5.0: |
||||
resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} |
resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} |
||||
|
|
||||
/tslog/4.7.2: |
|
||||
resolution: {integrity: sha512-NZCunFmbQK25tt+Egv28MLcmbo8M1HgUy6X2hdVbgrAlcR7zRGvPmM8SnpoljXZ48zHRRYWp9vYIHFHKWsR4HA==} |
|
||||
engines: {node: '>=16'} |
|
||||
dev: false |
|
||||
|
|
||||
/tsutils/[email protected]: |
/tsutils/[email protected]: |
||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} |
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} |
||||
engines: {node: '>= 6'} |
engines: {node: '>= 6'} |
||||
|
|||||
@ -1,46 +0,0 @@ |
|||||
import { Input } from "@components/form/Input.js"; |
|
||||
import { Button } from "@components/UI/Button.js"; |
|
||||
import { useDevice } from "@core/stores/deviceStore.js"; |
|
||||
import { Protobuf } from "@meshtastic/meshtasticjs"; |
|
||||
|
|
||||
enum LocationType { |
|
||||
MGRS, |
|
||||
LatLng, |
|
||||
DecimalDegrees |
|
||||
} |
|
||||
|
|
||||
export const NewLocationMessage = (): JSX.Element => { |
|
||||
const { connection } = useDevice(); |
|
||||
|
|
||||
return ( |
|
||||
<div className="m-4 w-96"> |
|
||||
<form |
|
||||
onSubmit={(e): void => { |
|
||||
e.preventDefault(); |
|
||||
}} |
|
||||
> |
|
||||
<Input label="Name" /> |
|
||||
<Input label="Description" /> |
|
||||
{/* <Select label="Type" value={LocationType.MGRS}> |
|
||||
{renderOptions(LocationType)} |
|
||||
</Select> */} |
|
||||
<Input label="Coordinates" /> |
|
||||
<Button |
|
||||
onClick={() => { |
|
||||
void connection?.sendWaypoint( |
|
||||
new Protobuf.Waypoint({ |
|
||||
latitudeI: Math.floor(3.89103 * 1e7), |
|
||||
longitudeI: Math.floor(105.87005 * 1e7), |
|
||||
name: "TEST", |
|
||||
description: "This is a description" |
|
||||
}), |
|
||||
"broadcast" |
|
||||
); |
|
||||
}} |
|
||||
> |
|
||||
Send |
|
||||
</Button> |
|
||||
</form> |
|
||||
</div> |
|
||||
); |
|
||||
}; |
|
||||
@ -0,0 +1,28 @@ |
|||||
|
import * as React from "react"; |
||||
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; |
||||
|
import { Check } from "lucide-react"; |
||||
|
|
||||
|
import { cn } from "@core/utils/cn.js"; |
||||
|
|
||||
|
const Checkbox = React.forwardRef< |
||||
|
React.ElementRef<typeof CheckboxPrimitive.Root>, |
||||
|
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> |
||||
|
>(({ className, ...props }, ref) => ( |
||||
|
<CheckboxPrimitive.Root |
||||
|
ref={ref} |
||||
|
className={cn( |
||||
|
"peer h-4 w-4 shrink-0 rounded-sm border border-slate-300 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-700 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900", |
||||
|
className |
||||
|
)} |
||||
|
{...props} |
||||
|
> |
||||
|
<CheckboxPrimitive.Indicator |
||||
|
className={cn("flex items-center justify-center")} |
||||
|
> |
||||
|
<Check className="h-4 w-4" /> |
||||
|
</CheckboxPrimitive.Indicator> |
||||
|
</CheckboxPrimitive.Root> |
||||
|
)); |
||||
|
Checkbox.displayName = CheckboxPrimitive.Root.displayName; |
||||
|
|
||||
|
export { Checkbox }; |
||||
@ -1,77 +0,0 @@ |
|||||
import React, { useEffect, useState } from "react"; |
|
||||
|
|
||||
import { bitwiseDecode, bitwiseEncode, enumLike } from "@core/utils/bitwise.js"; |
|
||||
import { InfoWrapper } from "@components/form/InfoWrapper.js"; |
|
||||
// import { Listbox } from "@headlessui/react";
|
|
||||
import { Protobuf } from "@meshtastic/meshtasticjs"; |
|
||||
|
|
||||
export interface BitwiseSelectProps { |
|
||||
label?: string; |
|
||||
description?: string; |
|
||||
error?: string; |
|
||||
selected: number; |
|
||||
decodeEnun: enumLike; |
|
||||
onChange: (value: number) => void; |
|
||||
} |
|
||||
|
|
||||
export const BitwiseSelect = ({ |
|
||||
label, |
|
||||
description, |
|
||||
error, |
|
||||
selected, |
|
||||
decodeEnun, |
|
||||
onChange |
|
||||
}: BitwiseSelectProps): JSX.Element => { |
|
||||
const [decodedSelected, setDecodedSelected] = useState<string[]>([]); |
|
||||
|
|
||||
const options = Object.entries(decodeEnun) |
|
||||
.filter((value) => typeof value[1] !== "number") |
|
||||
.map((value) => { |
|
||||
return { |
|
||||
value: parseInt(value[0]), |
|
||||
label: value[1] |
|
||||
.toString() |
|
||||
.replace("POS_", "") |
|
||||
.toLowerCase() |
|
||||
.toLocaleUpperCase() //TODO: Investigate
|
|
||||
}; |
|
||||
}); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
setDecodedSelected( |
|
||||
bitwiseDecode(selected, Protobuf.Config_PositionConfig_PositionFlags).map( |
|
||||
(flag) => |
|
||||
Protobuf.Config_PositionConfig_PositionFlags[flag] |
|
||||
.replace("POS_", "") |
|
||||
.toLowerCase() |
|
||||
) |
|
||||
); |
|
||||
}, [selected]); |
|
||||
|
|
||||
return ( |
|
||||
<InfoWrapper label={label} description={description} error={error}> |
|
||||
{/* <Listbox |
|
||||
value={bitwiseDecode(selected, decodeEnun)} |
|
||||
onChange={(value) => { |
|
||||
onChange(bitwiseEncode(value)); |
|
||||
}} |
|
||||
multiple |
|
||||
> |
|
||||
<Listbox.Button |
|
||||
className={`bg-orange-100 focus:ring-orange-500 flex h-10 w-full items-center gap-2 rounded-md border-transparent px-3 text-sm focus:border-transparent focus:outline-none focus:ring-2`} |
|
||||
> |
|
||||
{decodedSelected.map((option) => ( |
|
||||
<span className="bg-orange-300 rounded-md p-1">{option}</span> |
|
||||
))} |
|
||||
</Listbox.Button> |
|
||||
<Listbox.Options> |
|
||||
{options.map((option) => ( |
|
||||
<Listbox.Option key={option.value} value={option.value}> |
|
||||
{option.label} |
|
||||
</Listbox.Option> |
|
||||
))} |
|
||||
</Listbox.Options> |
|
||||
</Listbox> */} |
|
||||
</InfoWrapper> |
|
||||
); |
|
||||
}; |
|
||||
@ -1,36 +0,0 @@ |
|||||
import { forwardRef, InputHTMLAttributes } from "react"; |
|
||||
|
|
||||
export interface CheckboxProps extends InputHTMLAttributes<HTMLInputElement> { |
|
||||
label: string; |
|
||||
} |
|
||||
|
|
||||
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>( |
|
||||
function Input({ label, disabled, ...rest }: CheckboxProps, ref) { |
|
||||
return ( |
|
||||
<div className="relative flex items-start"> |
|
||||
<div className="flex h-5 items-center"> |
|
||||
<input |
|
||||
ref={ref} |
|
||||
type="checkbox" |
|
||||
className={`h-4 w-4 rounded border-none bg-backgroundPrimary text-accent focus:outline-none focus:ring-2 focus:ring-accent ${ |
|
||||
disabled |
|
||||
? "bg-orange-50 cursor-not-allowed text-accent brightness-disabled" |
|
||||
: "" |
|
||||
}`}
|
|
||||
disabled={disabled} |
|
||||
{...rest} |
|
||||
/> |
|
||||
</div> |
|
||||
<div className="ml-3 text-sm"> |
|
||||
<label |
|
||||
className={`font-medium ${ |
|
||||
disabled ? "text-textSecondary" : "text-textPrimary" |
|
||||
}`}
|
|
||||
> |
|
||||
{label} |
|
||||
</label> |
|
||||
</div> |
|
||||
</div> |
|
||||
); |
|
||||
} |
|
||||
); |
|
||||
@ -1,22 +0,0 @@ |
|||||
import type { ReactNode } from "react"; |
|
||||
|
|
||||
export interface FormSectionProps { |
|
||||
title: string; |
|
||||
children: ReactNode; |
|
||||
} |
|
||||
|
|
||||
export const FormSection = ({ |
|
||||
title, |
|
||||
children |
|
||||
}: FormSectionProps): JSX.Element => { |
|
||||
return ( |
|
||||
<div className="relative"> |
|
||||
<h3 className="absolute left-2 -top-2 bg-backgroundSecondary px-1 text-lg font-medium text-textPrimary"> |
|
||||
{title} |
|
||||
</h3> |
|
||||
<div className="mt-2 rounded-md border-2 border-backgroundPrimary p-2"> |
|
||||
{children} |
|
||||
</div> |
|
||||
</div> |
|
||||
); |
|
||||
}; |
|
||||
@ -1,72 +0,0 @@ |
|||||
import { forwardRef, useEffect } from "react"; |
|
||||
import { InfoWrapper } from "@components/form/InfoWrapper.js"; |
|
||||
import { useState } from "react"; |
|
||||
import type { InputProps } from "@components/form/Input.js"; |
|
||||
|
|
||||
export const IPInput = forwardRef<HTMLInputElement, InputProps>(function Input( |
|
||||
{ |
|
||||
label, |
|
||||
description, |
|
||||
prefix, |
|
||||
suffix, |
|
||||
action, |
|
||||
error, |
|
||||
disabled, |
|
||||
value, |
|
||||
...rest |
|
||||
}: InputProps, |
|
||||
ref |
|
||||
) { |
|
||||
const [numericalValue, setNumericalValue] = useState<number>(); |
|
||||
const [facadeInputValue, setFacadeInputValue] = useState<string>(); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
if (typeof value === "number") { |
|
||||
setFacadeInputValue( |
|
||||
value |
|
||||
.toString(16) |
|
||||
.match(/.{1,3}/g) |
|
||||
?.map((v) => parseInt(v, 10)) |
|
||||
?.join(".") |
|
||||
); |
|
||||
} |
|
||||
}, [value]); |
|
||||
|
|
||||
return ( |
|
||||
<InfoWrapper label={label} description={description} error={error}> |
|
||||
<div className="relative flex rounded-md"> |
|
||||
<input |
|
||||
value={numericalValue} |
|
||||
onChange={(e) => setNumericalValue(parseInt(e.target.value))} |
|
||||
ref={ref} |
|
||||
hidden |
|
||||
/> |
|
||||
<input |
|
||||
value={facadeInputValue} |
|
||||
onChange={(e) => { |
|
||||
setFacadeInputValue(e.target.value); |
|
||||
setNumericalValue( |
|
||||
parseInt( |
|
||||
e.target.value |
|
||||
.split(".") |
|
||||
.map((v) => parseInt(v).toString(16)) |
|
||||
.join(""), |
|
||||
16 |
|
||||
) |
|
||||
); |
|
||||
}} |
|
||||
className={`flex h-10 w-full rounded-md border-none bg-backgroundPrimary px-3 text-sm text-textPrimary focus:outline-none focus:ring-2 focus:ring-accent ${ |
|
||||
prefix ? "rounded-l-none" : "" |
|
||||
} ${action ? "rounded-r-none" : ""} ${ |
|
||||
disabled |
|
||||
? "cursor-not-allowed text-textSecondary brightness-disabled hover:brightness-disabled" |
|
||||
: "" |
|
||||
}`}
|
|
||||
disabled={disabled} |
|
||||
step="any" |
|
||||
{...rest} |
|
||||
/> |
|
||||
</div> |
|
||||
</InfoWrapper> |
|
||||
); |
|
||||
}); |
|
||||
@ -1,38 +0,0 @@ |
|||||
import { AlertCircleIcon } from "lucide-react"; |
|
||||
import type { ReactNode } from "react"; |
|
||||
|
|
||||
export interface InfoWrapperProps { |
|
||||
label?: string; |
|
||||
description?: string; |
|
||||
error?: string; |
|
||||
children: ReactNode; |
|
||||
} |
|
||||
|
|
||||
export const InfoWrapper = ({ |
|
||||
label, |
|
||||
description, |
|
||||
error, |
|
||||
children |
|
||||
}: InfoWrapperProps): JSX.Element => { |
|
||||
return ( |
|
||||
<div className="w-full"> |
|
||||
{/* Label */} |
|
||||
{label && ( |
|
||||
<label className="block text-sm font-medium text-textPrimary"> |
|
||||
{label} |
|
||||
</label> |
|
||||
)} |
|
||||
{/* */} |
|
||||
{children} |
|
||||
{error && ( |
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3"> |
|
||||
<AlertCircleIcon size={16} className="text-red-500" /> |
|
||||
</div> |
|
||||
)} |
|
||||
{description && ( |
|
||||
<p className="mt-2 text-sm text-textSecondary">{description}</p> |
|
||||
)} |
|
||||
{error && <p className="mt-2 text-sm text-red-600">{error}</p>} |
|
||||
</div> |
|
||||
); |
|
||||
}; |
|
||||
@ -1,72 +0,0 @@ |
|||||
import { forwardRef, InputHTMLAttributes } from "react"; |
|
||||
import { InfoWrapper, InfoWrapperProps } from "@components/form/InfoWrapper.js"; |
|
||||
import { AlertCircleIcon } from "lucide-react"; |
|
||||
|
|
||||
export interface InputProps |
|
||||
extends InputHTMLAttributes<HTMLInputElement>, |
|
||||
Omit<InfoWrapperProps, "children"> { |
|
||||
prefix?: string; |
|
||||
suffix?: string; |
|
||||
action?: { |
|
||||
icon: JSX.Element; |
|
||||
action: () => void; |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>(function Input( |
|
||||
{ |
|
||||
label, |
|
||||
description, |
|
||||
prefix, |
|
||||
suffix, |
|
||||
action, |
|
||||
error, |
|
||||
disabled, |
|
||||
...rest |
|
||||
}: InputProps, |
|
||||
ref |
|
||||
) { |
|
||||
return ( |
|
||||
<InfoWrapper label={label} description={description} error={error}> |
|
||||
<div className="relative flex rounded-md"> |
|
||||
{prefix && ( |
|
||||
<span className="inline-flex items-center rounded-l-md bg-backgroundPrimary px-3 font-mono text-sm text-textSecondary brightness-hover"> |
|
||||
{prefix} |
|
||||
</span> |
|
||||
)} |
|
||||
<input |
|
||||
ref={ref} |
|
||||
className={`flex h-10 w-full rounded-md border-none bg-backgroundPrimary px-3 text-sm text-textPrimary focus:outline-none focus:ring-2 focus:ring-accent ${ |
|
||||
prefix ? "rounded-l-none" : "" |
|
||||
} ${action ? "rounded-r-none" : ""} ${ |
|
||||
disabled |
|
||||
? "cursor-not-allowed text-textSecondary brightness-disabled hover:brightness-disabled" |
|
||||
: "" |
|
||||
}`}
|
|
||||
disabled={disabled} |
|
||||
step="any" |
|
||||
{...rest} |
|
||||
/> |
|
||||
{suffix && ( |
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3 font-mono text-textSecondary"> |
|
||||
<span className="text-gray-500 sm:text-sm">{suffix}</span> |
|
||||
</div> |
|
||||
)} |
|
||||
{action && ( |
|
||||
<button |
|
||||
type="button" |
|
||||
onClick={action.action} |
|
||||
className="relative -ml-px inline-flex items-center space-x-2 rounded-r-md bg-backgroundPrimary px-4 py-2 text-sm font-medium text-textSecondary brightness-hover hover:text-accent hover:brightness-hover focus:outline-none focus:ring-2 focus:ring-accent active:brightness-press" |
|
||||
> |
|
||||
{action.icon} |
|
||||
</button> |
|
||||
)} |
|
||||
{error && ( |
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3"> |
|
||||
<AlertCircleIcon size={16} className="text-red-500" /> |
|
||||
</div> |
|
||||
)} |
|
||||
</div> |
|
||||
</InfoWrapper> |
|
||||
); |
|
||||
}); |
|
||||
@ -1,28 +0,0 @@ |
|||||
import { Switch } from "../UI/Switch.js"; |
|
||||
import { InfoWrapper } from "./InfoWrapper.js"; |
|
||||
|
|
||||
export interface ToggleProps { |
|
||||
checked: boolean; |
|
||||
label?: string; |
|
||||
description?: string; |
|
||||
disabled?: boolean; |
|
||||
onChange?: (checked: boolean) => void; |
|
||||
} |
|
||||
|
|
||||
export const Toggle = ({ |
|
||||
checked, |
|
||||
label, |
|
||||
description, |
|
||||
disabled, |
|
||||
onChange |
|
||||
}: ToggleProps): JSX.Element => { |
|
||||
return ( |
|
||||
<InfoWrapper label={label} description={description}> |
|
||||
<Switch |
|
||||
checked={checked} |
|
||||
disabled={disabled} |
|
||||
onCheckedChange={onChange} |
|
||||
/> |
|
||||
</InfoWrapper> |
|
||||
); |
|
||||
}; |
|
||||
Loading…
Reference in new issue