@turtleclub/ui 0.7.0-beta.33 → 0.7.0-beta.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +10331 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7227 -48026
- package/dist/index.js.map +1 -1
- package/package.json +17 -11
- package/.prettierrc.json +0 -4
- package/.turbo/turbo-build.log +0 -182
- package/CHANGELOG.md +0 -801
- package/components.json +0 -21
- package/src/components/charts/QUICK_REFERENCE.md +0 -323
- package/src/components/charts/README.md +0 -658
- package/src/components/charts/RECHARTS_FEATURES.md +0 -458
- package/src/components/charts/area-chart.tsx +0 -248
- package/src/components/charts/bar-chart.tsx +0 -362
- package/src/components/charts/index.ts +0 -4
- package/src/components/charts/pie-chart.tsx +0 -277
- package/src/components/charts/radial-chart.tsx +0 -312
- package/src/components/features/api-status/index.tsx +0 -23
- package/src/components/features/data-table/data-table.tsx +0 -538
- package/src/components/features/data-table/expand-toggle.tsx +0 -17
- package/src/components/features/data-table/fuzzy-filter.tsx +0 -34
- package/src/components/features/data-table/index.ts +0 -3
- package/src/components/features/data-table/item-info.tsx +0 -19
- package/src/components/features/data-table/skeleton.tsx +0 -23
- package/src/components/features/data-table/sort-dropdown.tsx +0 -118
- package/src/components/features/data-table/sortable-header.tsx +0 -37
- package/src/components/features/index.ts +0 -6
- package/src/components/features/page-heading.tsx +0 -27
- package/src/components/features/search-bar.tsx +0 -55
- package/src/components/features/segmented-navigation.tsx +0 -18
- package/src/components/features/sidebar-layout.tsx +0 -279
- package/src/components/features/turtle-tooltip.tsx +0 -67
- package/src/components/icons/arrow.tsx +0 -23
- package/src/components/icons/beta.tsx +0 -95
- package/src/components/icons/dot.tsx +0 -102
- package/src/components/icons/index.ts +0 -7
- package/src/components/icons/issue.tsx +0 -106
- package/src/components/icons/turtle.tsx +0 -156
- package/src/components/icons/update.tsx +0 -113
- package/src/components/icons/warning.tsx +0 -95
- package/src/components/molecules/index.ts +0 -9
- package/src/components/molecules/opportunity/index.ts +0 -10
- package/src/components/molecules/opportunity/opportunity-apr.tsx +0 -129
- package/src/components/molecules/opportunity/opportunity-disclaimer.tsx +0 -46
- package/src/components/molecules/opportunity/opportunity-rate-estimator.tsx +0 -62
- package/src/components/molecules/opportunity/opportunity-section.tsx +0 -113
- package/src/components/molecules/opportunity/opportunity-selector.tsx +0 -30
- package/src/components/molecules/opportunity/opportunity-type.tsx +0 -16
- package/src/components/molecules/route-details.tsx +0 -112
- package/src/components/molecules/slippage-selector.tsx +0 -200
- package/src/components/molecules/swap-details.tsx +0 -55
- package/src/components/molecules/swap-input.tsx +0 -186
- package/src/components/molecules/tabs.tsx +0 -79
- package/src/components/molecules/token-selector.tsx +0 -180
- package/src/components/molecules/tx-status.tsx +0 -312
- package/src/components/molecules/widget/asset-list/asset-filters.tsx +0 -113
- package/src/components/molecules/widget/asset-list/asset-list.tsx +0 -178
- package/src/components/molecules/widget/asset-list/asset-row.tsx +0 -45
- package/src/components/molecules/widget/asset-list/hooks/index.ts +0 -2
- package/src/components/molecules/widget/asset-list/hooks/use-asset-filtering.ts +0 -44
- package/src/components/molecules/widget/asset-list/hooks/use-asset-grouping.ts +0 -87
- package/src/components/molecules/widget/asset-list/index.ts +0 -3
- package/src/components/molecules/widget/base-selector.tsx +0 -121
- package/src/components/molecules/widget/campaign-item.tsx +0 -82
- package/src/components/molecules/widget/deal-item.tsx +0 -92
- package/src/components/molecules/widget/index.ts +0 -36
- package/src/components/molecules/widget/opportunity-item.tsx +0 -105
- package/src/components/molecules/widget/widget-item-stats.tsx +0 -50
- package/src/components/molecules/widget/widget-item.tsx +0 -139
- package/src/components/molecules/widget/widget-list-items.tsx +0 -86
- package/src/components/ui/alert-dialog.tsx +0 -163
- package/src/components/ui/animated-background/animated-background.tsx +0 -182
- package/src/components/ui/animated-background/index.ts +0 -1
- package/src/components/ui/avatar.tsx +0 -73
- package/src/components/ui/badge.tsx +0 -59
- package/src/components/ui/banner.tsx +0 -84
- package/src/components/ui/button.tsx +0 -100
- package/src/components/ui/card.tsx +0 -119
- package/src/components/ui/chart.tsx +0 -346
- package/src/components/ui/checkbox.tsx +0 -32
- package/src/components/ui/chip.tsx +0 -52
- package/src/components/ui/collapsible.tsx +0 -34
- package/src/components/ui/combobox.tsx +0 -730
- package/src/components/ui/command.tsx +0 -184
- package/src/components/ui/dialog.tsx +0 -129
- package/src/components/ui/dropdown.tsx +0 -316
- package/src/components/ui/field.tsx +0 -244
- package/src/components/ui/heading.tsx +0 -74
- package/src/components/ui/hover-card.tsx +0 -139
- package/src/components/ui/icon-animation.tsx +0 -82
- package/src/components/ui/icon-list.tsx +0 -168
- package/src/components/ui/index.ts +0 -48
- package/src/components/ui/info-card.tsx +0 -110
- package/src/components/ui/input-group.tsx +0 -170
- package/src/components/ui/input.tsx +0 -72
- package/src/components/ui/label-with-icon.tsx +0 -122
- package/src/components/ui/label.tsx +0 -24
- package/src/components/ui/multi-select.tsx +0 -1090
- package/src/components/ui/navigation-bar.tsx +0 -153
- package/src/components/ui/navigation-menu.tsx +0 -188
- package/src/components/ui/opportunity-details-v1.tsx +0 -104
- package/src/components/ui/pagination.tsx +0 -127
- package/src/components/ui/popover.tsx +0 -48
- package/src/components/ui/scroll-area.tsx +0 -64
- package/src/components/ui/segment-control.tsx +0 -146
- package/src/components/ui/select.tsx +0 -199
- package/src/components/ui/separator.tsx +0 -26
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/sidebar.tsx +0 -728
- package/src/components/ui/skeleton.tsx +0 -14
- package/src/components/ui/slider.tsx +0 -58
- package/src/components/ui/sonner.tsx +0 -24
- package/src/components/ui/switch.tsx +0 -29
- package/src/components/ui/table-shadcn.tsx +0 -110
- package/src/components/ui/table.tsx +0 -117
- package/src/components/ui/textarea.tsx +0 -22
- package/src/components/ui/toggle-group.tsx +0 -71
- package/src/components/ui/toggle.tsx +0 -47
- package/src/components/ui/tooltip.tsx +0 -66
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useIsMobile.ts +0 -77
- package/src/index.ts +0 -16
- package/src/lib/utils.ts +0 -6
- package/src/styles/globals.css +0 -181
- package/src/styles/themes/index.css +0 -9
- package/src/styles/themes/semantic.css +0 -117
- package/src/styles/tokens/colors.css +0 -124
- package/src/styles/tokens/index.css +0 -15
- package/src/styles/tokens/radius.css +0 -18
- package/src/styles/tokens/spacing.css +0 -58
- package/src/styles/tokens/typography.css +0 -87
- package/src/tokens/index.ts +0 -108
- package/tsconfig.json +0 -20
- package/vite.config.js +0 -49
- /package/{src/images/enso.png → dist/enso-22FJ4GNK.png} +0 -0
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { ArrowDown, ArrowUp, ArrowUpDown, X } from "lucide-react";
|
|
5
|
-
import { flexRender, Header } from "@tanstack/react-table";
|
|
6
|
-
import {
|
|
7
|
-
DropdownMenu,
|
|
8
|
-
DropdownMenuContent,
|
|
9
|
-
DropdownMenuItem,
|
|
10
|
-
DropdownMenuLabel,
|
|
11
|
-
DropdownMenuSeparator,
|
|
12
|
-
DropdownMenuTrigger,
|
|
13
|
-
} from "../../ui/dropdown";
|
|
14
|
-
|
|
15
|
-
interface SortDropdownProps<TData> {
|
|
16
|
-
headers: Header<TData, unknown>[];
|
|
17
|
-
currentSort?: { id: string; desc: boolean } | null;
|
|
18
|
-
onSortChange: (columnId: string, desc: boolean) => void;
|
|
19
|
-
onClearSort?: () => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function SortDropdown<TData>({
|
|
23
|
-
headers,
|
|
24
|
-
currentSort,
|
|
25
|
-
onSortChange,
|
|
26
|
-
onClearSort,
|
|
27
|
-
}: SortDropdownProps<TData>) {
|
|
28
|
-
// Filter only sortable columns
|
|
29
|
-
const sortableHeaders = headers.filter(
|
|
30
|
-
(header) => header.column.getCanSort() && !header.isPlaceholder
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
const getCurrentSortLabel = () => {
|
|
34
|
-
if (!currentSort) return "Sort";
|
|
35
|
-
|
|
36
|
-
const header = sortableHeaders.find((h) => h.column.id === currentSort.id);
|
|
37
|
-
if (!header) return "Sort";
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<span className="flex items-center gap-2">
|
|
41
|
-
{currentSort.desc ? (
|
|
42
|
-
<ArrowDown className="text-primary size-3" />
|
|
43
|
-
) : (
|
|
44
|
-
<ArrowUp className="text-primary size-3" />
|
|
45
|
-
)}
|
|
46
|
-
<span className="truncate">
|
|
47
|
-
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
48
|
-
</span>
|
|
49
|
-
</span>
|
|
50
|
-
);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const handleColumnClick = (columnId: string) => {
|
|
54
|
-
// If this column is already sorted, toggle the direction
|
|
55
|
-
if (currentSort?.id === columnId) {
|
|
56
|
-
// If descending, switch to ascending; if ascending, switch to descending
|
|
57
|
-
onSortChange(columnId, !currentSort.desc);
|
|
58
|
-
} else {
|
|
59
|
-
// New column, start with ascending
|
|
60
|
-
onSortChange(columnId, false);
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
if (sortableHeaders.length === 0) return null;
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<DropdownMenu>
|
|
68
|
-
<DropdownMenuTrigger size="sm" className="bg-neutral-alpha-2">
|
|
69
|
-
{currentSort ? (
|
|
70
|
-
getCurrentSortLabel()
|
|
71
|
-
) : (
|
|
72
|
-
<div className="text-muted-foreground flex items-center gap-2">
|
|
73
|
-
<ArrowUpDown className="size-3" />
|
|
74
|
-
<span>Sort by</span>
|
|
75
|
-
</div>
|
|
76
|
-
)}
|
|
77
|
-
</DropdownMenuTrigger>
|
|
78
|
-
<DropdownMenuContent align="end" className="w-[200px] space-y-1">
|
|
79
|
-
<DropdownMenuLabel>Sort by</DropdownMenuLabel>
|
|
80
|
-
<DropdownMenuSeparator />
|
|
81
|
-
{sortableHeaders.map((header) => {
|
|
82
|
-
const isCurrentAsc = currentSort?.id === header.column.id && !currentSort.desc;
|
|
83
|
-
const isCurrentDesc = currentSort?.id === header.column.id && currentSort.desc;
|
|
84
|
-
const isActive = currentSort?.id === header.column.id;
|
|
85
|
-
|
|
86
|
-
return (
|
|
87
|
-
<DropdownMenuItem
|
|
88
|
-
key={header.column.id}
|
|
89
|
-
onClick={() => handleColumnClick(header.column.id)}
|
|
90
|
-
className={
|
|
91
|
-
isActive ? "bg-secondary flex items-center gap-2" : "flex items-center gap-2"
|
|
92
|
-
}
|
|
93
|
-
>
|
|
94
|
-
<span className="w-fit truncate">
|
|
95
|
-
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
96
|
-
</span>
|
|
97
|
-
{isActive &&
|
|
98
|
-
(isCurrentDesc ? (
|
|
99
|
-
<ArrowDown className="text-primary size-4" />
|
|
100
|
-
) : (
|
|
101
|
-
<ArrowUp className="text-primary size-4" />
|
|
102
|
-
))}
|
|
103
|
-
</DropdownMenuItem>
|
|
104
|
-
);
|
|
105
|
-
})}
|
|
106
|
-
{currentSort && onClearSort && (
|
|
107
|
-
<>
|
|
108
|
-
<DropdownMenuSeparator />
|
|
109
|
-
<DropdownMenuItem onClick={onClearSort}>
|
|
110
|
-
<X className="size-4" />
|
|
111
|
-
Clear sorting
|
|
112
|
-
</DropdownMenuItem>
|
|
113
|
-
</>
|
|
114
|
-
)}
|
|
115
|
-
</DropdownMenuContent>
|
|
116
|
-
</DropdownMenu>
|
|
117
|
-
);
|
|
118
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { flexRender, type Header } from "@tanstack/react-table";
|
|
4
|
-
import { ArrowDown, ArrowUp, ArrowUpDown } from "lucide-react";
|
|
5
|
-
import { useMemo } from "react";
|
|
6
|
-
|
|
7
|
-
export default function SortableHeader({
|
|
8
|
-
header,
|
|
9
|
-
}: {
|
|
10
|
-
header: Header<any, unknown>;
|
|
11
|
-
}) {
|
|
12
|
-
const getSortIcon = useMemo(() => {
|
|
13
|
-
if (header.column.getIsSorted() === "asc")
|
|
14
|
-
return <ArrowUp className="text-primary size-3.5" />;
|
|
15
|
-
if (header.column.getIsSorted() === "desc")
|
|
16
|
-
return <ArrowDown className="text-primary size-3.5" />;
|
|
17
|
-
return (
|
|
18
|
-
header.column.getCanSort() && (
|
|
19
|
-
<ArrowUpDown className="text-secondary group-hover:text-muted-foreground size-3" />
|
|
20
|
-
)
|
|
21
|
-
);
|
|
22
|
-
}, [header.column.getIsSorted(), header.column.getCanSort()]);
|
|
23
|
-
|
|
24
|
-
return header.isPlaceholder ? null : (
|
|
25
|
-
<div
|
|
26
|
-
className={
|
|
27
|
-
header.column.getCanSort()
|
|
28
|
-
? "group flex cursor-pointer items-center gap-1 select-none"
|
|
29
|
-
: ""
|
|
30
|
-
}
|
|
31
|
-
onClick={header.column.getToggleSortingHandler()}
|
|
32
|
-
>
|
|
33
|
-
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
34
|
-
{getSortIcon}
|
|
35
|
-
</div>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { HeadingH3, HeadingH6 } from "../ui";
|
|
3
|
-
|
|
4
|
-
type PageHeadingProps = {
|
|
5
|
-
icon?: React.ReactNode;
|
|
6
|
-
title: string;
|
|
7
|
-
description: string;
|
|
8
|
-
action?: React.ReactNode;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export function PageHeading({
|
|
12
|
-
icon,
|
|
13
|
-
title,
|
|
14
|
-
description,
|
|
15
|
-
action,
|
|
16
|
-
}: PageHeadingProps) {
|
|
17
|
-
return (
|
|
18
|
-
<div className="flex items-center gap-3">
|
|
19
|
-
<div>{icon}</div>
|
|
20
|
-
<div className="grow">
|
|
21
|
-
<HeadingH3>{title}</HeadingH3>
|
|
22
|
-
<HeadingH6>{description}</HeadingH6>
|
|
23
|
-
</div>
|
|
24
|
-
{action}
|
|
25
|
-
</div>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { Input, InputProps } from "../ui";
|
|
3
|
-
import { Search } from "lucide-react";
|
|
4
|
-
import { cn } from "@/lib/utils";
|
|
5
|
-
|
|
6
|
-
type Props = {
|
|
7
|
-
value: string;
|
|
8
|
-
onChange: (value: string) => void;
|
|
9
|
-
debounce?: number;
|
|
10
|
-
size?: "default" | "sm";
|
|
11
|
-
} & Omit<InputProps, "onChange">;
|
|
12
|
-
|
|
13
|
-
export const SearchBar: React.FC<Props> = ({
|
|
14
|
-
value: initialValue,
|
|
15
|
-
onChange,
|
|
16
|
-
debounce = 300,
|
|
17
|
-
size = "default",
|
|
18
|
-
...props
|
|
19
|
-
}) => {
|
|
20
|
-
const [value, setValue] = React.useState<string>(initialValue);
|
|
21
|
-
|
|
22
|
-
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) =>
|
|
23
|
-
setValue(event.target.value);
|
|
24
|
-
|
|
25
|
-
React.useEffect(() => {
|
|
26
|
-
setValue(initialValue);
|
|
27
|
-
}, [initialValue]);
|
|
28
|
-
|
|
29
|
-
React.useEffect(() => {
|
|
30
|
-
const timeout = setTimeout(() => {
|
|
31
|
-
onChange(value);
|
|
32
|
-
}, debounce);
|
|
33
|
-
|
|
34
|
-
return () => clearTimeout(timeout);
|
|
35
|
-
}, [value]);
|
|
36
|
-
|
|
37
|
-
const inputClasses = size === "sm" ? "text-sm" : "";
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<div className="border-border relative w-full max-w-md rounded-full border">
|
|
41
|
-
<Input
|
|
42
|
-
value={value}
|
|
43
|
-
onChange={handleInputChange}
|
|
44
|
-
className={cn("-my-px h-9", inputClasses)}
|
|
45
|
-
{...props}
|
|
46
|
-
/>
|
|
47
|
-
<button
|
|
48
|
-
type="button"
|
|
49
|
-
className="bg-border border-border absolute inset-y-0 right-0 flex aspect-square h-full items-center justify-center rounded rounded-full"
|
|
50
|
-
>
|
|
51
|
-
<Search className="text-primary size-3" />
|
|
52
|
-
</button>
|
|
53
|
-
</div>
|
|
54
|
-
);
|
|
55
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { SegmentControl, SegmentControlProps } from "../ui";
|
|
3
|
-
|
|
4
|
-
type SegmentedNavigationProps<T extends string> = SegmentControlProps<T> & {
|
|
5
|
-
shouldMobileSelect: boolean;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export function SegmentedNavigation<T extends string>({
|
|
9
|
-
value,
|
|
10
|
-
onChange,
|
|
11
|
-
items,
|
|
12
|
-
}: SegmentedNavigationProps<T>) {
|
|
13
|
-
return (
|
|
14
|
-
<div className="">
|
|
15
|
-
<SegmentControl value={value} onChange={onChange} items={items} />
|
|
16
|
-
</div>
|
|
17
|
-
);
|
|
18
|
-
}
|
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { ChevronRight, Lock } from "lucide-react";
|
|
3
|
-
import {
|
|
4
|
-
Sidebar,
|
|
5
|
-
SidebarProvider,
|
|
6
|
-
SidebarHeader,
|
|
7
|
-
SidebarContent,
|
|
8
|
-
SidebarGroup,
|
|
9
|
-
SidebarGroupContent,
|
|
10
|
-
SidebarMenu,
|
|
11
|
-
SidebarMenuItem,
|
|
12
|
-
SidebarMenuButton,
|
|
13
|
-
SidebarMenuSub,
|
|
14
|
-
SidebarMenuSubItem,
|
|
15
|
-
SidebarMenuSubButton,
|
|
16
|
-
Collapsible,
|
|
17
|
-
CollapsibleTrigger,
|
|
18
|
-
CollapsibleContent,
|
|
19
|
-
SidebarFooter,
|
|
20
|
-
useSidebar,
|
|
21
|
-
SidebarTrigger,
|
|
22
|
-
} from "../ui";
|
|
23
|
-
import { cn } from "@/lib/utils";
|
|
24
|
-
import { DotIcon, TurtleIcon } from "../icons";
|
|
25
|
-
import { TurtleTooltip } from "./turtle-tooltip";
|
|
26
|
-
|
|
27
|
-
type SidebarItemBase = {
|
|
28
|
-
title: string;
|
|
29
|
-
icon?: any;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// Item with a URL (no children)
|
|
33
|
-
type SidebarLink = SidebarItemBase & {
|
|
34
|
-
url: string;
|
|
35
|
-
isActive: boolean;
|
|
36
|
-
isEnabled?: boolean;
|
|
37
|
-
children?: never;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Item with children (no URL)
|
|
41
|
-
type SidebarGroupBase = SidebarItemBase & {
|
|
42
|
-
children: SidebarItem[];
|
|
43
|
-
url?: never;
|
|
44
|
-
isActive?: never;
|
|
45
|
-
isEnabled?: boolean;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export type SidebarItem = SidebarLink | SidebarGroupBase;
|
|
49
|
-
|
|
50
|
-
const LOCKED_TOOLTIP_CONTENT = "Contact the Turtle team to unlock.";
|
|
51
|
-
|
|
52
|
-
export function TurtleSidebarLayout({
|
|
53
|
-
children,
|
|
54
|
-
sidebarSlot,
|
|
55
|
-
sidebarFooter,
|
|
56
|
-
topSlot,
|
|
57
|
-
items,
|
|
58
|
-
linkComponent,
|
|
59
|
-
}: {
|
|
60
|
-
children: React.ReactNode;
|
|
61
|
-
sidebarSlot?: React.ReactNode;
|
|
62
|
-
sidebarFooter?: React.ReactNode;
|
|
63
|
-
topSlot?: React.ReactNode;
|
|
64
|
-
items: SidebarItem[];
|
|
65
|
-
linkComponent: any;
|
|
66
|
-
}) {
|
|
67
|
-
return (
|
|
68
|
-
<SidebarProvider>
|
|
69
|
-
<TurtleSidebar
|
|
70
|
-
items={items}
|
|
71
|
-
sidebarFooter={sidebarFooter}
|
|
72
|
-
sidebarSlot={sidebarSlot}
|
|
73
|
-
linkComponent={linkComponent}
|
|
74
|
-
/>
|
|
75
|
-
<TurtleContainer topSlot={topSlot} children={children} />
|
|
76
|
-
</SidebarProvider>
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function TurtleSidebar({
|
|
81
|
-
items,
|
|
82
|
-
sidebarSlot,
|
|
83
|
-
sidebarFooter,
|
|
84
|
-
linkComponent: Comp = "a",
|
|
85
|
-
}: {
|
|
86
|
-
items: SidebarItem[];
|
|
87
|
-
sidebarSlot?: React.ReactNode;
|
|
88
|
-
sidebarFooter?: React.ReactNode;
|
|
89
|
-
linkComponent: any;
|
|
90
|
-
}) {
|
|
91
|
-
return (
|
|
92
|
-
<Sidebar>
|
|
93
|
-
<SidebarHeader>
|
|
94
|
-
<div className="border-gradient-white border-opacity-20 relative h-[72px] w-full rounded-full shadow">
|
|
95
|
-
<TurtleIcon className="absolute -top-2 -left-2 aspect-square h-[86px]" />
|
|
96
|
-
|
|
97
|
-
<div className="text-primary mt-2 -space-y-1.5 pl-20">
|
|
98
|
-
<div className="text-3xl leading-tight font-bold">Turtle</div>
|
|
99
|
-
<div className="pl-1 text-sm font-medium">Partner Portal</div>
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
</SidebarHeader>
|
|
103
|
-
<SidebarContent>
|
|
104
|
-
<SidebarGroup>
|
|
105
|
-
<SidebarGroupContent>
|
|
106
|
-
<SidebarMenu>
|
|
107
|
-
{items.map((item) => {
|
|
108
|
-
if (item?.children?.length) {
|
|
109
|
-
const isEnabled = item.isEnabled !== false;
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<Collapsible
|
|
113
|
-
key={item.title}
|
|
114
|
-
className="group/collapsible"
|
|
115
|
-
open={isEnabled ? undefined : false}
|
|
116
|
-
>
|
|
117
|
-
<SidebarMenuItem>
|
|
118
|
-
{isEnabled ? (
|
|
119
|
-
<CollapsibleTrigger asChild>
|
|
120
|
-
<SidebarMenuButton>
|
|
121
|
-
{item.icon && <item.icon />}
|
|
122
|
-
<span className="grow">{item.title}</span>
|
|
123
|
-
<ChevronRight className="transition-all group-data-[state=open]/collapsible:rotate-90" />
|
|
124
|
-
</SidebarMenuButton>
|
|
125
|
-
</CollapsibleTrigger>
|
|
126
|
-
) : (
|
|
127
|
-
<TurtleTooltip
|
|
128
|
-
asChild
|
|
129
|
-
className="w-full"
|
|
130
|
-
content={LOCKED_TOOLTIP_CONTENT}
|
|
131
|
-
trigger={
|
|
132
|
-
<div className="w-full">
|
|
133
|
-
<SidebarMenuButton
|
|
134
|
-
disabled
|
|
135
|
-
className="opacity-70"
|
|
136
|
-
>
|
|
137
|
-
{item.icon && <item.icon />}
|
|
138
|
-
<span className="grow">{item.title}</span>
|
|
139
|
-
<Lock className="text-muted-foreground size-4 shrink-0" />
|
|
140
|
-
</SidebarMenuButton>
|
|
141
|
-
</div>
|
|
142
|
-
}
|
|
143
|
-
/>
|
|
144
|
-
)}
|
|
145
|
-
|
|
146
|
-
{isEnabled && (
|
|
147
|
-
<CollapsibleContent>
|
|
148
|
-
<SidebarMenuSub>
|
|
149
|
-
{item.children.map((child) => {
|
|
150
|
-
const isChildEnabled =
|
|
151
|
-
child.isEnabled !== false;
|
|
152
|
-
|
|
153
|
-
return (
|
|
154
|
-
<SidebarMenuSubItem key={child.title}>
|
|
155
|
-
{isChildEnabled ? (
|
|
156
|
-
<SidebarMenuSubButton
|
|
157
|
-
asChild
|
|
158
|
-
isActive={child.isActive}
|
|
159
|
-
>
|
|
160
|
-
<Comp href={child.url}>
|
|
161
|
-
{child.icon && <child.icon />}
|
|
162
|
-
<span className="grow">
|
|
163
|
-
{child.title}
|
|
164
|
-
</span>
|
|
165
|
-
|
|
166
|
-
<div className="relative size-[18px] shrink-0 group-data-[active=false]/menu-sub-button:hidden">
|
|
167
|
-
<DotIcon className="text-primary absolute top-[-15px] left-[-15px] size-12" />
|
|
168
|
-
</div>
|
|
169
|
-
</Comp>
|
|
170
|
-
</SidebarMenuSubButton>
|
|
171
|
-
) : (
|
|
172
|
-
<TurtleTooltip
|
|
173
|
-
asChild
|
|
174
|
-
className="w-full"
|
|
175
|
-
content={LOCKED_TOOLTIP_CONTENT}
|
|
176
|
-
trigger={
|
|
177
|
-
<div className="w-full">
|
|
178
|
-
<SidebarMenuSubButton
|
|
179
|
-
className="opacity-70"
|
|
180
|
-
aria-disabled="true"
|
|
181
|
-
>
|
|
182
|
-
{child.icon && <child.icon />}
|
|
183
|
-
<span className="grow">
|
|
184
|
-
{child.title}
|
|
185
|
-
</span>
|
|
186
|
-
<Lock className="text-muted-foreground size-4 shrink-0" />
|
|
187
|
-
</SidebarMenuSubButton>
|
|
188
|
-
</div>
|
|
189
|
-
}
|
|
190
|
-
/>
|
|
191
|
-
)}
|
|
192
|
-
</SidebarMenuSubItem>
|
|
193
|
-
);
|
|
194
|
-
})}
|
|
195
|
-
</SidebarMenuSub>
|
|
196
|
-
</CollapsibleContent>
|
|
197
|
-
)}
|
|
198
|
-
</SidebarMenuItem>
|
|
199
|
-
</Collapsible>
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const isEnabled = item.isEnabled !== false;
|
|
204
|
-
|
|
205
|
-
return (
|
|
206
|
-
<SidebarMenuItem key={item.title}>
|
|
207
|
-
{isEnabled ? (
|
|
208
|
-
<SidebarMenuButton asChild isActive={item.isActive}>
|
|
209
|
-
<Comp href={item.url}>
|
|
210
|
-
{item.icon && <item.icon />}
|
|
211
|
-
<span className="grow">{item.title}</span>
|
|
212
|
-
<div className="relative size-[18px] shrink-0 group-data-[active=false]/menu-button:hidden">
|
|
213
|
-
<DotIcon className="text-primary absolute top-[-15px] left-[-15px] size-12" />
|
|
214
|
-
</div>
|
|
215
|
-
</Comp>
|
|
216
|
-
</SidebarMenuButton>
|
|
217
|
-
) : (
|
|
218
|
-
<TurtleTooltip
|
|
219
|
-
asChild
|
|
220
|
-
className="w-full"
|
|
221
|
-
content={LOCKED_TOOLTIP_CONTENT}
|
|
222
|
-
trigger={
|
|
223
|
-
<div className="w-full">
|
|
224
|
-
<SidebarMenuButton
|
|
225
|
-
aria-disabled="true"
|
|
226
|
-
className="opacity-70"
|
|
227
|
-
>
|
|
228
|
-
{item.icon && <item.icon />}
|
|
229
|
-
<span className="grow">{item.title}</span>
|
|
230
|
-
<Lock className="text-muted-foreground size-4 shrink-0" />
|
|
231
|
-
</SidebarMenuButton>
|
|
232
|
-
</div>
|
|
233
|
-
}
|
|
234
|
-
/>
|
|
235
|
-
)}
|
|
236
|
-
</SidebarMenuItem>
|
|
237
|
-
);
|
|
238
|
-
})}
|
|
239
|
-
</SidebarMenu>
|
|
240
|
-
</SidebarGroupContent>
|
|
241
|
-
</SidebarGroup>
|
|
242
|
-
<span className="grow" />
|
|
243
|
-
<SidebarGroup>{sidebarSlot}</SidebarGroup>
|
|
244
|
-
</SidebarContent>
|
|
245
|
-
<SidebarFooter>{sidebarFooter}</SidebarFooter>
|
|
246
|
-
</Sidebar>
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
export function TurtleContainer({
|
|
251
|
-
children,
|
|
252
|
-
topSlot,
|
|
253
|
-
}: {
|
|
254
|
-
children: React.ReactNode;
|
|
255
|
-
topSlot?: React.ReactNode;
|
|
256
|
-
}) {
|
|
257
|
-
const { state, isMobile } = useSidebar();
|
|
258
|
-
return (
|
|
259
|
-
<div
|
|
260
|
-
className={cn(
|
|
261
|
-
"fixed inset-0 flex h-svh flex-col items-stretch gap-1 py-2 pr-2",
|
|
262
|
-
state === "collapsed" || isMobile ? "pl-2" : "left-64",
|
|
263
|
-
)}
|
|
264
|
-
>
|
|
265
|
-
<div className="flex gap-1">
|
|
266
|
-
<SidebarTrigger />
|
|
267
|
-
{topSlot}
|
|
268
|
-
</div>
|
|
269
|
-
|
|
270
|
-
<main
|
|
271
|
-
className={cn(
|
|
272
|
-
"bg-background border-border max-h-[94svh] grow overflow-y-auto rounded-lg border p-6",
|
|
273
|
-
)}
|
|
274
|
-
>
|
|
275
|
-
{children}
|
|
276
|
-
</main>
|
|
277
|
-
</div>
|
|
278
|
-
);
|
|
279
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { MouseEvent, ReactNode } from "react";
|
|
2
|
-
import { useIsMobile } from "@/hooks/useIsMobile";
|
|
3
|
-
import {
|
|
4
|
-
Tooltip,
|
|
5
|
-
TooltipContent,
|
|
6
|
-
TooltipTrigger,
|
|
7
|
-
} from "@/components/ui/tooltip";
|
|
8
|
-
import {
|
|
9
|
-
Popover,
|
|
10
|
-
PopoverContent,
|
|
11
|
-
PopoverTrigger,
|
|
12
|
-
} from "@/components/ui/popover";
|
|
13
|
-
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
|
14
|
-
|
|
15
|
-
interface TooltipProps {
|
|
16
|
-
trigger: ReactNode;
|
|
17
|
-
content: ReactNode;
|
|
18
|
-
className?: string;
|
|
19
|
-
contentClassName?: string;
|
|
20
|
-
maxWidth?: string;
|
|
21
|
-
href?: string;
|
|
22
|
-
side?: "top" | "bottom" | "left" | "right";
|
|
23
|
-
asChild?: boolean;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function TurtleTooltip({
|
|
27
|
-
trigger,
|
|
28
|
-
content,
|
|
29
|
-
className,
|
|
30
|
-
side,
|
|
31
|
-
asChild,
|
|
32
|
-
}: TooltipProps) {
|
|
33
|
-
const { isMobile } = useIsMobile();
|
|
34
|
-
|
|
35
|
-
const handleClick = (e: MouseEvent<HTMLDivElement>) => {
|
|
36
|
-
e.stopPropagation();
|
|
37
|
-
e.preventDefault();
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<div className={className} onClick={handleClick}>
|
|
42
|
-
{isMobile ? (
|
|
43
|
-
<Popover>
|
|
44
|
-
<PopoverTrigger
|
|
45
|
-
className="flex items-center justify-center"
|
|
46
|
-
asChild={asChild}
|
|
47
|
-
>
|
|
48
|
-
{trigger}
|
|
49
|
-
</PopoverTrigger>
|
|
50
|
-
<PopoverContent side={side}>{content}</PopoverContent>
|
|
51
|
-
</Popover>
|
|
52
|
-
) : (
|
|
53
|
-
<Tooltip>
|
|
54
|
-
<TooltipTrigger
|
|
55
|
-
className="flex items-center justify-center"
|
|
56
|
-
asChild={asChild}
|
|
57
|
-
>
|
|
58
|
-
{trigger}
|
|
59
|
-
</TooltipTrigger>
|
|
60
|
-
<TooltipPortal>
|
|
61
|
-
<TooltipContent side={side}>{content}</TooltipContent>
|
|
62
|
-
</TooltipPortal>
|
|
63
|
-
</Tooltip>
|
|
64
|
-
)}
|
|
65
|
-
</div>
|
|
66
|
-
);
|
|
67
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export const ArrowIcon = ({ className }: { className?: string }) => (
|
|
2
|
-
<svg
|
|
3
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
4
|
-
width="13"
|
|
5
|
-
height="14"
|
|
6
|
-
viewBox="0 0 13 14"
|
|
7
|
-
fill="none"
|
|
8
|
-
className={className}
|
|
9
|
-
>
|
|
10
|
-
<path
|
|
11
|
-
d="M3.65174 10.4506L3.05176 9.85059L9.05161 3.85073L9.6516 4.45072L3.65174 10.4506Z"
|
|
12
|
-
fill="currentColor"
|
|
13
|
-
/>
|
|
14
|
-
<path
|
|
15
|
-
d="M9.68168 4.66626L3.31787 4.66626V3.81775L9.68168 3.81775V4.66626Z"
|
|
16
|
-
fill="currentColor"
|
|
17
|
-
/>
|
|
18
|
-
<path
|
|
19
|
-
d="M9.68249 10.1821H8.83398L8.83398 3.81832H9.68249L9.68249 10.1821Z"
|
|
20
|
-
fill="currentColor"
|
|
21
|
-
/>
|
|
22
|
-
</svg>
|
|
23
|
-
);
|