@shipfox/react-ui 0.13.0 → 0.15.0

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 (268) hide show
  1. package/.storybook/preview.tsx +7 -0
  2. package/.turbo/turbo-build.log +7 -7
  3. package/.turbo/turbo-check.log +2 -2
  4. package/.turbo/turbo-type.log +1 -1
  5. package/CHANGELOG.md +16 -0
  6. package/dist/components/avatar/avatar.js +1 -1
  7. package/dist/components/avatar/avatar.js.map +1 -1
  8. package/dist/components/button-group/button-group.d.ts +17 -0
  9. package/dist/components/button-group/button-group.d.ts.map +1 -0
  10. package/dist/components/button-group/button-group.js +74 -0
  11. package/dist/components/button-group/button-group.js.map +1 -0
  12. package/dist/components/button-group/button-group.stories.js +644 -0
  13. package/dist/components/button-group/button-group.stories.js.map +1 -0
  14. package/dist/components/button-group/index.d.ts +2 -0
  15. package/dist/components/button-group/index.d.ts.map +1 -0
  16. package/dist/components/button-group/index.js +3 -0
  17. package/dist/components/button-group/index.js.map +1 -0
  18. package/dist/components/code-block/code-block-footer.d.ts.map +1 -1
  19. package/dist/components/code-block/code-block-footer.js +13 -5
  20. package/dist/components/code-block/code-block-footer.js.map +1 -1
  21. package/dist/components/command/command.d.ts +28 -0
  22. package/dist/components/command/command.d.ts.map +1 -0
  23. package/dist/components/command/command.js +190 -0
  24. package/dist/components/command/command.js.map +1 -0
  25. package/dist/components/command/command.stories.js +228 -0
  26. package/dist/components/command/command.stories.js.map +1 -0
  27. package/dist/components/command/index.d.ts +2 -0
  28. package/dist/components/command/index.d.ts.map +1 -0
  29. package/dist/components/command/index.js +3 -0
  30. package/dist/components/command/index.js.map +1 -0
  31. package/dist/components/confetti/confetti.d.ts +21 -0
  32. package/dist/components/confetti/confetti.d.ts.map +1 -0
  33. package/dist/components/confetti/confetti.js +101 -0
  34. package/dist/components/confetti/confetti.js.map +1 -0
  35. package/dist/components/confetti/confetti.stories.js +41 -0
  36. package/dist/components/confetti/confetti.stories.js.map +1 -0
  37. package/dist/components/confetti/index.d.ts +2 -0
  38. package/dist/components/confetti/index.d.ts.map +1 -0
  39. package/dist/components/confetti/index.js +3 -0
  40. package/dist/components/confetti/index.js.map +1 -0
  41. package/dist/components/dashboard/components/analytics-content.d.ts +2 -0
  42. package/dist/components/dashboard/components/analytics-content.d.ts.map +1 -0
  43. package/dist/components/dashboard/components/analytics-content.js +180 -0
  44. package/dist/components/dashboard/components/analytics-content.js.map +1 -0
  45. package/dist/components/dashboard/components/animated-logo.d.ts +4 -0
  46. package/dist/components/dashboard/components/animated-logo.d.ts.map +1 -0
  47. package/dist/components/dashboard/components/animated-logo.js +23 -0
  48. package/dist/components/dashboard/components/animated-logo.js.map +1 -0
  49. package/dist/components/dashboard/components/complete-setup-button.d.ts +4 -0
  50. package/dist/components/dashboard/components/complete-setup-button.d.ts.map +1 -0
  51. package/dist/components/dashboard/components/complete-setup-button.js +28 -0
  52. package/dist/components/dashboard/components/complete-setup-button.js.map +1 -0
  53. package/dist/components/dashboard/components/jobs-content.d.ts +2 -0
  54. package/dist/components/dashboard/components/jobs-content.d.ts.map +1 -0
  55. package/dist/components/dashboard/components/jobs-content.js +69 -0
  56. package/dist/components/dashboard/components/jobs-content.js.map +1 -0
  57. package/dist/components/dashboard/components/mobile-menu.d.ts +2 -0
  58. package/dist/components/dashboard/components/mobile-menu.d.ts.map +1 -0
  59. package/dist/components/dashboard/components/mobile-menu.js +65 -0
  60. package/dist/components/dashboard/components/mobile-menu.js.map +1 -0
  61. package/dist/components/dashboard/components/organization-selector.d.ts +2 -0
  62. package/dist/components/dashboard/components/organization-selector.d.ts.map +1 -0
  63. package/dist/components/dashboard/components/organization-selector.js +92 -0
  64. package/dist/components/dashboard/components/organization-selector.js.map +1 -0
  65. package/dist/components/dashboard/components/top-menu.d.ts +5 -0
  66. package/dist/components/dashboard/components/top-menu.d.ts.map +1 -0
  67. package/dist/components/dashboard/components/top-menu.js +31 -0
  68. package/dist/components/dashboard/components/top-menu.js.map +1 -0
  69. package/dist/components/dashboard/components/topbar-button.d.ts +7 -0
  70. package/dist/components/dashboard/components/topbar-button.d.ts.map +1 -0
  71. package/dist/components/dashboard/components/topbar-button.js +18 -0
  72. package/dist/components/dashboard/components/topbar-button.js.map +1 -0
  73. package/dist/components/dashboard/components/topbar.d.ts +4 -0
  74. package/dist/components/dashboard/components/topbar.d.ts.map +1 -0
  75. package/dist/components/dashboard/components/topbar.js +62 -0
  76. package/dist/components/dashboard/components/topbar.js.map +1 -0
  77. package/dist/components/dashboard/components/user-profile.d.ts +2 -0
  78. package/dist/components/dashboard/components/user-profile.d.ts.map +1 -0
  79. package/dist/components/dashboard/components/user-profile.js +146 -0
  80. package/dist/components/dashboard/components/user-profile.js.map +1 -0
  81. package/dist/components/dashboard/dashboard.d.ts +2 -0
  82. package/dist/components/dashboard/dashboard.d.ts.map +1 -0
  83. package/dist/components/dashboard/dashboard.js +70 -0
  84. package/dist/components/dashboard/dashboard.js.map +1 -0
  85. package/dist/components/dashboard/dashboard.stories.js +23 -0
  86. package/dist/components/dashboard/dashboard.stories.js.map +1 -0
  87. package/dist/components/dashboard/index.d.ts +2 -0
  88. package/dist/components/dashboard/index.d.ts.map +1 -0
  89. package/dist/components/dashboard/index.js +3 -0
  90. package/dist/components/dashboard/index.js.map +1 -0
  91. package/dist/components/form/form.stories.js +6 -1
  92. package/dist/components/form/form.stories.js.map +1 -1
  93. package/dist/components/icon/icon.d.ts +3 -2
  94. package/dist/components/icon/icon.d.ts.map +1 -1
  95. package/dist/components/icon/icon.js +7 -2
  96. package/dist/components/icon/icon.js.map +1 -1
  97. package/dist/components/index.d.ts +9 -0
  98. package/dist/components/index.d.ts.map +1 -1
  99. package/dist/components/index.js +9 -0
  100. package/dist/components/index.js.map +1 -1
  101. package/dist/components/kbd/index.d.ts +2 -0
  102. package/dist/components/kbd/index.d.ts.map +1 -0
  103. package/dist/components/kbd/index.js +3 -0
  104. package/dist/components/kbd/index.js.map +1 -0
  105. package/dist/components/kbd/kbd.d.ts +7 -0
  106. package/dist/components/kbd/kbd.d.ts.map +1 -0
  107. package/dist/components/kbd/kbd.js +18 -0
  108. package/dist/components/kbd/kbd.js.map +1 -0
  109. package/dist/components/kbd/kbd.stories.js +119 -0
  110. package/dist/components/kbd/kbd.stories.js.map +1 -0
  111. package/dist/components/modal/modal.stories.js +227 -168
  112. package/dist/components/modal/modal.stories.js.map +1 -1
  113. package/dist/components/search/index.d.ts +7 -0
  114. package/dist/components/search/index.d.ts.map +1 -0
  115. package/dist/components/search/index.js +8 -0
  116. package/dist/components/search/index.js.map +1 -0
  117. package/dist/components/search/search-context.d.ts +11 -0
  118. package/dist/components/search/search-context.d.ts.map +1 -0
  119. package/dist/components/search/search-context.js +56 -0
  120. package/dist/components/search/search-context.js.map +1 -0
  121. package/dist/components/search/search-inline.d.ts +9 -0
  122. package/dist/components/search/search-inline.d.ts.map +1 -0
  123. package/dist/components/search/search-inline.js +85 -0
  124. package/dist/components/search/search-inline.js.map +1 -0
  125. package/dist/components/search/search-modal.d.ts +25 -0
  126. package/dist/components/search/search-modal.d.ts.map +1 -0
  127. package/dist/components/search/search-modal.js +162 -0
  128. package/dist/components/search/search-modal.js.map +1 -0
  129. package/dist/components/search/search-trigger.d.ts +9 -0
  130. package/dist/components/search/search-trigger.d.ts.map +1 -0
  131. package/dist/components/search/search-trigger.js +37 -0
  132. package/dist/components/search/search-trigger.js.map +1 -0
  133. package/dist/components/search/search-variants.d.ts +14 -0
  134. package/dist/components/search/search-variants.d.ts.map +1 -0
  135. package/dist/components/search/search-variants.js +90 -0
  136. package/dist/components/search/search-variants.js.map +1 -0
  137. package/dist/components/search/search.d.ts +11 -0
  138. package/dist/components/search/search.d.ts.map +1 -0
  139. package/dist/components/search/search.js +35 -0
  140. package/dist/components/search/search.js.map +1 -0
  141. package/dist/components/search/search.stories.js +630 -0
  142. package/dist/components/search/search.stories.js.map +1 -0
  143. package/dist/components/select/index.d.ts +2 -0
  144. package/dist/components/select/index.d.ts.map +1 -0
  145. package/dist/components/select/index.js +3 -0
  146. package/dist/components/select/index.js.map +1 -0
  147. package/dist/components/select/select.d.ts +25 -0
  148. package/dist/components/select/select.d.ts.map +1 -0
  149. package/dist/components/select/select.js +153 -0
  150. package/dist/components/select/select.js.map +1 -0
  151. package/dist/components/select/select.stories.js +393 -0
  152. package/dist/components/select/select.stories.js.map +1 -0
  153. package/dist/components/shiny-text/index.d.ts +2 -0
  154. package/dist/components/shiny-text/index.d.ts.map +1 -0
  155. package/dist/components/shiny-text/index.js +3 -0
  156. package/dist/components/shiny-text/index.js.map +1 -0
  157. package/dist/components/shiny-text/shiny-text.d.ts +10 -0
  158. package/dist/components/shiny-text/shiny-text.d.ts.map +1 -0
  159. package/dist/components/shiny-text/shiny-text.js +17 -0
  160. package/dist/components/shiny-text/shiny-text.js.map +1 -0
  161. package/dist/components/skeleton/index.d.ts +2 -0
  162. package/dist/components/skeleton/index.d.ts.map +1 -0
  163. package/dist/components/skeleton/index.js +3 -0
  164. package/dist/components/skeleton/index.js.map +1 -0
  165. package/dist/components/skeleton/skeleton.d.ts +5 -0
  166. package/dist/components/skeleton/skeleton.d.ts.map +1 -0
  167. package/dist/components/skeleton/skeleton.js +11 -0
  168. package/dist/components/skeleton/skeleton.js.map +1 -0
  169. package/dist/components/skeleton/skeleton.stories.js +345 -0
  170. package/dist/components/skeleton/skeleton.stories.js.map +1 -0
  171. package/dist/components/table/data-table.d.ts +70 -0
  172. package/dist/components/table/data-table.d.ts.map +1 -0
  173. package/dist/components/table/data-table.js +159 -0
  174. package/dist/components/table/data-table.js.map +1 -0
  175. package/dist/components/table/index.d.ts +6 -0
  176. package/dist/components/table/index.d.ts.map +1 -0
  177. package/dist/components/table/index.js +6 -0
  178. package/dist/components/table/index.js.map +1 -0
  179. package/dist/components/table/table-column-header.d.ts +79 -0
  180. package/dist/components/table/table-column-header.d.ts.map +1 -0
  181. package/dist/components/table/table-column-header.js +99 -0
  182. package/dist/components/table/table-column-header.js.map +1 -0
  183. package/dist/components/table/table-pagination.d.ts +53 -0
  184. package/dist/components/table/table-pagination.d.ts.map +1 -0
  185. package/dist/components/table/table-pagination.js +139 -0
  186. package/dist/components/table/table-pagination.js.map +1 -0
  187. package/dist/components/table/table.d.ts +11 -0
  188. package/dist/components/table/table.d.ts.map +1 -0
  189. package/dist/components/table/table.js +64 -0
  190. package/dist/components/table/table.js.map +1 -0
  191. package/dist/components/table/table.stories.columns.d.ts +24 -0
  192. package/dist/components/table/table.stories.columns.d.ts.map +1 -0
  193. package/dist/components/table/table.stories.columns.js +310 -0
  194. package/dist/components/table/table.stories.columns.js.map +1 -0
  195. package/dist/components/table/table.stories.components.d.ts +14 -0
  196. package/dist/components/table/table.stories.components.d.ts.map +1 -0
  197. package/dist/components/table/table.stories.components.js +107 -0
  198. package/dist/components/table/table.stories.components.js.map +1 -0
  199. package/dist/components/table/table.stories.data.d.ts +54 -0
  200. package/dist/components/table/table.stories.data.d.ts.map +1 -0
  201. package/dist/components/table/table.stories.data.js +122 -0
  202. package/dist/components/table/table.stories.data.js.map +1 -0
  203. package/dist/components/table/table.stories.js +302 -0
  204. package/dist/components/table/table.stories.js.map +1 -0
  205. package/dist/index.d.ts +1 -0
  206. package/dist/index.d.ts.map +1 -1
  207. package/dist/index.js +1 -0
  208. package/dist/index.js.map +1 -1
  209. package/dist/styles.css +1 -1
  210. package/index.css +79 -0
  211. package/package.json +6 -2
  212. package/src/components/avatar/avatar.tsx +1 -1
  213. package/src/components/button-group/button-group.stories.tsx +361 -0
  214. package/src/components/button-group/button-group.tsx +111 -0
  215. package/src/components/button-group/index.ts +1 -0
  216. package/src/components/code-block/code-block-footer.tsx +19 -2
  217. package/src/components/command/command.stories.tsx +133 -0
  218. package/src/components/command/command.tsx +265 -0
  219. package/src/components/command/index.ts +1 -0
  220. package/src/components/confetti/confetti.stories.tsx +38 -0
  221. package/src/components/confetti/confetti.tsx +140 -0
  222. package/src/components/confetti/index.ts +1 -0
  223. package/src/components/dashboard/components/analytics-content.tsx +102 -0
  224. package/src/components/dashboard/components/animated-logo.tsx +25 -0
  225. package/src/components/dashboard/components/complete-setup-button.tsx +30 -0
  226. package/src/components/dashboard/components/jobs-content.tsx +51 -0
  227. package/src/components/dashboard/components/mobile-menu.tsx +50 -0
  228. package/src/components/dashboard/components/organization-selector.tsx +51 -0
  229. package/src/components/dashboard/components/top-menu.tsx +26 -0
  230. package/src/components/dashboard/components/topbar-button.tsx +27 -0
  231. package/src/components/dashboard/components/topbar.tsx +40 -0
  232. package/src/components/dashboard/components/user-profile.tsx +90 -0
  233. package/src/components/dashboard/dashboard.stories.tsx +25 -0
  234. package/src/components/dashboard/dashboard.tsx +61 -0
  235. package/src/components/dashboard/index.ts +1 -0
  236. package/src/components/form/form.stories.tsx +5 -0
  237. package/src/components/icon/icon.tsx +7 -3
  238. package/src/components/index.ts +9 -0
  239. package/src/components/kbd/index.ts +1 -0
  240. package/src/components/kbd/kbd.stories.tsx +64 -0
  241. package/src/components/kbd/kbd.tsx +32 -0
  242. package/src/components/modal/modal.stories.tsx +58 -4
  243. package/src/components/search/index.ts +28 -0
  244. package/src/components/search/search-context.tsx +78 -0
  245. package/src/components/search/search-inline.tsx +107 -0
  246. package/src/components/search/search-modal.tsx +198 -0
  247. package/src/components/search/search-trigger.tsx +47 -0
  248. package/src/components/search/search-variants.ts +88 -0
  249. package/src/components/search/search.stories.tsx +392 -0
  250. package/src/components/search/search.tsx +47 -0
  251. package/src/components/select/index.ts +1 -0
  252. package/src/components/select/select.stories.tsx +207 -0
  253. package/src/components/select/select.tsx +220 -0
  254. package/src/components/shiny-text/index.ts +1 -0
  255. package/src/components/shiny-text/shiny-text.tsx +21 -0
  256. package/src/components/skeleton/index.ts +1 -0
  257. package/src/components/skeleton/skeleton.stories.tsx +178 -0
  258. package/src/components/skeleton/skeleton.tsx +14 -0
  259. package/src/components/table/data-table.tsx +254 -0
  260. package/src/components/table/index.ts +5 -0
  261. package/src/components/table/table-column-header.tsx +141 -0
  262. package/src/components/table/table-pagination.tsx +161 -0
  263. package/src/components/table/table.stories.columns.tsx +198 -0
  264. package/src/components/table/table.stories.components.tsx +104 -0
  265. package/src/components/table/table.stories.data.ts +117 -0
  266. package/src/components/table/table.stories.tsx +256 -0
  267. package/src/components/table/table.tsx +95 -0
  268. package/src/index.ts +1 -0
