arcway 0.1.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 (274) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +711 -0
  3. package/client/env.js +55 -0
  4. package/client/fetcher.js +50 -0
  5. package/client/graphql.js +35 -0
  6. package/client/head.js +140 -0
  7. package/client/hooks/use-api.js +80 -0
  8. package/client/hooks/use-debounce.js +12 -0
  9. package/client/hooks/use-form.js +86 -0
  10. package/client/hooks/use-graphql.js +30 -0
  11. package/client/hooks/use-interval.js +12 -0
  12. package/client/hooks/use-mutation.js +27 -0
  13. package/client/hooks/use-query.js +45 -0
  14. package/client/hooks/web/use-click-outside.js +22 -0
  15. package/client/hooks/web/use-local-storage.js +42 -0
  16. package/client/index.js +62 -0
  17. package/client/page-loader.js +155 -0
  18. package/client/provider.js +53 -0
  19. package/client/query.js +13 -0
  20. package/client/router.jsx +303 -0
  21. package/client/ui/accordion.jsx +65 -0
  22. package/client/ui/accordion.stories.jsx +48 -0
  23. package/client/ui/alert-dialog.jsx +122 -0
  24. package/client/ui/alert-dialog.stories.jsx +44 -0
  25. package/client/ui/alert.jsx +52 -0
  26. package/client/ui/alert.stories.jsx +31 -0
  27. package/client/ui/app-shell.jsx +39 -0
  28. package/client/ui/app-shell.stories.jsx +51 -0
  29. package/client/ui/aspect-ratio.jsx +6 -0
  30. package/client/ui/aspect-ratio.stories.jsx +69 -0
  31. package/client/ui/avatar.jsx +78 -0
  32. package/client/ui/avatar.stories.jsx +62 -0
  33. package/client/ui/badge.jsx +34 -0
  34. package/client/ui/badge.stories.js +32 -0
  35. package/client/ui/breadcrumb.jsx +86 -0
  36. package/client/ui/breadcrumb.stories.jsx +43 -0
  37. package/client/ui/button-group.jsx +58 -0
  38. package/client/ui/button-group.stories.jsx +67 -0
  39. package/client/ui/button.jsx +46 -0
  40. package/client/ui/button.stories.js +72 -0
  41. package/client/ui/calendar.jsx +172 -0
  42. package/client/ui/card.jsx +57 -0
  43. package/client/ui/card.stories.jsx +33 -0
  44. package/client/ui/carousel.jsx +167 -0
  45. package/client/ui/chart.jsx +244 -0
  46. package/client/ui/checkbox.jsx +24 -0
  47. package/client/ui/checkbox.stories.js +33 -0
  48. package/client/ui/collapsible.jsx +12 -0
  49. package/client/ui/collapsible.stories.jsx +42 -0
  50. package/client/ui/combobox.jsx +223 -0
  51. package/client/ui/command.jsx +128 -0
  52. package/client/ui/context-menu.jsx +170 -0
  53. package/client/ui/context-menu.stories.jsx +35 -0
  54. package/client/ui/dialog.jsx +109 -0
  55. package/client/ui/dialog.stories.jsx +37 -0
  56. package/client/ui/direction.jsx +9 -0
  57. package/client/ui/drawer.jsx +87 -0
  58. package/client/ui/dropdown-menu.jsx +172 -0
  59. package/client/ui/dropdown-menu.stories.jsx +34 -0
  60. package/client/ui/empty.jsx +76 -0
  61. package/client/ui/empty.stories.jsx +64 -0
  62. package/client/ui/field.jsx +174 -0
  63. package/client/ui/field.stories.jsx +118 -0
  64. package/client/ui/form.jsx +17 -0
  65. package/client/ui/hooks/use-mobile.js +16 -0
  66. package/client/ui/hover-card.jsx +26 -0
  67. package/client/ui/hover-card.stories.jsx +28 -0
  68. package/client/ui/index.js +649 -0
  69. package/client/ui/input-group.jsx +116 -0
  70. package/client/ui/input-group.stories.jsx +65 -0
  71. package/client/ui/input-otp.jsx +62 -0
  72. package/client/ui/input.jsx +16 -0
  73. package/client/ui/input.stories.js +27 -0
  74. package/client/ui/item.jsx +155 -0
  75. package/client/ui/item.stories.jsx +118 -0
  76. package/client/ui/kbd.jsx +24 -0
  77. package/client/ui/kbd.stories.jsx +32 -0
  78. package/client/ui/label.jsx +16 -0
  79. package/client/ui/label.stories.js +25 -0
  80. package/client/ui/lib/utils.js +6 -0
  81. package/client/ui/main-content.jsx +30 -0
  82. package/client/ui/menubar.jsx +189 -0
  83. package/client/ui/menubar.stories.jsx +43 -0
  84. package/client/ui/native-select.jsx +34 -0
  85. package/client/ui/native-select.stories.jsx +67 -0
  86. package/client/ui/navigation-menu.jsx +120 -0
  87. package/client/ui/navigation-menu.stories.jsx +45 -0
  88. package/client/ui/pagination.jsx +92 -0
  89. package/client/ui/pagination.stories.jsx +52 -0
  90. package/client/ui/panel.jsx +66 -0
  91. package/client/ui/popover.jsx +54 -0
  92. package/client/ui/popover.stories.jsx +27 -0
  93. package/client/ui/progress.jsx +19 -0
  94. package/client/ui/progress.stories.js +34 -0
  95. package/client/ui/radio-group.jsx +33 -0
  96. package/client/ui/radio-group.stories.jsx +49 -0
  97. package/client/ui/resizable.jsx +33 -0
  98. package/client/ui/scroll-area.jsx +41 -0
  99. package/client/ui/scroll-area.stories.jsx +43 -0
  100. package/client/ui/select.jsx +145 -0
  101. package/client/ui/select.stories.jsx +80 -0
  102. package/client/ui/separator.jsx +18 -0
  103. package/client/ui/separator.stories.jsx +37 -0
  104. package/client/ui/sheet.jsx +95 -0
  105. package/client/ui/sheet.stories.jsx +56 -0
  106. package/client/ui/sidebar.jsx +544 -0
  107. package/client/ui/skeleton.jsx +8 -0
  108. package/client/ui/skeleton.stories.js +23 -0
  109. package/client/ui/slider.jsx +41 -0
  110. package/client/ui/slider.stories.js +31 -0
  111. package/client/ui/sonner.jsx +37 -0
  112. package/client/ui/spinner.jsx +14 -0
  113. package/client/ui/spinner.stories.js +16 -0
  114. package/client/ui/style-mira.css +1316 -0
  115. package/client/ui/switch.jsx +22 -0
  116. package/client/ui/switch.stories.js +44 -0
  117. package/client/ui/table.jsx +33 -0
  118. package/client/ui/table.stories.jsx +42 -0
  119. package/client/ui/tabs.jsx +63 -0
  120. package/client/ui/tabs.stories.jsx +45 -0
  121. package/client/ui/textarea.jsx +15 -0
  122. package/client/ui/textarea.stories.js +33 -0
  123. package/client/ui/theme.css +459 -0
  124. package/client/ui/toggle-group.jsx +62 -0
  125. package/client/ui/toggle-group.stories.jsx +68 -0
  126. package/client/ui/toggle.jsx +34 -0
  127. package/client/ui/toggle.stories.js +46 -0
  128. package/client/ui/tooltip.jsx +37 -0
  129. package/client/ui/tooltip.stories.jsx +32 -0
  130. package/client/ui/use-transition.js +35 -0
  131. package/client/ws.js +132 -0
  132. package/package.json +134 -0
  133. package/server/bin/cli.js +42 -0
  134. package/server/bin/commands/build.js +23 -0
  135. package/server/bin/commands/dev.js +57 -0
  136. package/server/bin/commands/docs.js +30 -0
  137. package/server/bin/commands/graphql-schema.js +32 -0
  138. package/server/bin/commands/lint.js +35 -0
  139. package/server/bin/commands/mcp.js +26 -0
  140. package/server/bin/commands/migrate.js +82 -0
  141. package/server/bin/commands/schema.js +41 -0
  142. package/server/bin/commands/seed.js +36 -0
  143. package/server/bin/commands/start.js +31 -0
  144. package/server/bin/commands/test.js +20 -0
  145. package/server/bin/solo.js +4 -0
  146. package/server/boot/index.js +150 -0
  147. package/server/boot.js +2 -0
  148. package/server/build.js +23 -0
  149. package/server/cache/drivers/memory.js +23 -0
  150. package/server/cache/drivers/redis.js +28 -0
  151. package/server/cache/index.js +69 -0
  152. package/server/config/loader.js +89 -0
  153. package/server/config/modules/api.js +17 -0
  154. package/server/config/modules/build.js +9 -0
  155. package/server/config/modules/cache.js +10 -0
  156. package/server/config/modules/database.js +29 -0
  157. package/server/config/modules/events.js +15 -0
  158. package/server/config/modules/files.js +15 -0
  159. package/server/config/modules/jobs.js +20 -0
  160. package/server/config/modules/logger.js +9 -0
  161. package/server/config/modules/mail.js +11 -0
  162. package/server/config/modules/mcp.js +9 -0
  163. package/server/config/modules/pages.js +20 -0
  164. package/server/config/modules/queue.js +10 -0
  165. package/server/config/modules/redis.js +9 -0
  166. package/server/config/modules/server.js +30 -0
  167. package/server/config/modules/session.js +9 -0
  168. package/server/config/modules/websocket.js +11 -0
  169. package/server/constants.js +67 -0
  170. package/server/context.js +15 -0
  171. package/server/db/index.js +87 -0
  172. package/server/db/schema/drivers/mysql.js +28 -0
  173. package/server/db/schema/drivers/pg.js +34 -0
  174. package/server/db/schema/drivers/sqlite.js +22 -0
  175. package/server/db/schema/index.js +78 -0
  176. package/server/db/seeds.js +22 -0
  177. package/server/discovery.js +67 -0
  178. package/server/docs/openapi.js +153 -0
  179. package/server/env.js +17 -0
  180. package/server/events/drivers/memory.js +45 -0
  181. package/server/events/drivers/redis.js +64 -0
  182. package/server/events/handler.js +67 -0
  183. package/server/events/index.js +35 -0
  184. package/server/events/pattern.js +5 -0
  185. package/server/files/drivers/local.js +83 -0
  186. package/server/files/drivers/s3.js +113 -0
  187. package/server/files/index.js +57 -0
  188. package/server/filewatcher/index.js +156 -0
  189. package/server/glob.js +6 -0
  190. package/server/graphql/discovery.js +70 -0
  191. package/server/graphql/handler.js +41 -0
  192. package/server/graphql/index.js +13 -0
  193. package/server/graphql/loaders.js +19 -0
  194. package/server/graphql/merge.js +48 -0
  195. package/server/graphql/subscriptions.js +43 -0
  196. package/server/health.js +34 -0
  197. package/server/helpers.js +9 -0
  198. package/server/index.js +55 -0
  199. package/server/internals.js +139 -0
  200. package/server/jobs/cron.js +10 -0
  201. package/server/jobs/drivers/knex-queue.js +207 -0
  202. package/server/jobs/drivers/lease.js +148 -0
  203. package/server/jobs/drivers/memory-queue.js +134 -0
  204. package/server/jobs/queue.js +27 -0
  205. package/server/jobs/runner.js +197 -0
  206. package/server/jobs/throughput.js +63 -0
  207. package/server/lib/vault/encrypt.js +40 -0
  208. package/server/lib/vault/ids.js +9 -0
  209. package/server/lib/vault/index.js +14 -0
  210. package/server/lib/vault/jwt.js +55 -0
  211. package/server/lib/vault/password.js +10 -0
  212. package/server/lint/boundaries.js +77 -0
  213. package/server/logger/index.js +130 -0
  214. package/server/mail/drivers/console.js +31 -0
  215. package/server/mail/drivers/smtp.js +34 -0
  216. package/server/mail/imap.js +105 -0
  217. package/server/mail/inbound-store.js +58 -0
  218. package/server/mail/inbound.js +79 -0
  219. package/server/mail/index.js +112 -0
  220. package/server/mcp/debug-api.js +137 -0
  221. package/server/mcp/helpers.js +30 -0
  222. package/server/mcp/index.js +77 -0
  223. package/server/mcp/runtime.js +7 -0
  224. package/server/mcp/server.js +19 -0
  225. package/server/mcp/tools/debugging.js +133 -0
  226. package/server/mcp/tools/introspection.js +87 -0
  227. package/server/middlewares/cors.js +30 -0
  228. package/server/middlewares/index.js +3 -0
  229. package/server/middlewares/require-session.js +15 -0
  230. package/server/module-loader.js +9 -0
  231. package/server/pages/build-client.js +187 -0
  232. package/server/pages/build-css.js +47 -0
  233. package/server/pages/build-manifest.js +55 -0
  234. package/server/pages/build-plugins.js +75 -0
  235. package/server/pages/build-server.js +115 -0
  236. package/server/pages/build.js +116 -0
  237. package/server/pages/discovery.js +120 -0
  238. package/server/pages/fonts.js +128 -0
  239. package/server/pages/handler.js +276 -0
  240. package/server/pages/hmr.js +176 -0
  241. package/server/pages/pages-router.js +78 -0
  242. package/server/pages/ssr.js +276 -0
  243. package/server/pages/static.js +92 -0
  244. package/server/pages/watcher.js +90 -0
  245. package/server/queue/drivers/knex.js +67 -0
  246. package/server/queue/drivers/redis.js +91 -0
  247. package/server/queue/index.js +61 -0
  248. package/server/rate-limit/consume.js +21 -0
  249. package/server/rate-limit/drivers/memory.js +24 -0
  250. package/server/rate-limit/drivers/redis.js +32 -0
  251. package/server/rate-limit/index.js +33 -0
  252. package/server/redis/index.js +67 -0
  253. package/server/ring-buffer.js +44 -0
  254. package/server/route.js +4 -0
  255. package/server/router/api-router.js +317 -0
  256. package/server/router/cors.js +31 -0
  257. package/server/router/middleware.js +91 -0
  258. package/server/router/routes.js +132 -0
  259. package/server/server.js +35 -0
  260. package/server/session/helpers.js +21 -0
  261. package/server/session/index.js +89 -0
  262. package/server/static/index.js +36 -0
  263. package/server/system-jobs/index.js +50 -0
  264. package/server/system-routes/index.js +84 -0
  265. package/server/testing/index.js +263 -0
  266. package/server/validation.js +41 -0
  267. package/server/watcher.js +34 -0
  268. package/server/web-server.js +231 -0
  269. package/server/ws/discovery.js +54 -0
  270. package/server/ws/index.js +14 -0
  271. package/server/ws/realtime.js +318 -0
  272. package/server/ws/registry.js +17 -0
  273. package/server/ws/server.js +152 -0
  274. package/server/ws/ws-router.js +335 -0
