feature/frontend-login #10
@@ -1,6 +1,7 @@
|
|||||||
import type { AnyFieldMeta } from '@tanstack/react-form';
|
import type { AnyFieldMeta } from '@tanstack/react-form';
|
||||||
import { LucideEye, LucideEyeClosed } from 'lucide-react';
|
import { LucideEye, LucideEyeClosed } from 'lucide-react';
|
||||||
import { useCallback, useId, useState } from 'react';
|
import { useCallback, useId, useState } from 'react';
|
||||||
|
import { InfoIcon, type InfoIconProps } from '../info';
|
||||||
|
|
||||||
export type TextFieldProps = {
|
export type TextFieldProps = {
|
||||||
label?: string;
|
label?: string;
|
||||||
@@ -8,12 +9,13 @@ export type TextFieldProps = {
|
|||||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
labelProps?: React.LabelHTMLAttributes<HTMLLabelElement>;
|
labelProps?: React.LabelHTMLAttributes<HTMLLabelElement>;
|
||||||
labelDivProps?: React.HTMLAttributes<HTMLDivElement>;
|
labelDivProps?: React.HTMLAttributes<HTMLDivElement>;
|
||||||
|
infoIconProps?: InfoIconProps;
|
||||||
} & React.InputHTMLAttributes<HTMLInputElement> & {
|
} & React.InputHTMLAttributes<HTMLInputElement> & {
|
||||||
type?: 'password';
|
type?: 'password';
|
||||||
showPasswordToggle?: boolean;
|
showPasswordToggle?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function TextField({ label, value, onChange, labelProps, labelDivProps, showPasswordToggle, ...rest }: TextFieldProps) {
|
export function TextField({ label, value, onChange, labelProps, labelDivProps, showPasswordToggle, infoIconProps, ...rest }: TextFieldProps) {
|
||||||
const id = useId();
|
const id = useId();
|
||||||
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
||||||
const handlePasswordVisibilitySet = useCallback(
|
const handlePasswordVisibilitySet = useCallback(
|
||||||
@@ -24,11 +26,13 @@ export function TextField({ label, value, onChange, labelProps, labelDivProps, s
|
|||||||
},
|
},
|
||||||
[rest.type]
|
[rest.type]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label htmlFor={id} style={{ display: 'block', marginBottom: 8 }} {...labelProps}>
|
<label htmlFor={id} style={{ display: 'block', marginBottom: 8 }} {...labelProps}>
|
||||||
{label && (
|
{label && (
|
||||||
<div style={{ fontSize: 12, color: 'var(--gray-9)', marginBottom: 6 }} {...labelDivProps}>
|
<div style={{ fontSize: 12, color: 'var(--gray-9)', marginBottom: 6, display: 'flex', alignItems: 'center' }} {...labelDivProps}>
|
||||||
{label}
|
{label}
|
||||||
|
{infoIconProps && <InfoIcon {...infoIconProps} style={{ marginLeft: 4, verticalAlign: 'middle' }} />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div style={{ position: 'relative', display: 'flex', alignItems: 'center', gap: 8 }}>
|
<div style={{ position: 'relative', display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||||
|
|||||||
59
apps/frontend/app/components/info.tsx
Normal file
59
apps/frontend/app/components/info.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Box } from '@radix-ui/themes';
|
||||||
|
import { Info, type LucideProps } from 'lucide-react';
|
||||||
|
import { Tooltip } from 'radix-ui';
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
export type InfoIconProps = PropsWithChildren<
|
||||||
|
{
|
||||||
|
tooltipContainerProps?: Omit<Tooltip.TooltipContentProps & React.RefAttributes<HTMLDivElement>, 'children'>;
|
||||||
|
} & Omit<LucideProps, 'ref'> &
|
||||||
|
React.RefAttributes<SVGSVGElement>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export function InfoIcon({ tooltipContainerProps, children, ...iconProps }: InfoIconProps) {
|
||||||
|
return (
|
||||||
|
<Tooltip.Root>
|
||||||
|
<Tooltip.Trigger asChild>
|
||||||
|
<Info size={16} {...iconProps} />
|
||||||
|
</Tooltip.Trigger>
|
||||||
|
<Tooltip.Portal>
|
||||||
|
<Tooltip.Content
|
||||||
|
//
|
||||||
|
side="top"
|
||||||
|
align="center"
|
||||||
|
sideOffset={5}
|
||||||
|
alignOffset={0}
|
||||||
|
avoidCollisions={true}
|
||||||
|
style={{
|
||||||
|
color: 'black',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
fontSize: 12,
|
||||||
|
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.3)',
|
||||||
|
border: '1px solid var(--gray-5)',
|
||||||
|
}}
|
||||||
|
{...tooltipContainerProps}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<Tooltip.Arrow className="TooltipArrow" fill="white" />
|
||||||
|
</Tooltip.Content>
|
||||||
|
</Tooltip.Portal>
|
||||||
|
</Tooltip.Root>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TooltipContentContainer({ children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
padding: '8px 12px',
|
||||||
|
color: 'black',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
borderRadius: 4,
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
"generate:openapi": "typed-openapi ../api/swagger.json --tanstack tanstack-client.ts -o ./app/generated/api-client/api-client.ts"
|
"generate:openapi": "typed-openapi ../api/swagger.json --tanstack tanstack-client.ts -o ./app/generated/api-client/api-client.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@radix-ui/themes": "^3.2.1",
|
"@radix-ui/themes": "^3.2.1",
|
||||||
"@react-router/node": "^7.9.2",
|
"@react-router/node": "^7.9.2",
|
||||||
"@react-router/serve": "^7.9.2",
|
"@react-router/serve": "^7.9.2",
|
||||||
|
|||||||
3
apps/frontend/pnpm-lock.yaml
generated
3
apps/frontend/pnpm-lock.yaml
generated
@@ -8,6 +8,9 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@radix-ui/react-tooltip':
|
||||||
|
specifier: ^1.2.8
|
||||||
|
version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
'@radix-ui/themes':
|
'@radix-ui/themes':
|
||||||
specifier: ^3.2.1
|
specifier: ^3.2.1
|
||||||
version: 3.2.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
version: 3.2.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
|
|||||||
Reference in New Issue
Block a user