🐛 fix(deps): Remove outdated @iconify/react dependency and add iconify-json packages
This commit is contained in:
@@ -21,6 +21,10 @@
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@hookform/resolvers": "^3.10.0",
|
||||
"@iconify-json/flagpack": "^1.2.2",
|
||||
"@iconify-json/mdi": "^1.2.2",
|
||||
"@iconify-json/uil": "^1.2.3",
|
||||
"@iconify/react": "^5.2.0",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@radix-ui/react-accordion": "^1.2.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.4",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { Icon } from '@iconify/react';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import {
|
||||
Command,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
CommandList,
|
||||
} from '@workspace/ui/components/command';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@workspace/ui/components/popover';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { cn } from '@workspace/ui/lib/utils';
|
||||
import { countries, type ICountry } from '@workspace/ui/utils/countries';
|
||||
import { BoxIcon, Check, ChevronsUpDown } from 'lucide-react';
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import { Label } from '@workspace/ui/components/label.js';
|
||||
import { Switch } from '@workspace/ui/components/switch';
|
||||
import { Combobox } from '@workspace/ui/custom-components/combobox';
|
||||
import { EnhancedInput, EnhancedInputProps } from '@workspace/ui/custom-components/enhanced-input';
|
||||
import { cn } from '@workspace/ui/lib/utils';
|
||||
@@ -7,7 +9,7 @@ import { useEffect, useState } from 'react';
|
||||
|
||||
interface FieldConfig extends Omit<EnhancedInputProps, 'type'> {
|
||||
name: string;
|
||||
type: 'text' | 'number' | 'select' | 'time';
|
||||
type: 'text' | 'number' | 'select' | 'time' | 'boolean';
|
||||
options?: { label: string; value: string }[];
|
||||
internal?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -18,6 +20,7 @@ interface ObjectInputProps<T> {
|
||||
value: T;
|
||||
onChange: (value: T) => void;
|
||||
fields: FieldConfig[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -25,6 +28,7 @@ export function ObjectInput<T extends Record<string, any>>({
|
||||
value,
|
||||
onChange,
|
||||
fields,
|
||||
className,
|
||||
}: ObjectInputProps<T>) {
|
||||
const [internalState, setInternalState] = useState<T>(value);
|
||||
|
||||
@@ -32,7 +36,7 @@ export function ObjectInput<T extends Record<string, any>>({
|
||||
setInternalState(value);
|
||||
}, [value]);
|
||||
|
||||
const updateField = (key: keyof T, fieldValue: string | number) => {
|
||||
const updateField = (key: keyof T, fieldValue: string | number | boolean) => {
|
||||
let updatedInternalState = { ...internalState, [key]: fieldValue };
|
||||
fields.forEach((field) => {
|
||||
if (field.calculateValue && field.name === key) {
|
||||
@@ -52,28 +56,44 @@ export function ObjectInput<T extends Record<string, any>>({
|
||||
|
||||
onChange(filteredValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='flex flex-1 flex-wrap gap-4'>
|
||||
{fields.map(({ name, type, options, className, ...fieldProps }) => (
|
||||
<div key={name} className={cn('flex-1', className)}>
|
||||
{type === 'select' && options ? (
|
||||
const renderField = (field: FieldConfig) => {
|
||||
switch (field.type) {
|
||||
case 'select':
|
||||
return (
|
||||
field.options && (
|
||||
<Combobox<string, false>
|
||||
placeholder={fieldProps.placeholder}
|
||||
options={options}
|
||||
value={internalState[name]}
|
||||
onChange={(fieldValue) => {
|
||||
updateField(name, fieldValue);
|
||||
}}
|
||||
placeholder={field.placeholder}
|
||||
options={field.options}
|
||||
value={internalState[field.name]}
|
||||
onChange={(fieldValue) => updateField(field.name, fieldValue)}
|
||||
/>
|
||||
) : (
|
||||
<EnhancedInput
|
||||
value={internalState[name]}
|
||||
onValueChange={(fieldValue) => updateField(name, fieldValue)}
|
||||
type={type}
|
||||
{...fieldProps}
|
||||
)
|
||||
);
|
||||
case 'boolean':
|
||||
return (
|
||||
<div className='flex h-full items-center space-x-2'>
|
||||
<Switch
|
||||
checked={internalState[field.name] as boolean}
|
||||
onCheckedChange={(fieldValue) => updateField(field.name, fieldValue)}
|
||||
/>
|
||||
)}
|
||||
{field.placeholder && <Label>{field.placeholder}</Label>}
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<EnhancedInput
|
||||
value={internalState[field.name]}
|
||||
onValueChange={(fieldValue) => updateField(field.name, fieldValue)}
|
||||
{...field}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className={cn('flex flex-1 flex-wrap gap-4', className)}>
|
||||
{fields.map((field) => (
|
||||
<div key={field.name} className={cn('flex-1', field.className)}>
|
||||
{renderField(field)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -83,6 +103,8 @@ interface ArrayInputProps<T> {
|
||||
value?: T[];
|
||||
onChange: (value: T[]) => void;
|
||||
fields: FieldConfig[];
|
||||
isReverse?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -90,6 +112,8 @@ export function ArrayInput<T extends Record<string, any>>({
|
||||
value = [],
|
||||
onChange,
|
||||
fields,
|
||||
isReverse = false,
|
||||
className,
|
||||
}: ArrayInputProps<T>) {
|
||||
const initializeDefaultItem = (): T =>
|
||||
fields.reduce((acc, field) => {
|
||||
@@ -117,7 +141,11 @@ export function ArrayInput<T extends Record<string, any>>({
|
||||
};
|
||||
|
||||
const createField = () => {
|
||||
setDisplayItems([...displayItems, initializeDefaultItem()]);
|
||||
if (isReverse) {
|
||||
setDisplayItems([initializeDefaultItem(), ...displayItems]);
|
||||
} else {
|
||||
setDisplayItems([...displayItems, initializeDefaultItem()]);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteField = (index: number) => {
|
||||
@@ -142,6 +170,7 @@ export function ArrayInput<T extends Record<string, any>>({
|
||||
value={item}
|
||||
onChange={(updatedItem) => handleItemChange(index, updatedItem)}
|
||||
fields={fields}
|
||||
className={className}
|
||||
/>
|
||||
<div className='flex min-w-20 items-center'>
|
||||
{displayItems.length > 1 && (
|
||||
@@ -155,7 +184,7 @@ export function ArrayInput<T extends Record<string, any>>({
|
||||
<CircleMinusIcon />
|
||||
</Button>
|
||||
)}
|
||||
{index === displayItems.length - 1 && (
|
||||
{(isReverse ? index === 0 : index === displayItems.length - 1) && (
|
||||
<Button
|
||||
variant='ghost'
|
||||
size='icon'
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import { icons as FlagPack } from '@iconify-json/flagpack';
|
||||
import { icons as Mdi } from '@iconify-json/mdi';
|
||||
import { icons as Uil } from '@iconify-json/uil';
|
||||
|
||||
import { addCollection, Icon as Iconify, IconProps } from '@iconify/react';
|
||||
|
||||
addCollection(FlagPack);
|
||||
addCollection(Mdi);
|
||||
addCollection(Uil);
|
||||
|
||||
export function Icon(props: IconProps) {
|
||||
return <Iconify {...props} />;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { Input } from '@workspace/ui/components/input';
|
||||
import { Label } from '@workspace/ui/components/label';
|
||||
import { cn } from '@workspace/ui/lib/utils';
|
||||
import { Upload } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
type ReturnType = 'base64' | 'file';
|
||||
|
||||
interface UploadImageProps {
|
||||
onChange: (value: string | File) => void;
|
||||
returnType?: ReturnType;
|
||||
id?: string;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const UploadImage = ({
|
||||
onChange,
|
||||
returnType = 'base64',
|
||||
id = 'image-upload',
|
||||
children,
|
||||
className,
|
||||
}: UploadImageProps) => {
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
const toBase64 = (file: File): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result as string);
|
||||
reader.onerror = (error) => reject(error);
|
||||
});
|
||||
};
|
||||
|
||||
const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
try {
|
||||
if (returnType === 'base64') {
|
||||
const base64 = await toBase64(file);
|
||||
onChange(base64);
|
||||
} else {
|
||||
onChange(file);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragOver = (e: React.DragEvent<HTMLLabelElement>) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(true);
|
||||
};
|
||||
|
||||
const handleDragLeave = (e: React.DragEvent<HTMLLabelElement>) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(false);
|
||||
};
|
||||
|
||||
const handleDrop = async (e: React.DragEvent<HTMLLabelElement>) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(false);
|
||||
|
||||
const file = e.dataTransfer.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
try {
|
||||
if (returnType === 'base64') {
|
||||
const base64 = await toBase64(file);
|
||||
onChange(base64);
|
||||
} else {
|
||||
onChange(file);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input type='file' accept='image/*' className='hidden' id={id} onChange={handleImageUpload} />
|
||||
<Label
|
||||
htmlFor={id}
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
className={cn(
|
||||
'cursor-pointer',
|
||||
!children && 'flex items-center justify-center rounded-lg border-2 border-dashed p-4',
|
||||
isDragging && 'border-primary bg-muted/50',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children || <Upload />}
|
||||
</Label>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user