api ulandi
This commit is contained in:
51
src/shared/ui/avatar.tsx
Normal file
51
src/shared/ui/avatar.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import * as React from "react"
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
||||
|
||||
import { cn } from "@/shared/lib/utils"
|
||||
|
||||
function Avatar({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
||||
return (
|
||||
<AvatarPrimitive.Root
|
||||
data-slot="avatar"
|
||||
className={cn(
|
||||
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AvatarImage({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
||||
return (
|
||||
<AvatarPrimitive.Image
|
||||
data-slot="avatar-image"
|
||||
className={cn("aspect-square size-full", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AvatarFallback({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
||||
return (
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot="avatar-fallback"
|
||||
className={cn(
|
||||
"bg-muted flex size-full items-center justify-center rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback }
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { XIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/shared/lib/utils";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import * as React from "react";
|
||||
import {
|
||||
Controller,
|
||||
FormProvider,
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
|
||||
import { cn } from "@/shared/lib/utils";
|
||||
import { Label } from "@/shared/ui/label";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const Form = FormProvider;
|
||||
|
||||
@@ -138,6 +139,7 @@ function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
|
||||
|
||||
function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
|
||||
const { error, formMessageId } = useFormField();
|
||||
const { t } = useTranslation();
|
||||
const body = error ? String(error?.message ?? "") : props.children;
|
||||
|
||||
if (!body) {
|
||||
@@ -151,18 +153,18 @@ function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
|
||||
className={cn("text-destructive text-sm", className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
{t(error?.message ? error.message : "")}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
useFormField,
|
||||
Form,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormMessage,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
useFormField,
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ import React, {
|
||||
type ComponentType,
|
||||
type LazyExoticComponent,
|
||||
} from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
// 🔹 Lazy icon faqat tanlangan icon uchun
|
||||
const LazyIcon: React.FC<{ name: string }> = ({ name }) => {
|
||||
@@ -49,6 +50,7 @@ const IconSelect: React.FC<IconSelectProps> = ({
|
||||
setSelectedIcon,
|
||||
}) => {
|
||||
const [icons, setIcons] = useState<string[]>([]);
|
||||
const { t } = useTranslation();
|
||||
const [visibleIcons, setVisibleIcons] = useState<string[]>([]);
|
||||
const [chunkSize] = useState(100);
|
||||
const [index, setIndex] = useState(1);
|
||||
@@ -116,14 +118,14 @@ const IconSelect: React.FC<IconSelectProps> = ({
|
||||
onOpenChange={handleOpenChange}
|
||||
>
|
||||
<SelectTrigger className="!h-12 w-[220px] text-md">
|
||||
<SelectValue placeholder="Ikonka tanlang">
|
||||
<SelectValue placeholder={t("Ikonka tanlang")}>
|
||||
{selectedIcon ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<LazyIcon name={selectedIcon} />
|
||||
{selectedIcon}
|
||||
</div>
|
||||
) : (
|
||||
"Ikonka tanlang"
|
||||
t("Ikonka tanlang")
|
||||
)}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
@@ -151,7 +153,9 @@ const IconSelect: React.FC<IconSelectProps> = ({
|
||||
{!searchTerm && isOpen && (
|
||||
<div ref={loaderRef} className="h-6 flex justify-center items-center">
|
||||
{visibleIcons.length < icons.length && (
|
||||
<span className="text-xs text-gray-400">Yuklanmoqda...</span>
|
||||
<span className="text-xs text-gray-400">
|
||||
{t("Yuklanmoqda...")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
43
src/shared/ui/radio-group.tsx
Normal file
43
src/shared/ui/radio-group.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import * as React from "react"
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
||||
import { CircleIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/shared/lib/utils"
|
||||
|
||||
function RadioGroup({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
|
||||
return (
|
||||
<RadioGroupPrimitive.Root
|
||||
data-slot="radio-group"
|
||||
className={cn("grid gap-3", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function RadioGroupItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
|
||||
return (
|
||||
<RadioGroupPrimitive.Item
|
||||
data-slot="radio-group-item"
|
||||
className={cn(
|
||||
"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator
|
||||
data-slot="radio-group-indicator"
|
||||
className="relative flex items-center justify-center"
|
||||
>
|
||||
<CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
)
|
||||
}
|
||||
|
||||
export { RadioGroup, RadioGroupItem }
|
||||
38
src/shared/ui/sonner.tsx
Normal file
38
src/shared/ui/sonner.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
CircleCheckIcon,
|
||||
InfoIcon,
|
||||
Loader2Icon,
|
||||
OctagonXIcon,
|
||||
TriangleAlertIcon,
|
||||
} from "lucide-react"
|
||||
import { useTheme } from "next-themes"
|
||||
import { Toaster as Sonner, type ToasterProps } from "sonner"
|
||||
|
||||
const Toaster = ({ ...props }: ToasterProps) => {
|
||||
const { theme = "system" } = useTheme()
|
||||
|
||||
return (
|
||||
<Sonner
|
||||
theme={theme as ToasterProps["theme"]}
|
||||
className="toaster group"
|
||||
icons={{
|
||||
success: <CircleCheckIcon className="size-4" />,
|
||||
info: <InfoIcon className="size-4" />,
|
||||
warning: <TriangleAlertIcon className="size-4" />,
|
||||
error: <OctagonXIcon className="size-4" />,
|
||||
loading: <Loader2Icon className="size-4 animate-spin" />,
|
||||
}}
|
||||
style={
|
||||
{
|
||||
"--normal-bg": "var(--popover)",
|
||||
"--normal-text": "var(--popover-foreground)",
|
||||
"--normal-border": "var(--border)",
|
||||
"--border-radius": "var(--radius)",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Toaster }
|
||||
Reference in New Issue
Block a user