45 changed files with 323 additions and 602 deletions
@ -5,6 +5,8 @@ specifiers: |
|||
'@hookform/error-message': ^2.0.1 |
|||
'@hookform/resolvers': ^2.9.11 |
|||
'@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-label': ^2.0.0 |
|||
'@radix-ui/react-menubar': ^1.0.0 |
|||
@ -73,7 +75,9 @@ dependencies: |
|||
'@emeraldpay/hashicon-react': 0.5.2 |
|||
'@hookform/error-message': 2.0.1_zf7ga3u4zrffjlingb6kh5ipva |
|||
'@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-label': 2.0.0_biqbaboplfbrettd7655fr4n2y |
|||
'@radix-ui/react-menubar': 1.0.0_zula6vjvt3wdocc4mwcxqa6nzi |
|||
@ -1326,18 +1330,6 @@ packages: |
|||
to-fast-properties: 2.0.0 |
|||
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: |
|||
resolution: {integrity: sha512-XCoYKpq8QQOniiSZf5ouzdvXbKfG6q4ICHRqCO/GNofiF0Ra+LR/7+tomHlXVcLPBS9sDAoZQQw/Sr24KRAbJg==} |
|||
engines: {node: '>=8'} |
|||
@ -1725,18 +1717,6 @@ packages: |
|||
engines: {node: '>=6.0.0'} |
|||
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: |
|||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} |
|||
engines: {node: '>= 8'} |
|||
@ -1779,6 +1759,26 @@ packages: |
|||
'@babel/runtime': 7.20.13 |
|||
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: |
|||
resolution: {integrity: sha512-1yientwXqXcErDHEv8av9ZVNEBldH8L9scVR3is20lL+jOCfcJyMFZFEY5cgIrgexsq1qggSXqiEL/d/4f+QXA==} |
|||
peerDependencies: |
|||
@ -1791,6 +1791,44 @@ packages: |
|||
react-dom: 18.2[email protected] |
|||
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: |
|||
resolution: {integrity: sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==} |
|||
peerDependencies: |
|||
@ -4273,16 +4311,6 @@ packages: |
|||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} |
|||
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: |
|||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} |
|||
engines: {node: '>= 8'} |
|||
@ -6753,11 +6781,6 @@ packages: |
|||
engines: {node: '>=8'} |
|||
dev: true |
|||
|
|||
/sub-events/1.9.0: |
|||
resolution: {integrity: sha512-dnFBayilG9Ku0k/lNs1Y7WV4kv91+ovCoeBV3uIYrY49DylvBb6z9d9ED2ctcrvX2YlReFalpCgJNtSgmrOaJg==} |
|||
engines: {node: '>=10.0.0'} |
|||
dev: false |
|||
|
|||
/supercluster/7.1.5: |
|||
resolution: {integrity: sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==} |
|||
dependencies: |
|||
@ -6974,11 +6997,6 @@ packages: |
|||
/tslib/2.5.0: |
|||
resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} |
|||
|
|||
/tslog/4.7.2: |
|||
resolution: {integrity: sha512-NZCunFmbQK25tt+Egv28MLcmbo8M1HgUy6X2hdVbgrAlcR7zRGvPmM8SnpoljXZ48zHRRYWp9vYIHFHKWsR4HA==} |
|||
engines: {node: '>=16'} |
|||
dev: false |
|||
|
|||
/tsutils/[email protected]: |
|||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} |
|||
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