@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,50 @@
1
+ import {Button} from 'components/button';
2
+ import {
3
+ DropdownMenu,
4
+ DropdownMenuContent,
5
+ DropdownMenuItem,
6
+ DropdownMenuSeparator,
7
+ DropdownMenuTrigger,
8
+ } from 'components/dropdown-menu';
9
+ import {Icon} from 'components/icon';
10
+ import {ShinyText} from 'components/shiny-text';
11
+ import {useResolvedTheme} from 'hooks/useResolvedTheme';
12
+ import {ShipfoxLoader} from 'shipfox-loader-react';
13
+
14
+ export function MobileMenu() {
15
+ const resolvedTheme = useResolvedTheme();
16
+ return (
17
+ <DropdownMenu>
18
+ <DropdownMenuTrigger asChild>
19
+ <Button
20
+ type="button"
21
+ variant="transparent"
22
+ className="flex md:hidden items-center justify-center shrink-0 w-40 h-40 bg-background-subtle-base hover:bg-background-neutral-hover transition-colors rounded-none border-l border-border-neutral-strong"
23
+ aria-label="Menu"
24
+ >
25
+ <Icon name="menuLine" className="size-18 text-foreground-neutral-subtle" />
26
+ </Button>
27
+ </DropdownMenuTrigger>
28
+ <DropdownMenuContent align="end" className="w-200">
29
+ <DropdownMenuItem>
30
+ <div className="flex items-center gap-8">
31
+ <ShipfoxLoader
32
+ size={13}
33
+ animation="circular"
34
+ color={resolvedTheme === 'dark' ? 'white' : 'orange'}
35
+ background={resolvedTheme === 'dark' ? 'dark' : 'light'}
36
+ />
37
+ <ShinyText
38
+ text="Complete setup"
39
+ className="flex-1 text-sm font-medium leading-20 text-foreground-neutral-base truncate text-left"
40
+ />
41
+ </div>
42
+ </DropdownMenuItem>
43
+ <DropdownMenuSeparator />
44
+ <DropdownMenuItem icon="searchLine">Search</DropdownMenuItem>
45
+ <DropdownMenuItem icon="questionLine">Help</DropdownMenuItem>
46
+ <DropdownMenuItem icon="notification3Line">Notifications</DropdownMenuItem>
47
+ </DropdownMenuContent>
48
+ </DropdownMenu>
49
+ );
50
+ }
@@ -0,0 +1,51 @@
1
+ import {Avatar} from 'components/avatar';
2
+ import {Button} from 'components/button';
3
+ import {Icon} from 'components/icon';
4
+ import {
5
+ Select,
6
+ SelectContent,
7
+ SelectItem,
8
+ SelectSeparator,
9
+ SelectTrigger,
10
+ SelectValue,
11
+ } from 'components/select';
12
+
13
+ export function OrganizationSelector() {
14
+ return (
15
+ <Select defaultValue="stripe">
16
+ <SelectTrigger className="w-200 h-40 shadow-none bg-background-neutral-base hover:bg-background-neutral-hover rounded-none gap-8 pl-12 border-l min-[321px]:border-r border-border-neutral-strong">
17
+ <div className="flex items-center gap-8 flex-1 min-w-0">
18
+ <SelectValue placeholder="Select organization" />
19
+ </div>
20
+ </SelectTrigger>
21
+ <SelectContent>
22
+ <SelectItem value="stripe">
23
+ <div className="flex items-center gap-8">
24
+ <Avatar size="3xs" content="logo" logoName="stripe" radius="rounded" />
25
+ <span>Stripe</span>
26
+ </div>
27
+ </SelectItem>
28
+ <SelectItem value="shipfox">
29
+ <div className="flex items-center gap-8">
30
+ <Avatar size="3xs" content="logo" logoName="shipfox" radius="rounded" />
31
+ <span>Shipfox</span>
32
+ </div>
33
+ </SelectItem>
34
+ <SelectItem value="github">
35
+ <div className="flex items-center gap-8">
36
+ <Avatar size="3xs" content="logo" logoName="github" radius="rounded" />
37
+ <span>GitHub</span>
38
+ </div>
39
+ </SelectItem>
40
+ <SelectSeparator />
41
+ <Button
42
+ variant="transparent"
43
+ className="w-full justify-start text-foreground-neutral-subtle"
44
+ >
45
+ <Icon name="addLine" className="size-16 shrink-0" />
46
+ <span>New organization</span>
47
+ </Button>
48
+ </SelectContent>
49
+ </Select>
50
+ );
51
+ }
@@ -0,0 +1,26 @@
1
+ import {Tabs, TabsList, TabsTrigger} from 'components/tabs';
2
+
3
+ export function TopMenu({
4
+ activeTab,
5
+ onTabChange,
6
+ }: {
7
+ activeTab: string;
8
+ onTabChange: (tab: string) => void;
9
+ }) {
10
+ return (
11
+ <div className="flex items-center justify-between w-full">
12
+ <div className="flex items-center flex-1 min-w-0 pl-12 md:pl-20 pr-8">
13
+ <Tabs value={activeTab} onValueChange={onTabChange}>
14
+ <TabsList className="h-48 gap-8 md:gap-12">
15
+ <TabsTrigger value="analytics" className="text-sm font-medium">
16
+ Analytics
17
+ </TabsTrigger>
18
+ <TabsTrigger value="jobs" className="text-sm font-medium">
19
+ Jobs
20
+ </TabsTrigger>
21
+ </TabsList>
22
+ </Tabs>
23
+ </div>
24
+ </div>
25
+ );
26
+ }
@@ -0,0 +1,27 @@
1
+ import {Button} from 'components/button';
2
+ import {Icon, type IconName} from 'components/icon';
3
+ import {cn} from 'utils/cn';
4
+
5
+ export function TopbarButton({
6
+ className,
7
+ icon,
8
+ label,
9
+ }: {
10
+ className?: string;
11
+ icon: IconName;
12
+ label?: string;
13
+ }) {
14
+ return (
15
+ <Button
16
+ type="button"
17
+ variant="transparent"
18
+ className={cn(
19
+ 'flex items-center justify-center overflow-hidden shrink-0 w-40 h-40 bg-background-subtle-base hover:bg-background-neutral-hover transition-colors rounded-none border-l border-border-neutral-strong',
20
+ className,
21
+ )}
22
+ aria-label={label ?? undefined}
23
+ >
24
+ <Icon name={icon} className="size-18 text-foreground-neutral-subtle" />
25
+ </Button>
26
+ );
27
+ }
@@ -0,0 +1,40 @@
1
+ import {cn} from 'utils/cn';
2
+ import {CompleteSetupButton} from './complete-setup-button';
3
+ import {MobileMenu} from './mobile-menu';
4
+ import {OrganizationSelector} from './organization-selector';
5
+ import {TopbarButton} from './topbar-button';
6
+ import {UserProfile} from './user-profile';
7
+
8
+ export function Topbar({hideLogo = false}: {hideLogo?: boolean}) {
9
+ return (
10
+ <div className="flex flex-col items-start w-full bg-background-subtle-base">
11
+ <div className="flex items-center justify-between w-full shrink-0 border-b border-border-neutral-strong">
12
+ <div className="flex items-center flex-1 min-w-0">
13
+ <div className={cn('shrink-0', hideLogo ? 'opacity-0' : 'opacity-100')}>
14
+ <TopbarButton icon="shipfox" label="Shipfox" className="border-none" />
15
+ </div>
16
+ <OrganizationSelector />
17
+ <div className="hidden md:block flex-1 h-40 bg-background-subtle-base" />
18
+ </div>
19
+
20
+ <CompleteSetupButton className="hidden md:flex" />
21
+
22
+ <div className="hidden md:block">
23
+ <TopbarButton icon="searchLine" label="Search" />
24
+ </div>
25
+
26
+ <div className="hidden md:block">
27
+ <TopbarButton icon="questionLine" label="Help" />
28
+ </div>
29
+
30
+ <div className="hidden md:block">
31
+ <TopbarButton icon="notification3Line" label="Notifications" />
32
+ </div>
33
+
34
+ <MobileMenu />
35
+
36
+ <UserProfile />
37
+ </div>
38
+ </div>
39
+ );
40
+ }
@@ -0,0 +1,90 @@
1
+ import {Avatar} from 'components/avatar';
2
+ import {UserBadge} from 'components/badge';
3
+ import {Button} from 'components/button';
4
+ import {
5
+ DropdownMenu,
6
+ DropdownMenuContent,
7
+ DropdownMenuItem,
8
+ DropdownMenuSeparator,
9
+ DropdownMenuTrigger,
10
+ } from 'components/dropdown-menu';
11
+ import {ShinyText} from 'components/shiny-text';
12
+
13
+ function UsageGauge({used, total}: {used: number; total: number}) {
14
+ const percentage = total <= 0 ? 0 : Math.min((used / total) * 100, 100);
15
+
16
+ return (
17
+ <div className="flex h-8 w-full rounded-full bg-tag-neutral-bg overflow-hidden">
18
+ <div className="h-full bg-tag-success-icon rounded-l-2" style={{width: `${percentage}%`}} />
19
+ </div>
20
+ );
21
+ }
22
+
23
+ export function UserProfile() {
24
+ const userName = 'Thierry Abalea';
25
+ const userEmail = 'thierryabalea@acme.com';
26
+ const creditsUsed = 3213;
27
+ const creditsTotal = 6000;
28
+
29
+ return (
30
+ <DropdownMenu>
31
+ <DropdownMenuTrigger asChild>
32
+ <div className="flex items-center justify-center h-40 px-8 cursor-pointer border-l border-border-neutral-strong">
33
+ <Avatar className="size-24 md:hidden" content="image" fallback="TA" />
34
+ <UserBadge name={userName} className="hidden md:inline-flex" />
35
+ </div>
36
+ </DropdownMenuTrigger>
37
+ <DropdownMenuContent align="end" className="w-220">
38
+ <div className="flex items-center gap-12 px-8 py-4">
39
+ <Avatar className="size-28 shrink-0" content="image" fallback="TA" />
40
+ <div className="flex flex-col min-w-0">
41
+ <span className="text-sm font-medium leading-20 text-foreground-neutral-base truncate">
42
+ {userName}
43
+ </span>
44
+ <span className="text-xs leading-20 text-foreground-neutral-subtle truncate">
45
+ {userEmail}
46
+ </span>
47
+ </div>
48
+ </div>
49
+
50
+ <DropdownMenuSeparator />
51
+
52
+ <DropdownMenuItem icon="sparkling2Fill">Getting started</DropdownMenuItem>
53
+ <DropdownMenuItem icon="userLine">Profile settings</DropdownMenuItem>
54
+
55
+ <DropdownMenuSeparator />
56
+
57
+ <div className="flex flex-col gap-8 px-8 py-4">
58
+ <div className="flex items-center justify-between gap-8">
59
+ <div className="flex items-center gap-8">
60
+ <Avatar className="size-12" content="logo" logoName="stripe" radius="rounded" />
61
+ <span className="text-sm font-medium leading-20 text-foreground-neutral-subtle">
62
+ Usage
63
+ </span>
64
+ </div>
65
+ <span className="text-sm font-medium leading-20 text-foreground-neutral-subtle">
66
+ {creditsUsed} / {creditsTotal}
67
+ </span>
68
+ </div>
69
+ <UsageGauge used={creditsUsed} total={creditsTotal} />
70
+ <span className="text-xs leading-20 text-foreground-neutral-subtle">
71
+ {creditsTotal} free credits every month.
72
+ </span>
73
+ </div>
74
+
75
+ <div className="px-8 pb-12">
76
+ <Button type="button" className="w-full">
77
+ <ShinyText
78
+ text="Upgrade Plan"
79
+ className="flex-1 text-sm font-medium leading-20 text-foreground-neutral-base truncate text-center"
80
+ />
81
+ </Button>
82
+ </div>
83
+
84
+ <DropdownMenuSeparator />
85
+
86
+ <DropdownMenuItem icon="logoutCircleLine">Log out</DropdownMenuItem>
87
+ </DropdownMenuContent>
88
+ </DropdownMenu>
89
+ );
90
+ }
@@ -0,0 +1,25 @@
1
+ import type {Meta, StoryObj} from '@storybook/react';
2
+ import {Dashboard} from './dashboard';
3
+
4
+ const meta = {
5
+ title: 'Dashboard/Example',
6
+ tags: ['autodocs'],
7
+ parameters: {
8
+ layout: 'fullscreen',
9
+ viewport: {
10
+ defaultViewport: 'extraLarge',
11
+ },
12
+ },
13
+ } satisfies Meta;
14
+
15
+ export default meta;
16
+
17
+ type Story = StoryObj<typeof meta>;
18
+
19
+ export const Default: Story = {
20
+ render: () => (
21
+ <div className="h-screen w-full">
22
+ <Dashboard />
23
+ </div>
24
+ ),
25
+ };
@@ -0,0 +1,61 @@
1
+ import {useMotionValueEvent, useScroll} from 'framer-motion';
2
+ import {useCallback, useRef, useState} from 'react';
3
+ import {AnalyticsContent} from './components/analytics-content';
4
+ import {AnimatedLogo} from './components/animated-logo';
5
+ import {JobsContent} from './components/jobs-content';
6
+ import {TopMenu} from './components/top-menu';
7
+ import {Topbar} from './components/topbar';
8
+
9
+ const LOGO_HEIGHT = 48;
10
+ const TOPBAR_HEIGHT = 41;
11
+
12
+ export function Dashboard() {
13
+ const containerRef = useRef<HTMLDivElement>(null);
14
+ const topbarRef = useRef<HTMLDivElement>(null);
15
+ const [scrollProgress, setScrollProgress] = useState(0);
16
+ const [activeTab, setActiveTab] = useState('analytics');
17
+
18
+ const {scrollY} = useScroll({
19
+ container: containerRef,
20
+ });
21
+
22
+ useMotionValueEvent(scrollY, 'change', (latest) => {
23
+ const progress = Math.min(latest / TOPBAR_HEIGHT, 1);
24
+ setScrollProgress(progress);
25
+ });
26
+
27
+ const handleTabChange = useCallback((tab: string) => {
28
+ setActiveTab(tab);
29
+ if (containerRef.current) {
30
+ containerRef.current.scrollTo({top: 0, behavior: 'instant'});
31
+ }
32
+ setScrollProgress(0);
33
+ }, []);
34
+
35
+ const isTopbarHidden = scrollProgress >= 1;
36
+
37
+ return (
38
+ <div className="flex flex-col w-full h-full">
39
+ <AnimatedLogo scrollProgress={scrollProgress} />
40
+
41
+ <div ref={containerRef} className="flex flex-col w-full h-full overflow-auto">
42
+ <div ref={topbarRef}>
43
+ <Topbar hideLogo={isTopbarHidden} />
44
+ </div>
45
+
46
+ <div className="sticky top-0 z-40 border-b border-border-neutral-strong bg-background-neutral-base">
47
+ <div
48
+ style={{
49
+ paddingLeft: `${(1 - (1 - scrollProgress) ** 3) * (LOGO_HEIGHT - 8)}px`,
50
+ }}
51
+ >
52
+ <TopMenu activeTab={activeTab} onTabChange={handleTabChange} />
53
+ </div>
54
+ </div>
55
+
56
+ {activeTab === 'analytics' && <AnalyticsContent />}
57
+ {activeTab === 'jobs' && <JobsContent />}
58
+ </div>
59
+ </div>
60
+ );
61
+ }
@@ -0,0 +1 @@
1
+ export * from './dashboard';
@@ -1,3 +1,4 @@
1
+ import {argosScreenshot} from '@argos-ci/storybook/vitest';
1
2
  import {zodResolver} from '@hookform/resolvers/zod';
