mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 02:33:01 +00:00
use Icon component
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { UserRound } from "lucide-react";
|
|
||||||
import { useSession } from "@/components/session-provider";
|
import { useSession } from "@/components/session-provider";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const FALLBACK_COLOURS = [
|
const FALLBACK_COLOURS = [
|
||||||
@@ -89,7 +89,7 @@ export default function Avatar({
|
|||||||
) : name ? (
|
) : name ? (
|
||||||
<span className={textClass}>{getInitials(name)}</span>
|
<span className={textClass}>{getInitials(name)}</span>
|
||||||
) : (
|
) : (
|
||||||
<UserRound className={"size-10"} />
|
<Icon icon="userRound" className={"size-10"} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { IssueResponse, ProjectResponse, SprintRecord, UserRecord } from "@sprint/shared";
|
import type { IssueResponse, ProjectResponse, SprintRecord, UserRecord } from "@sprint/shared";
|
||||||
import { Check, Link, Trash, X } from "lucide-react";
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { MultiAssigneeSelect } from "@/components/multi-assignee-select";
|
import { MultiAssigneeSelect } from "@/components/multi-assignee-select";
|
||||||
@@ -10,6 +9,7 @@ import StatusTag from "@/components/status-tag";
|
|||||||
import { TimerDisplay } from "@/components/timer-display";
|
import { TimerDisplay } from "@/components/timer-display";
|
||||||
import { TimerModal } from "@/components/timer-modal";
|
import { TimerModal } from "@/components/timer-modal";
|
||||||
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { SelectTrigger } from "@/components/ui/select";
|
import { SelectTrigger } from "@/components/ui/select";
|
||||||
import { issue } from "@/lib/server";
|
import { issue } from "@/lib/server";
|
||||||
import { issueID } from "@/lib/utils";
|
import { issueID } from "@/lib/utils";
|
||||||
@@ -246,13 +246,13 @@ export function IssueDetailPane({
|
|||||||
</span>
|
</span>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<IconButton onClick={handleCopyLink} title={linkCopied ? "Copied" : "Copy link"}>
|
<IconButton onClick={handleCopyLink} title={linkCopied ? "Copied" : "Copy link"}>
|
||||||
{linkCopied ? <Check /> : <Link />}
|
{linkCopied ? <Icon icon="check" /> : <Icon icon="link" />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton variant="destructive" onClick={handleDelete} title={"Delete issue"}>
|
<IconButton variant="destructive" onClick={handleDelete} title={"Delete issue"}>
|
||||||
<Trash />
|
<Icon icon="trash" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton onClick={close} title={"Close"}>
|
<IconButton onClick={close} title={"Close"}>
|
||||||
<X />
|
<Icon icon="x" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { LogOut } from "lucide-react";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { clearAuth, cn, getCsrfToken, getServerURL } from "@/lib/utils";
|
import { clearAuth, cn, getCsrfToken, getServerURL } from "@/lib/utils";
|
||||||
|
|
||||||
export default function LogOutButton({
|
export default function LogOutButton({
|
||||||
@@ -37,7 +37,7 @@ export default function LogOutButton({
|
|||||||
size={noStyle ? "none" : "default"}
|
size={noStyle ? "none" : "default"}
|
||||||
>
|
>
|
||||||
Log out
|
Log out
|
||||||
<LogOut size={15} />
|
<Icon icon="logOut" size={15} />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
/** biome-ignore-all lint/correctness/useExhaustiveDependencies: <> */
|
/** biome-ignore-all lint/correctness/useExhaustiveDependencies: <> */
|
||||||
|
|
||||||
import { USER_NAME_MAX_LENGTH, USER_USERNAME_MAX_LENGTH } from "@sprint/shared";
|
import { USER_NAME_MAX_LENGTH, USER_USERNAME_MAX_LENGTH } from "@sprint/shared";
|
||||||
import { AlertTriangle, X } from "lucide-react";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
import Avatar from "@/components/avatar";
|
import Avatar from "@/components/avatar";
|
||||||
@@ -10,6 +9,7 @@ import { useSession } from "@/components/session-provider";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
import { Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||||
import { Field } from "@/components/ui/field";
|
import { Field } from "@/components/ui/field";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { IconButton } from "@/components/ui/icon-button";
|
import { IconButton } from "@/components/ui/icon-button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { UploadAvatar } from "@/components/upload-avatar";
|
import { UploadAvatar } from "@/components/upload-avatar";
|
||||||
@@ -153,9 +153,9 @@ export default function LogInForm() {
|
|||||||
setShowWarning(false);
|
setShowWarning(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<X />
|
<Icon icon="x" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<AlertTriangle className="w-16 h-16 text-yellow-500" strokeWidth={1.5} />
|
<Icon icon="alertTriangle" className="w-16 h-16 text-yellow-500" />
|
||||||
<div className="text-center text-sm text-muted-foreground font-500">
|
<div className="text-center text-sm text-muted-foreground font-500">
|
||||||
<p>
|
<p>
|
||||||
This application is currently under construction. Your data is very likely to be
|
This application is currently under construction. Your data is very likely to be
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { UserRecord } from "@sprint/shared";
|
import type { UserRecord } from "@sprint/shared";
|
||||||
import { Plus } from "lucide-react";
|
import Icon from "@/components/ui/icon";
|
||||||
import { IconButton } from "@/components/ui/icon-button";
|
import { IconButton } from "@/components/ui/icon-button";
|
||||||
import { UserSelect } from "@/components/user-select";
|
import { UserSelect } from "@/components/user-select";
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ export function MultiAssigneeSelect({
|
|||||||
</div>
|
</div>
|
||||||
{index === assigneeIds.length - 1 && canAddMore && (
|
{index === assigneeIds.length - 1 && canAddMore && (
|
||||||
<IconButton onClick={handleAddAssignee} title={"Add assignee"} className="w-9 h-9">
|
<IconButton onClick={handleAddAssignee} title={"Add assignee"} className="w-9 h-9">
|
||||||
<Plus className="h-4 w-4" />
|
<Icon icon="plus" className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
type ProjectResponse,
|
type ProjectResponse,
|
||||||
type SprintRecord,
|
type SprintRecord,
|
||||||
} from "@sprint/shared";
|
} from "@sprint/shared";
|
||||||
import { ChevronDown, ChevronUp, EllipsisVertical, Plus, X } from "lucide-react";
|
|
||||||
import { type ReactNode, useCallback, useEffect, useState } from "react";
|
import { type ReactNode, useCallback, useEffect, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { AddMemberDialog } from "@/components/add-member-dialog";
|
import { AddMemberDialog } from "@/components/add-member-dialog";
|
||||||
@@ -28,6 +27,7 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { IconButton } from "@/components/ui/icon-button";
|
import { IconButton } from "@/components/ui/icon-button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
@@ -571,9 +571,15 @@ function OrganisationsDialog({
|
|||||||
>
|
>
|
||||||
{member.OrganisationMember.role ===
|
{member.OrganisationMember.role ===
|
||||||
"admin" ? (
|
"admin" ? (
|
||||||
<ChevronDown className="size-5" />
|
<Icon
|
||||||
|
icon="chevronDown"
|
||||||
|
className="size-5"
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ChevronUp className="size-5" />
|
<Icon
|
||||||
|
icon="chevronUp"
|
||||||
|
className="size-5"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -585,7 +591,7 @@ function OrganisationsDialog({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<X className="size-5" />
|
<Icon icon="x" className="size-5" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -609,7 +615,7 @@ function OrganisationsDialog({
|
|||||||
}}
|
}}
|
||||||
trigger={
|
trigger={
|
||||||
<Button variant="outline">
|
<Button variant="outline">
|
||||||
Add user <Plus className="size-4" />
|
Add user <Icon icon="plus" className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -683,7 +689,10 @@ function OrganisationsDialog({
|
|||||||
trigger={
|
trigger={
|
||||||
<Button variant="outline" size="sm">
|
<Button variant="outline" size="sm">
|
||||||
Create sprint{" "}
|
Create sprint{" "}
|
||||||
<Plus className="size-4" />
|
<Icon
|
||||||
|
icon="plus"
|
||||||
|
className="size-4"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
sprints={sprints}
|
sprints={sprints}
|
||||||
@@ -726,7 +735,10 @@ function OrganisationsDialog({
|
|||||||
noStyle
|
noStyle
|
||||||
className="hover:opacity-80 cursor-pointer"
|
className="hover:opacity-80 cursor-pointer"
|
||||||
>
|
>
|
||||||
<EllipsisVertical className="size-4 text-foreground" />
|
<Icon
|
||||||
|
icon="ellipsisVertical"
|
||||||
|
className="size-4 text-foreground"
|
||||||
|
/>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
align="end"
|
align="end"
|
||||||
@@ -740,7 +752,10 @@ function OrganisationsDialog({
|
|||||||
}
|
}
|
||||||
className="hover:bg-primary-foreground"
|
className="hover:bg-primary-foreground"
|
||||||
>
|
>
|
||||||
<ChevronUp className="size-4 text-muted-foreground" />
|
<Icon
|
||||||
|
icon="chevronUp"
|
||||||
|
className="size-4 text-muted-foreground"
|
||||||
|
/>
|
||||||
Move up
|
Move up
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
@@ -753,7 +768,10 @@ function OrganisationsDialog({
|
|||||||
}
|
}
|
||||||
className="hover:bg-primary-foreground"
|
className="hover:bg-primary-foreground"
|
||||||
>
|
>
|
||||||
<ChevronDown className="size-4 text-muted-foreground" />
|
<Icon
|
||||||
|
icon="chevronDown"
|
||||||
|
className="size-4 text-muted-foreground"
|
||||||
|
/>
|
||||||
Move down
|
Move down
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
@@ -766,7 +784,7 @@ function OrganisationsDialog({
|
|||||||
}
|
}
|
||||||
className="hover:bg-destructive/10"
|
className="hover:bg-destructive/10"
|
||||||
>
|
>
|
||||||
<X className="size-4" />
|
<Icon icon="x" className="size-4" />
|
||||||
Remove
|
Remove
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
@@ -815,7 +833,7 @@ function OrganisationsDialog({
|
|||||||
ISSUE_STATUS_MAX_LENGTH
|
ISSUE_STATUS_MAX_LENGTH
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Plus className="size-4" />
|
<Icon icon="plus" className="size-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
{statusError && (
|
{statusError && (
|
||||||
@@ -833,7 +851,7 @@ function OrganisationsDialog({
|
|||||||
}}
|
}}
|
||||||
className="flex gap-2 w-full min-w-0"
|
className="flex gap-2 w-full min-w-0"
|
||||||
>
|
>
|
||||||
Create status <Plus className="size-4" />
|
Create status <Icon icon="plus" className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CheckIcon, ServerIcon, Undo2 } from "lucide-react";
|
|
||||||
import { type ReactNode, useState } from "react";
|
import { type ReactNode, useState } from "react";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { IconButton } from "@/components/ui/icon-button";
|
import { IconButton } from "@/components/ui/icon-button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
@@ -118,7 +118,7 @@ export function ServerConfigurationDialog({ trigger }: { trigger?: ReactNode })
|
|||||||
className="absolute top-2 right-2"
|
className="absolute top-2 right-2"
|
||||||
title={"Server Configuration"}
|
title={"Server Configuration"}
|
||||||
>
|
>
|
||||||
<ServerIcon className="size-4" />
|
<Icon icon="serverIcon" className="size-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
@@ -152,7 +152,7 @@ export function ServerConfigurationDialog({ trigger }: { trigger?: ReactNode })
|
|||||||
disabled={!canSave || isCheckingHealth}
|
disabled={!canSave || isCheckingHealth}
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
>
|
>
|
||||||
<CheckIcon className="size-4" />
|
<Icon icon="checkIcon" className="size-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
@@ -161,7 +161,7 @@ export function ServerConfigurationDialog({ trigger }: { trigger?: ReactNode })
|
|||||||
onClick={handleResetToDefault}
|
onClick={handleResetToDefault}
|
||||||
title="Reset to default"
|
title="Reset to default"
|
||||||
>
|
>
|
||||||
<Undo2 className="size-4" />
|
<Icon icon="undo2" className="size-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
{!isValid && (
|
{!isValid && (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Moon, Sun } from "lucide-react";
|
|
||||||
import { useTheme } from "@/components/theme-provider";
|
import { useTheme } from "@/components/theme-provider";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { IconButton } from "@/components/ui/icon-button";
|
import { IconButton } from "@/components/ui/icon-button";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ function ThemeToggle({ className }: { className?: string }) {
|
|||||||
onClick={() => setTheme(isDark ? "light" : "dark")}
|
onClick={() => setTheme(isDark ? "light" : "dark")}
|
||||||
title={isDark ? "Switch to light mode" : "Switch to dark mode"}
|
title={isDark ? "Switch to light mode" : "Switch to dark mode"}
|
||||||
>
|
>
|
||||||
{isDark ? <Sun className="size-5" /> : <Moon className="size-5" />}
|
{isDark ? <Icon icon="sun" className="size-5" /> : <Icon icon="moon" className="size-5" />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Timer } from "lucide-react";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { IssueTimer } from "@/components/issue-timer";
|
import { IssueTimer } from "@/components/issue-timer";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
|
|
||||||
export function TimerModal({ issueId }: { issueId: number }) {
|
export function TimerModal({ issueId }: { issueId: number }) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -11,7 +11,7 @@ export function TimerModal({ issueId }: { issueId: number }) {
|
|||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="outline" size="sm">
|
<Button variant="outline" size="sm">
|
||||||
<Timer className="size-4" />
|
<Icon icon="timer" className="size-4" />
|
||||||
Timer
|
Timer
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { SprintRecord } from "@sprint/shared";
|
import type { SprintRecord } from "@sprint/shared";
|
||||||
import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { type DayButton, DayPicker, getDefaultClassNames } from "react-day-picker";
|
import { type DayButton, DayPicker, getDefaultClassNames } from "react-day-picker";
|
||||||
import { Button, buttonVariants } from "@/components/ui/button";
|
import { Button, buttonVariants } from "@/components/ui/button";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function Calendar({
|
function Calendar({
|
||||||
@@ -109,14 +109,16 @@ function Calendar({
|
|||||||
},
|
},
|
||||||
Chevron: ({ className, orientation, ...props }) => {
|
Chevron: ({ className, orientation, ...props }) => {
|
||||||
if (orientation === "left") {
|
if (orientation === "left") {
|
||||||
return <ChevronLeftIcon className={cn("size-4", className)} {...props} />;
|
return <Icon icon="chevronLeftIcon" className={cn("size-4", className)} {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (orientation === "right") {
|
if (orientation === "right") {
|
||||||
return <ChevronRightIcon className={cn("size-4", className)} {...props} />;
|
return (
|
||||||
|
<Icon icon="chevronRightIcon" className={cn("size-4", className)} {...props} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ChevronDownIcon className={cn("size-4", className)} {...props} />;
|
return <Icon icon="chevronDownIcon" className={cn("size-4", className)} {...props} />;
|
||||||
},
|
},
|
||||||
DayButton: (props) => <CalendarDayButton {...props} sprints={sprints} isEnd={isEnd} />,
|
DayButton: (props) => <CalendarDayButton {...props} sprints={sprints} isEnd={isEnd} />,
|
||||||
WeekNumber: ({ children, ...props }) => {
|
WeekNumber: ({ children, ...props }) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||||
import { XIcon } from "lucide-react";
|
|
||||||
import type * as React from "react";
|
import type * as React from "react";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ function DialogContent({
|
|||||||
"[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
"[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<XIcon />
|
<Icon icon="x" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</DialogPrimitive.Close>
|
</DialogPrimitive.Close>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
|
|
||||||
import type * as React from "react";
|
import type * as React from "react";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
||||||
@@ -136,7 +136,7 @@ function DropdownMenuCheckboxItem({
|
|||||||
>
|
>
|
||||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||||
<DropdownMenuPrimitive.ItemIndicator>
|
<DropdownMenuPrimitive.ItemIndicator>
|
||||||
<CheckIcon className="size-4" />
|
<Icon icon="checkIcon" className="size-4" />
|
||||||
</DropdownMenuPrimitive.ItemIndicator>
|
</DropdownMenuPrimitive.ItemIndicator>
|
||||||
</span>
|
</span>
|
||||||
{children}
|
{children}
|
||||||
@@ -168,7 +168,7 @@ function DropdownMenuRadioItem({
|
|||||||
>
|
>
|
||||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||||
<DropdownMenuPrimitive.ItemIndicator>
|
<DropdownMenuPrimitive.ItemIndicator>
|
||||||
<CircleIcon className="size-2 fill-current" />
|
<Icon icon="circleIcon" className="size-2 fill-current" />
|
||||||
</DropdownMenuPrimitive.ItemIndicator>
|
</DropdownMenuPrimitive.ItemIndicator>
|
||||||
</span>
|
</span>
|
||||||
{children}
|
{children}
|
||||||
@@ -242,7 +242,7 @@ function DropdownMenuSubTrigger({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<ChevronRightIcon className="ml-auto size-4" />
|
<Icon icon="chevronRightIcon" className="ml-auto size-4" />
|
||||||
</DropdownMenuPrimitive.SubTrigger>
|
</DropdownMenuPrimitive.SubTrigger>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import {
|
|||||||
WarningIcon as PhosphorWarning,
|
WarningIcon as PhosphorWarning,
|
||||||
XIcon as PhosphorX,
|
XIcon as PhosphorX,
|
||||||
} from "@phosphor-icons/react";
|
} from "@phosphor-icons/react";
|
||||||
|
import type { IconStyle } from "@sprint/shared";
|
||||||
import {
|
import {
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
Check,
|
Check,
|
||||||
@@ -92,6 +93,7 @@ import {
|
|||||||
UserRound,
|
UserRound,
|
||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { useSessionSafe } from "@/components/session-provider";
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
alertTriangle: { lucide: AlertTriangle, pixel: PixelAlert, phosphor: PhosphorWarning },
|
alertTriangle: { lucide: AlertTriangle, pixel: PixelAlert, phosphor: PhosphorWarning },
|
||||||
@@ -141,11 +143,11 @@ const icons = {
|
|||||||
export type IconName = keyof typeof icons;
|
export type IconName = keyof typeof icons;
|
||||||
export const iconNames = Object.keys(icons) as IconName[];
|
export const iconNames = Object.keys(icons) as IconName[];
|
||||||
export const iconStyles = ["lucide", "pixel", "phosphor"] as const;
|
export const iconStyles = ["lucide", "pixel", "phosphor"] as const;
|
||||||
export type IconStyle = (typeof iconStyles)[number];
|
export type { IconStyle };
|
||||||
|
|
||||||
export default function Icon({
|
export default function Icon({
|
||||||
icon,
|
icon,
|
||||||
iconStyle = "lucide",
|
iconStyle,
|
||||||
size = 24,
|
size = 24,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
@@ -154,7 +156,9 @@ export default function Icon({
|
|||||||
size?: number | string;
|
size?: number | string;
|
||||||
color?: string;
|
color?: string;
|
||||||
} & React.ComponentProps<"svg">) {
|
} & React.ComponentProps<"svg">) {
|
||||||
const IconComponent = icons[icon]?.[iconStyle];
|
const session = useSessionSafe();
|
||||||
|
const resolvedStyle = (iconStyle ?? session?.user?.iconPreference ?? "lucide") as IconStyle;
|
||||||
|
const IconComponent = icons[icon]?.[resolvedStyle];
|
||||||
|
|
||||||
if (!IconComponent) {
|
if (!IconComponent) {
|
||||||
return null;
|
return null;
|
||||||
@@ -164,9 +168,9 @@ export default function Icon({
|
|||||||
<IconComponent
|
<IconComponent
|
||||||
size={size}
|
size={size}
|
||||||
fill={
|
fill={
|
||||||
(iconStyle === "pixel" && icon === "moon") ||
|
(resolvedStyle === "pixel" && icon === "moon") ||
|
||||||
(iconStyle === "pixel" && icon === "hash") ||
|
(resolvedStyle === "pixel" && icon === "hash") ||
|
||||||
iconStyle === "phosphor"
|
resolvedStyle === "phosphor"
|
||||||
? "var(--foreground)"
|
? "var(--foreground)"
|
||||||
: "transparent"
|
: "transparent"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Hash } from "lucide-react";
|
|
||||||
import type * as React from "react";
|
import type * as React from "react";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function Input({
|
function Input({
|
||||||
@@ -25,7 +25,7 @@ function Input({
|
|||||||
>
|
>
|
||||||
{showHashPrefix && (
|
{showHashPrefix && (
|
||||||
<span className="border-r px-1 py-1 text-muted-foreground">
|
<span className="border-r px-1 py-1 text-muted-foreground">
|
||||||
<Hash className="size-3.5" strokeWidth={1.5} />
|
<Icon icon="hash" className="size-3.5" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { GripVerticalIcon } from "lucide-react";
|
|
||||||
import type * as React from "react";
|
import type * as React from "react";
|
||||||
import { Group, Panel, Separator } from "react-resizable-panels";
|
import { Group, Panel, Separator } from "react-resizable-panels";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ function ResizableSeparator({
|
|||||||
>
|
>
|
||||||
{withHandle && (
|
{withHandle && (
|
||||||
<div className="bg-border z-10 flex h-4 w-3 items-center justify-center rounded-xs border">
|
<div className="bg-border z-10 flex h-4 w-3 items-center justify-center rounded-xs border">
|
||||||
<GripVerticalIcon className="size-2.5" />
|
<Icon icon="gripVerticalIcon" className="size-2.5" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Separator>
|
</Separator>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||||
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
||||||
import type * as React from "react";
|
import type * as React from "react";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
@@ -72,8 +72,9 @@ function SelectTrigger({
|
|||||||
)}
|
)}
|
||||||
{children}
|
{children}
|
||||||
<SelectPrimitive.Icon asChild>
|
<SelectPrimitive.Icon asChild>
|
||||||
<ChevronDownIcon
|
<Icon
|
||||||
className={cn("size-4 opacity-50", chevronClassName)}
|
icon="chevronDownIcon"
|
||||||
|
className={cn("size-4.5 opacity-50", chevronClassName)}
|
||||||
style={{ rotate: isOpen ? "180deg" : "0deg" }}
|
style={{ rotate: isOpen ? "180deg" : "0deg" }}
|
||||||
/>
|
/>
|
||||||
</SelectPrimitive.Icon>
|
</SelectPrimitive.Icon>
|
||||||
@@ -165,7 +166,7 @@ function SelectItem({
|
|||||||
className="absolute right-2 flex size-3.5 items-center justify-center"
|
className="absolute right-2 flex size-3.5 items-center justify-center"
|
||||||
>
|
>
|
||||||
<SelectPrimitive.ItemIndicator>
|
<SelectPrimitive.ItemIndicator>
|
||||||
<CheckIcon className="size-4" />
|
<Icon icon="checkIcon" className="size-4" />
|
||||||
</SelectPrimitive.ItemIndicator>
|
</SelectPrimitive.ItemIndicator>
|
||||||
</span>
|
</span>
|
||||||
{textClassName ? (
|
{textClassName ? (
|
||||||
@@ -197,7 +198,7 @@ function SelectScrollUpButton({
|
|||||||
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ChevronUpIcon className="size-4" />
|
<Icon icon="chevronUpIcon" className="size-4" />
|
||||||
</SelectPrimitive.ScrollUpButton>
|
</SelectPrimitive.ScrollUpButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -212,7 +213,7 @@ function SelectScrollDownButton({
|
|||||||
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ChevronDownIcon className="size-4" />
|
<Icon icon="chevronDownIcon" className="size-4" />
|
||||||
</SelectPrimitive.ScrollDownButton>
|
</SelectPrimitive.ScrollDownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { CircleCheckIcon, InfoIcon, Loader2Icon, OctagonXIcon, TriangleAlertIcon } from "lucide-react";
|
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { Toaster as Sonner, type ToasterProps } from "sonner";
|
import { Toaster as Sonner, type ToasterProps } from "sonner";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
|
|
||||||
const Toaster = ({ ...props }: ToasterProps) => {
|
const Toaster = ({ ...props }: ToasterProps) => {
|
||||||
const { theme = "system" } = useTheme();
|
const { theme = "system" } = useTheme();
|
||||||
@@ -10,11 +10,11 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
|||||||
theme={theme as ToasterProps["theme"]}
|
theme={theme as ToasterProps["theme"]}
|
||||||
className="toaster group"
|
className="toaster group"
|
||||||
icons={{
|
icons={{
|
||||||
success: <CircleCheckIcon className="size-4" />,
|
success: <Icon icon="circleCheckIcon" className="size-4" />,
|
||||||
info: <InfoIcon className="size-4" />,
|
info: <Icon icon="infoIcon" className="size-4" />,
|
||||||
warning: <TriangleAlertIcon className="size-4" />,
|
warning: <Icon icon="triangleAlertIcon" className="size-4" />,
|
||||||
error: <OctagonXIcon className="size-4" />,
|
error: <Icon icon="octagonXIcon" className="size-4" />,
|
||||||
loading: <Loader2Icon className="size-4 animate-spin" />,
|
loading: <Icon icon="loader2Icon" className="size-4 animate-spin" />,
|
||||||
}}
|
}}
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Loader } from "lucide-react";
|
import Icon from "@/components/ui/icon";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
|
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
|
||||||
return (
|
return (
|
||||||
<Loader
|
<Icon
|
||||||
|
icon="loader"
|
||||||
role="status"
|
role="status"
|
||||||
aria-label="Loading"
|
aria-label="Loading"
|
||||||
className={cn("size-4 animate-spin", className)}
|
className={cn("size-4 animate-spin", className)}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Edit } from "lucide-react";
|
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import Avatar from "@/components/avatar";
|
import Avatar from "@/components/avatar";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { parseError, user } from "@/lib/server";
|
import { parseError, user } from "@/lib/server";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@@ -82,7 +82,7 @@ export function UploadAvatar({
|
|||||||
|
|
||||||
{!uploading && showEdit && (
|
{!uploading && showEdit && (
|
||||||
<div className="absolute inset-0 flex items-center justify-center bg-black/40">
|
<div className="absolute inset-0 flex items-center justify-center bg-black/40">
|
||||||
<Edit className="size-6 text-white drop-shadow-md" />
|
<Icon icon="edit" className="size-6 text-white drop-shadow-md" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { CircleQuestionMark } from "lucide-react";
|
import Icon from "@/components/ui/icon";
|
||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<div className={`w-full h-[100vh] flex flex-col items-center justify-center gap-4`}>
|
<div className={`w-full h-[100vh] flex flex-col items-center justify-center gap-4`}>
|
||||||
<CircleQuestionMark size={72} />
|
<Icon icon="circleQuestionMark" size={72} />
|
||||||
<span className="text-7xl font-500">404</span>
|
<span className="text-7xl font-500">404</span>
|
||||||
<span className="text-2xl font-400">Not Found</span>
|
<span className="text-2xl font-400">Not Found</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user