@shipfox/react-ui 0.3.0 → 0.5.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 (314) hide show
  1. package/.storybook/main.ts +20 -10
  2. package/.storybook/preview.tsx +1 -1
  3. package/.storybook/vitest.setup.ts +4 -0
  4. package/.turbo/turbo-build.log +2 -2
  5. package/.turbo/turbo-check.log +2 -2
  6. package/.turbo/turbo-type.log +1 -1
  7. package/CHANGELOG.md +18 -0
  8. package/README.md +40 -1
  9. package/argos.config.ts +33 -0
  10. package/dist/components/alert/alert.d.ts +2 -2
  11. package/dist/components/alert/alert.js +3 -3
  12. package/dist/components/alert/alert.js.map +1 -1
  13. package/dist/components/alert/alert.stories.js +2 -2
  14. package/dist/components/alert/alert.stories.js.map +1 -1
  15. package/dist/components/avatar/avatar-group.js +3 -3
  16. package/dist/components/avatar/avatar-group.js.map +1 -1
  17. package/dist/components/avatar/avatar.d.ts +4 -1
  18. package/dist/components/avatar/avatar.d.ts.map +1 -1
  19. package/dist/components/avatar/avatar.js +7 -8
  20. package/dist/components/avatar/avatar.js.map +1 -1
  21. package/dist/components/avatar/avatar.stories.js +15 -3
  22. package/dist/components/avatar/avatar.stories.js.map +1 -1
  23. package/dist/components/badge/badge.d.ts +48 -0
  24. package/dist/components/badge/badge.d.ts.map +1 -0
  25. package/dist/components/badge/badge.js +72 -0
  26. package/dist/components/badge/badge.js.map +1 -0
  27. package/dist/components/badge/badge.stories.js +802 -0
  28. package/dist/components/badge/badge.stories.js.map +1 -0
  29. package/dist/components/badge/icon-badge.d.ts +9 -0
  30. package/dist/components/badge/icon-badge.d.ts.map +1 -0
  31. package/dist/components/badge/icon-badge.js +32 -0
  32. package/dist/components/badge/icon-badge.js.map +1 -0
  33. package/dist/components/badge/index.d.ts +5 -0
  34. package/dist/components/badge/index.d.ts.map +1 -0
  35. package/dist/components/badge/index.js +6 -0
  36. package/dist/components/badge/index.js.map +1 -0
  37. package/dist/components/badge/status-badge.d.ts +9 -0
  38. package/dist/components/badge/status-badge.d.ts.map +1 -0
  39. package/dist/components/badge/status-badge.js +29 -0
  40. package/dist/components/badge/status-badge.js.map +1 -0
  41. package/dist/components/badge/user-badge.d.ts +8 -0
  42. package/dist/components/badge/user-badge.d.ts.map +1 -0
  43. package/dist/components/badge/user-badge.js +24 -0
  44. package/dist/components/badge/user-badge.js.map +1 -0
  45. package/dist/components/button/button-link.d.ts +14 -0
  46. package/dist/components/button/button-link.d.ts.map +1 -0
  47. package/dist/components/button/button-link.js +63 -0
  48. package/dist/components/button/button-link.js.map +1 -0
  49. package/dist/components/button/button-link.stories.js +127 -0
  50. package/dist/components/button/button-link.stories.js.map +1 -0
  51. package/dist/components/{button.d.ts → button/button.d.ts} +2 -2
  52. package/dist/components/button/button.d.ts.map +1 -0
  53. package/dist/components/{button.js → button/button.js} +9 -8
  54. package/dist/components/button/button.js.map +1 -0
  55. package/dist/components/{button.stories.js → button/button.stories.js} +2 -14
  56. package/dist/components/button/button.stories.js.map +1 -0
  57. package/dist/components/button/icon-button.d.ts +14 -0
  58. package/dist/components/button/icon-button.d.ts.map +1 -0
  59. package/dist/components/button/icon-button.js +53 -0
  60. package/dist/components/button/icon-button.js.map +1 -0
  61. package/dist/components/button/icon-button.stories.js +254 -0
  62. package/dist/components/button/icon-button.stories.js.map +1 -0
  63. package/dist/components/button/index.d.ts +4 -0
  64. package/dist/components/button/index.d.ts.map +1 -0
  65. package/dist/components/button/index.js +5 -0
  66. package/dist/components/button/index.js.map +1 -0
  67. package/dist/components/checkbox/checkbox-label.d.ts +14 -0
  68. package/dist/components/checkbox/checkbox-label.d.ts.map +1 -0
  69. package/dist/components/checkbox/checkbox-label.js +82 -0
  70. package/dist/components/checkbox/checkbox-label.js.map +1 -0
  71. package/dist/components/checkbox/checkbox-links.d.ts +18 -0
  72. package/dist/components/checkbox/checkbox-links.d.ts.map +1 -0
  73. package/dist/components/checkbox/checkbox-links.js +58 -0
  74. package/dist/components/checkbox/checkbox-links.js.map +1 -0
  75. package/dist/components/checkbox/checkbox.d.ts +9 -0
  76. package/dist/components/checkbox/checkbox.d.ts.map +1 -0
  77. package/dist/components/checkbox/checkbox.js +49 -0
  78. package/dist/components/checkbox/checkbox.js.map +1 -0
  79. package/dist/components/checkbox/checkbox.stories.js +566 -0
  80. package/dist/components/checkbox/checkbox.stories.js.map +1 -0
  81. package/dist/components/checkbox/index.d.ts +4 -0
  82. package/dist/components/checkbox/index.d.ts.map +1 -0
  83. package/dist/components/checkbox/index.js +5 -0
  84. package/dist/components/checkbox/index.js.map +1 -0
  85. package/dist/components/code-block/code-block-footer.d.ts +26 -0
  86. package/dist/components/code-block/code-block-footer.d.ts.map +1 -0
  87. package/dist/components/code-block/code-block-footer.js +86 -0
  88. package/dist/components/code-block/code-block-footer.js.map +1 -0
  89. package/dist/components/code-block/code-block.d.ts +50 -0
  90. package/dist/components/code-block/code-block.d.ts.map +1 -0
  91. package/dist/components/code-block/code-block.js +142 -0
  92. package/dist/components/code-block/code-block.js.map +1 -0
  93. package/dist/components/code-block/code-block.stories.js +341 -0
  94. package/dist/components/code-block/code-block.stories.js.map +1 -0
  95. package/dist/components/code-block/code-content.d.ts +11 -0
  96. package/dist/components/code-block/code-content.d.ts.map +1 -0
  97. package/dist/components/code-block/code-content.js +29 -0
  98. package/dist/components/code-block/code-content.js.map +1 -0
  99. package/dist/components/code-block/code-copy-button.d.ts +11 -0
  100. package/dist/components/code-block/code-copy-button.d.ts.map +1 -0
  101. package/dist/components/code-block/code-copy-button.js +49 -0
  102. package/dist/components/code-block/code-copy-button.js.map +1 -0
  103. package/dist/components/code-block/code-tabs.d.ts +16 -0
  104. package/dist/components/code-block/code-tabs.d.ts.map +1 -0
  105. package/dist/components/code-block/code-tabs.js +98 -0
  106. package/dist/components/code-block/code-tabs.js.map +1 -0
  107. package/dist/components/code-block/index.d.ts +4 -0
  108. package/dist/components/code-block/index.d.ts.map +1 -0
  109. package/dist/components/code-block/index.js +5 -0
  110. package/dist/components/code-block/index.js.map +1 -0
  111. package/dist/components/dynamic-item/dynamic-item.d.ts +13 -0
  112. package/dist/components/dynamic-item/dynamic-item.d.ts.map +1 -0
  113. package/dist/components/dynamic-item/dynamic-item.js +43 -0
  114. package/dist/components/dynamic-item/dynamic-item.js.map +1 -0
  115. package/dist/components/dynamic-item/dynamic-item.stories.js +375 -0
  116. package/dist/components/dynamic-item/dynamic-item.stories.js.map +1 -0
  117. package/dist/components/dynamic-item/index.d.ts +2 -0
  118. package/dist/components/dynamic-item/index.d.ts.map +1 -0
  119. package/dist/components/dynamic-item/index.js +3 -0
  120. package/dist/components/dynamic-item/index.js.map +1 -0
  121. package/dist/components/icon/custom/index.d.ts +2 -0
  122. package/dist/components/icon/custom/index.d.ts.map +1 -1
  123. package/dist/components/icon/custom/index.js +2 -0
  124. package/dist/components/icon/custom/index.js.map +1 -1
  125. package/dist/components/icon/custom/slack-logo.d.ts +6 -0
  126. package/dist/components/icon/custom/slack-logo.d.ts.map +1 -0
  127. package/dist/components/icon/custom/slack-logo.js +34 -0
  128. package/dist/components/icon/custom/slack-logo.js.map +1 -0
  129. package/dist/components/icon/custom/stripe-logo.d.ts +8 -0
  130. package/dist/components/icon/custom/stripe-logo.d.ts.map +1 -0
  131. package/dist/components/icon/custom/stripe-logo.js +24 -0
  132. package/dist/components/icon/custom/stripe-logo.js.map +1 -0
  133. package/dist/components/icon/icon.d.ts +13 -2
  134. package/dist/components/icon/icon.d.ts.map +1 -1
  135. package/dist/components/icon/icon.js +14 -3
  136. package/dist/components/icon/icon.js.map +1 -1
  137. package/dist/components/icon/icon.stories.js +6 -3
  138. package/dist/components/icon/icon.stories.js.map +1 -1
  139. package/dist/components/index.d.ts +9 -1
  140. package/dist/components/index.d.ts.map +1 -1
  141. package/dist/components/index.js +10 -2
  142. package/dist/components/index.js.map +1 -1
  143. package/dist/components/inline-tips/inline-tips.d.ts +1 -1
  144. package/dist/components/inline-tips/inline-tips.d.ts.map +1 -1
  145. package/dist/components/inline-tips/inline-tips.js +1 -1
  146. package/dist/components/inline-tips/inline-tips.js.map +1 -1
  147. package/dist/components/inline-tips/inline-tips.stories.js +5 -5
  148. package/dist/components/inline-tips/inline-tips.stories.js.map +1 -1
  149. package/dist/components/input/index.d.ts +2 -0
  150. package/dist/components/input/index.d.ts.map +1 -0
  151. package/dist/components/input/index.js +3 -0
  152. package/dist/components/input/index.js.map +1 -0
  153. package/dist/components/input/input.d.ts.map +1 -0
  154. package/dist/components/{input.js → input/input.js} +2 -2
  155. package/dist/components/input/input.js.map +1 -0
  156. package/dist/components/{input.stories.js → input/input.stories.js} +1 -1
  157. package/dist/components/input/input.stories.js.map +1 -0
  158. package/dist/components/item/index.d.ts +2 -0
  159. package/dist/components/item/index.d.ts.map +1 -0
  160. package/dist/components/item/index.js +3 -0
  161. package/dist/components/item/index.js.map +1 -0
  162. package/dist/components/item/item.d.ts +32 -0
  163. package/dist/components/item/item.d.ts.map +1 -0
  164. package/dist/components/item/item.js +120 -0
  165. package/dist/components/item/item.js.map +1 -0
  166. package/dist/components/item/item.stories.js +232 -0
  167. package/dist/components/item/item.stories.js.map +1 -0
  168. package/dist/components/label/index.d.ts +2 -0
  169. package/dist/components/label/index.d.ts.map +1 -0
  170. package/dist/components/label/index.js +3 -0
  171. package/dist/components/label/index.js.map +1 -0
  172. package/dist/components/label/label.d.ts +7 -0
  173. package/dist/components/label/label.d.ts.map +1 -0
  174. package/dist/components/label/label.js +13 -0
  175. package/dist/components/label/label.js.map +1 -0
  176. package/dist/components/label/label.stories.js +105 -0
  177. package/dist/components/label/label.stories.js.map +1 -0
  178. package/dist/components/moving-border/moving-border.d.ts +9 -0
  179. package/dist/components/moving-border/moving-border.d.ts.map +1 -0
  180. package/dist/components/moving-border/moving-border.js +54 -0
  181. package/dist/components/moving-border/moving-border.js.map +1 -0
  182. package/dist/components/textarea/textarea.js +1 -1
  183. package/dist/components/textarea/textarea.js.map +1 -1
  184. package/dist/components/theme/index.d.ts +2 -0
  185. package/dist/components/theme/index.d.ts.map +1 -0
  186. package/dist/components/theme/index.js +3 -0
  187. package/dist/components/theme/index.js.map +1 -0
  188. package/dist/components/{theme-provider.d.ts → theme/theme-provider.d.ts} +1 -1
  189. package/dist/components/theme/theme-provider.d.ts.map +1 -0
  190. package/dist/components/{theme-provider.js → theme/theme-provider.js} +1 -1
  191. package/dist/components/theme/theme-provider.js.map +1 -0
  192. package/dist/components/toast/index.d.ts +3 -0
  193. package/dist/components/toast/index.d.ts.map +1 -0
  194. package/dist/components/toast/index.js +4 -0
  195. package/dist/components/toast/index.js.map +1 -0
  196. package/dist/components/toast/toast-custom.d.ts +19 -0
  197. package/dist/components/toast/toast-custom.d.ts.map +1 -0
  198. package/dist/components/toast/toast-custom.js +134 -0
  199. package/dist/components/toast/toast-custom.js.map +1 -0
  200. package/dist/components/toast/toast.d.ts +5 -0
  201. package/dist/components/toast/toast.d.ts.map +1 -0
  202. package/dist/components/toast/toast.js +40 -0
  203. package/dist/components/toast/toast.js.map +1 -0
  204. package/dist/components/toast/toast.stories.js +326 -0
  205. package/dist/components/toast/toast.stories.js.map +1 -0
  206. package/dist/components/tooltip/index.d.ts +2 -0
  207. package/dist/components/tooltip/index.d.ts.map +1 -0
  208. package/dist/components/tooltip/index.js +3 -0
  209. package/dist/components/tooltip/index.js.map +1 -0
  210. package/dist/components/tooltip/tooltip.d.ts +18 -5
  211. package/dist/components/tooltip/tooltip.d.ts.map +1 -1
  212. package/dist/components/tooltip/tooltip.js +63 -3
  213. package/dist/components/tooltip/tooltip.js.map +1 -1
  214. package/dist/components/tooltip/tooltip.stories.js +560 -0
  215. package/dist/components/tooltip/tooltip.stories.js.map +1 -0
  216. package/dist/hooks/index.d.ts +3 -0
  217. package/dist/hooks/index.d.ts.map +1 -1
  218. package/dist/hooks/index.js +3 -0
  219. package/dist/hooks/index.js.map +1 -1
  220. package/dist/hooks/useResolvedTheme.d.ts +2 -0
  221. package/dist/hooks/useResolvedTheme.d.ts.map +1 -0
  222. package/dist/hooks/useResolvedTheme.js +24 -0
  223. package/dist/hooks/useResolvedTheme.js.map +1 -0
  224. package/dist/hooks/useShikiHighlight.d.ts +28 -0
  225. package/dist/hooks/useShikiHighlight.d.ts.map +1 -0
  226. package/dist/hooks/useShikiHighlight.js +106 -0
  227. package/dist/hooks/useShikiHighlight.js.map +1 -0
  228. package/dist/hooks/useShikiStyleInjection.d.ts +2 -0
  229. package/dist/hooks/useShikiStyleInjection.d.ts.map +1 -0
  230. package/dist/hooks/useShikiStyleInjection.js +34 -0
  231. package/dist/hooks/useShikiStyleInjection.js.map +1 -0
  232. package/dist/onboarding/sign-in.stories.js +93 -0
  233. package/dist/onboarding/sign-in.stories.js.map +1 -0
  234. package/index.css +130 -12
  235. package/package.json +14 -3
  236. package/src/assets/illustration-1.svg +92 -0
  237. package/src/assets/illustration-2.svg +14 -0
  238. package/src/assets/illustration-gradient.svg +7049 -0
  239. package/src/components/alert/alert.stories.tsx +2 -2
  240. package/src/components/alert/alert.tsx +3 -3
  241. package/src/components/avatar/avatar-group.tsx +3 -3
  242. package/src/components/avatar/avatar.stories.tsx +9 -2
  243. package/src/components/avatar/avatar.tsx +10 -6
  244. package/src/components/badge/badge.stories.tsx +468 -0
  245. package/src/components/badge/badge.tsx +147 -0
  246. package/src/components/badge/icon-badge.tsx +43 -0
  247. package/src/components/badge/index.ts +4 -0
  248. package/src/components/badge/status-badge.tsx +43 -0
  249. package/src/components/badge/user-badge.tsx +34 -0
  250. package/src/components/button/button-link.stories.tsx +86 -0
  251. package/src/components/button/button-link.tsx +76 -0
  252. package/src/components/{button.stories.tsx → button/button.stories.tsx} +1 -7
  253. package/src/components/{button.tsx → button/button.tsx} +9 -7
  254. package/src/components/button/icon-button.stories.tsx +182 -0
  255. package/src/components/button/icon-button.tsx +69 -0
  256. package/src/components/button/index.ts +3 -0
  257. package/src/components/checkbox/checkbox-label.tsx +125 -0
  258. package/src/components/checkbox/checkbox-links.tsx +90 -0
  259. package/src/components/checkbox/checkbox.stories.tsx +375 -0
  260. package/src/components/checkbox/checkbox.tsx +71 -0
  261. package/src/components/checkbox/index.ts +3 -0
  262. package/src/components/code-block/code-block-footer.tsx +173 -0
  263. package/src/components/code-block/code-block.stories.tsx +323 -0
  264. package/src/components/code-block/code-block.tsx +283 -0
  265. package/src/components/code-block/code-content.tsx +63 -0
  266. package/src/components/code-block/code-copy-button.tsx +73 -0
  267. package/src/components/code-block/code-tabs.tsx +170 -0
  268. package/src/components/code-block/index.ts +3 -0
  269. package/src/components/dynamic-item/dynamic-item.stories.tsx +261 -0
  270. package/src/components/dynamic-item/dynamic-item.tsx +68 -0
  271. package/src/components/dynamic-item/index.ts +1 -0
  272. package/src/components/icon/custom/index.ts +2 -0
  273. package/src/components/icon/custom/slack-logo.tsx +35 -0
  274. package/src/components/icon/custom/stripe-logo.tsx +27 -0
  275. package/src/components/icon/icon.stories.tsx +3 -1
  276. package/src/components/icon/icon.tsx +23 -1
  277. package/src/components/index.ts +9 -1
  278. package/src/components/inline-tips/inline-tips.stories.tsx +3 -3
  279. package/src/components/inline-tips/inline-tips.tsx +2 -2
  280. package/src/components/input/index.ts +1 -0
  281. package/src/components/{input.tsx → input/input.tsx} +1 -1
  282. package/src/components/item/index.ts +1 -0
  283. package/src/components/item/item.stories.tsx +150 -0
  284. package/src/components/item/item.tsx +182 -0
  285. package/src/components/label/index.ts +1 -0
  286. package/src/components/label/label.stories.tsx +67 -0
  287. package/src/components/label/label.tsx +15 -0
  288. package/src/components/moving-border/moving-border.tsx +67 -0
  289. package/src/components/textarea/textarea.tsx +1 -1
  290. package/src/components/theme/index.ts +1 -0
  291. package/src/components/toast/index.ts +2 -0
  292. package/src/components/toast/toast-custom.tsx +154 -0
  293. package/src/components/toast/toast.stories.tsx +369 -0
  294. package/src/components/toast/toast.tsx +41 -0
  295. package/src/components/tooltip/index.ts +1 -0
  296. package/src/components/tooltip/tooltip.stories.tsx +284 -0
  297. package/src/components/tooltip/tooltip.tsx +79 -10
  298. package/src/hooks/index.ts +3 -0
  299. package/src/hooks/useResolvedTheme.ts +34 -0
  300. package/src/hooks/useShikiHighlight.ts +140 -0
  301. package/src/hooks/useShikiStyleInjection.ts +34 -0
  302. package/src/onboarding/sign-in.stories.tsx +73 -0
  303. package/vitest.config.ts +30 -3
  304. package/dist/components/button.d.ts.map +0 -1
  305. package/dist/components/button.js.map +0 -1
  306. package/dist/components/button.stories.js.map +0 -1
  307. package/dist/components/input.d.ts.map +0 -1
  308. package/dist/components/input.js.map +0 -1
  309. package/dist/components/input.stories.js.map +0 -1
  310. package/dist/components/theme-provider.d.ts.map +0 -1
  311. package/dist/components/theme-provider.js.map +0 -1
  312. /package/dist/components/{input.d.ts → input/input.d.ts} +0 -0
  313. /package/src/components/{input.stories.tsx → input/input.stories.tsx} +0 -0
  314. /package/src/components/{theme-provider.tsx → theme/theme-provider.tsx} +0 -0
