mirror of
https://github.com/khairul169/vaulterm.git
synced 2026-09-15 17:03:30 +07:00
feat: add hosts management
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import React, { ComponentPropsWithoutRef } from "react";
|
||||
import { Label, Text, View, XStack } from "tamagui";
|
||||
|
||||
type FormFieldProps = ComponentPropsWithoutRef<typeof XStack> & {
|
||||
label?: string;
|
||||
htmlFor?: string;
|
||||
};
|
||||
|
||||
const FormField = ({ label, htmlFor, ...props }: FormFieldProps) => {
|
||||
return (
|
||||
<XStack alignItems="flex-start" {...props}>
|
||||
<Label htmlFor={htmlFor} w={120} $xs={{ w: 100 }}>
|
||||
{label}
|
||||
</Label>
|
||||
<View w="auto" flex={1}>
|
||||
{props.children}
|
||||
</View>
|
||||
</XStack>
|
||||
);
|
||||
};
|
||||
|
||||
type ErrorMessageProps = ComponentPropsWithoutRef<typeof Text> & {
|
||||
error?: unknown | null;
|
||||
};
|
||||
|
||||
export const ErrorMessage = ({ error, ...props }: ErrorMessageProps) => {
|
||||
if (!error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const message = (error as any)?.message || "Something went wrong";
|
||||
|
||||
return (
|
||||
<Text color="red" fontSize="$3" mt="$1" {...props}>
|
||||
{message}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export default FormField;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Controller, FieldValues } from "react-hook-form";
|
||||
import { FormFieldBaseProps } from "./utility";
|
||||
import { Input, View } from "tamagui";
|
||||
import { ComponentPropsWithoutRef } from "react";
|
||||
import { ErrorMessage } from "./form";
|
||||
|
||||
type InputFieldProps<T extends FieldValues> = FormFieldBaseProps<T> &
|
||||
ComponentPropsWithoutRef<typeof Input>;
|
||||
|
||||
export const InputField = <T extends FieldValues>({
|
||||
form,
|
||||
name,
|
||||
...props
|
||||
}: InputFieldProps<T>) => (
|
||||
<Controller
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field, fieldState }) => (
|
||||
<>
|
||||
<Input {...field} {...props} />
|
||||
<ErrorMessage error={fieldState.error} />
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
export default Input;
|
||||
@@ -0,0 +1,91 @@
|
||||
import { Adapt, Button, Dialog, Sheet, Text, View } from "tamagui";
|
||||
import React from "react";
|
||||
import { createDisclosure } from "@/lib/utils";
|
||||
import Icons from "./icons";
|
||||
|
||||
type ModalProps = {
|
||||
disclosure: ReturnType<typeof createDisclosure<any>>;
|
||||
title?: string;
|
||||
description?: string;
|
||||
children?: React.ReactNode;
|
||||
width?: number | string;
|
||||
};
|
||||
|
||||
const Modal = ({
|
||||
disclosure,
|
||||
children,
|
||||
title,
|
||||
description,
|
||||
width = 512,
|
||||
}: ModalProps) => {
|
||||
const { open, onOpenChange } = disclosure.use();
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange} modal>
|
||||
<Adapt when="sm">
|
||||
<Sheet
|
||||
animation="quick"
|
||||
zIndex={999}
|
||||
modal
|
||||
dismissOnSnapToBottom
|
||||
disableDrag
|
||||
>
|
||||
<Sheet.Frame>
|
||||
<Adapt.Contents />
|
||||
</Sheet.Frame>
|
||||
<Sheet.Overlay
|
||||
animation="quicker"
|
||||
enterStyle={{ opacity: 0 }}
|
||||
exitStyle={{ opacity: 0 }}
|
||||
zIndex={0}
|
||||
/>
|
||||
</Sheet>
|
||||
</Adapt>
|
||||
|
||||
<Dialog.Portal zIndex={999}>
|
||||
<Dialog.Overlay
|
||||
key="overlay"
|
||||
animation="quickest"
|
||||
opacity={0.5}
|
||||
enterStyle={{ opacity: 0 }}
|
||||
exitStyle={{ opacity: 0 }}
|
||||
/>
|
||||
|
||||
<Dialog.Content
|
||||
bordered
|
||||
key="content"
|
||||
elevate
|
||||
animateOnly={["transform", "opacity"]}
|
||||
animation={["quickest", { opacity: { overshootClamping: true } }]}
|
||||
enterStyle={{ x: 0, opacity: 0, scale: 0.95 }}
|
||||
exitStyle={{ x: 0, opacity: 0, scale: 0.98 }}
|
||||
p="$1"
|
||||
width="90%"
|
||||
maxWidth={width}
|
||||
height="90%"
|
||||
maxHeight={600}
|
||||
>
|
||||
<View p="$4">
|
||||
<Dialog.Title>{title}</Dialog.Title>
|
||||
<Dialog.Description>{description}</Dialog.Description>
|
||||
<Dialog.Close asChild>
|
||||
<Button
|
||||
position="absolute"
|
||||
top="$2"
|
||||
right="$2"
|
||||
size="$3"
|
||||
bg="$colorTransparent"
|
||||
circular
|
||||
icon={<Icons name="close" size={16} />}
|
||||
/>
|
||||
</Dialog.Close>
|
||||
</View>
|
||||
|
||||
{children}
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default Modal;
|
||||
@@ -1,5 +1,9 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { Controller, FieldValues } from "react-hook-form";
|
||||
import { Select as BaseSelect } from "tamagui";
|
||||
import { FormFieldBaseProps } from "./utility";
|
||||
import { ErrorMessage } from "./form";
|
||||
import Icons from "./icons";
|
||||
|
||||
export type SelectItem = {
|
||||
label: string;
|
||||
@@ -50,7 +54,10 @@ const Select = forwardRef<SelectRef, SelectProps>(
|
||||
key={item.value}
|
||||
value={item.value}
|
||||
index={idx + 1}
|
||||
justifyContent="flex-start"
|
||||
gap="$2"
|
||||
>
|
||||
{value === item.value && <Icons name="check" size={16} />}
|
||||
<BaseSelect.ItemText>{item.label}</BaseSelect.ItemText>
|
||||
</BaseSelect.Item>
|
||||
))}
|
||||
@@ -62,4 +69,24 @@ const Select = forwardRef<SelectRef, SelectProps>(
|
||||
}
|
||||
);
|
||||
|
||||
type SelectFieldProps<T extends FieldValues> = FormFieldBaseProps<T> &
|
||||
SelectProps;
|
||||
|
||||
export const SelectField = <T extends FieldValues>({
|
||||
form,
|
||||
name,
|
||||
...props
|
||||
}: SelectFieldProps<T>) => (
|
||||
<Controller
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field, fieldState }) => (
|
||||
<>
|
||||
<Select w="auto" f={1} {...field} {...props} />
|
||||
<ErrorMessage error={fieldState.error} />
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
export default Select;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { UseZFormReturn } from "@/hooks/useZForm";
|
||||
import { FieldPath, FieldValues } from "react-hook-form";
|
||||
|
||||
export type FormFieldBaseProps<T extends FieldValues> = {
|
||||
form: UseZFormReturn<T>;
|
||||
name: FieldPath<T>;
|
||||
};
|
||||
Reference in New Issue
Block a user