🐛 fix(ui): Components
This commit is contained in:
@@ -1,52 +1,20 @@
|
||||
// @ts-nocheck
|
||||
// Input component extends from shadcnui - https://ui.shadcn.com/docs/components/input
|
||||
'use client';
|
||||
import { motion, useMotionTemplate, useMotionValue } from 'framer-motion';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
const radius = 100; // change this to increase the rdaius of the hover effect
|
||||
const [visible, setVisible] = React.useState(false);
|
||||
|
||||
let mouseX = useMotionValue(0);
|
||||
let mouseY = useMotionValue(0);
|
||||
|
||||
function handleMouseMove({ currentTarget, clientX, clientY }: any) {
|
||||
let { left, top } = currentTarget.getBoundingClientRect();
|
||||
|
||||
mouseX.set(clientX - left);
|
||||
mouseY.set(clientY - top);
|
||||
}
|
||||
return (
|
||||
<motion.div
|
||||
style={{
|
||||
background: useMotionTemplate`
|
||||
radial-gradient(
|
||||
${visible ? radius + 'px' : '0px'} circle at ${mouseX}px ${mouseY}px,
|
||||
var(--blue-500),
|
||||
transparent 80%
|
||||
)
|
||||
`,
|
||||
}}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseEnter={() => setVisible(true)}
|
||||
onMouseLeave={() => setVisible(false)}
|
||||
className='group/input rounded-lg p-[2px] transition duration-300'
|
||||
>
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
`shadow-input dark:placeholder-text-neutral-600 duration-400 flex h-10 w-full rounded-md border-none bg-gray-50 px-3 py-2 text-sm text-black transition file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-neutral-400 focus-visible:outline-none focus-visible:ring-[2px] focus-visible:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50 group-hover/input:shadow-none dark:bg-zinc-800 dark:text-white dark:shadow-[0px_0px_1px_1px_var(--neutral-700)] dark:focus-visible:ring-neutral-600`,
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
</motion.div>
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
'border-input file:text-foreground placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
// @ts-nocheck
|
||||
// Label component extends from shadcnui - https://ui.shadcn.com/docs/components/label
|
||||
|
||||
'use client';
|
||||
|
||||
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
const labelVariants = cva(
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||
);
|
||||
|
||||
const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'text-sm font-medium leading-none text-black peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-white',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
|
||||
));
|
||||
Label.displayName = LabelPrimitive.Root.displayName;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
'use client';
|
||||
|
||||
import { ViewVerticalIcon } from '@radix-ui/react-icons';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
'use client';
|
||||
|
||||
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
'use client';
|
||||
|
||||
// Inspired by react-hot-toast library
|
||||
|
||||
@@ -3,9 +3,9 @@ module.exports = {
|
||||
root: true,
|
||||
extends: ['@repo/eslint-config/react-internal.js'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: './tsconfig.lint.json',
|
||||
},
|
||||
// parserOptions: {
|
||||
// project: './tsconfig.lint.json',
|
||||
// },
|
||||
rules: {
|
||||
'no-redeclare': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.49",
|
||||
"react": "^18.3.1",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"typescript": "^5.6.3"
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { Button } from '@shadcn/ui/button';
|
||||
import { CircleMinusIcon, CirclePlusIcon } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Combobox } from './combobox';
|
||||
import { EnhancedInput } from './enhanced-input';
|
||||
import { EnhancedInput, EnhancedInputProps } from './enhanced-input';
|
||||
|
||||
interface FieldConfig {
|
||||
interface FieldConfig extends Omit<EnhancedInputProps, 'type'> {
|
||||
name: string;
|
||||
type: 'text' | 'number' | 'select';
|
||||
placeholder?: string;
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
options?: { label: string; value: string }[];
|
||||
internal?: boolean;
|
||||
calculateValue?: (value: Record<string, any>) => any;
|
||||
}
|
||||
|
||||
interface ObjectInputProps<T> {
|
||||
@@ -26,8 +23,31 @@ export function ObjectInput<T extends Record<string, any>>({
|
||||
onChange,
|
||||
fields,
|
||||
}: ObjectInputProps<T>) {
|
||||
const [internalState, setInternalState] = useState<T>(value);
|
||||
|
||||
useEffect(() => {
|
||||
setInternalState(value);
|
||||
}, [value]);
|
||||
|
||||
const updateField = (key: keyof T, fieldValue: string | number) => {
|
||||
onChange({ ...value, [key]: fieldValue });
|
||||
let updatedInternalState = { ...internalState, [key]: fieldValue };
|
||||
fields.forEach((field) => {
|
||||
if (field.calculateValue && field.name === key) {
|
||||
const newValue = field.calculateValue(updatedInternalState);
|
||||
updatedInternalState = newValue;
|
||||
}
|
||||
});
|
||||
setInternalState(updatedInternalState);
|
||||
|
||||
const filteredValue = Object.keys(updatedInternalState).reduce((acc, fieldKey) => {
|
||||
const field = fields.find((f) => f.name === fieldKey);
|
||||
if (field && !field.internal) {
|
||||
acc[fieldKey as keyof T] = updatedInternalState[fieldKey as keyof T];
|
||||
}
|
||||
return acc;
|
||||
}, {} as T);
|
||||
|
||||
onChange(filteredValue);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -38,14 +58,14 @@ export function ObjectInput<T extends Record<string, any>>({
|
||||
<Combobox<string, false>
|
||||
placeholder={fieldProps.placeholder}
|
||||
options={options}
|
||||
value={value[name]}
|
||||
value={internalState[name]}
|
||||
onChange={(fieldValue) => {
|
||||
updateField(name, fieldValue);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<EnhancedInput
|
||||
value={value[name]}
|
||||
value={internalState[name]}
|
||||
onValueChange={(fieldValue) => updateField(name, fieldValue)}
|
||||
type={type}
|
||||
{...fieldProps}
|
||||
@@ -56,7 +76,6 @@ export function ObjectInput<T extends Record<string, any>>({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ArrayInputProps<T> {
|
||||
value?: T[];
|
||||
onChange: (value: T[]) => void;
|
||||
|
||||
@@ -2,7 +2,8 @@ import { Input } from '@shadcn/ui/input';
|
||||
import { cn } from '@shadcn/ui/lib/utils';
|
||||
import { ChangeEvent, ReactNode, useEffect, useState } from 'react';
|
||||
|
||||
interface EnhancedInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
|
||||
export interface EnhancedInputProps
|
||||
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
|
||||
prefix?: ReactNode;
|
||||
suffix?: ReactNode;
|
||||
formatInput?: (value: string | number) => string;
|
||||
@@ -39,7 +40,7 @@ export function EnhancedInput({
|
||||
}, [initialValue, formatInput]);
|
||||
|
||||
const processValue = (inputValue: string) => {
|
||||
let processedValue: number | string = inputValue?.trim();
|
||||
let processedValue: number | string = inputValue?.toString().trim();
|
||||
if (processedValue && props.type === 'number') processedValue = Number(processedValue);
|
||||
return formatOutput ? formatOutput(processedValue) : processedValue;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { evaluate } from 'mathjs';
|
||||
import { evaluate, format } from 'mathjs';
|
||||
|
||||
export function unitConversion(
|
||||
type: 'centsToDollars' | 'dollarsToCents' | 'bitsToMb' | 'mbToBits' | 'bytesToGb' | 'gbToBytes',
|
||||
@@ -22,3 +22,11 @@ export function unitConversion(
|
||||
throw new Error('Invalid conversion type');
|
||||
}
|
||||
}
|
||||
|
||||
export function evaluateWithPrecision(expression: string) {
|
||||
const result = evaluate(expression);
|
||||
|
||||
const formatted = format(result, { notation: 'fixed', precision: 2 });
|
||||
|
||||
return Number(formatted);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
"outDir": "dist",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler"
|
||||
},
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"extends": "@repo/typescript-config/react-library.json",
|
||||
|
||||
Reference in New Issue
Block a user