2
3
  import type {Meta, StoryObj} from '@storybook/react';
3
4
  import {Button} from 'components/button';
@@ -86,6 +87,10 @@ function BasicFormExample() {
86
87
 
87
88
  export const Basic: Story = {
88
89
  render: () => <BasicFormExample />,
90
+ play: async (context) => {
91
+ await new Promise((resolve) => setTimeout(resolve, 100));
92
+ await argosScreenshot(context, 'Form Basic');
93
+ },
89
94
  };
90
95
 
91
96
  const componentsFormSchema = z.object({
@@ -78,9 +78,13 @@ export type IconName = keyof typeof iconsMap;
78
78
  export const iconNames = Object.keys(iconsMap) as IconName[];
79
79
 
80
80
  type BaseIconProps = ComponentProps<RemixiconComponentType>;
81
- type IconProps = {name: IconName} & Omit<BaseIconProps, 'name'>;
81
+ type IconProps = {name: IconName; size?: number | string} & Omit<
82
+ BaseIconProps,
83
+ 'name' | 'size' | 'width' | 'height'
84
+ >;
82
85
 
83
- export function Icon({name, ...props}: IconProps) {
86
+ export function Icon({name, size, ...props}: IconProps) {
84
87
  const IconComponent = iconsMap[name];
85
- return <IconComponent {...props} />;
88
+ const svgProps = size && typeof size === 'number' ? {...props, width: size, height: size} : props;
89
+ return <IconComponent {...svgProps} />;
86
90
  }
@@ -2,9 +2,12 @@ export * from './alert';
2
2
  export * from './avatar';
3
3
  export * from './badge';
4
4
  export * from './button';
5
+ export * from './button-group';
5
6
  export * from './calendar';
6
7
  export * from './checkbox';
7
8
  export * from './code-block';
9
+ export * from './command';
10
+ export * from './confetti';
8
11
  export * from './date-picker';
9
12
  export * from './date-time-range-picker';
10
13
  export * from './dot-grid';
@@ -15,10 +18,16 @@ export * from './icon';
15
18
  export * from './inline-tips';
16
19
  export * from './input';
17
20
  export * from './item';
21
+ export * from './kbd';
18
22
  export * from './label';
19
23
  export * from './modal';
20
24
  export * from './moving-border';
21
25
  export * from './popover';
26
+ export * from './search';
27
+ export * from './select';
28
+ export * from './shiny-text';
29
+ export * from './skeleton';
30
+ export * from './table';
22
31
  export * from './tabs';
23
32
  export * from './textarea';
24
33
  export * from './theme';
@@ -0,0 +1 @@
1
+ export * from './kbd';
@@ -0,0 +1,64 @@
1
+ import type {Meta, StoryObj} from '@storybook/react';
2
+ import {Kbd, KbdGroup} from './kbd';
3
+
4
+ const meta = {
5
+ title: 'Components/Kbd',
6
+ component: Kbd,
7
+ tags: ['autodocs'],
8
+ } satisfies Meta<typeof Kbd>;
9
+
10
+ export default meta;
11
+
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ export const Default: Story = {
15
+ render: () => (
16
+ <div className="flex flex-wrap gap-8">
17
+ <Kbd>Ctrl</Kbd>
18
+ <Kbd>Alt</Kbd>
19
+ <Kbd>Shift</Kbd>
20
+ <Kbd>⌘</Kbd>
21
+ <Kbd>⌥</Kbd>
22
+ <Kbd>⇧</Kbd>
23
+ </div>
24
+ ),
25
+ };
26
+
27
+ export const KeyCombination: Story = {
28
+ render: () => (
29
+ <div className="flex flex-wrap items-center gap-12">
30
+ <Kbd>⌘K</Kbd>
31
+ <KbdGroup>
32
+ <Kbd>Ctrl</Kbd>
33
+ <Kbd>Shift</Kbd>
34
+ <Kbd>P</Kbd>
35
+ </KbdGroup>
36
+ <KbdGroup>
37
+ <Kbd>Alt</Kbd>
38
+ <Kbd>Enter</Kbd>
39
+ </KbdGroup>
40
+ </div>
41
+ ),
42
+ };
43
+
44
+ export const InMenu: Story = {
45
+ render: () => (
46
+ <div className="max-w-400 rounded-10 border border-border-neutral-base p-8">
47
+ <div className="flex flex-col gap-4">
48
+ <div className="flex items-center justify-between p-8 rounded-6 hover:bg-background-components-hover transition-colors">
49
+ <span className="text-sm text-foreground-neutral-subtle">Copy</span>
50
+ <Kbd>⌘C</Kbd>
51
+ </div>
52
+ <div className="flex items-center justify-between p-8 rounded-6 hover:bg-background-components-hover transition-colors">
53
+ <span className="text-sm text-foreground-neutral-subtle">Paste</span>
54
+ <Kbd>⌘V</Kbd>
55
+ </div>
56
+ <div className="h-px bg-border-neutral-base my-4" />
57
+ <div className="flex items-center justify-between p-8 rounded-6 hover:bg-background-components-hover transition-colors">
58
+ <span className="text-sm text-foreground-neutral-subtle">Command Palette</span>
59
+ <Kbd>⌘K</Kbd>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ ),
64
+ };
@@ -0,0 +1,32 @@
1
+ import type {ComponentProps} from 'react';
2
+ import {cn} from 'utils/cn';
3
+
4
+ type KbdProps = ComponentProps<'kbd'>;
5
+
6
+ export function Kbd({className, ...props}: KbdProps) {
7
+ return (
8
+ <kbd
9
+ data-slot="kbd"
10
+ className={cn(
11
+ 'pointer-events-none inline-flex h-20 w-fit min-w-20 items-center justify-center gap-1 rounded-4 px-4 font-display text-xs font-medium select-none',
12
+ 'bg-background-components-base text-foreground-neutral-subtle border border-border-neutral-base shadow-button-neutral',
13
+ '[&_svg:not([class*="size-"])]:size-12',
14
+ 'in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10',
15
+ className,
16
+ )}
17
+ {...props}
18
+ />
19
+ );
20
+ }
21
+
22
+ type KbdGroupProps = ComponentProps<'div'>;
23
+
24
+ export function KbdGroup({className, ...props}: KbdGroupProps) {
25
+ return (
26
+ <div
27
+ data-slot="kbd-group"
28
+ className={cn('inline-flex items-center gap-4', className)}
29
+ {...props}
30
+ />
31
+ );
32
+ }
@@ -14,6 +14,7 @@ import {
14
14
  CodeBlockHeader,
15
15
  CodeBlockItem,
16
16
  } from 'components/code-block';
17
+ import {Confetti, type ConfettiRef} from 'components/confetti';
17
18
  import {DatePicker} from 'components/date-picker';
18
19
  import {DynamicItem} from 'components/dynamic-item';
19
20
  import {Icon} from 'components/icon';
@@ -22,7 +23,7 @@ import {ItemTitle} from 'components/item';
22
23
  import {Label} from 'components/label';
23
24
  import {MovingBorder} from 'components/moving-border';
24
25
  import {Text} from 'components/typography';
25
- import {useState} from 'react';
26
+ import {useEffect, useRef, useState} from 'react';
26
27
  import {cn} from 'utils/cn';
27
28
  import illustration2 from '../../assets/illustration-2.svg';
28
29
  import illustrationBg from '../../assets/illustration-gradient.svg';
@@ -214,9 +215,54 @@ export const GithubActions: Story = {
214
215
  },
215
216
  render: () => {
216
217
  const [open, setOpen] = useState(false);
218
+ const [footerState, setFooterState] = useState<'running' | 'done'>('running');
219
+ const confettiRef = useRef<ConfettiRef>(null);
220
+
221
+ useEffect(() => {
222
+ if (open && footerState === 'running') {
223
+ const timer = setTimeout(() => {
224
+ setFooterState('done');
225
+ }, 3000);
226
+
227
+ return () => {
228
+ clearTimeout(timer);
229
+ };
230
+ }
231
+ }, [open, footerState]);
232
+
233
+ useEffect(() => {
234
+ if (footerState === 'done' && open) {
235
+ const timer = setTimeout(() => {
236
+ if (confettiRef.current) {
237
+ void confettiRef.current.fire();
238
+ }
239
+ }, 200);
240
+
241
+ return () => {
242
+ clearTimeout(timer);
243
+ };
244
+ }
245
+ }, [footerState, open]);
246
+
247
+ useEffect(() => {
248
+ if (!open) {
249
+ setFooterState('running');
250
+ }
251
+ }, [open]);
217
252
 
218
253
  return (
219
254
  <div className="flex h-[50vh] w-[calc(100vw/2)] items-center justify-center rounded-16 bg-background-subtle-base shadow-tooltip">
255
+ <Confetti
256
+ ref={confettiRef}
257
+ manualstart
258
+ options={{
259
+ particleCount: 200,
260
+ spread: 100,
261
+ colors: ['#ff6b6b', '#4ecdc4', '#ffe66d', '#95e1d3'],
262
+ }}
263
+ className="fixed inset-0 pointer-events-none z-100"
264
+ style={{width: '100%', height: '100%'}}
265
+ />
220
266
  <Modal open={open} onOpenChange={setOpen}>
221
267
  <ModalTrigger asChild>
222
268
  <Button>Run GitHub Actions on Shipfox</Button>
@@ -316,9 +362,17 @@ export const GithubActions: Story = {
316
362
  )}
317
363
  </CodeBlockBody>
318
364
  <CodeBlockFooter
319
- state="running"
320
- message="Waiting for Shipfox runner event…"
321
- description="This usually takes 30-60 seconds after you commit the workflow file."
365
+ state={footerState}
366
+ message={
367
+ footerState === 'running'
368
+ ? 'Waiting for Shipfox runner event…'
369
+ : 'Runners connected!'
370
+ }
371
+ description={
372
+ footerState === 'running'
373
+ ? 'This usually takes 30-60 seconds after you commit the workflow file.'
374
+ : ''
375
+ }
322
376
  />
323
377
  </CodeBlock>
324
378
  </div>
@@ -0,0 +1,28 @@
1
+ export {Search, type SearchProps} from './search';
2
+ export {useSearchContext} from './search-context';
3
+ export {SearchInline, type SearchInlineProps} from './search-inline';
4
+ export {
5
+ SearchContent,
6
+ type SearchContentProps,
7
+ SearchEmpty,
8
+ type SearchEmptyProps,
9
+ SearchFooter,
10
+ type SearchFooterProps,
11
+ SearchGroup,
12
+ type SearchGroupProps,
13
+ SearchInput,
14
+ type SearchInputProps,
15
+ SearchItem,
16
+ type SearchItemProps,
17
+ SearchList,
18
+ type SearchListProps,
19
+ SearchSeparator,
20
+ type SearchSeparatorProps,
21
+ } from './search-modal';
22
+ export {SearchTrigger, type SearchTriggerProps} from './search-trigger';
23
+
24
+ export {
25
+ searchDefaultTransition,
26
+ searchInputVariants,
27
+ searchTriggerVariants,
28
+ } from './search-variants';