@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.
Files changed (135) hide show
  1. package/dist/index.cjs +10331 -110
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +7227 -48026
  4. package/dist/index.js.map +1 -1
  5. package/package.json +17 -11
  6. package/.prettierrc.json +0 -4
  7. package/.turbo/turbo-build.log +0 -182
  8. package/CHANGELOG.md +0 -801
  9. package/components.json +0 -21
  10. package/src/components/charts/QUICK_REFERENCE.md +0 -323
  11. package/src/components/charts/README.md +0 -658
  12. package/src/components/charts/RECHARTS_FEATURES.md +0 -458
  13. package/src/components/charts/area-chart.tsx +0 -248
  14. package/src/components/charts/bar-chart.tsx +0 -362
  15. package/src/components/charts/index.ts +0 -4
  16. package/src/components/charts/pie-chart.tsx +0 -277
  17. package/src/components/charts/radial-chart.tsx +0 -312
  18. package/src/components/features/api-status/index.tsx +0 -23
  19. package/src/components/features/data-table/data-table.tsx +0 -538
  20. package/src/components/features/data-table/expand-toggle.tsx +0 -17
  21. package/src/components/features/data-table/fuzzy-filter.tsx +0 -34
  22. package/src/components/features/data-table/index.ts +0 -3
  23. package/src/components/features/data-table/item-info.tsx +0 -19
  24. package/src/components/features/data-table/skeleton.tsx +0 -23
  25. package/src/components/features/data-table/sort-dropdown.tsx +0 -118
  26. package/src/components/features/data-table/sortable-header.tsx +0 -37
  27. package/src/components/features/index.ts +0 -6
  28. package/src/components/features/page-heading.tsx +0 -27
  29. package/src/components/features/search-bar.tsx +0 -55
  30. package/src/components/features/segmented-navigation.tsx +0 -18
  31. package/src/components/features/sidebar-layout.tsx +0 -279
  32. package/src/components/features/turtle-tooltip.tsx +0 -67
  33. package/src/components/icons/arrow.tsx +0 -23
  34. package/src/components/icons/beta.tsx +0 -95
  35. package/src/components/icons/dot.tsx +0 -102
  36. package/src/components/icons/index.ts +0 -7
  37. package/src/components/icons/issue.tsx +0 -106
  38. package/src/components/icons/turtle.tsx +0 -156
  39. package/src/components/icons/update.tsx +0 -113
  40. package/src/components/icons/warning.tsx +0 -95
  41. package/src/components/molecules/index.ts +0 -9
  42. package/src/components/molecules/opportunity/index.ts +0 -10
  43. package/src/components/molecules/opportunity/opportunity-apr.tsx +0 -129
  44. package/src/components/molecules/opportunity/opportunity-disclaimer.tsx +0 -46
  45. package/src/components/molecules/opportunity/opportunity-rate-estimator.tsx +0 -62
  46. package/src/components/molecules/opportunity/opportunity-section.tsx +0 -113
  47. package/src/components/molecules/opportunity/opportunity-selector.tsx +0 -30
  48. package/src/components/molecules/opportunity/opportunity-type.tsx +0 -16
  49. package/src/components/molecules/route-details.tsx +0 -112
  50. package/src/components/molecules/slippage-selector.tsx +0 -200
  51. package/src/components/molecules/swap-details.tsx +0 -55
  52. package/src/components/molecules/swap-input.tsx +0 -186
  53. package/src/components/molecules/tabs.tsx +0 -79
  54. package/src/components/molecules/token-selector.tsx +0 -180
  55. package/src/components/molecules/tx-status.tsx +0 -312
  56. package/src/components/molecules/widget/asset-list/asset-filters.tsx +0 -113
  57. package/src/components/molecules/widget/asset-list/asset-list.tsx +0 -178
  58. package/src/components/molecules/widget/asset-list/asset-row.tsx +0 -45
  59. package/src/components/molecules/widget/asset-list/hooks/index.ts +0 -2
  60. package/src/components/molecules/widget/asset-list/hooks/use-asset-filtering.ts +0 -44
  61. package/src/components/molecules/widget/asset-list/hooks/use-asset-grouping.ts +0 -87
  62. package/src/components/molecules/widget/asset-list/index.ts +0 -3
  63. package/src/components/molecules/widget/base-selector.tsx +0 -121
  64. package/src/components/molecules/widget/campaign-item.tsx +0 -82
  65. package/src/components/molecules/widget/deal-item.tsx +0 -92
  66. package/src/components/molecules/widget/index.ts +0 -36
  67. package/src/components/molecules/widget/opportunity-item.tsx +0 -105
  68. package/src/components/molecules/widget/widget-item-stats.tsx +0 -50
  69. package/src/components/molecules/widget/widget-item.tsx +0 -139
  70. package/src/components/molecules/widget/widget-list-items.tsx +0 -86
  71. package/src/components/ui/alert-dialog.tsx +0 -163
  72. package/src/components/ui/animated-background/animated-background.tsx +0 -182
  73. package/src/components/ui/animated-background/index.ts +0 -1
  74. package/src/components/ui/avatar.tsx +0 -73
  75. package/src/components/ui/badge.tsx +0 -59
  76. package/src/components/ui/banner.tsx +0 -84
  77. package/src/components/ui/button.tsx +0 -100
  78. package/src/components/ui/card.tsx +0 -119
  79. package/src/components/ui/chart.tsx +0 -346
  80. package/src/components/ui/checkbox.tsx +0 -32
  81. package/src/components/ui/chip.tsx +0 -52
  82. package/src/components/ui/collapsible.tsx +0 -34
  83. package/src/components/ui/combobox.tsx +0 -730
  84. package/src/components/ui/command.tsx +0 -184
  85. package/src/components/ui/dialog.tsx +0 -129
  86. package/src/components/ui/dropdown.tsx +0 -316
  87. package/src/components/ui/field.tsx +0 -244
  88. package/src/components/ui/heading.tsx +0 -74
  89. package/src/components/ui/hover-card.tsx +0 -139
  90. package/src/components/ui/icon-animation.tsx +0 -82
  91. package/src/components/ui/icon-list.tsx +0 -168
  92. package/src/components/ui/index.ts +0 -48
  93. package/src/components/ui/info-card.tsx +0 -110
  94. package/src/components/ui/input-group.tsx +0 -170
  95. package/src/components/ui/input.tsx +0 -72
  96. package/src/components/ui/label-with-icon.tsx +0 -122
  97. package/src/components/ui/label.tsx +0 -24
  98. package/src/components/ui/multi-select.tsx +0 -1090
  99. package/src/components/ui/navigation-bar.tsx +0 -153
  100. package/src/components/ui/navigation-menu.tsx +0 -188
  101. package/src/components/ui/opportunity-details-v1.tsx +0 -104
  102. package/src/components/ui/pagination.tsx +0 -127
  103. package/src/components/ui/popover.tsx +0 -48
  104. package/src/components/ui/scroll-area.tsx +0 -64
  105. package/src/components/ui/segment-control.tsx +0 -146
  106. package/src/components/ui/select.tsx +0 -199
  107. package/src/components/ui/separator.tsx +0 -26
  108. package/src/components/ui/sheet.tsx +0 -139
  109. package/src/components/ui/sidebar.tsx +0 -728
  110. package/src/components/ui/skeleton.tsx +0 -14
  111. package/src/components/ui/slider.tsx +0 -58
  112. package/src/components/ui/sonner.tsx +0 -24
  113. package/src/components/ui/switch.tsx +0 -29
  114. package/src/components/ui/table-shadcn.tsx +0 -110
  115. package/src/components/ui/table.tsx +0 -117
  116. package/src/components/ui/textarea.tsx +0 -22
  117. package/src/components/ui/toggle-group.tsx +0 -71
  118. package/src/components/ui/toggle.tsx +0 -47
  119. package/src/components/ui/tooltip.tsx +0 -66
  120. package/src/hooks/index.ts +0 -1
  121. package/src/hooks/useIsMobile.ts +0 -77
  122. package/src/index.ts +0 -16
  123. package/src/lib/utils.ts +0 -6
  124. package/src/styles/globals.css +0 -181
  125. package/src/styles/themes/index.css +0 -9
  126. package/src/styles/themes/semantic.css +0 -117
  127. package/src/styles/tokens/colors.css +0 -124
  128. package/src/styles/tokens/index.css +0 -15
  129. package/src/styles/tokens/radius.css +0 -18
  130. package/src/styles/tokens/spacing.css +0 -58
  131. package/src/styles/tokens/typography.css +0 -87
  132. package/src/tokens/index.ts +0 -108
  133. package/tsconfig.json +0 -20
  134. package/vite.config.js +0 -49
  135. /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 };