@turtleclub/ui 0.7.0-beta.32 → 0.7.0-beta.34

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.
Files changed (139) hide show
  1. package/dist/index.cjs +10331 -110
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +7652 -47844
  4. package/dist/index.js.map +1 -1
  5. package/dist/types/components/features/sidebar-layout.d.ts +2 -0
  6. package/dist/types/components/features/sidebar-layout.d.ts.map +1 -1
  7. package/dist/types/components/ui/chart.d.ts +1 -1
  8. package/dist/types/components/ui/chart.d.ts.map +1 -1
  9. package/package.json +26 -22
  10. package/.prettierrc.json +0 -4
  11. package/.turbo/turbo-build.log +0 -182
  12. package/CHANGELOG.md +0 -795
  13. package/components.json +0 -21
  14. package/src/components/charts/QUICK_REFERENCE.md +0 -323
  15. package/src/components/charts/README.md +0 -658
  16. package/src/components/charts/RECHARTS_FEATURES.md +0 -458
  17. package/src/components/charts/area-chart.tsx +0 -248
  18. package/src/components/charts/bar-chart.tsx +0 -362
  19. package/src/components/charts/index.ts +0 -4
  20. package/src/components/charts/pie-chart.tsx +0 -277
  21. package/src/components/charts/radial-chart.tsx +0 -312
  22. package/src/components/features/api-status/index.tsx +0 -23
  23. package/src/components/features/data-table/data-table.tsx +0 -538
  24. package/src/components/features/data-table/expand-toggle.tsx +0 -17
  25. package/src/components/features/data-table/fuzzy-filter.tsx +0 -34
  26. package/src/components/features/data-table/index.ts +0 -3
  27. package/src/components/features/data-table/item-info.tsx +0 -19
  28. package/src/components/features/data-table/skeleton.tsx +0 -23
  29. package/src/components/features/data-table/sort-dropdown.tsx +0 -118
  30. package/src/components/features/data-table/sortable-header.tsx +0 -37
  31. package/src/components/features/index.ts +0 -6
  32. package/src/components/features/page-heading.tsx +0 -27
  33. package/src/components/features/search-bar.tsx +0 -55
  34. package/src/components/features/segmented-navigation.tsx +0 -18
  35. package/src/components/features/sidebar-layout.tsx +0 -193
  36. package/src/components/features/turtle-tooltip.tsx +0 -67
  37. package/src/components/icons/arrow.tsx +0 -23
  38. package/src/components/icons/beta.tsx +0 -95
  39. package/src/components/icons/dot.tsx +0 -102
  40. package/src/components/icons/index.ts +0 -7
  41. package/src/components/icons/issue.tsx +0 -106
  42. package/src/components/icons/turtle.tsx +0 -156
  43. package/src/components/icons/update.tsx +0 -113
  44. package/src/components/icons/warning.tsx +0 -95
  45. package/src/components/molecules/index.ts +0 -9
  46. package/src/components/molecules/opportunity/index.ts +0 -10
  47. package/src/components/molecules/opportunity/opportunity-apr.tsx +0 -129
  48. package/src/components/molecules/opportunity/opportunity-disclaimer.tsx +0 -46
  49. package/src/components/molecules/opportunity/opportunity-rate-estimator.tsx +0 -62
  50. package/src/components/molecules/opportunity/opportunity-section.tsx +0 -113
  51. package/src/components/molecules/opportunity/opportunity-selector.tsx +0 -30
  52. package/src/components/molecules/opportunity/opportunity-type.tsx +0 -16
  53. package/src/components/molecules/route-details.tsx +0 -112
  54. package/src/components/molecules/slippage-selector.tsx +0 -200
  55. package/src/components/molecules/swap-details.tsx +0 -55
  56. package/src/components/molecules/swap-input.tsx +0 -186
  57. package/src/components/molecules/tabs.tsx +0 -79
  58. package/src/components/molecules/token-selector.tsx +0 -180
  59. package/src/components/molecules/tx-status.tsx +0 -312
  60. package/src/components/molecules/widget/asset-list/asset-filters.tsx +0 -113
  61. package/src/components/molecules/widget/asset-list/asset-list.tsx +0 -178
  62. package/src/components/molecules/widget/asset-list/asset-row.tsx +0 -45
  63. package/src/components/molecules/widget/asset-list/hooks/index.ts +0 -2
  64. package/src/components/molecules/widget/asset-list/hooks/use-asset-filtering.ts +0 -44
  65. package/src/components/molecules/widget/asset-list/hooks/use-asset-grouping.ts +0 -87
  66. package/src/components/molecules/widget/asset-list/index.ts +0 -3
  67. package/src/components/molecules/widget/base-selector.tsx +0 -121
  68. package/src/components/molecules/widget/campaign-item.tsx +0 -82
  69. package/src/components/molecules/widget/deal-item.tsx +0 -92
  70. package/src/components/molecules/widget/index.ts +0 -36
  71. package/src/components/molecules/widget/opportunity-item.tsx +0 -105
  72. package/src/components/molecules/widget/widget-item-stats.tsx +0 -50
  73. package/src/components/molecules/widget/widget-item.tsx +0 -139
  74. package/src/components/molecules/widget/widget-list-items.tsx +0 -86
  75. package/src/components/ui/alert-dialog.tsx +0 -163
  76. package/src/components/ui/animated-background/animated-background.tsx +0 -182
  77. package/src/components/ui/animated-background/index.ts +0 -1
  78. package/src/components/ui/avatar.tsx +0 -73
  79. package/src/components/ui/badge.tsx +0 -59
  80. package/src/components/ui/banner.tsx +0 -84
  81. package/src/components/ui/button.tsx +0 -100
  82. package/src/components/ui/card.tsx +0 -119
  83. package/src/components/ui/chart.tsx +0 -346
  84. package/src/components/ui/checkbox.tsx +0 -32
  85. package/src/components/ui/chip.tsx +0 -52
  86. package/src/components/ui/collapsible.tsx +0 -34
  87. package/src/components/ui/combobox.tsx +0 -730
  88. package/src/components/ui/command.tsx +0 -184
  89. package/src/components/ui/dialog.tsx +0 -129
  90. package/src/components/ui/dropdown.tsx +0 -316
  91. package/src/components/ui/field.tsx +0 -244
  92. package/src/components/ui/heading.tsx +0 -74
  93. package/src/components/ui/hover-card.tsx +0 -139
  94. package/src/components/ui/icon-animation.tsx +0 -82
  95. package/src/components/ui/icon-list.tsx +0 -168
  96. package/src/components/ui/index.ts +0 -48
  97. package/src/components/ui/info-card.tsx +0 -110
  98. package/src/components/ui/input-group.tsx +0 -170
  99. package/src/components/ui/input.tsx +0 -72
  100. package/src/components/ui/label-with-icon.tsx +0 -122
  101. package/src/components/ui/label.tsx +0 -24
  102. package/src/components/ui/multi-select.tsx +0 -1090
  103. package/src/components/ui/navigation-bar.tsx +0 -153
  104. package/src/components/ui/navigation-menu.tsx +0 -188
  105. package/src/components/ui/opportunity-details-v1.tsx +0 -104
  106. package/src/components/ui/pagination.tsx +0 -127
  107. package/src/components/ui/popover.tsx +0 -48
  108. package/src/components/ui/scroll-area.tsx +0 -64
  109. package/src/components/ui/segment-control.tsx +0 -146
  110. package/src/components/ui/select.tsx +0 -199
  111. package/src/components/ui/separator.tsx +0 -26
  112. package/src/components/ui/sheet.tsx +0 -139
  113. package/src/components/ui/sidebar.tsx +0 -728
  114. package/src/components/ui/skeleton.tsx +0 -14
  115. package/src/components/ui/slider.tsx +0 -58
  116. package/src/components/ui/sonner.tsx +0 -24
  117. package/src/components/ui/switch.tsx +0 -29
  118. package/src/components/ui/table-shadcn.tsx +0 -110
  119. package/src/components/ui/table.tsx +0 -117
  120. package/src/components/ui/textarea.tsx +0 -22
  121. package/src/components/ui/toggle-group.tsx +0 -71
  122. package/src/components/ui/toggle.tsx +0 -47
  123. package/src/components/ui/tooltip.tsx +0 -66
  124. package/src/hooks/index.ts +0 -1
  125. package/src/hooks/useIsMobile.ts +0 -77
  126. package/src/index.ts +0 -16
  127. package/src/lib/utils.ts +0 -6
  128. package/src/styles/globals.css +0 -181
  129. package/src/styles/themes/index.css +0 -9
  130. package/src/styles/themes/semantic.css +0 -117
  131. package/src/styles/tokens/colors.css +0 -124
  132. package/src/styles/tokens/index.css +0 -15
  133. package/src/styles/tokens/radius.css +0 -18
  134. package/src/styles/tokens/spacing.css +0 -58
  135. package/src/styles/tokens/typography.css +0 -87
  136. package/src/tokens/index.ts +0 -108
  137. package/tsconfig.json +0 -20
  138. package/vite.config.js +0 -49
  139. /package/{src/images/enso.png → dist/enso-22FJ4GNK.png} +0 -0