@@ -0,0 +1,220 @@
1
+ import * as SelectPrimitive from '@radix-ui/react-select';
2
+ import {cva, type VariantProps} from 'class-variance-authority';
3
+ import type {ComponentProps} from 'react';
4
+ import {cn} from 'utils/cn';
5
+ import {Icon, type IconName} from '../icon';
6
+
7
+ function Select({...props}: ComponentProps<typeof SelectPrimitive.Root>) {
8
+ return <SelectPrimitive.Root data-slot="select" {...props} />;
9
+ }
10
+
11
+ function SelectGroup({...props}: ComponentProps<typeof SelectPrimitive.Group>) {
12
+ return <SelectPrimitive.Group data-slot="select-group" {...props} />;
13
+ }
14
+
15
+ function SelectValue({...props}: ComponentProps<typeof SelectPrimitive.Value>) {
16
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />;
17
+ }
18
+
19
+ const selectTriggerVariants = cva(
20
+ [
21
+ 'flex items-center justify-between gap-8',
22
+ 'w-full rounded-6 px-8 text-sm leading-20',
23
+ 'bg-background-field-base text-foreground-neutral-subtle',
24
+ 'shadow-button-neutral transition-[color,box-shadow] outline-none',
25
+ 'hover:bg-background-field-hover',
26
+ 'focus-visible:shadow-border-interactive-with-active',
27
+ 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-background-neutral-disabled disabled:shadow-none disabled:text-foreground-neutral-disabled',
28
+ 'data-[placeholder]:text-foreground-neutral-muted',
29
+ ],
30
+ {
31
+ variants: {
32
+ variant: {
33
+ base: 'bg-background-field-base',
34
+ component: 'bg-background-field-component',
35
+ },
36
+ size: {
37
+ small: 'h-28 py-4',
38
+ base: 'h-32 py-6',
39
+ },
40
+ },
41
+ defaultVariants: {
42
+ variant: 'base',
43
+ size: 'base',
44
+ },
45
+ },
46
+ );
47
+
48
+ type SelectTriggerProps = ComponentProps<typeof SelectPrimitive.Trigger> &
49
+ VariantProps<typeof selectTriggerVariants>;
50
+
51
+ function SelectTrigger({className, variant, size, children, ...props}: SelectTriggerProps) {
52
+ return (
53
+ <SelectPrimitive.Trigger
54
+ data-slot="select-trigger"
55
+ className={cn(selectTriggerVariants({variant, size}), className)}
56
+ {...props}
57
+ >
58
+ {children}
59
+ <SelectPrimitive.Icon asChild>
60
+ <Icon name="expandUpDownLine" className="size-16 text-foreground-neutral-muted shrink-0" />
61
+ </SelectPrimitive.Icon>
62
+ </SelectPrimitive.Trigger>
63
+ );
64
+ }
65
+
66
+ function SelectScrollUpButton({
67
+ className,
68
+ ...props
69
+ }: ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
70
+ return (
71
+ <SelectPrimitive.ScrollUpButton
72
+ data-slot="select-scroll-up-button"
73
+ className={cn('flex cursor-default items-center justify-center py-4', className)}
74
+ {...props}
75
+ >
76
+ <Icon name="arrowUpSLine" className="size-16 text-foreground-neutral-muted" />
77
+ </SelectPrimitive.ScrollUpButton>
78
+ );
79
+ }
80
+
81
+ function SelectScrollDownButton({
82
+ className,
83
+ ...props
84
+ }: ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
85
+ return (
86
+ <SelectPrimitive.ScrollDownButton
87
+ data-slot="select-scroll-down-button"
88
+ className={cn('flex cursor-default items-center justify-center py-4', className)}
89
+ {...props}
90
+ >
91
+ <Icon name="arrowDownSLine" className="size-16 text-foreground-neutral-muted" />
92
+ </SelectPrimitive.ScrollDownButton>
93
+ );
94
+ }
95
+
96
+ function SelectContent({
97
+ className,
98
+ children,
99
+ position = 'popper',
100
+ sideOffset = 4,
101
+ align = 'center',
102
+ ...props
103
+ }: ComponentProps<typeof SelectPrimitive.Content>) {
104
+ return (
105
+ <SelectPrimitive.Portal>
106
+ <SelectPrimitive.Content
107
+ data-slot="select-content"
108
+ className={cn(
109
+ 'relative z-50 max-h-300 min-w-180 overflow-hidden rounded-10 p-4',
110
+ 'bg-background-neutral-overlay shadow-tooltip',
111
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out',
112
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
113
+ 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
114
+ 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
115
+ 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
116
+ position === 'popper' &&
117
+ 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
118
+ className,
119
+ )}
120
+ position={position}
121
+ sideOffset={sideOffset}
122
+ align={align}
123
+ {...props}
124
+ >
125
+ <SelectScrollUpButton />
126
+ <SelectPrimitive.Viewport
127
+ data-slot="select-viewport"
128
+ className={cn(
129
+ 'p-0',
130
+ position === 'popper' &&
131
+ 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',
132
+ )}
133
+ >
134
+ {children}
135
+ </SelectPrimitive.Viewport>
136
+ <SelectScrollDownButton />
137
+ </SelectPrimitive.Content>
138
+ </SelectPrimitive.Portal>
139
+ );
140
+ }
141
+
142
+ function SelectLabel({className, ...props}: ComponentProps<typeof SelectPrimitive.Label>) {
143
+ return (
144
+ <SelectPrimitive.Label
145
+ data-slot="select-label"
146
+ className={cn(
147
+ 'px-8 py-4 text-xs leading-20 text-foreground-neutral-subtle select-none',
148
+ className,
149
+ )}
150
+ {...props}
151
+ />
152
+ );
153
+ }
154
+
155
+ type SelectItemProps = ComponentProps<typeof SelectPrimitive.Item> & {
156
+ icon?: IconName;
157
+ };
158
+
159
+ function SelectItem({className, children, icon, ...props}: SelectItemProps) {
160
+ return (
161
+ <SelectPrimitive.Item
162
+ data-slot="select-item"
163
+ className={cn(
164
+ 'relative flex cursor-pointer select-none items-center gap-8 rounded-6 py-6 pl-32 pr-8',
165
+ 'text-sm leading-20 text-foreground-neutral-subtle outline-none transition-colors',
166
+ 'focus:bg-background-components-hover',
167
+ 'data-disabled:pointer-events-none data-disabled:text-foreground-neutral-disabled',
168
+ 'data-[state=checked]:text-foreground-neutral-base',
169
+ icon && 'pl-56',
170
+ className,
171
+ )}
172
+ {...props}
173
+ >
174
+ <span className="absolute left-8 flex size-16 items-center justify-center">
175
+ <SelectPrimitive.ItemIndicator>
176
+ <Icon name="check" className="size-14 text-foreground-neutral-base" />
177
+ </SelectPrimitive.ItemIndicator>
178
+ </span>
179
+ {icon && (
180
+ <Icon
181
+ name={icon}
182
+ className="size-16 shrink-0 text-foreground-neutral-subtle absolute left-32"
183
+ />
184
+ )}
185
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
186
+ </SelectPrimitive.Item>
187
+ );
188
+ }
189
+
190
+ function SelectSeparator({className, ...props}: ComponentProps<typeof SelectPrimitive.Separator>) {
191
+ return (
192
+ <SelectPrimitive.Separator
193
+ data-slot="select-separator"
194
+ className={cn(
195
+ 'relative -mx-4 my-4 h-px',
196
+ 'bg-border-neutral-menu-top',
197
+ 'after:absolute after:inset-x-0 after:top-px after:h-px',
198
+ 'after:bg-border-neutral-menu-bottom',
199
+ className,
200
+ )}
201
+ {...props}
202
+ />
203
+ );
204
+ }
205
+
206
+ export {
207
+ Select,
208
+ SelectGroup,
209
+ SelectValue,
210
+ SelectTrigger,
211
+ SelectContent,
212
+ SelectLabel,
213
+ SelectItem,
214
+ SelectSeparator,
215
+ SelectScrollUpButton,
216
+ SelectScrollDownButton,
217
+ selectTriggerVariants,
218
+ };
219
+
220
+ export type {SelectTriggerProps, SelectItemProps};
@@ -0,0 +1 @@
1
+ export * from './shiny-text';
@@ -0,0 +1,21 @@
1
+ import {cn} from 'utils/cn';
2
+
3
+ type ShinyTextProps = {
4
+ text: string;
5
+ disabled?: boolean;
6
+ speed?: number;
7
+ className?: string;
8
+ };
9
+
10
+ function ShinyText({text, disabled = false, speed = 5, className = ''}: ShinyTextProps) {
11
+ const animationDuration = `${speed}s`;
12
+
13
+ return (
14
+ <span className={cn('shiny-text', {disabled: disabled}, className)} style={{animationDuration}}>
15
+ {text}
16
+ </span>
17
+ );
18
+ }
19
+
20
+ export {ShinyText};
21
+ export type {ShinyTextProps};
@@ -0,0 +1 @@
1
+ export * from './skeleton';
@@ -0,0 +1,178 @@
1
+ import type {Meta, StoryObj} from '@storybook/react';
2
+ import {Code, Header} from 'components/typography';
3
+ import {Skeleton} from './skeleton';
4
+
5
+ const meta = {
6
+ title: 'Components/Skeleton',
7
+ component: Skeleton,
8
+ tags: ['autodocs'],
9
+ } satisfies Meta<typeof Skeleton>;
10
+
11
+ export default meta;
12
+
13
+ type Story = StoryObj<typeof meta>;
14
+
15
+ export const Default: Story = {
16
+ render: () => <Skeleton className="w-200 h-20" />,
17
+ };
18
+
19
+ export const Card: Story = {
20
+ render: () => (
21
+ <div className="flex flex-col gap-12 p-16 rounded-10 border border-border-neutral-base bg-background-field-base max-w-400">
22
+ <Skeleton className="h-200 w-full rounded-6" />
23
+ <div className="space-y-8">
24
+ <Skeleton className="h-20 w-3/4" />
25
+ <Skeleton className="h-16 w-full" />
26
+ <Skeleton className="h-16 w-5/6" />
27
+ </div>
28
+ <div className="flex gap-8 pt-8">
29
+ <Skeleton className="h-32 w-100 rounded-6" />
30
+ <Skeleton className="h-32 w-100 rounded-6" />
31
+ </div>
32
+ </div>
33
+ ),
34
+ };
35
+
36
+ export const List: Story = {
37
+ render: () => (
38
+ <div className="flex flex-col gap-8 max-w-500">
39
+ {Array.from({length: 5}, (_, i) => i).map((id) => (
40
+ <div
41
+ key={`list-${id}`}
42
+ className="flex items-center gap-12 p-12 rounded-6 border border-border-neutral-base bg-background-field-base"
43
+ >
44
+ <Skeleton className="h-48 w-48 rounded-full shrink-0" />
45
+ <div className="flex-1 space-y-8">
46
+ <Skeleton className="h-16 w-3/4" />
47
+ <Skeleton className="h-14 w-1/2" />
48
+ </div>
49
+ <Skeleton className="h-32 w-80 rounded-6" />
50
+ </div>
51
+ ))}
52
+ </div>
53
+ ),
54
+ };
55
+
56
+ export const Showcase: Story = {
57
+ render: () => (
58
+ <div className="flex flex-col gap-32">
59
+ <div className="flex flex-col gap-16">
60
+ <Header variant="h3">Basic Shapes</Header>
61
+ <div className="flex flex-col gap-12">
62
+ <div className="flex flex-col gap-8">
63
+ <Code variant="label" className="text-foreground-neutral-subtle">
64
+ Rectangle
65
+ </Code>
66
+ <Skeleton className="h-20 w-200" />
67
+ </div>
68
+
69
+ <div className="flex flex-col gap-8">
70
+ <Code variant="label" className="text-foreground-neutral-subtle">
71
+ Circle
72
+ </Code>
73
+ <Skeleton className="h-48 w-48 rounded-full" />
74
+ </div>
75
+
76
+ <div className="flex flex-col gap-8">
77
+ <Code variant="label" className="text-foreground-neutral-subtle">
78
+ Square
79
+ </Code>
80
+ <Skeleton className="h-100 w-100" />
81
+ </div>
82
+ </div>
83
+ </div>
84
+
85
+ <div className="flex flex-col gap-16">
86
+ <Header variant="h3">Card Loading</Header>
87
+ <div className="flex flex-col gap-12 p-16 rounded-10 border border-border-neutral-base bg-background-field-base max-w-400">
88
+ <Skeleton className="h-200 w-full rounded-6" />
89
+ <div className="space-y-8">
90
+ <Skeleton className="h-20 w-3/4" />
91
+ <Skeleton className="h-16 w-full" />
92
+ <Skeleton className="h-16 w-2/3" />
93
+ </div>
94
+ <div className="flex gap-8 pt-8">
95
+ <Skeleton className="h-32 w-100 rounded-6" />
96
+ <Skeleton className="h-32 w-100 rounded-6" />
97
+ </div>
98
+ </div>
99
+ </div>
100
+
101
+ <div className="flex flex-col gap-16">
102
+ <Header variant="h3">List Loading</Header>
103
+ <div className="flex flex-col gap-8 max-w-500">
104
+ {Array.from({length: 4}, (_, i) => i).map((id) => (
105
+ <div
106
+ key={`list-loading-${id}`}
107
+ className="flex items-center gap-12 p-12 rounded-6 border border-border-neutral-base bg-background-field-base"
108
+ >
109
+ <Skeleton className="h-48 w-48 rounded-full shrink-0" />
110
+ <div className="flex-1 space-y-8">
111
+ <Skeleton className="h-16 w-3/4" />
112
+ <Skeleton className="h-14 w-1/2" />
113
+ </div>
114
+ <Skeleton className="h-32 w-80 rounded-6" />
115
+ </div>
116
+ ))}
117
+ </div>
118
+ </div>
119
+
120
+ <div className="flex flex-col gap-16">
121
+ <Header variant="h3">Table Loading</Header>
122
+ <div className="rounded-10 border border-border-neutral-base bg-background-field-base overflow-hidden max-w-700">
123
+ <div className="flex gap-16 p-12 border-b border-border-neutral-base bg-background-neutral-base">
124
+ <Skeleton className="h-16 w-140" />
125
+ <Skeleton className="h-16 w-220" />
126
+ <Skeleton className="h-16 w-120" />
127
+ <Skeleton className="h-16 w-100 ml-auto" />
128
+ </div>
129
+ {Array.from({length: 5}, (_, i) => i).map((id) => (
130
+ <div
131
+ key={`table-row-${id}`}
132
+ className="flex gap-16 p-12 border-b border-border-neutral-base last:border-0"
133
+ >
134
+ <Skeleton className="h-14 w-140" />
135
+ <Skeleton className="h-14 w-220" />
136
+ <Skeleton className="h-14 w-120" />
137
+ <Skeleton className="h-14 w-100 ml-auto" />
138
+ </div>
139
+ ))}
140
+ </div>
141
+ </div>
142
+
143
+ <div className="flex flex-col gap-16">
144
+ <Header variant="h3">Form Loading</Header>
145
+ <div className="max-w-400 space-y-16 p-16 rounded-10 border border-border-neutral-base bg-background-field-base">
146
+ <div className="space-y-8">
147
+ <Skeleton className="h-16 w-100" />
148
+ <Skeleton className="h-32 w-full rounded-6" />
149
+ </div>
150
+ <div className="space-y-8">
151
+ <Skeleton className="h-16 w-120" />
152
+ <Skeleton className="h-32 w-full rounded-6" />
153
+ </div>
154
+ <div className="space-y-8">
155
+ <Skeleton className="h-16 w-80" />
156
+ <Skeleton className="h-96 w-full rounded-6" />
157
+ </div>
158
+ <div className="flex gap-8 pt-8">
159
+ <Skeleton className="h-32 w-100 rounded-6" />
160
+ <Skeleton className="h-32 w-100 rounded-6" />
161
+ </div>
162
+ </div>
163
+ </div>
164
+
165
+ <div className="flex flex-col gap-16">
166
+ <Header variant="h3">Avatar Group Loading</Header>
167
+ <div className="flex -space-x-8">
168
+ {Array.from({length: 5}, (_, i) => i).map((id) => (
169
+ <Skeleton
170
+ key={`avatar-${id}`}
171
+ className="h-40 w-40 rounded-full border-2 border-background-field-base"
172
+ />
173
+ ))}
174
+ </div>
175
+ </div>
176
+ </div>
177
+ ),
178
+ };
@@ -0,0 +1,14 @@
1
+ import type {ComponentProps} from 'react';
2
+ import {cn} from 'utils/cn';
3
+
4
+ type SkeletonProps = ComponentProps<'div'>;
5
+
6
+ export function Skeleton({className, ...props}: SkeletonProps) {
7
+ return (
8
+ <div
9
+ data-slot="skeleton"
10
+ className={cn('animate-pulse rounded-6 bg-background-neutral-disabled', className)}
11
+ {...props}
12
+ />
13
+ );
14
+ }
@@ -0,0 +1,254 @@
1
+ import type {
2
+ ColumnDef,
3
+ ColumnFiltersState,
4
+ PaginationState,
5
+ SortingState,
6
+ VisibilityState,
7
+ } from '@tanstack/react-table';
8
+ import {
9
+ flexRender,
10
+ getCoreRowModel,
11
+ getFilteredRowModel,
12
+ getPaginationRowModel,
13
+ getSortedRowModel,
14
+ useReactTable,
15
+ } from '@tanstack/react-table';
16
+ import {Checkbox} from 'components/checkbox';
17
+ import {Icon} from 'components/icon';
18
+ import {Text} from 'components/typography';
19
+ import {type ComponentProps, useEffect, useMemo, useState} from 'react';
20
+ import {cn} from 'utils/cn';
21
+ import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from './table';
22
+ import {TablePagination} from './table-pagination';
23
+
24
+ /**
25
+ * Props for the {@link DataTable} component.
26
+ *
27
+ * @typeParam TData - The shape of the row data.
28
+ * @typeParam TValue - The value type used by column definitions.
29
+ */
30
+ interface DataTableProps<TData, TValue> extends Omit<ComponentProps<'div'>, 'children'> {
31
+ /**
32
+ * Column definitions for the table, as expected by `@tanstack/react-table`.
33
+ *
34
+ * These control how each field in {@link data} is rendered and interacted with.
35
+ */
36
+ columns: ColumnDef<TData, TValue>[];
37
+ /**
38
+ * Array of data items to render as table rows.
39
+ */
40
+ data: TData[];
41
+ /**
42
+ * Enables client-side pagination when `true`.
43
+ *
44
+ * Defaults to `true`. When set to `false`, all rows are rendered in a single
45
+ * page and the pagination controls are disabled. In that case, the
46
+ * {@link pageSize} value is effectively ignored.
47
+ */
48
+ pagination?: boolean;
49
+ /**
50
+ * Number of rows to display per page when pagination is enabled.
51
+ *
52
+ * Defaults to `10`. This value is used to initialize the internal pagination
53
+ * state and only has an effect when {@link pagination} is `true`.
54
+ */
55
+ pageSize?: number;
56
+ /**
57
+ * Array of page size options to display in the page size selector.
58
+ * When provided, a dropdown will be rendered in the pagination footer allowing
59
+ * users to change the number of rows per page.
60
+ *
61
+ * @default [10, 20, 50, 100]
62
+ * @example
63
+ * ```tsx
64
+ * <DataTable columns={columns} data={data} pageSizeOptions={[5, 10, 25]} />
65
+ * ```
66
+ */
67
+ pageSizeOptions?: number[];
68
+ /**
69
+ * When `true`, displays the count of selected rows.
70
+ *
71
+ * This is useful when row selection is enabled to give users feedback on how
72
+ * many rows are currently selected.
73
+ */
74
+ showSelectedCount?: boolean;
75
+ /**
76
+ * Optional callback invoked when a table row is clicked.
77
+ *
78
+ * The callback receives the corresponding data item for the clicked row.
79
+ */
80
+ onRowClick?: (row: TData) => void;
81
+ /**
82
+ * React node to render when there are no rows to display.
83
+ *
84
+ * If not provided, the table will render without rows and without a custom
85
+ * empty state message or placeholder.
86
+ */
87
+ emptyState?: React.ReactNode;
88
+ }
89
+
90
+ export function DataTable<TData, TValue>({
91
+ columns,
92
+ data,
93
+ pagination = true,
94
+ pageSize = 10,
95
+ pageSizeOptions,
96
+ showSelectedCount = false,
97
+ onRowClick,
98
+ emptyState,
99
+ className,
100
+ ...props
101
+ }: DataTableProps<TData, TValue>) {
102
+ const [sorting, setSorting] = useState<SortingState>([]);
103
+ const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
104
+ const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
105
+ const [rowSelection, setRowSelection] = useState({});
106
+ const [paginationState, setPaginationState] = useState<PaginationState>({
107
+ pageIndex: 0,
108
+ pageSize,
109
+ });
110
+
111
+ useEffect(() => {
112
+ setPaginationState((prev) => ({...prev, pageSize, pageIndex: 0}));
113
+ }, [pageSize]);
114
+
115
+ // Add selection column if showSelectedCount is enabled
116
+ const columnsWithSelection = useMemo(() => {
117
+ if (!showSelectedCount) {
118
+ return columns;
119
+ }
120
+
121
+ const selectionColumn: ColumnDef<TData, TValue> = {
122
+ id: 'select',
123
+ header: ({table}) => {
124
+ const isAllSelected = table.getIsAllPageRowsSelected();
125
+ const isSomeSelected = table.getIsSomePageRowsSelected();
126
+ return (
127
+ <Checkbox
128
+ checked={isAllSelected ? true : isSomeSelected ? 'indeterminate' : false}
129
+ onCheckedChange={(checked) => table.toggleAllPageRowsSelected(!!checked)}
130
+ onClick={(e) => e.stopPropagation()}
131
+ aria-label="Select all"
132
+ />
133
+ );
134
+ },
135
+ cell: ({row}) => (
136
+ <Checkbox
137
+ checked={row.getIsSelected()}
138
+ onCheckedChange={(checked) => row.toggleSelected(!!checked)}
139
+ onClick={(e) => e.stopPropagation()}
140
+ aria-label="Select row"
141
+ />
142
+ ),
143
+ enableSorting: false,
144
+ enableHiding: false,
145
+ };
146
+
147
+ return [selectionColumn, ...columns];
148
+ }, [columns, showSelectedCount]);
149
+
150
+ const table = useReactTable({
151
+ data,
152
+ columns: columnsWithSelection,
153
+ getCoreRowModel: getCoreRowModel(),
154
+ getFilteredRowModel: getFilteredRowModel(),
155
+ getSortedRowModel: getSortedRowModel(),
156
+ getPaginationRowModel: pagination ? getPaginationRowModel() : undefined,
157
+ enableRowSelection: showSelectedCount,
158
+ onSortingChange: setSorting,
159
+ onColumnFiltersChange: setColumnFilters,
160
+ onColumnVisibilityChange: setColumnVisibility,
161
+ onRowSelectionChange: setRowSelection,
162
+ onPaginationChange: setPaginationState,
163
+ state: {
164
+ sorting,
165
+ columnFilters,
166
+ columnVisibility,
167
+ rowSelection,
168
+ pagination: pagination ? paginationState : undefined,
169
+ },
170
+ });
171
+
172
+ return (
173
+ <div
174
+ className={cn('rounded-8 border border-border-neutral-base overflow-hidden', className)}
175
+ {...props}
176
+ >
177
+ <Table>
178
+ {table.getRowModel().rows.length > 0 ? (
179
+ <TableHeader>
180
+ {table.getHeaderGroups().map((headerGroup) => (
181
+ <TableRow key={headerGroup.id} className="hover:bg-transparent border-b">
182
+ {headerGroup.headers.map((header) => (
183
+ <TableHead key={header.id}>
184
+ {header.isPlaceholder
185
+ ? null
186
+ : flexRender(header.column.columnDef.header, header.getContext())}
187
+ </TableHead>
188
+ ))}
189
+ </TableRow>
190
+ ))}
191
+ </TableHeader>
192
+ ) : null}
193
+ <TableBody>
194
+ {table.getRowModel().rows?.length ? (
195
+ table.getRowModel().rows.map((row) => (
196
+ <TableRow
197
+ key={row.id}
198
+ onClick={() => onRowClick?.(row.original)}
199
+ data-selected={row.getIsSelected()}
200
+ className={onRowClick ? 'cursor-pointer' : ''}
201
+ tabIndex={onRowClick ? 0 : undefined}
202
+ role={onRowClick ? 'button' : undefined}
203
+ onKeyDown={(event) => {
204
+ if (!onRowClick) return;
205
+ if (event.key === 'Enter' || event.key === ' ') {
206
+ event.preventDefault();
207
+ onRowClick(row.original);
208
+ }
209
+ }}
210
+ >
211
+ {row.getVisibleCells().map((cell) => (
212
+ <TableCell key={cell.id}>
213
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
214
+ </TableCell>
215
+ ))}
216
+ </TableRow>
217
+ ))
218
+ ) : (
219
+ <TableRow className="hover:bg-transparent">
220
+ <TableCell colSpan={table.getAllColumns().length} className="h-240 text-center">
221
+ {emptyState || (
222
+ <div className="flex flex-col items-center justify-center gap-12 py-48">
223
+ <div className="size-32 rounded-6 bg-transparent border border-border-neutral-strong flex items-center justify-center">
224
+ <Icon
225
+ name="fileDamageLine"
226
+ className="size-16 text-foreground-neutral-subtle"
227
+ color="var(--foreground-neutral-subtle, #a1a1aa)"
228
+ />
229
+ </div>
230
+ <div className="text-center space-y-4">
231
+ <Text size="sm" className="text-foreground-neutral-base">
232
+ No results
233
+ </Text>
234
+ <Text size="xs" className="text-foreground-neutral-muted">
235
+ Looks like there are no results.
236
+ </Text>
237
+ </div>
238
+ </div>
239
+ )}
240
+ </TableCell>
241
+ </TableRow>
242
+ )}
243
+ </TableBody>
244
+ {pagination && table.getRowModel().rows?.length > 0 && (
245
+ <TablePagination
246
+ table={table}
247
+ pageSizeOptions={pageSizeOptions}
248
+ showSelectedCount={showSelectedCount}
249
+ />
250
+ )}
251
+ </Table>
252
+ </div>
253
+ );
254
+ }