@@ -0,0 +1,116 @@
1
+ import React from 'react';
2
+ import { cva } from 'class-variance-authority';
3
+ import { cn } from './lib/utils.js';
4
+ import { Button } from './button.jsx';
5
+ import { Input } from './input.jsx';
6
+ import { Textarea } from './textarea.jsx';
7
+ function InputGroup({ className, ...props }) {
8
+ return (
9
+ <div
10
+ data-slot="input-group"
11
+ role="group"
12
+ className={cn(
13
+ 'group/input-group cn-input-group relative flex w-full min-w-0 items-center outline-none has-[>textarea]:h-auto',
14
+ className,
15
+ )}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+ const inputGroupAddonVariants = cva(
21
+ 'cn-input-group-addon flex cursor-text items-center justify-center select-none',
22
+ {
23
+ variants: {
24
+ align: {
25
+ 'inline-start': 'cn-input-group-addon-align-inline-start order-first',
26
+ 'inline-end': 'cn-input-group-addon-align-inline-end order-last',
27
+ 'block-start': 'cn-input-group-addon-align-block-start order-first w-full justify-start',
28
+ 'block-end': 'cn-input-group-addon-align-block-end order-last w-full justify-start',
29
+ },
30
+ },
31
+ defaultVariants: {
32
+ align: 'inline-start',
33
+ },
34
+ },
35
+ );
36
+ function InputGroupAddon({ className, align = 'inline-start', ...props }) {
37
+ return (
38
+ <div
39
+ role="group"
40
+ data-slot="input-group-addon"
41
+ data-align={align}
42
+ className={cn(inputGroupAddonVariants({ align }), className)}
43
+ onClick={(e) => {
44
+ if (e.target.closest('button')) {
45
+ return;
46
+ }
47
+ e.currentTarget.parentElement?.querySelector('input')?.focus();
48
+ }}
49
+ {...props}
50
+ />
51
+ );
52
+ }
53
+ const inputGroupButtonVariants = cva('cn-input-group-button shadow-none flex items-center', {
54
+ variants: {
55
+ size: {
56
+ xs: 'cn-input-group-button-size-xs',
57
+ sm: 'cn-input-group-button-size-sm',
58
+ 'icon-xs': 'cn-input-group-button-size-icon-xs',
59
+ 'icon-sm': 'cn-input-group-button-size-icon-sm',
60
+ },
61
+ },
62
+ defaultVariants: {
63
+ size: 'xs',
64
+ },
65
+ });
66
+ function InputGroupButton({
67
+ className,
68
+ type = 'button',
69
+ variant = 'ghost',
70
+ size = 'xs',
71
+ ...props
72
+ }) {
73
+ return (
74
+ <Button
75
+ type={type}
76
+ data-size={size}
77
+ variant={variant}
78
+ className={cn(inputGroupButtonVariants({ size }), className)}
79
+ {...props}
80
+ />
81
+ );
82
+ }
83
+ function InputGroupText({ className, ...props }) {
84
+ return (
85
+ <span
86
+ className={cn('cn-input-group-text flex items-center [&_svg]:pointer-events-none', className)}
87
+ {...props}
88
+ />
89
+ );
90
+ }
91
+ function InputGroupInput({ className, ...props }) {
92
+ return (
93
+ <Input
94
+ data-slot="input-group-control"
95
+ className={cn('cn-input-group-input flex-1', className)}
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+ function InputGroupTextarea({ className, ...props }) {
101
+ return (
102
+ <Textarea
103
+ data-slot="input-group-control"
104
+ className={cn('cn-input-group-textarea flex-1 resize-none', className)}
105
+ {...props}
106
+ />
107
+ );
108
+ }
109
+ export {
110
+ InputGroup,
111
+ InputGroupAddon,
112
+ InputGroupButton,
113
+ InputGroupInput,
114
+ InputGroupText,
115
+ InputGroupTextarea,
116
+ };
@@ -0,0 +1,65 @@
1
+ import React from 'react';
2
+ import { expect, within } from 'storybook/test';
3
+ import { SearchIcon, MailIcon } from 'lucide-react';
4
+ import {
5
+ InputGroup,
6
+ InputGroupAddon,
7
+ InputGroupButton,
8
+ InputGroupText,
9
+ InputGroupInput,
10
+ } from './input-group.jsx';
11
+ const meta = {
12
+ title: 'UI/InputGroup',
13
+ component: InputGroup,
14
+ };
15
+ var stdin_default = meta;
16
+ const WithAddon = {
17
+ render: () => (
18
+ <InputGroup>
19
+ <InputGroupAddon align="inline-start">
20
+ <InputGroupText>
21
+ <SearchIcon className="size-4" />
22
+ </InputGroupText>
23
+ </InputGroupAddon>
24
+ <InputGroupInput placeholder="Search..." />
25
+ </InputGroup>
26
+ ),
27
+ play: async ({ canvasElement }) => {
28
+ const canvas = within(canvasElement);
29
+ const group = canvasElement.querySelector('[data-slot="input-group"]');
30
+ await expect(group).toBeInTheDocument();
31
+ await expect(canvas.getByPlaceholderText('Search...')).toBeInTheDocument();
32
+ },
33
+ };
34
+ const WithButton = {
35
+ render: () => (
36
+ <InputGroup>
37
+ <InputGroupInput placeholder="Email" />
38
+ <InputGroupAddon align="inline-end">
39
+ <InputGroupButton>
40
+ <MailIcon className="size-4" />
41
+ </InputGroupButton>
42
+ </InputGroupAddon>
43
+ </InputGroup>
44
+ ),
45
+ play: async ({ canvasElement }) => {
46
+ const canvas = within(canvasElement);
47
+ await expect(canvas.getByRole('button')).toBeInTheDocument();
48
+ await expect(canvas.getByPlaceholderText('Email')).toBeInTheDocument();
49
+ },
50
+ };
51
+ const WithTextAddon = {
52
+ render: () => (
53
+ <InputGroup>
54
+ <InputGroupAddon align="inline-start">
55
+ <InputGroupText>https://</InputGroupText>
56
+ </InputGroupAddon>
57
+ <InputGroupInput placeholder="example.com" />
58
+ </InputGroup>
59
+ ),
60
+ play: async ({ canvasElement }) => {
61
+ const canvas = within(canvasElement);
62
+ await expect(canvas.getByText('https://')).toBeInTheDocument();
63
+ },
64
+ };
65
+ export { WithAddon, WithButton, WithTextAddon, stdin_default as default };
@@ -0,0 +1,62 @@
1
+ import * as React from 'react';
2
+ import { OTPInput, OTPInputContext } from 'input-otp';
3
+ import { cn } from './lib/utils.js';
4
+ import { MinusIcon } from 'lucide-react';
5
+ function InputOTP({ className, containerClassName, ...props }) {
6
+ return (
7
+ <OTPInput
8
+ data-slot="input-otp"
9
+ containerClassName={cn(
10
+ 'cn-input-otp flex items-center has-disabled:opacity-50',
11
+ containerClassName,
12
+ )}
13
+ spellCheck={false}
14
+ className={cn('cn-input-otp-input disabled:cursor-not-allowed', className)}
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+ function InputOTPGroup({ className, ...props }) {
20
+ return (
21
+ <div
22
+ data-slot="input-otp-group"
23
+ className={cn('cn-input-otp-group flex items-center', className)}
24
+ {...props}
25
+ />
26
+ );
27
+ }
28
+ function InputOTPSlot({ index, className, ...props }) {
29
+ const inputOTPContext = React.useContext(OTPInputContext);
30
+ const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
31
+ return (
32
+ <div
33
+ data-slot="input-otp-slot"
34
+ data-active={isActive}
35
+ className={cn(
36
+ 'cn-input-otp-slot relative flex items-center justify-center data-[active=true]:z-10',
37
+ className,
38
+ )}
39
+ {...props}
40
+ >
41
+ {char}
42
+ {hasFakeCaret && (
43
+ <div className="cn-input-otp-caret pointer-events-none absolute inset-0 flex items-center justify-center">
44
+ <div className="cn-input-otp-caret-line" />
45
+ </div>
46
+ )}
47
+ </div>
48
+ );
49
+ }
50
+ function InputOTPSeparator({ ...props }) {
51
+ return (
52
+ <div
53
+ data-slot="input-otp-separator"
54
+ className="cn-input-otp-separator flex items-center"
55
+ role="separator"
56
+ {...props}
57
+ >
58
+ <MinusIcon />
59
+ </div>
60
+ );
61
+ }
62
+ export { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot };
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { cn } from './lib/utils.js';
3
+ function Input({ className, type, ...props }) {
4
+ return (
5
+ <input
6
+ type={type}
7
+ data-slot="input"
8
+ className={cn(
9
+ 'cn-input file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
10
+ className,
11
+ )}
12
+ {...props}
13
+ />
14
+ );
15
+ }
16
+ export { Input };
@@ -0,0 +1,27 @@
1
+ import { expect, within } from 'storybook/test';
2
+ import { Input } from './input.jsx';
3
+ const meta = {
4
+ title: 'UI/Input',
5
+ component: Input,
6
+ };
7
+ var stdin_default = meta;
8
+ const Default = {
9
+ args: { placeholder: 'Enter text...' },
10
+ play: async ({ canvasElement }) => {
11
+ const canvas = within(canvasElement);
12
+ const input = canvas.getByPlaceholderText('Enter text...');
13
+ await expect(input).toBeInTheDocument();
14
+ await expect(input.dataset.slot).toBe('input');
15
+ },
16
+ };
17
+ const WithType = {
18
+ args: { type: 'email', placeholder: 'Email address' },
19
+ };
20
+ const Disabled = {
21
+ args: { placeholder: 'Disabled', disabled: true },
22
+ play: async ({ canvasElement }) => {
23
+ const canvas = within(canvasElement);
24
+ await expect(canvas.getByPlaceholderText('Disabled')).toBeDisabled();
25
+ },
26
+ };
27
+ export { Default, Disabled, WithType, stdin_default as default };
@@ -0,0 +1,155 @@
1
+ import React from 'react';
2
+ import { cva } from 'class-variance-authority';
3
+ import { Slot } from 'radix-ui';
4
+ import { cn } from './lib/utils.js';
5
+ import { Separator } from './separator.jsx';
6
+ function ItemGroup({ className, ...props }) {
7
+ return (
8
+ <div
9
+ role="list"
10
+ data-slot="item-group"
11
+ className={cn('cn-item-group group/item-group flex w-full flex-col', className)}
12
+ {...props}
13
+ />
14
+ );
15
+ }
16
+ function ItemSeparator({ className, ...props }) {
17
+ return (
18
+ <Separator
19
+ data-slot="item-separator"
20
+ orientation="horizontal"
21
+ className={cn('cn-item-separator', className)}
22
+ {...props}
23
+ />
24
+ );
25
+ }
26
+ const itemVariants = cva(
27
+ 'cn-item w-full group/item focus-visible:border-ring focus-visible:ring-ring/50 flex items-center flex-wrap outline-none transition-colors duration-100 focus-visible:ring-[3px] [a]:transition-colors',
28
+ {
29
+ variants: {
30
+ variant: {
31
+ default: 'cn-item-variant-default',
32
+ outline: 'cn-item-variant-outline',
33
+ muted: 'cn-item-variant-muted',
34
+ },
35
+ size: {
36
+ default: 'cn-item-size-default',
37
+ sm: 'cn-item-size-sm',
38
+ xs: 'cn-item-size-xs',
39
+ },
40
+ },
41
+ defaultVariants: {
42
+ variant: 'default',
43
+ size: 'default',
44
+ },
45
+ },
46
+ );
47
+ function Item({ className, variant = 'default', size = 'default', asChild = false, ...props }) {
48
+ const Comp = asChild ? Slot.Root : 'div';
49
+ return (
50
+ <Comp
51
+ data-slot="item"
52
+ data-variant={variant}
53
+ data-size={size}
54
+ className={cn(itemVariants({ variant, size, className }))}
55
+ {...props}
56
+ />
57
+ );
58
+ }
59
+ const itemMediaVariants = cva(
60
+ 'cn-item-media flex shrink-0 items-center justify-center [&_svg]:pointer-events-none',
61
+ {
62
+ variants: {
63
+ variant: {
64
+ default: 'cn-item-media-variant-default',
65
+ icon: 'cn-item-media-variant-icon',
66
+ image: 'cn-item-media-variant-image',
67
+ },
68
+ },
69
+ defaultVariants: {
70
+ variant: 'default',
71
+ },
72
+ },
73
+ );
74
+ function ItemMedia({ className, variant = 'default', ...props }) {
75
+ return (
76
+ <div
77
+ data-slot="item-media"
78
+ data-variant={variant}
79
+ className={cn(itemMediaVariants({ variant, className }))}
80
+ {...props}
81
+ />
82
+ );
83
+ }
84
+ function ItemContent({ className, ...props }) {
85
+ return (
86
+ <div
87
+ data-slot="item-content"
88
+ className={cn(
89
+ 'cn-item-content flex flex-1 flex-col [&+[data-slot=item-content]]:flex-none',
90
+ className,
91
+ )}
92
+ {...props}
93
+ />
94
+ );
95
+ }
96
+ function ItemTitle({ className, ...props }) {
97
+ return (
98
+ <div
99
+ data-slot="item-title"
100
+ className={cn('cn-item-title line-clamp-1 flex w-fit items-center', className)}
101
+ {...props}
102
+ />
103
+ );
104
+ }
105
+ function ItemDescription({ className, ...props }) {
106
+ return (
107
+ <p
108
+ data-slot="item-description"
109
+ className={cn(
110
+ 'cn-item-description [&>a:hover]:text-primary line-clamp-2 font-normal [&>a]:underline [&>a]:underline-offset-4',
111
+ className,
112
+ )}
113
+ {...props}
114
+ />
115
+ );
116
+ }
117
+ function ItemActions({ className, ...props }) {
118
+ return (
119
+ <div
120
+ data-slot="item-actions"
121
+ className={cn('cn-item-actions flex items-center', className)}
122
+ {...props}
123
+ />
124
+ );
125
+ }
126
+ function ItemHeader({ className, ...props }) {
127
+ return (
128
+ <div
129
+ data-slot="item-header"
130
+ className={cn('cn-item-header flex basis-full items-center justify-between', className)}
131
+ {...props}
132
+ />
133
+ );
134
+ }
135
+ function ItemFooter({ className, ...props }) {
136
+ return (
137
+ <div
138
+ data-slot="item-footer"
139
+ className={cn('cn-item-footer flex basis-full items-center justify-between', className)}
140
+ {...props}
141
+ />
142
+ );
143
+ }
144
+ export {
145
+ Item,
146
+ ItemActions,
147
+ ItemContent,
148
+ ItemDescription,
149
+ ItemFooter,
150
+ ItemGroup,
151
+ ItemHeader,
152
+ ItemMedia,
153
+ ItemSeparator,
154
+ ItemTitle,
155
+ };
@@ -0,0 +1,118 @@
1
+ import React from 'react';
2
+ import { expect, within } from 'storybook/test';
3
+ import { UserIcon } from 'lucide-react';
4
+ import {
5
+ Item,
6
+ ItemMedia,
7
+ ItemContent,
8
+ ItemTitle,
9
+ ItemDescription,
10
+ ItemActions,
11
+ ItemGroup,
12
+ ItemSeparator,
13
+ } from './item.jsx';
14
+ import { Button } from './button.jsx';
15
+ const meta = {
16
+ title: 'UI/Item',
17
+ component: Item,
18
+ };
19
+ var stdin_default = meta;
20
+ const Default = {
21
+ render: () => (
22
+ <Item>
23
+ <ItemMedia variant="icon">
24
+ <UserIcon className="size-4" />
25
+ </ItemMedia>
26
+ <ItemContent>
27
+ <ItemTitle>John Doe</ItemTitle>
28
+ <ItemDescription>Software Engineer</ItemDescription>
29
+ </ItemContent>
30
+ </Item>
31
+ ),
32
+ play: async ({ canvasElement }) => {
33
+ const canvas = within(canvasElement);
34
+ const item = canvasElement.querySelector('[data-slot="item"]');
35
+ await expect(item).toBeInTheDocument();
36
+ await expect(canvas.getByText('John Doe')).toBeInTheDocument();
37
+ await expect(canvas.getByText('Software Engineer')).toBeInTheDocument();
38
+ },
39
+ };
40
+ const WithActions = {
41
+ render: () => (
42
+ <Item>
43
+ <ItemContent>
44
+ <ItemTitle>Task Item</ItemTitle>
45
+ <ItemDescription>Complete this task</ItemDescription>
46
+ </ItemContent>
47
+ <ItemActions>
48
+ <Button size="sm" variant="outline">
49
+ Edit
50
+ </Button>
51
+ </ItemActions>
52
+ </Item>
53
+ ),
54
+ play: async ({ canvasElement }) => {
55
+ const canvas = within(canvasElement);
56
+ await expect(canvas.getByRole('button', { name: 'Edit' })).toBeInTheDocument();
57
+ },
58
+ };
59
+ const GroupWithSeparators = {
60
+ render: () => (
61
+ <ItemGroup>
62
+ <Item>
63
+ <ItemContent>
64
+ <ItemTitle>Item 1</ItemTitle>
65
+ </ItemContent>
66
+ </Item>
67
+ <ItemSeparator />
68
+ <Item>
69
+ <ItemContent>
70
+ <ItemTitle>Item 2</ItemTitle>
71
+ </ItemContent>
72
+ </Item>
73
+ <ItemSeparator />
74
+ <Item>
75
+ <ItemContent>
76
+ <ItemTitle>Item 3</ItemTitle>
77
+ </ItemContent>
78
+ </Item>
79
+ </ItemGroup>
80
+ ),
81
+ play: async ({ canvasElement }) => {
82
+ const canvas = within(canvasElement);
83
+ const list = canvas.getByRole('list');
84
+ await expect(list).toBeInTheDocument();
85
+ await expect(canvas.getByText('Item 1')).toBeInTheDocument();
86
+ await expect(canvas.getByText('Item 2')).toBeInTheDocument();
87
+ await expect(canvas.getByText('Item 3')).toBeInTheDocument();
88
+ },
89
+ };
90
+ const Variants = {
91
+ render: () => (
92
+ <ItemGroup>
93
+ <Item variant="default">
94
+ <ItemContent>
95
+ <ItemTitle>Default</ItemTitle>
96
+ </ItemContent>
97
+ </Item>
98
+ <Item variant="outline">
99
+ <ItemContent>
100
+ <ItemTitle>Outline</ItemTitle>
101
+ </ItemContent>
102
+ </Item>
103
+ <Item variant="muted">
104
+ <ItemContent>
105
+ <ItemTitle>Muted</ItemTitle>
106
+ </ItemContent>
107
+ </Item>
108
+ </ItemGroup>
109
+ ),
110
+ play: async ({ canvasElement }) => {
111
+ const items = canvasElement.querySelectorAll('[data-slot="item"]');
112
+ await expect(items).toHaveLength(3);
113
+ await expect(items[0].getAttribute('data-variant')).toBe('default');
114
+ await expect(items[1].getAttribute('data-variant')).toBe('outline');
115
+ await expect(items[2].getAttribute('data-variant')).toBe('muted');
116
+ },
117
+ };
118
+ export { Default, GroupWithSeparators, Variants, WithActions, stdin_default as default };
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { cn } from './lib/utils.js';
3
+ function Kbd({ className, ...props }) {
4
+ return (
5
+ <kbd
6
+ data-slot="kbd"
7
+ className={cn(
8
+ 'cn-kbd pointer-events-none inline-flex items-center justify-center select-none',
9
+ className,
10
+ )}
11
+ {...props}
12
+ />
13
+ );
14
+ }
15
+ function KbdGroup({ className, ...props }) {
16
+ return (
17
+ <kbd
18
+ data-slot="kbd-group"
19
+ className={cn('cn-kbd-group inline-flex items-center', className)}
20
+ {...props}
21
+ />
22
+ );
23
+ }
24
+ export { Kbd, KbdGroup };
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import { expect, within } from 'storybook/test';
3
+ import { Kbd, KbdGroup } from './kbd.jsx';
4
+ const meta = {
5
+ title: 'UI/Kbd',
6
+ component: Kbd,
7
+ };
8
+ var stdin_default = meta;
9
+ const Default = {
10
+ args: { children: 'K' },
11
+ play: async ({ canvasElement }) => {
12
+ const canvas = within(canvasElement);
13
+ const kbd = canvas.getByText('K');
14
+ await expect(kbd).toBeInTheDocument();
15
+ await expect(kbd.dataset.slot).toBe('kbd');
16
+ },
17
+ };
18
+ const Group = {
19
+ render: () => (
20
+ <KbdGroup>
21
+ <Kbd>Ctrl</Kbd>
22
+ <Kbd>K</Kbd>
23
+ </KbdGroup>
24
+ ),
25
+ play: async ({ canvasElement }) => {
26
+ const group = canvasElement.querySelector('[data-slot="kbd-group"]');
27
+ await expect(group).toBeInTheDocument();
28
+ const keys = canvasElement.querySelectorAll('[data-slot="kbd"]');
29
+ await expect(keys.length).toBe(2);
30
+ },
31
+ };
32
+ export { Default, Group, stdin_default as default };
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { Label as LabelPrimitive } from 'radix-ui';
3
+ import { cn } from './lib/utils.js';
4
+ function Label({ className, ...props }) {
5
+ return (
6
+ <LabelPrimitive.Root
7
+ data-slot="label"
8
+ className={cn(
9
+ 'cn-label flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed',
10
+ className,
11
+ )}
12
+ {...props}
13
+ />
14
+ );
15
+ }
16
+ export { Label };
@@ -0,0 +1,25 @@
1
+ import { expect, within } from 'storybook/test';
2
+ import { Label } from './label.jsx';
3
+ const meta = {
4
+ title: 'UI/Label',
5
+ component: Label,
6
+ };
7
+ var stdin_default = meta;
8
+ const Default = {
9
+ args: { children: 'Email address' },
10
+ play: async ({ canvasElement }) => {
11
+ const canvas = within(canvasElement);
12
+ const label = canvas.getByText('Email address');
13
+ await expect(label).toBeInTheDocument();
14
+ await expect(label.dataset.slot).toBe('label');
15
+ },
16
+ };
17
+ const WithHtmlFor = {
18
+ args: { children: 'Username', htmlFor: 'username' },
19
+ play: async ({ canvasElement }) => {
20
+ const canvas = within(canvasElement);
21
+ const label = canvas.getByText('Username');
22
+ await expect(label).toHaveAttribute('for', 'username');
23
+ },
24
+ };
25
+ export { Default, WithHtmlFor, stdin_default as default };
@@ -0,0 +1,6 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
6
+ export { cn };