@@ -0,0 +1,71 @@
1
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
2
+ import {cva, type VariantProps} from 'class-variance-authority';
3
+ import {Icon} from 'components/icon';
4
+ import type {ComponentProps} from 'react';
5
+ import {cn} from 'utils/cn';
6
+
7
+ export const checkboxVariants = cva(
8
+ 'peer shrink-0 border-none transition-all duration-100 outline-none cursor-pointer',
9
+ {
10
+ variants: {
11
+ size: {
12
+ sm: 'size-16',
13
+ md: 'size-20',
14
+ lg: 'size-24',
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ size: 'sm',
19
+ },
20
+ },
21
+ );
22
+
23
+ export type CheckboxProps = ComponentProps<typeof CheckboxPrimitive.Root> &
24
+ VariantProps<typeof checkboxVariants>;
25
+
26
+ export function Checkbox({
27
+ className,
28
+ size,
29
+ checked,
30
+ defaultChecked,
31
+ onCheckedChange,
32
+ ...props
33
+ }: CheckboxProps) {
34
+ return (
35
+ <CheckboxPrimitive.Root
36
+ data-slot="checkbox"
37
+ checked={checked}
38
+ defaultChecked={defaultChecked}
39
+ onCheckedChange={onCheckedChange}
40
+ className={cn(
41
+ checkboxVariants({size}),
42
+ // Unchecked state - default
43
+ 'rounded-4 bg-checkbox-unchecked-bg shadow-checkbox-unchecked',
44
+ // Unchecked state - hover
45
+ 'hover:bg-checkbox-unchecked-bg-hover',
46
+ // Unchecked state - focus
47
+ 'focus-visible:shadow-checkbox-unchecked-focus',
48
+ // Checked state
49
+ 'data-[state=checked]:bg-checkbox-checked-bg data-[state=checked]:text-foreground-neutral-on-color data-[state=checked]:shadow-checkbox-checked',
50
+ 'data-[state=checked]:hover:bg-checkbox-checked-bg-hover',
51
+ 'data-[state=checked]:focus-visible:shadow-checkbox-checked-focus',
52
+ // Indeterminate state
53
+ 'data-[state=indeterminate]:bg-checkbox-indeterminate-bg data-[state=indeterminate]:text-foreground-neutral-on-color data-[state=indeterminate]:shadow-checkbox-indeterminate',
54
+ 'data-[state=indeterminate]:hover:bg-checkbox-indeterminate-bg-hover',
55
+ 'data-[state=indeterminate]:focus-visible:shadow-checkbox-indeterminate-focus',
56
+ // Disabled state
57
+ 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
58
+ className,
59
+ )}
60
+ {...props}
61
+ >
62
+ <CheckboxPrimitive.Indicator className="flex items-center justify-center text-current">
63
+ {checked === 'indeterminate' ? (
64
+ <Icon name="subtractLine" className="size-16" aria-hidden="true" />
65
+ ) : (
66
+ <Icon name="check" className="size-16" aria-hidden="true" />
67
+ )}
68
+ </CheckboxPrimitive.Indicator>
69
+ </CheckboxPrimitive.Root>
70
+ );
71
+ }
@@ -0,0 +1,3 @@
1
+ export * from './checkbox';
2
+ export * from './checkbox-label';
3
+ export * from './checkbox-links';
@@ -0,0 +1,173 @@
1
+ import {Slot} from '@radix-ui/react-slot';
2
+ import {Icon} from 'components/icon/icon';
3
+ import type {ComponentProps, HTMLAttributes, ReactNode} from 'react';
4
+ import {cn} from 'utils/cn';
5
+
6
+ export type CodeBlockFooterProps = HTMLAttributes<HTMLDivElement> & {
7
+ asChild?: boolean;
8
+ state?: 'running' | 'done';
9
+ message?: ReactNode;
10
+ description?: ReactNode;
11
+ icon?: ReactNode;
12
+ };
13
+
14
+ export function CodeBlockFooter({
15
+ className,
16
+ asChild = false,
17
+ state = 'running',
18
+ message,
19
+ description,
20
+ icon,
21
+ children,
22
+ ...props
23
+ }: CodeBlockFooterProps) {
24
+ const Comp = asChild ? Slot : 'div';
25
+
26
+ const defaultIcon =
27
+ icon ??
28
+ (state === 'running' ? (
29
+ <Icon name="shipfox" className="size-20" aria-hidden="true" />
30
+ ) : (
31
+ <Icon
32
+ name="checkCircleSolid"
33
+ className="size-20 text-foreground-neutral-base"
34
+ aria-hidden="true"
35
+ />
36
+ ));
37
+
38
+ if (asChild || children) {
39
+ return (
40
+ <Comp
41
+ data-slot="code-block-footer"
42
+ className={cn('flex w-full items-center justify-start gap-12 px-16 py-12', className)}
43
+ {...props}
44
+ >
45
+ {children}
46
+ </Comp>
47
+ );
48
+ }
49
+
50
+ return (
51
+ <Comp
52
+ data-slot="code-block-footer"
53
+ className={cn('flex w-full items-center justify-start gap-12 px-16 py-12', className)}
54
+ {...props}
55
+ >
56
+ <div className="flex shrink-0 items-center justify-center size-20 text-tag-success-icon">
57
+ {defaultIcon}
58
+ </div>
59
+ {(message || description) && (
60
+ <div className="flex flex-col items-start justify-center gap-0">
61
+ {message && (
62
+ <div className="overflow-hidden text-ellipsis whitespace-nowrap text-xs leading-20 text-foreground-neutral-base">
63
+ {message}
64
+ </div>
65
+ )}
66
+ {description && (
67
+ <div className="overflow-hidden text-ellipsis whitespace-nowrap text-xs leading-20 text-foreground-neutral-subtle">
68
+ {description}
69
+ </div>
70
+ )}
71
+ </div>
72
+ )}
73
+ </Comp>
74
+ );
75
+ }
76
+
77
+ export type CodeBlockFooterIconProps = ComponentProps<'div'> & {
78
+ asChild?: boolean;
79
+ };
80
+
81
+ export function CodeBlockFooterIcon({
82
+ className,
83
+ asChild = false,
84
+ children,
85
+ ...props
86
+ }: CodeBlockFooterIconProps) {
87
+ const Comp = asChild ? Slot : 'div';
88
+
89
+ return (
90
+ <Comp
91
+ data-slot="code-block-footer-icon"
92
+ className={cn('flex shrink-0 items-center justify-center size-20', className)}
93
+ {...props}
94
+ >
95
+ {children}
96
+ </Comp>
97
+ );
98
+ }
99
+
100
+ export type CodeBlockFooterContentProps = ComponentProps<'div'> & {
101
+ asChild?: boolean;
102
+ };
103
+
104
+ export function CodeBlockFooterContent({
105
+ className,
106
+ asChild = false,
107
+ children,
108
+ ...props
109
+ }: CodeBlockFooterContentProps) {
110
+ const Comp = asChild ? Slot : 'div';
111
+
112
+ return (
113
+ <Comp
114
+ data-slot="code-block-footer-content"
115
+ className={cn('flex flex-col items-start justify-center gap-0', className)}
116
+ {...props}
117
+ >
118
+ {children}
119
+ </Comp>
120
+ );
121
+ }
122
+
123
+ export type CodeBlockFooterMessageProps = ComponentProps<'div'> & {
124
+ asChild?: boolean;
125
+ };
126
+
127
+ export function CodeBlockFooterMessage({
128
+ className,
129
+ asChild = false,
130
+ children,
131
+ ...props
132
+ }: CodeBlockFooterMessageProps) {
133
+ const Comp = asChild ? Slot : 'div';
134
+
135
+ return (
136
+ <Comp
137
+ data-slot="code-block-footer-message"
138
+ className={cn(
139
+ 'overflow-hidden text-ellipsis whitespace-nowrap text-xs leading-20 text-foreground-neutral-base',
140
+ className,
141
+ )}
142
+ {...props}
143
+ >
144
+ {children}
145
+ </Comp>
146
+ );
147
+ }
148
+
149
+ export type CodeBlockFooterDescriptionProps = ComponentProps<'div'> & {
150
+ asChild?: boolean;
151
+ };
152
+
153
+ export function CodeBlockFooterDescription({
154
+ className,
155
+ asChild = false,
156
+ children,
157
+ ...props
158
+ }: CodeBlockFooterDescriptionProps) {
159
+ const Comp = asChild ? Slot : 'div';
160
+
161
+ return (
162
+ <Comp
163
+ data-slot="code-block-footer-description"
164
+ className={cn(
165
+ 'overflow-hidden text-ellipsis whitespace-nowrap text-xs leading-20 text-foreground-neutral-subtle',
166
+ className,
167
+ )}
168
+ {...props}
169
+ >
170
+ {children}
171
+ </Comp>
172
+ );
173
+ }
@@ -0,0 +1,323 @@
1
+ import type {Meta, StoryObj} from '@storybook/react';
2
+ import {
3
+ CodeBlock,
4
+ CodeBlockBody,
5
+ CodeBlockContent,
6
+ CodeBlockCopyButton,
7
+ CodeBlockFilename,
8
+ CodeBlockFiles,
9
+ CodeBlockFooter,
10
+ CodeBlockHeader,
11
+ CodeBlockItem,
12
+ } from 'components/code-block';
13
+ import {CodeTabs} from 'components/code-block/code-tabs';
14
+
15
+ const meta = {
16
+ title: 'Components/CodeBlock',
17
+ component: CodeBlock,
18
+ parameters: {
19
+ layout: 'padded',
20
+ },
21
+ } satisfies Meta<typeof CodeBlock>;
22
+
23
+ export default meta;
24
+ type Story = StoryObj<typeof meta>;
25
+
26
+ const exampleCode = `jobs:
27
+ build:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v3
31
+ - name: Build
32
+ run: npm run build`;
33
+
34
+ const diffCode = `jobs:
35
+ build:
36
+ - runs-on: ubuntu-latest
37
+ + runs-on: shipfox-2vcpu-ubuntu-2404`;
38
+
39
+ export const Default: Story = {
40
+ args: {
41
+ data: [
42
+ {
43
+ language: 'yaml',
44
+ filename: '.github/workflows/<workflow-name>.yml',
45
+ code: exampleCode,
46
+ },
47
+ ],
48
+ defaultValue: 'yaml',
49
+ },
50
+ render: (args) => (
51
+ <CodeBlock {...args}>
52
+ <CodeBlockHeader>
53
+ <CodeBlockFiles>
54
+ {(item) => <CodeBlockFilename value={item.language}>{item.filename}</CodeBlockFilename>}
55
+ </CodeBlockFiles>
56
+ <CodeBlockCopyButton />
57
+ </CodeBlockHeader>
58
+ <CodeBlockBody>
59
+ {(item) => (
60
+ <CodeBlockItem value={item.language}>
61
+ <CodeBlockContent language={item.language}>{item.code}</CodeBlockContent>
62
+ </CodeBlockItem>
63
+ )}
64
+ </CodeBlockBody>
65
+ </CodeBlock>
66
+ ),
67
+ };
68
+
69
+ export const WithDiff: Story = {
70
+ args: {
71
+ data: [
72
+ {
73
+ language: 'yaml',
74
+ filename: '.github/workflows/<workflow-name>.yml',
75
+ code: diffCode,
76
+ },
77
+ ],
78
+ defaultValue: 'yaml',
79
+ },
80
+ render: (args) => (
81
+ <CodeBlock {...args}>
82
+ <CodeBlockHeader>
83
+ <CodeBlockFiles>
84
+ {(item) => <CodeBlockFilename value={item.language}>{item.filename}</CodeBlockFilename>}
85
+ </CodeBlockFiles>
86
+ <CodeBlockCopyButton />
87
+ </CodeBlockHeader>
88
+ <CodeBlockBody>
89
+ {(item) => (
90
+ <CodeBlockItem value={item.language}>
91
+ <CodeBlockContent language={item.language}>{item.code}</CodeBlockContent>
92
+ </CodeBlockItem>
93
+ )}
94
+ </CodeBlockBody>
95
+ </CodeBlock>
96
+ ),
97
+ };
98
+
99
+ export const WithFooterRunning: Story = {
100
+ args: {
101
+ data: [
102
+ {
103
+ language: 'yaml',
104
+ filename: '.github/workflows/<workflow-name>.yml',
105
+ code: diffCode,
106
+ },
107
+ ],
108
+ defaultValue: 'yaml',
109
+ },
110
+ render: (args) => (
111
+ <CodeBlock {...args}>
112
+ <CodeBlockHeader>
113
+ <CodeBlockFiles>
114
+ {(item) => <CodeBlockFilename value={item.language}>{item.filename}</CodeBlockFilename>}
115
+ </CodeBlockFiles>
116
+ <CodeBlockCopyButton />
117
+ </CodeBlockHeader>
118
+ <CodeBlockBody>
119
+ {(item) => (
120
+ <CodeBlockItem value={item.language}>
121
+ <CodeBlockContent language={item.language}>{item.code}</CodeBlockContent>
122
+ </CodeBlockItem>
123
+ )}
124
+ </CodeBlockBody>
125
+ <CodeBlockFooter
126
+ state="running"
127
+ message="Waiting for Shipfox runner event…"
128
+ description="This usually takes 30-60 seconds after you commit the workflow file."
129
+ />
130
+ </CodeBlock>
131
+ ),
132
+ };
133
+
134
+ export const WithFooterDone: Story = {
135
+ args: {
136
+ data: [
137
+ {
138
+ language: 'yaml',
139
+ filename: '.github/workflows/<workflow-name>.yml',
140
+ code: diffCode,
141
+ },
142
+ ],
143
+ defaultValue: 'yaml',
144
+ },
145
+ render: (args) => (
146
+ <CodeBlock {...args}>
147
+ <CodeBlockHeader>
148
+ <CodeBlockFiles>
149
+ {(item) => <CodeBlockFilename value={item.language}>{item.filename}</CodeBlockFilename>}
150
+ </CodeBlockFiles>
151
+ <CodeBlockCopyButton />
152
+ </CodeBlockHeader>
153
+ <CodeBlockBody>
154
+ {(item) => (
155
+ <CodeBlockItem value={item.language}>
156
+ <CodeBlockContent language={item.language}>{item.code}</CodeBlockContent>
157
+ </CodeBlockItem>
158
+ )}
159
+ </CodeBlockBody>
160
+ <CodeBlockFooter state="done" message="Runner connected!" />
161
+ </CodeBlock>
162
+ ),
163
+ };
164
+
165
+ const multipleFilesCode = {
166
+ 'src/utils/format.ts': `export function formatDate(date: Date): string {
167
+ return new Intl.DateTimeFormat('en-US', {
168
+ year: 'numeric',
169
+ month: 'long',
170
+ day: 'numeric',
171
+ }).format(date);
172
+ }
173
+
174
+ export function formatCurrency(amount: number): string {
175
+ return new Intl.NumberFormat('en-US', {
176
+ style: 'currency',
177
+ currency: 'USD',
178
+ }).format(amount);
179
+ }`,
180
+ 'src/api/client.ts': `import type {User} from './types';
181
+
182
+ export class ApiClient {
183
+ private baseUrl: string;
184
+
185
+ constructor(baseUrl: string) {
186
+ this.baseUrl = baseUrl;
187
+ }
188
+
189
+ async getUser(id: string): Promise<User> {
190
+ const response = await fetch(\`\${this.baseUrl}/users/\${id}\`);
191
+ if (!response.ok) {
192
+ throw new Error('Failed to fetch user');
193
+ }
194
+ return response.json();
195
+ }
196
+ }`,
197
+ 'src/components/Button.tsx': `import type {ComponentProps} from 'react';
198
+
199
+ export function Button({
200
+ children,
201
+ variant = 'primary',
202
+ ...props
203
+ }: ComponentProps<'button'> & {
204
+ variant?: 'primary' | 'secondary';
205
+ }) {
206
+ return (
207
+ <button
208
+ className={\`btn btn-\${variant}\`}
209
+ {...props}
210
+ >
211
+ {children}
212
+ </button>
213
+ );
214
+ }`,
215
+ };
216
+
217
+ export const MultipleFiles: Story = {
218
+ args: {
219
+ data: [],
220
+ defaultValue: '',
221
+ },
222
+ render: () => (
223
+ <CodeTabs
224
+ codes={multipleFilesCode}
225
+ defaultValue="src/api/client.ts"
226
+ syntaxHighlighting={true}
227
+ lang="typescript"
228
+ lineNumbers={true}
229
+ />
230
+ ),
231
+ };
232
+
233
+ export const WithoutLineNumbers: Story = {
234
+ args: {
235
+ data: [
236
+ {
237
+ language: 'yaml',
238
+ filename: '.github/workflows/<workflow-name>.yml',
239
+ code: exampleCode,
240
+ },
241
+ ],
242
+ defaultValue: 'yaml',
243
+ },
244
+ render: (args) => (
245
+ <CodeBlock {...args}>
246
+ <CodeBlockHeader>
247
+ <CodeBlockFiles>
248
+ {(item) => <CodeBlockFilename value={item.language}>{item.filename}</CodeBlockFilename>}
249
+ </CodeBlockFiles>
250
+ <CodeBlockCopyButton />
251
+ </CodeBlockHeader>
252
+ <CodeBlockBody>
253
+ {(item) => (
254
+ <CodeBlockItem value={item.language} lineNumbers={false}>
255
+ <CodeBlockContent language={item.language}>{item.code}</CodeBlockContent>
256
+ </CodeBlockItem>
257
+ )}
258
+ </CodeBlockBody>
259
+ </CodeBlock>
260
+ ),
261
+ };
262
+
263
+ const npmCode = `npm install @shipfox/tooling`;
264
+ const yarnCode = `yarn add @shipfox/tooling`;
265
+ const pnpmCode = `pnpm add @shipfox/tooling`;
266
+
267
+ export const Snippet: Story = {
268
+ args: {
269
+ data: [],
270
+ defaultValue: '',
271
+ },
272
+ render: () => (
273
+ <CodeTabs
274
+ codes={{
275
+ npm: npmCode,
276
+ yarn: yarnCode,
277
+ pnpm: pnpmCode,
278
+ }}
279
+ defaultValue="npm"
280
+ />
281
+ ),
282
+ };
283
+
284
+ const syntaxHighlightingCode = {
285
+ 'index.ts': `export function hello(name: string = 'World'): void {
286
+ // Say hello to the provided name
287
+ console.log(\`Hello, \${name}!\`);
288
+ }
289
+
290
+ export function greetEveryone(names: string[]): void {
291
+ for (const name of names) {
292
+ hello(name);
293
+ }
294
+ }
295
+
296
+ export type Greeting = {
297
+ language: string;
298
+ message: string;
299
+ };
300
+
301
+ export const greetings: Greeting[] = [
302
+ { language: 'en', message: 'Hello' },
303
+ { language: 'fr', message: 'Bonjour' },
304
+ { language: 'es', message: 'Hola' },
305
+ { language: 'de', message: 'Hallo' },
306
+ ];
307
+
308
+ export function printGreetings(): void {
309
+ for (const { language, message } of greetings) {
310
+ console.log(\`\${message}, \${language}!\`);
311
+ }
312
+ }`,
313
+ };
314
+
315
+ export const SyntaxHighlighting: StoryObj<typeof CodeTabs> = {
316
+ args: {
317
+ codes: syntaxHighlightingCode,
318
+ defaultValue: 'index.ts',
319
+ syntaxHighlighting: true,
320
+ lang: 'typescript',
321
+ },
322
+ render: (args) => <CodeTabs {...args} />,
323
+ };