@@ -1,244 +0,0 @@
1
- "use client";
2
-
3
- import React, { useMemo } from "react";
4
- import { cva, type VariantProps } from "class-variance-authority";
5
-
6
- import { cn } from "@/lib/utils";
7
- import { Label } from "@/components/ui/label";
8
- import { Separator } from "@/components/ui/separator";
9
-
10
- function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
11
- return (
12
- <fieldset
13
- data-slot="field-set"
14
- className={cn(
15
- "flex flex-col gap-6",
16
- "has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
17
- className,
18
- )}
19
- {...props}
20
- />
21
- );
22
- }
23
-
24
- function FieldLegend({
25
- className,
26
- variant = "legend",
27
- ...props
28
- }: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
29
- return (
30
- <legend
31
- data-slot="field-legend"
32
- data-variant={variant}
33
- className={cn(
34
- "mb-3 font-medium",
35
- "data-[variant=legend]:text-base",
36
- "data-[variant=label]:text-sm",
37
- className,
38
- )}
39
- {...props}
40
- />
41
- );
42
- }
43
-
44
- function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
45
- return (
46
- <div
47
- data-slot="field-group"
48
- className={cn(
49
- "group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
50
- className,
51
- )}
52
- {...props}
53
- />
54
- );
55
- }
56
-
57
- const fieldVariants = cva(
58
- "group/field data-[invalid=true]:text-destructive flex w-full gap-3",
59
- {
60
- variants: {
61
- orientation: {
62
- vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
63
- horizontal: [
64
- "flex-row items-center",
65
- "[&>[data-slot=field-label]]:flex-auto",
66
- "has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
67
- ],
68
- responsive: [
69
- "flex-col @md/field-group:flex-row @md/field-group:items-center [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto",
70
- "@md/field-group:[&>[data-slot=field-label]]:flex-auto",
71
- "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
72
- ],
73
- },
74
- },
75
- defaultVariants: {
76
- orientation: "vertical",
77
- },
78
- },
79
- );
80
-
81
- function Field({
82
- className,
83
- orientation = "vertical",
84
- ...props
85
- }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
86
- return (
87
- <div
88
- role="group"
89
- data-slot="field"
90
- data-orientation={orientation}
91
- className={cn(fieldVariants({ orientation }), className)}
92
- {...props}
93
- />
94
- );
95
- }
96
-
97
- function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
98
- return (
99
- <div
100
- data-slot="field-content"
101
- className={cn(
102
- "group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
103
- className,
104
- )}
105
- {...props}
106
- />
107
- );
108
- }
109
-
110
- function FieldLabel({
111
- className,
112
- ...props
113
- }: React.ComponentProps<typeof Label>) {
114
- return (
115
- <Label
116
- data-slot="field-label"
117
- className={cn(
118
- "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
119
- "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4",
120
- "has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary",
121
- className,
122
- )}
123
- {...props}
124
- />
125
- );
126
- }
127
-
128
- function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
129
- return (
130
- <div
131
- data-slot="field-label"
132
- className={cn(
133
- "flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50",
134
- className,
135
- )}
136
- {...props}
137
- />
138
- );
139
- }
140
-
141
- function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
142
- return (
143
- <p
144
- data-slot="field-description"
145
- className={cn(
146
- "text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
147
- "last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5",
148
- "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
149
- className,
150
- )}
151
- {...props}
152
- />
153
- );
154
- }
155
-
156
- function FieldSeparator({
157
- children,
158
- className,
159
- ...props
160
- }: React.ComponentProps<"div"> & {
161
- children?: React.ReactNode;
162
- }) {
163
- return (
164
- <div
165
- data-slot="field-separator"
166
- data-content={!!children}
167
- className={cn(
168
- "relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
169
- className,
170
- )}
171
- {...props}
172
- >
173
- <Separator className="absolute inset-0 top-1/2" />
174
- {children && (
175
- <span
176
- className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
177
- data-slot="field-separator-content"
178
- >
179
- {children}
180
- </span>
181
- )}
182
- </div>
183
- );
184
- }
185
-
186
- function FieldError({
187
- className,
188
- children,
189
- errors,
190
- ...props
191
- }: React.ComponentProps<"div"> & {
192
- errors?: Array<{ message?: string } | undefined>;
193
- }) {
194
- const content = useMemo(() => {
195
- if (children) {
196
- return children;
197
- }
198
-
199
- if (!errors) {
200
- return null;
201
- }
202
-
203
- if (errors?.length === 1 && errors[0]?.message) {
204
- return errors[0].message;
205
- }
206
-
207
- return (
208
- <ul className="ml-4 flex list-disc flex-col gap-1">
209
- {errors.map(
210
- (error, index) =>
211
- error?.message && <li key={index}>{error.message}</li>,
212
- )}
213
- </ul>
214
- );
215
- }, [children, errors]);
216
-
217
- if (!content) {
218
- return null;
219
- }
220
-
221
- return (
222
- <div
223
- role="alert"
224
- data-slot="field-error"
225
- className={cn("text-destructive text-sm font-normal", className)}
226
- {...props}
227
- >
228
- {content}
229
- </div>
230
- );
231
- }
232
-
233
- export {
234
- Field,
235
- FieldLabel,
236
- FieldDescription,
237
- FieldError,
238
- FieldGroup,
239
- FieldLegend,
240
- FieldSeparator,
241
- FieldSet,
242
- FieldContent,
243
- FieldTitle,
244
- };
@@ -1,74 +0,0 @@
1
- import { cn } from "@/lib/utils";
2
- import React from "react";
3
-
4
- export type TypographyProps = { children: React.ReactNode; className?: string };
5
-
6
- export function HeadingH1({ children, className }: TypographyProps) {
7
- return (
8
- <h1
9
- className={cn(
10
- "scroll-m-20 text-4xl font-semibold tracking-tight text-balance",
11
- className,
12
- )}
13
- >
14
- {children}
15
- </h1>
16
- );
17
- }
18
-
19
- export function HeadingH2({ children, className }: TypographyProps) {
20
- return (
21
- <h2
22
- className={cn(
23
- "scroll-m-20 text-3xl font-normal tracking-tight first:mt-0",
24
- className,
25
- )}
26
- >
27
- {children}
28
- </h2>
29
- );
30
- }
31
-
32
- export function HeadingH3({ children, className }: TypographyProps) {
33
- return (
34
- <h3
35
- className={cn(
36
- "scroll-m-20 text-xl font-normal tracking-tight",
37
- className,
38
- )}
39
- >
40
- {children}
41
- </h3>
42
- );
43
- }
44
-
45
- export function HeadingH4({ children, className }: TypographyProps) {
46
- return (
47
- <h4
48
- className={cn("scroll-m-20 text-lg font-light tracking-tight", className)}
49
- >
50
- {children}
51
- </h4>
52
- );
53
- }
54
-
55
- export function HeadingH5({ children, className }: TypographyProps) {
56
- return (
57
- <h5 className={cn("scroll-m-20 font-light tracking-tight", className)}>
58
- {children}
59
- </h5>
60
- );
61
- }
62
-
63
- export function HeadingH6({ children, className }: TypographyProps) {
64
- return (
65
- <h6
66
- className={cn(
67
- "text-muted-foreground scroll-m-20 text-sm font-light tracking-tight",
68
- className,
69
- )}
70
- >
71
- {children}
72
- </h6>
73
- );
74
- }
@@ -1,139 +0,0 @@
1
- import * as React from "react";
2
- import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
3
- import { cva, type VariantProps } from "class-variance-authority";
4
-
5
- import { cn } from "@/lib/utils";
6
-
7
- const hoverCardContentVariants = cva(
8
- "text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) shadow-md outline-hidden transition-all",
9
- {
10
- variants: {
11
- variant: {
12
- container: "bg-background relative drop-shadow-lg",
13
- simple: "bg-muted",
14
- item: "bg-muted drop-shadow-lg",
15
- },
16
- gradientBorder: {
17
- none: "",
18
- white:
19
- "relative before:pointer-events-none before:absolute before:inset-0 before:rounded-[inherit] before:bg-gradient-to-br before:from-white/40 before:via-transparent before:to-white/10 before:[mask-composite:exclude] before:p-px before:content-[''] before:[mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)]",
20
- green:
21
- "relative before:pointer-events-none before:absolute before:inset-0 before:rounded-[inherit] before:bg-gradient-to-br before:from-[#73F36C]/60 before:via-transparent before:to-[#73F36C]/20 before:[mask-composite:exclude] before:p-px before:content-[''] before:[mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)]",
22
- auto: "light:before:from-black/20 light:before:to-black/5 relative before:pointer-events-none before:absolute before:inset-0 before:rounded-[inherit] before:bg-gradient-to-br before:from-white/40 before:via-transparent before:to-white/10 before:[mask-composite:exclude] before:p-px before:content-[''] before:[mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)]",
23
- },
24
- padding: {
25
- none: "p-0",
26
- sm: "px-3.5 py-3.5",
27
- default: "px-4.5 py-4.5",
28
- md: "px-6 py-6.5",
29
- lg: "px-8 py-8.5",
30
- },
31
- rounded: {
32
- default: "rounded-lg",
33
- infoCard: "rounded-info-card",
34
- none: "rounded-none",
35
- sm: "rounded-sm",
36
- md: "rounded-md",
37
- lg: "rounded-lg",
38
- xl: "rounded-lg",
39
- full: "rounded-full",
40
- },
41
- },
42
- defaultVariants: {
43
- variant: "container",
44
- padding: "default",
45
- rounded: "default",
46
- gradientBorder: "none",
47
- },
48
- },
49
- );
50
-
51
- function HoverCard({
52
- ...props
53
- }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
54
- return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
55
- }
56
-
57
- function HoverCardTrigger({
58
- ...props
59
- }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
60
- return (
61
- <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
62
- );
63
- }
64
-
65
- function HoverCardContent({
66
- className,
67
- align = "center",
68
- sideOffset = 4,
69
- variant,
70
- padding,
71
- rounded,
72
- gradientBorder,
73
- ...props
74
- }: React.ComponentProps<typeof HoverCardPrimitive.Content> &
75
- VariantProps<typeof hoverCardContentVariants>) {
76
- // Auto-apply gradient border for container variant if not specified
77
- const finalGradientBorder =
78
- gradientBorder ?? (variant === "container" ? "white" : "none");
79
-
80
- const hasGradientBorder = finalGradientBorder !== "none";
81
-
82
- // Get the gradient border classes
83
- const gradientClasses = hoverCardContentVariants({
84
- gradientBorder: finalGradientBorder,
85
- });
86
-
87
- // Get the main hover card classes without gradient
88
- const hoverCardClasses = cn(
89
- hoverCardContentVariants({
90
- variant,
91
- padding,
92
- rounded,
93
- gradientBorder: "none",
94
- className,
95
- }),
96
- );
97
-
98
- // Get the rounded class for the wrapper
99
- const roundedClass = hoverCardContentVariants({ rounded });
100
-
101
- return (
102
- <HoverCardPrimitive.Portal
103
- container={
104
- document.querySelectorAll(".turtle-widget-root")[0] ?? undefined
105
- }
106
- data-slot="hover-card-portal"
107
- >
108
- {hasGradientBorder ? (
109
- <HoverCardPrimitive.Content
110
- data-slot="hover-card-content-wrapper"
111
- align={align}
112
- sideOffset={sideOffset}
113
- className={cn(gradientClasses, roundedClass, "p-[1px]")}
114
- {...props}
115
- >
116
- <div
117
- data-slot="hover-card-content-inner"
118
- className={hoverCardClasses}
119
- />
120
- </HoverCardPrimitive.Content>
121
- ) : (
122
- <HoverCardPrimitive.Content
123
- data-slot="hover-card-content"
124
- align={align}
125
- sideOffset={sideOffset}
126
- className={hoverCardClasses}
127
- {...props}
128
- />
129
- )}
130
- </HoverCardPrimitive.Portal>
131
- );
132
- }
133
-
134
- export {
135
- HoverCard,
136
- HoverCardTrigger,
137
- HoverCardContent,
138
- hoverCardContentVariants,
139
- };
@@ -1,82 +0,0 @@
1
- import * as React from "react";
2
- import { cva, type VariantProps } from "class-variance-authority";
3
-
4
- import { cn } from "@/lib/utils";
5
-
6
- const iconAnimationVariants = cva(
7
- "relative inline-flex items-center justify-center rounded-full",
8
- {
9
- variants: {
10
- size: {
11
- default: "h-12 w-12",
12
- sm: "h-8 w-8",
13
- lg: "h-16 w-16",
14
- xl: "h-20 w-20",
15
- },
16
- variant: {
17
- default: "bg-background",
18
- transparent: "bg-transparent",
19
- },
20
- },
21
- defaultVariants: {
22
- size: "default",
23
- variant: "default",
24
- },
25
- },
26
- );
27
-
28
- export interface IconAnimationProps
29
- extends React.ComponentProps<"div">,
30
- VariantProps<typeof iconAnimationVariants> {
31
- children: React.ReactNode;
32
- spinning?: boolean;
33
- }
34
-
35
- const IconAnimation = React.forwardRef<HTMLDivElement, IconAnimationProps>(
36
- ({ className, size, variant, children, spinning = true, ...props }, ref) => {
37
- return (
38
- <div
39
- ref={ref}
40
- className={cn(iconAnimationVariants({ size, variant }), className)}
41
- {...props}
42
- >
43
- {/* Animated border */}
44
- <div
45
- className={cn(
46
- "absolute inset-0 rounded-full",
47
- "via-primary bg-gradient-to-r from-transparent to-transparent",
48
- "animate-spin",
49
- {
50
- "animate-spin": spinning,
51
- "animate-none": !spinning,
52
- },
53
- )}
54
- style={{
55
- background: spinning
56
- ? "conic-gradient(from 0deg, transparent 0deg, var(--primary) 180deg, transparent 360deg)"
57
- : "conic-gradient(from 0deg, var(--primary) 0deg, var(--primary) 360deg)",
58
- padding: "1px",
59
- borderRadius: "9999px",
60
- }}
61
- >
62
- {/* Inner circle to create the border effect */}
63
- <div
64
- className={cn(
65
- "h-full w-full rounded-full",
66
- variant === "transparent" ? "bg-transparent" : "bg-background",
67
- )}
68
- />
69
- </div>
70
-
71
- {/* Icon container - stays fixed */}
72
- <div className="relative z-10 flex items-center justify-center">
73
- {children}
74
- </div>
75
- </div>
76
- );
77
- },
78
- );
79
-
80
- IconAnimation.displayName = "IconAnimation";
81
-
82
- export { IconAnimation, iconAnimationVariants };
@@ -1,168 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
- import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
4
- import {
5
- Tooltip,
6
- TooltipContent,
7
- TooltipProvider,
8
- TooltipTrigger,
9
- } from "@/components/ui/tooltip";
10
- import { useMemo } from "react";
11
-
12
- interface IconListItem {
13
- icon: string;
14
- name: string;
15
- id?: string; // Optional unique identifier
16
- }
17
-
18
- interface IconListProps extends React.ComponentProps<"div"> {
19
- items: IconListItem[] | string[];
20
- label?: string;
21
- size?: "xs" | "sm" | "md" | "lg";
22
- maxVisible?: number;
23
- showTooltip?: boolean;
24
- }
25
-
26
- const sizeClasses = {
27
- xs: "w-3 h-3 text-[8px]",
28
- sm: "w-4 h-4 text-[10px]",
29
- md: "w-5 h-5 text-xs",
30
- lg: "w-6 h-6 text-sm",
31
- };
32
-
33
- const IconList = React.forwardRef<HTMLDivElement, IconListProps>(
34
- (
35
- {
36
- className,
37
- items,
38
- label,
39
- size = "sm",
40
- maxVisible = 6,
41
- showTooltip = false,
42
- ...props
43
- },
44
- ref,
45
- ) => {
46
- const [loadedImages, setLoadedImages] = React.useState<Set<string>>(
47
- new Set(),
48
- );
49
-
50
- if (!items || items.length === 0) return null;
51
-
52
- const uniqueItems = useMemo(() => {
53
- const normalized: IconListItem[] = items.map(
54
- (item: IconListItem | string, index: number) =>
55
- typeof item === "string"
56
- ? { icon: item, name: "", id: `item-${index}` }
57
- : { ...item, id: item.id || `item-${index}` },
58
- );
59
-
60
- const unique = normalized.filter(
61
- (item, index, self) =>
62
- index === self.findIndex((t) => t.icon === item.icon),
63
- );
64
-
65
- return unique;
66
- }, [items]);
67
-
68
- const visibleItems = uniqueItems.slice(0, maxVisible);
69
- const remainingCount = uniqueItems.length - maxVisible;
70
- const hiddenItems = uniqueItems.slice(maxVisible);
71
-
72
- const handleImageLoad = (icon: string) => {
73
- setLoadedImages((prev) => new Set(prev).add(icon));
74
- };
75
-
76
- const renderAvatar = (item: IconListItem, index: number) => (
77
- <Avatar
78
- key={item.id || index}
79
- className={cn(
80
- sizeClasses[size],
81
- "border-background border transition-all hover:z-10 hover:scale-110",
82
- !loadedImages.has(item.icon) && "animate-pulse",
83
- )}
84
- >
85
- <AvatarImage
86
- src={item.icon}
87
- alt={item.name || "Icon"}
88
- onLoad={() => handleImageLoad(item.icon)}
89
- />
90
- <AvatarFallback
91
- className={cn("font-medium", sizeClasses[size].split(" ").pop())}
92
- >
93
- {item.name ? item.name.slice(0, 2).toUpperCase() : "?"}
94
- </AvatarFallback>
95
- </Avatar>
96
- );
97
-
98
- const renderWithTooltip = (
99
- content: React.ReactNode,
100
- tooltipText: string,
101
- ) => {
102
- if (!showTooltip || !tooltipText) return content;
103
-
104
- return (
105
- <Tooltip>
106
- <TooltipTrigger asChild>{content}</TooltipTrigger>
107
- <TooltipContent side="top" className="text-xs">
108
- {tooltipText}
109
- </TooltipContent>
110
- </Tooltip>
111
- );
112
- };
113
-
114
- return (
115
- <TooltipProvider>
116
- <div
117
- ref={ref}
118
- className={cn("flex items-center gap-1", className)}
119
- role="list"
120
- aria-label={label || "Icon list"}
121
- {...props}
122
- >
123
- {label && (
124
- <span className="text-muted-foreground mr-1 text-xs">{label}:</span>
125
- )}
126
- <div className="flex -space-x-1">
127
- {visibleItems.map((item, index) => (
128
- <div key={item.id || index} role="listitem">
129
- {renderWithTooltip(renderAvatar(item, index), item.name)}
130
- </div>
131
- ))}
132
- {remainingCount > 0 && (
133
- <div role="listitem">
134
- {renderWithTooltip(
135
- <Avatar
136
- className={cn(
137
- sizeClasses[size],
138
- "border-background bg-muted hover:bg-muted/80 border transition-all",
139
- )}
140
- aria-label={`${remainingCount} more items`}
141
- >
142
- <AvatarFallback
143
- className={cn(
144
- "font-semibold",
145
- sizeClasses[size].split(" ").pop(),
146
- )}
147
- >
148
- +{remainingCount}
149
- </AvatarFallback>
150
- </Avatar>,
151
- hiddenItems
152
- .map((item) => item.name)
153
- .filter(Boolean)
154
- .join(", ") || `${remainingCount} more items`,
155
- )}
156
- </div>
157
- )}
158
- </div>
159
- </div>
160
- </TooltipProvider>
161
- );
162
- },
163
- );
164
-
165
- IconList.displayName = "IconList";
166
-
167
- export { IconList };
168
- export type { IconListProps, IconListItem };