create-nexst-monorepo 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 (303) hide show
  1. package/dist/index.js +1727 -0
  2. package/package.json +37 -0
  3. package/template/.agents/plugins/marketplace.json +21 -0
  4. package/template/.claude/settings.json +6 -0
  5. package/template/.claude/skills/frontend-design/LICENSE.txt +177 -0
  6. package/template/.claude/skills/frontend-design/SKILL.md +154 -0
  7. package/template/.claude/skills/theme-factory/SKILL.md +95 -0
  8. package/template/.claude/skills/theme-factory/references/schema.md +104 -0
  9. package/template/.claude/skills/theme-factory/scripts/theme.mjs +261 -0
  10. package/template/.dockerignore +43 -0
  11. package/template/.editorconfig +10 -0
  12. package/template/.github/workflows/ci.yaml +289 -0
  13. package/template/.github/workflows/publish-app.yaml +66 -0
  14. package/template/.github/workflows/publish.yaml +148 -0
  15. package/template/.github/workflows/snyk.yaml +34 -0
  16. package/template/.vscode/settings.json +8 -0
  17. package/template/AGENTS.md +42 -0
  18. package/template/Dockerfile.base +9 -0
  19. package/template/README.md +54 -0
  20. package/template/_gitignore +10 -0
  21. package/template/apps/api/.env.example +32 -0
  22. package/template/apps/api/AGENTS.md +44 -0
  23. package/template/apps/api/Dockerfile +69 -0
  24. package/template/apps/api/_gitignore +8 -0
  25. package/template/apps/api/knip.config.ts +22 -0
  26. package/template/apps/api/kustomize/base/application.properties +16 -0
  27. package/template/apps/api/kustomize/base/deployment.yaml +94 -0
  28. package/template/apps/api/kustomize/base/kustomization.yaml +17 -0
  29. package/template/apps/api/kustomize/base/service.yaml +10 -0
  30. package/template/apps/api/kustomize/overlays/prod/kustomization.yaml +17 -0
  31. package/template/apps/api/kustomize/overlays/staging/application.properties +1 -0
  32. package/template/apps/api/kustomize/overlays/staging/kustomization.yaml +21 -0
  33. package/template/apps/api/kustomize/secret.keys +14 -0
  34. package/template/apps/api/oxfmt.config.ts +8 -0
  35. package/template/apps/api/oxlint.config.ts +107 -0
  36. package/template/apps/api/package.json +79 -0
  37. package/template/apps/api/prisma/migrations/20260927000000_init/migration.sql +113 -0
  38. package/template/apps/api/prisma/migrations/migration_lock.toml +3 -0
  39. package/template/apps/api/prisma/schema.prisma +113 -0
  40. package/template/apps/api/prisma.config.ts +7 -0
  41. package/template/apps/api/scripts/auth.cli.ts +19 -0
  42. package/template/apps/api/scripts/seal-secret.sh +171 -0
  43. package/template/apps/api/sonar-project.properties +9 -0
  44. package/template/apps/api/src/app-info/app-info.dto.ts +10 -0
  45. package/template/apps/api/src/app-info/app-info.module.ts +7 -0
  46. package/template/apps/api/src/app-info/app-info.resolver.spec.ts +14 -0
  47. package/template/apps/api/src/app-info/app-info.resolver.ts +21 -0
  48. package/template/apps/api/src/app.module.spec.ts +93 -0
  49. package/template/apps/api/src/app.module.ts +77 -0
  50. package/template/apps/api/src/auth/auth.guard.spec.ts +90 -0
  51. package/template/apps/api/src/auth/auth.guard.ts +97 -0
  52. package/template/apps/api/src/auth/auth.ts +129 -0
  53. package/template/apps/api/src/auth/decorator/allow-anonymous.decorator.ts +17 -0
  54. package/template/apps/api/src/auth/decorator/is-email-verified.decorator.spec.ts +41 -0
  55. package/template/apps/api/src/auth/decorator/is-email-verified.decorator.ts +20 -0
  56. package/template/apps/api/src/auth/decorator/roles.decorator.ts +27 -0
  57. package/template/apps/api/src/auth/testing/make-session.ts +31 -0
  58. package/template/apps/api/src/auth/types/auth.types.ts +27 -0
  59. package/template/apps/api/src/bootstrap/register-graphql-enums.ts +12 -0
  60. package/template/apps/api/src/bootstrap/security.ts +24 -0
  61. package/template/apps/api/src/bootstrap/swagger.ts +28 -0
  62. package/template/apps/api/src/config/app.config.spec.ts +22 -0
  63. package/template/apps/api/src/config/app.config.ts +122 -0
  64. package/template/apps/api/src/config/graphql-depth-limit.ts +103 -0
  65. package/template/apps/api/src/config/graphql.config.ts +77 -0
  66. package/template/apps/api/src/config/resources/app.yaml +29 -0
  67. package/template/apps/api/src/errors/app-parse-uuid.pipe.ts +27 -0
  68. package/template/apps/api/src/errors/app.exception.ts +47 -0
  69. package/template/apps/api/src/errors/error-code.enum.ts +54 -0
  70. package/template/apps/api/src/errors/http-status-fallback-code.util.ts +27 -0
  71. package/template/apps/api/src/errors/validation-exception.factory.ts +43 -0
  72. package/template/apps/api/src/health/health.controller.spec.ts +82 -0
  73. package/template/apps/api/src/health/health.controller.ts +57 -0
  74. package/template/apps/api/src/health/health.module.ts +12 -0
  75. package/template/apps/api/src/logging/context-logger.ts +18 -0
  76. package/template/apps/api/src/logging/logger.config.ts +23 -0
  77. package/template/apps/api/src/logging/logging.module.ts +22 -0
  78. package/template/apps/api/src/mail/mail.module.ts +12 -0
  79. package/template/apps/api/src/mail/mail.service.spec.ts +47 -0
  80. package/template/apps/api/src/mail/mail.service.ts +123 -0
  81. package/template/apps/api/src/main.ts +41 -0
  82. package/template/apps/api/src/prisma/prisma.module.ts +12 -0
  83. package/template/apps/api/src/prisma/prisma.service.spec.ts +136 -0
  84. package/template/apps/api/src/prisma/prisma.service.ts +138 -0
  85. package/template/apps/api/src/queue/dto/enqueue-example.input.ts +11 -0
  86. package/template/apps/api/src/queue/example-queue.resolver.ts +14 -0
  87. package/template/apps/api/src/queue/example-queue.service.spec.ts +21 -0
  88. package/template/apps/api/src/queue/example-queue.service.ts +32 -0
  89. package/template/apps/api/src/queue/queue.module.ts +27 -0
  90. package/template/apps/api/src/r2/r2.module.ts +12 -0
  91. package/template/apps/api/src/r2/r2.service.spec.ts +117 -0
  92. package/template/apps/api/src/r2/r2.service.ts +131 -0
  93. package/template/apps/api/src/user/dto/user.dto.ts +26 -0
  94. package/template/apps/api/src/user/user.module.ts +10 -0
  95. package/template/apps/api/src/user/user.resolver.spec.ts +18 -0
  96. package/template/apps/api/src/user/user.resolver.ts +24 -0
  97. package/template/apps/api/src/utils/catch-everything-exception.filter.spec.ts +292 -0
  98. package/template/apps/api/src/utils/catch-everything-exception.filter.ts +70 -0
  99. package/template/apps/api/src/utils/error-handler.util.spec.ts +20 -0
  100. package/template/apps/api/src/utils/error-handler.util.ts +12 -0
  101. package/template/apps/api/src/utils/escape-like-pattern.util.spec.ts +29 -0
  102. package/template/apps/api/src/utils/escape-like-pattern.util.ts +11 -0
  103. package/template/apps/api/src/utils/pagination.input.ts +20 -0
  104. package/template/apps/api/src/utils/prisma-exception.filter.spec.ts +186 -0
  105. package/template/apps/api/src/utils/prisma-exception.filter.ts +45 -0
  106. package/template/apps/api/src/utils/prisma-graphql-exception.filter.spec.ts +216 -0
  107. package/template/apps/api/src/utils/prisma-graphql-exception.filter.ts +43 -0
  108. package/template/apps/api/tests/app.e2e-spec.ts +35 -0
  109. package/template/apps/api/tests/auth.e2e-spec.ts +38 -0
  110. package/template/apps/api/tests/helpers/api.ts +27 -0
  111. package/template/apps/api/tsconfig.json +11 -0
  112. package/template/apps/api/vitest.config.e2e.ts +16 -0
  113. package/template/apps/api/vitest.config.ts +40 -0
  114. package/template/apps/web/.env.example +7 -0
  115. package/template/apps/web/.storybook/decorators.tsx +38 -0
  116. package/template/apps/web/.storybook/main.ts +17 -0
  117. package/template/apps/web/.storybook/preview.ts +38 -0
  118. package/template/apps/web/.storybook/vitest.setup.ts +5 -0
  119. package/template/apps/web/AGENTS.md +29 -0
  120. package/template/apps/web/Dockerfile +49 -0
  121. package/template/apps/web/_gitignore +16 -0
  122. package/template/apps/web/knip.config.ts +15 -0
  123. package/template/apps/web/kustomize/base/application.properties +5 -0
  124. package/template/apps/web/kustomize/base/deployment.yaml +60 -0
  125. package/template/apps/web/kustomize/base/kustomization.yaml +17 -0
  126. package/template/apps/web/kustomize/base/service.yaml +10 -0
  127. package/template/apps/web/kustomize/overlays/prod/kustomization.yaml +12 -0
  128. package/template/apps/web/kustomize/overlays/staging/application.properties +2 -0
  129. package/template/apps/web/kustomize/overlays/staging/kustomization.yaml +18 -0
  130. package/template/apps/web/messages/de.json +124 -0
  131. package/template/apps/web/messages/en.json +124 -0
  132. package/template/apps/web/next-env.d.ts +7 -0
  133. package/template/apps/web/next.config.ts +39 -0
  134. package/template/apps/web/oxfmt.config.ts +9 -0
  135. package/template/apps/web/oxlint.config.ts +36 -0
  136. package/template/apps/web/package.json +80 -0
  137. package/template/apps/web/playwright.config.ts +34 -0
  138. package/template/apps/web/postcss.config.mjs +7 -0
  139. package/template/apps/web/sonar-project.properties +9 -0
  140. package/template/apps/web/src/app/[locale]/(auth)/recovery/page.tsx +22 -0
  141. package/template/apps/web/src/app/[locale]/(auth)/reset-password/page.tsx +29 -0
  142. package/template/apps/web/src/app/[locale]/(auth)/sign-in/page.tsx +24 -0
  143. package/template/apps/web/src/app/[locale]/(auth)/sign-up/page.tsx +24 -0
  144. package/template/apps/web/src/app/[locale]/(auth)/verify/page.tsx +24 -0
  145. package/template/apps/web/src/app/[locale]/dashboard/page.tsx +34 -0
  146. package/template/apps/web/src/app/[locale]/layout.tsx +60 -0
  147. package/template/apps/web/src/app/[locale]/page.tsx +23 -0
  148. package/template/apps/web/src/app/api/health/route.ts +3 -0
  149. package/template/apps/web/src/app/global-error.tsx +19 -0
  150. package/template/apps/web/src/app/globals.css +98 -0
  151. package/template/apps/web/src/app/icon.svg +1 -0
  152. package/template/apps/web/src/app/robots.ts +15 -0
  153. package/template/apps/web/src/app/sitemap.ts +24 -0
  154. package/template/apps/web/src/components/alert/alert.stories.tsx +25 -0
  155. package/template/apps/web/src/components/alert/alert.tsx +50 -0
  156. package/template/apps/web/src/components/avatar/avatar.stories.tsx +116 -0
  157. package/template/apps/web/src/components/avatar/avatar.tsx +88 -0
  158. package/template/apps/web/src/components/button/button-variants.ts +28 -0
  159. package/template/apps/web/src/components/button/button.stories.tsx +33 -0
  160. package/template/apps/web/src/components/button/button.tsx +32 -0
  161. package/template/apps/web/src/components/checkbox/checkbox.stories.tsx +312 -0
  162. package/template/apps/web/src/components/checkbox/checkbox.tsx +38 -0
  163. package/template/apps/web/src/components/dialog/danger-panel.stories.tsx +41 -0
  164. package/template/apps/web/src/components/dialog/danger-panel.tsx +32 -0
  165. package/template/apps/web/src/components/dialog/dialog.stories.tsx +239 -0
  166. package/template/apps/web/src/components/dialog/dialog.tsx +136 -0
  167. package/template/apps/web/src/components/dialog-buttons.stories.tsx +55 -0
  168. package/template/apps/web/src/components/dialog-buttons.tsx +48 -0
  169. package/template/apps/web/src/components/drawer/drawer.stories.tsx +251 -0
  170. package/template/apps/web/src/components/drawer/drawer.tsx +120 -0
  171. package/template/apps/web/src/components/dropdown-menu/dropdown-menu.stories.tsx +329 -0
  172. package/template/apps/web/src/components/dropdown-menu/dropdown-menu.tsx +263 -0
  173. package/template/apps/web/src/components/empty-state.stories.tsx +49 -0
  174. package/template/apps/web/src/components/empty-state.tsx +33 -0
  175. package/template/apps/web/src/components/field-group.stories.tsx +47 -0
  176. package/template/apps/web/src/components/field-group.tsx +20 -0
  177. package/template/apps/web/src/components/field-label.stories.tsx +34 -0
  178. package/template/apps/web/src/components/field-label.tsx +28 -0
  179. package/template/apps/web/src/components/icons.tsx +25 -0
  180. package/template/apps/web/src/components/input/input-otp.stories.tsx +86 -0
  181. package/template/apps/web/src/components/input/input-otp.tsx +184 -0
  182. package/template/apps/web/src/components/input/input.stories.tsx +35 -0
  183. package/template/apps/web/src/components/input/input.tsx +180 -0
  184. package/template/apps/web/src/components/popover/popover.stories.tsx +479 -0
  185. package/template/apps/web/src/components/popover/popover.tsx +45 -0
  186. package/template/apps/web/src/components/responsive-dialog/responsive-dialog.stories.tsx +307 -0
  187. package/template/apps/web/src/components/responsive-dialog/responsive-dialog.tsx +96 -0
  188. package/template/apps/web/src/components/searchbar/searchbar.stories.tsx +59 -0
  189. package/template/apps/web/src/components/searchbar/searchbar.tsx +64 -0
  190. package/template/apps/web/src/components/select/select.stories.tsx +293 -0
  191. package/template/apps/web/src/components/select/select.tsx +445 -0
  192. package/template/apps/web/src/components/skeleton/skeleton.stories.tsx +76 -0
  193. package/template/apps/web/src/components/skeleton/skeleton.tsx +13 -0
  194. package/template/apps/web/src/components/spinner/spinner.tsx +18 -0
  195. package/template/apps/web/src/components/switch/switch.stories.tsx +71 -0
  196. package/template/apps/web/src/components/switch/switch.tsx +35 -0
  197. package/template/apps/web/src/components/tabs/tabs.stories.tsx +209 -0
  198. package/template/apps/web/src/components/tabs/tabs.tsx +69 -0
  199. package/template/apps/web/src/components/tabs/url-tabs.stories.tsx +68 -0
  200. package/template/apps/web/src/components/tabs/url-tabs.tsx +42 -0
  201. package/template/apps/web/src/components/textarea/textarea.stories.tsx +144 -0
  202. package/template/apps/web/src/components/textarea/textarea.tsx +115 -0
  203. package/template/apps/web/src/components/toast/toaster.tsx +38 -0
  204. package/template/apps/web/src/components/tooltip/tooltip.stories.tsx +237 -0
  205. package/template/apps/web/src/components/tooltip/tooltip.tsx +49 -0
  206. package/template/apps/web/src/config/csp.ts +59 -0
  207. package/template/apps/web/src/config/routes.ts +14 -0
  208. package/template/apps/web/src/config/site.ts +5 -0
  209. package/template/apps/web/src/env.ts +15 -0
  210. package/template/apps/web/src/features/auth/auth-card.tsx +20 -0
  211. package/template/apps/web/src/features/auth/auth-divider.tsx +14 -0
  212. package/template/apps/web/src/features/auth/google-auth-button.tsx +49 -0
  213. package/template/apps/web/src/features/auth/google-one-tap.tsx +23 -0
  214. package/template/apps/web/src/features/auth/lib/auth-error.test.ts +22 -0
  215. package/template/apps/web/src/features/auth/lib/auth-error.ts +16 -0
  216. package/template/apps/web/src/features/auth/lib/return-to.test.ts +41 -0
  217. package/template/apps/web/src/features/auth/lib/return-to.ts +19 -0
  218. package/template/apps/web/src/features/auth/lib/validator.test.ts +45 -0
  219. package/template/apps/web/src/features/auth/lib/validator.ts +49 -0
  220. package/template/apps/web/src/features/auth/passkey-settings.tsx +106 -0
  221. package/template/apps/web/src/features/auth/recovery-form.tsx +61 -0
  222. package/template/apps/web/src/features/auth/resend-verification-button.tsx +36 -0
  223. package/template/apps/web/src/features/auth/reset-password-form.tsx +62 -0
  224. package/template/apps/web/src/features/auth/sign-in-form.stories.tsx +22 -0
  225. package/template/apps/web/src/features/auth/sign-in-form.tsx +116 -0
  226. package/template/apps/web/src/features/auth/sign-out-button.tsx +26 -0
  227. package/template/apps/web/src/features/auth/sign-up-form.tsx +107 -0
  228. package/template/apps/web/src/features/shared/provider.tsx +16 -0
  229. package/template/apps/web/src/features/shared/site-header.tsx +48 -0
  230. package/template/apps/web/src/features/shared/theme-toggle.tsx +24 -0
  231. package/template/apps/web/src/i18n/navigation.ts +5 -0
  232. package/template/apps/web/src/i18n/request.ts +21 -0
  233. package/template/apps/web/src/i18n/routing.ts +6 -0
  234. package/template/apps/web/src/lib/auth-client.ts +19 -0
  235. package/template/apps/web/src/lib/auth-server.ts +46 -0
  236. package/template/apps/web/src/lib/auth.ts +8 -0
  237. package/template/apps/web/src/lib/codegen.ts +30 -0
  238. package/template/apps/web/src/lib/graphql-client.ts +29 -0
  239. package/template/apps/web/src/lib/graphql-server.ts +28 -0
  240. package/template/apps/web/src/lib/graphql-utils.test.ts +32 -0
  241. package/template/apps/web/src/lib/graphql-utils.ts +76 -0
  242. package/template/apps/web/src/lib/hooks/use-media-query.ts +21 -0
  243. package/template/apps/web/src/lib/metadata.ts +17 -0
  244. package/template/apps/web/src/lib/network-error.test.ts +30 -0
  245. package/template/apps/web/src/lib/network-error.ts +17 -0
  246. package/template/apps/web/src/lib/proxy-utils.ts +42 -0
  247. package/template/apps/web/src/lib/query-client.ts +29 -0
  248. package/template/apps/web/src/lib/utils.ts +5 -0
  249. package/template/apps/web/src/proxy.ts +57 -0
  250. package/template/apps/web/test-results/.last-run.json +4 -0
  251. package/template/apps/web/tests/auth.spec.ts +17 -0
  252. package/template/apps/web/tests/csp-theme-script.spec.ts +37 -0
  253. package/template/apps/web/tests/home.spec.ts +25 -0
  254. package/template/apps/web/tsconfig.build.json +4 -0
  255. package/template/apps/web/tsconfig.json +28 -0
  256. package/template/apps/web/vitest.config.ts +40 -0
  257. package/template/apps/web/vitest.shims.d.ts +1 -0
  258. package/template/apps/worker/.env.example +4 -0
  259. package/template/apps/worker/AGENTS.md +11 -0
  260. package/template/apps/worker/Dockerfile +35 -0
  261. package/template/apps/worker/knip.config.ts +3 -0
  262. package/template/apps/worker/kustomize/base/application.properties +4 -0
  263. package/template/apps/worker/kustomize/base/deployment.yaml +62 -0
  264. package/template/apps/worker/kustomize/base/kustomization.yaml +16 -0
  265. package/template/apps/worker/kustomize/overlays/prod/kustomization.yaml +12 -0
  266. package/template/apps/worker/kustomize/overlays/staging/application.properties +1 -0
  267. package/template/apps/worker/kustomize/overlays/staging/kustomization.yaml +18 -0
  268. package/template/apps/worker/oxfmt.config.ts +8 -0
  269. package/template/apps/worker/oxlint.config.ts +15 -0
  270. package/template/apps/worker/package.json +33 -0
  271. package/template/apps/worker/sonar-project.properties +8 -0
  272. package/template/apps/worker/src/env.ts +13 -0
  273. package/template/apps/worker/src/lib/graceful-shutdown.ts +27 -0
  274. package/template/apps/worker/src/lib/logger.ts +20 -0
  275. package/template/apps/worker/src/main.ts +17 -0
  276. package/template/apps/worker/src/processors/example-processor.ts +15 -0
  277. package/template/apps/worker/src/worker/redis.ts +19 -0
  278. package/template/apps/worker/src/worker/worker.ts +37 -0
  279. package/template/apps/worker/tests/example-processor.test.ts +13 -0
  280. package/template/apps/worker/tests/graceful-shutdown.test.ts +34 -0
  281. package/template/apps/worker/tsconfig.json +4 -0
  282. package/template/apps/worker/vitest.config.ts +13 -0
  283. package/template/bun.lock +3053 -0
  284. package/template/bunfig.toml +2 -0
  285. package/template/compose.yaml +53 -0
  286. package/template/docker-bake.hcl +86 -0
  287. package/template/package.json +47 -0
  288. package/template/packages/lint/knip.ts +40 -0
  289. package/template/packages/lint/oxfmt.ts +8 -0
  290. package/template/packages/lint/oxlint.ts +136 -0
  291. package/template/packages/lint/package.json +13 -0
  292. package/template/packages/queues/oxfmt.config.ts +8 -0
  293. package/template/packages/queues/oxlint.config.ts +7 -0
  294. package/template/packages/queues/package.json +15 -0
  295. package/template/packages/queues/src/example-job.ts +11 -0
  296. package/template/packages/queues/src/index.ts +3 -0
  297. package/template/packages/queues/src/queue-names.ts +2 -0
  298. package/template/packages/queues/src/redis-connection.ts +27 -0
  299. package/template/packages/queues/tsconfig.json +4 -0
  300. package/template/packages/tsconfig/base.json +23 -0
  301. package/template/packages/tsconfig/node.json +11 -0
  302. package/template/packages/tsconfig/package.json +4 -0
  303. package/template/turbo.json +50 -0
@@ -0,0 +1,479 @@
1
+ import type { Meta, StoryObj } from "@storybook/nextjs-vite";
2
+ import {
3
+ Calendar,
4
+ Clock,
5
+ HelpCircle,
6
+ Info,
7
+ MapPin,
8
+ Settings,
9
+ Share2,
10
+ Tag,
11
+ User,
12
+ } from "lucide-react";
13
+ import { useState } from "react";
14
+ import { expect, userEvent, within } from "storybook/test";
15
+ import { Button } from "@/components/button/button";
16
+ import { Input } from "@/components/input/input";
17
+ import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "./popover";
18
+
19
+ const meta: Meta<typeof Popover> = {
20
+ component: Popover,
21
+ parameters: {
22
+ layout: "centered",
23
+ },
24
+ tags: ["autodocs"],
25
+ } satisfies Meta<typeof Popover>;
26
+
27
+ export default meta;
28
+ type Story = StoryObj<typeof meta>;
29
+
30
+ export const Default: Story = {
31
+ play: async ({ canvasElement }) => {
32
+ await userEvent.click(within(canvasElement).getByRole("button", { name: "Open Popover" }));
33
+
34
+ await expect(await within(document.body).findByText("Popover Title")).toBeInTheDocument();
35
+ },
36
+ render: () => (
37
+ <Popover>
38
+ <PopoverTrigger asChild>
39
+ <Button variant="outline">Open Popover</Button>
40
+ </PopoverTrigger>
41
+ <PopoverContent aria-label="Popover Title">
42
+ <div className="space-y-2">
43
+ <h4 className="text-sm font-semibold">Popover Title</h4>
44
+ <p className="text-muted-foreground text-sm">
45
+ This is a beautiful popover with glassmorphism styling.
46
+ </p>
47
+ </div>
48
+ </PopoverContent>
49
+ </Popover>
50
+ ),
51
+ };
52
+
53
+ export const WithIcon: Story = {
54
+ render: () => (
55
+ <Popover>
56
+ <PopoverTrigger asChild>
57
+ <Button variant="outline">
58
+ <Info className="size-4" />
59
+ Information
60
+ </Button>
61
+ </PopoverTrigger>
62
+ <PopoverContent>
63
+ <div className="flex gap-3">
64
+ <div className="bg-brand-500/10 rounded-full p-2">
65
+ <Info className="text-brand-500 size-5" />
66
+ </div>
67
+ <div className="flex-1 space-y-1">
68
+ <h4 className="text-sm font-semibold">Did you know?</h4>
69
+ <p className="text-muted-foreground text-xs">
70
+ You can customize the appearance and behavior of this popover using various props.
71
+ </p>
72
+ </div>
73
+ </div>
74
+ </PopoverContent>
75
+ </Popover>
76
+ ),
77
+ };
78
+
79
+ export const WithForm: Story = {
80
+ render: () => {
81
+ const [name, setName] = useState("");
82
+ const [email, setEmail] = useState("");
83
+
84
+ return (
85
+ <Popover>
86
+ <PopoverTrigger asChild>
87
+ <Button variant="outline">
88
+ <User className="size-4" />
89
+ Update Profile
90
+ </Button>
91
+ </PopoverTrigger>
92
+ <PopoverContent className="w-80">
93
+ <div className="space-y-4">
94
+ <div className="space-y-2">
95
+ <h4 className="text-sm font-semibold">Update Profile</h4>
96
+ <p className="text-muted-foreground text-xs">Make changes to your profile here.</p>
97
+ </div>
98
+ <div className="space-y-3">
99
+ <Input
100
+ label="Name"
101
+ onChange={(e) => setName(e.target.value)}
102
+ placeholder="Enter your name"
103
+ value={name}
104
+ />
105
+ <Input
106
+ label="Email"
107
+ onChange={(e) => setEmail(e.target.value)}
108
+ placeholder="Enter your email"
109
+ type="email"
110
+ value={email}
111
+ />
112
+ </div>
113
+ <div className="flex justify-end gap-2">
114
+ <Button size="sm" variant="outline">
115
+ Cancel
116
+ </Button>
117
+ <Button size="sm">Save Changes</Button>
118
+ </div>
119
+ </div>
120
+ </PopoverContent>
121
+ </Popover>
122
+ );
123
+ },
124
+ };
125
+
126
+ export const QuickActions: Story = {
127
+ render: () => (
128
+ <Popover>
129
+ <PopoverTrigger asChild>
130
+ <Button variant="outline">
131
+ <Settings className="size-4" />
132
+ Quick Actions
133
+ </Button>
134
+ </PopoverTrigger>
135
+ <PopoverContent className="w-72">
136
+ <div className="space-y-3">
137
+ <h4 className="text-sm font-semibold">Quick Actions</h4>
138
+ <div className="grid grid-cols-2 gap-2">
139
+ <button
140
+ className="border-border/40 bg-background/50 hover:bg-accent flex flex-col items-center gap-2 rounded-lg border p-3 transition-colors"
141
+ type="button"
142
+ >
143
+ <Share2 className="text-brand-500 size-5" />
144
+ <span className="text-xs font-medium">Share</span>
145
+ </button>
146
+ <button
147
+ className="border-border/40 bg-background/50 hover:bg-accent flex flex-col items-center gap-2 rounded-lg border p-3 transition-colors"
148
+ type="button"
149
+ >
150
+ <Calendar className="size-5 text-green-500" />
151
+ <span className="text-xs font-medium">Schedule</span>
152
+ </button>
153
+ <button
154
+ className="border-border/40 bg-background/50 hover:bg-accent flex flex-col items-center gap-2 rounded-lg border p-3 transition-colors"
155
+ type="button"
156
+ >
157
+ <Tag className="text-brand-500 size-5" />
158
+ <span className="text-xs font-medium">Tag</span>
159
+ </button>
160
+ <button
161
+ className="border-border/40 bg-background/50 hover:bg-accent flex flex-col items-center gap-2 rounded-lg border p-3 transition-colors"
162
+ type="button"
163
+ >
164
+ <MapPin className="size-5 text-red-500" />
165
+ <span className="text-xs font-medium">Location</span>
166
+ </button>
167
+ </div>
168
+ </div>
169
+ </PopoverContent>
170
+ </Popover>
171
+ ),
172
+ };
173
+
174
+ export const HelpTooltip: Story = {
175
+ render: () => (
176
+ <div className="flex items-center gap-2">
177
+ <span className="text-sm">Complex Feature</span>
178
+ <Popover>
179
+ <PopoverTrigger asChild>
180
+ <button
181
+ aria-label="Help"
182
+ className="text-muted-foreground hover:text-foreground rounded-full transition-colors"
183
+ type="button"
184
+ >
185
+ <HelpCircle className="size-4" />
186
+ </button>
187
+ </PopoverTrigger>
188
+ <PopoverContent className="w-64">
189
+ <div className="space-y-2">
190
+ <h4 className="text-sm font-semibold">Need Help?</h4>
191
+ <p className="text-muted-foreground text-xs">
192
+ This feature allows you to perform advanced operations with custom configurations.
193
+ Click the settings icon to customize your preferences.
194
+ </p>
195
+ <div className="pt-2">
196
+ <Button className="h-auto p-0 text-xs" size="sm" variant="ghost">
197
+ Learn more →
198
+ </Button>
199
+ </div>
200
+ </div>
201
+ </PopoverContent>
202
+ </Popover>
203
+ </div>
204
+ ),
205
+ };
206
+
207
+ export const Positions: Story = {
208
+ render: () => (
209
+ <div className="grid grid-cols-3 gap-8 p-8">
210
+ <div className="flex justify-center">
211
+ <Popover>
212
+ <PopoverTrigger asChild>
213
+ <Button size="sm" variant="outline">
214
+ Top
215
+ </Button>
216
+ </PopoverTrigger>
217
+ <PopoverContent className="w-48" side="top">
218
+ <p className="text-center text-xs">Popover positioned at the top</p>
219
+ </PopoverContent>
220
+ </Popover>
221
+ </div>
222
+
223
+ <div className="flex justify-center">
224
+ <Popover>
225
+ <PopoverTrigger asChild>
226
+ <Button size="sm" variant="outline">
227
+ Top Start
228
+ </Button>
229
+ </PopoverTrigger>
230
+ <PopoverContent align="start" className="w-48" side="top">
231
+ <p className="text-center text-xs">Aligned to start</p>
232
+ </PopoverContent>
233
+ </Popover>
234
+ </div>
235
+
236
+ <div className="flex justify-center">
237
+ <Popover>
238
+ <PopoverTrigger asChild>
239
+ <Button size="sm" variant="outline">
240
+ Top End
241
+ </Button>
242
+ </PopoverTrigger>
243
+ <PopoverContent align="end" className="w-48" side="top">
244
+ <p className="text-center text-xs">Aligned to end</p>
245
+ </PopoverContent>
246
+ </Popover>
247
+ </div>
248
+
249
+ <div className="flex justify-center">
250
+ <Popover>
251
+ <PopoverTrigger asChild>
252
+ <Button size="sm" variant="outline">
253
+ Left
254
+ </Button>
255
+ </PopoverTrigger>
256
+ <PopoverContent className="w-48" side="left">
257
+ <p className="text-center text-xs">Positioned on the left</p>
258
+ </PopoverContent>
259
+ </Popover>
260
+ </div>
261
+
262
+ <div className="flex justify-center">
263
+ <Popover>
264
+ <PopoverTrigger asChild>
265
+ <Button size="sm" variant="outline">
266
+ Center
267
+ </Button>
268
+ </PopoverTrigger>
269
+ <PopoverContent className="w-48">
270
+ <p className="text-center text-xs">Default position</p>
271
+ </PopoverContent>
272
+ </Popover>
273
+ </div>
274
+
275
+ <div className="flex justify-center">
276
+ <Popover>
277
+ <PopoverTrigger asChild>
278
+ <Button size="sm" variant="outline">
279
+ Right
280
+ </Button>
281
+ </PopoverTrigger>
282
+ <PopoverContent className="w-48" side="right">
283
+ <p className="text-center text-xs">Positioned on the right</p>
284
+ </PopoverContent>
285
+ </Popover>
286
+ </div>
287
+
288
+ <div className="flex justify-center">
289
+ <Popover>
290
+ <PopoverTrigger asChild>
291
+ <Button size="sm" variant="outline">
292
+ Bottom
293
+ </Button>
294
+ </PopoverTrigger>
295
+ <PopoverContent className="w-48" side="bottom">
296
+ <p className="text-center text-xs">Popover at the bottom</p>
297
+ </PopoverContent>
298
+ </Popover>
299
+ </div>
300
+
301
+ <div className="flex justify-center">
302
+ <Popover>
303
+ <PopoverTrigger asChild>
304
+ <Button size="sm" variant="outline">
305
+ Bottom Start
306
+ </Button>
307
+ </PopoverTrigger>
308
+ <PopoverContent align="start" className="w-48" side="bottom">
309
+ <p className="text-center text-xs">Aligned to start</p>
310
+ </PopoverContent>
311
+ </Popover>
312
+ </div>
313
+
314
+ <div className="flex justify-center">
315
+ <Popover>
316
+ <PopoverTrigger asChild>
317
+ <Button size="sm" variant="outline">
318
+ Bottom End
319
+ </Button>
320
+ </PopoverTrigger>
321
+ <PopoverContent align="end" className="w-48" side="bottom">
322
+ <p className="text-center text-xs">Aligned to end</p>
323
+ </PopoverContent>
324
+ </Popover>
325
+ </div>
326
+ </div>
327
+ ),
328
+ };
329
+
330
+ export const Controlled: Story = {
331
+ render: () => {
332
+ const [open, setOpen] = useState(false);
333
+
334
+ return (
335
+ <div className="space-y-4">
336
+ <div className="flex items-center gap-2">
337
+ <span className="text-sm">Popover is:</span>
338
+ <span className="text-sm font-semibold">{open ? "Open" : "Closed"}</span>
339
+ </div>
340
+ <Popover onOpenChange={setOpen} open={open}>
341
+ <PopoverTrigger asChild>
342
+ <Button variant="outline">Toggle Popover</Button>
343
+ </PopoverTrigger>
344
+ <PopoverContent>
345
+ <div className="space-y-3">
346
+ <h4 className="text-sm font-semibold">Controlled Popover</h4>
347
+ <p className="text-muted-foreground text-xs">
348
+ This popover's open state is controlled externally.
349
+ </p>
350
+ <Button onClick={() => setOpen(false)} size="sm">
351
+ Close Popover
352
+ </Button>
353
+ </div>
354
+ </PopoverContent>
355
+ </Popover>
356
+ </div>
357
+ );
358
+ },
359
+ };
360
+
361
+ export const WithAnchor: Story = {
362
+ render: () => (
363
+ <div className="space-y-4">
364
+ <p className="text-sm">
365
+ This is some text with an{" "}
366
+ <span className="text-brand-500 cursor-pointer font-semibold underline decoration-dotted underline-offset-4">
367
+ anchor element
368
+ </span>{" "}
369
+ that triggers the popover.
370
+ </p>
371
+ <Popover>
372
+ <PopoverAnchor />
373
+ <PopoverTrigger asChild>
374
+ <Button size="sm" variant="outline">
375
+ Show Popover at Anchor
376
+ </Button>
377
+ </PopoverTrigger>
378
+ <PopoverContent className="w-64">
379
+ <div className="space-y-2">
380
+ <h4 className="text-sm font-semibold">Anchored Popover</h4>
381
+ <p className="text-muted-foreground text-xs">
382
+ This popover is positioned relative to a custom anchor element in the text above.
383
+ </p>
384
+ </div>
385
+ </PopoverContent>
386
+ </Popover>
387
+ </div>
388
+ ),
389
+ };
390
+
391
+ export const Calendar_Example: Story = {
392
+ render: () => {
393
+ const [date, setDate] = useState<Date | undefined>(() => new Date());
394
+
395
+ return (
396
+ <Popover>
397
+ <PopoverTrigger asChild>
398
+ <Button variant="outline">
399
+ <Calendar className="size-4" />
400
+ {date ? date.toLocaleDateString() : "Pick a date"}
401
+ </Button>
402
+ </PopoverTrigger>
403
+ <PopoverContent className="w-auto p-0">
404
+ <div className="p-4">
405
+ <div className="space-y-3">
406
+ <h4 className="text-sm font-semibold">Select Date</h4>
407
+ <div className="grid grid-cols-7 gap-2">
408
+ {["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => (
409
+ <div className="text-muted-foreground text-center text-xs font-medium" key={day}>
410
+ {day}
411
+ </div>
412
+ ))}
413
+ {Array.from({ length: 35 }, (_, i) => i + 1).map((dayNum) => {
414
+ const dayNumber = dayNum > 31 ? "" : dayNum;
415
+ return (
416
+ <button
417
+ className="hover:bg-accent h-8 w-8 rounded-lg text-xs transition-colors"
418
+ key={`day-${dayNum}`}
419
+ onClick={() => setDate(new Date())}
420
+ type="button"
421
+ >
422
+ {dayNumber}
423
+ </button>
424
+ );
425
+ })}
426
+ </div>
427
+ </div>
428
+ </div>
429
+ </PopoverContent>
430
+ </Popover>
431
+ );
432
+ },
433
+ };
434
+
435
+ export const TimePickerExample: Story = {
436
+ render: () => {
437
+ const [time, setTime] = useState("12:00");
438
+
439
+ return (
440
+ <Popover>
441
+ <PopoverTrigger asChild>
442
+ <Button variant="outline">
443
+ <Clock className="size-4" />
444
+ {time}
445
+ </Button>
446
+ </PopoverTrigger>
447
+ <PopoverContent className="w-64">
448
+ <div className="space-y-3">
449
+ <h4 className="text-sm font-semibold">Select Time</h4>
450
+ <div className="flex items-center gap-2">
451
+ <Input
452
+ className="text-center"
453
+ containerClassName="flex-1"
454
+ max="23"
455
+ min="0"
456
+ onChange={(e) => setTime(`${e.target.value}:${time.split(":")[1]}`)}
457
+ type="number"
458
+ value={time.split(":")[0]}
459
+ />
460
+ <span className="text-lg font-semibold">:</span>
461
+ <Input
462
+ className="text-center"
463
+ containerClassName="flex-1"
464
+ max="59"
465
+ min="0"
466
+ onChange={(e) => setTime(`${time.split(":")[0]}:${e.target.value}`)}
467
+ type="number"
468
+ value={time.split(":")[1]}
469
+ />
470
+ </div>
471
+ <Button className="w-full" size="sm">
472
+ Set Time
473
+ </Button>
474
+ </div>
475
+ </PopoverContent>
476
+ </Popover>
477
+ );
478
+ },
479
+ };
@@ -0,0 +1,45 @@
1
+ "use client";
2
+
3
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
4
+ import * as React from "react";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const Popover = PopoverPrimitive.Root;
8
+
9
+ const PopoverTrigger = PopoverPrimitive.Trigger;
10
+
11
+ const PopoverAnchor = PopoverPrimitive.Anchor;
12
+
13
+ function PopoverContent({
14
+ align = "center",
15
+ className,
16
+ ref,
17
+ sideOffset = 4,
18
+ ...props
19
+ }: React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & {
20
+ ref?: React.RefObject<null | React.ComponentRef<typeof PopoverPrimitive.Content>>;
21
+ }) {
22
+ return (
23
+ <PopoverPrimitive.Portal>
24
+ <PopoverPrimitive.Content
25
+ align={align}
26
+ className={cn(
27
+ "z-50 min-w-64 overflow-hidden rounded-xl",
28
+ "border-border bg-popover text-popover-foreground border",
29
+ "p-4",
30
+ "shadow-lg shadow-black/8",
31
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
32
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
33
+ "origin-[--radix-popover-content-transform-origin]",
34
+ className,
35
+ )}
36
+ ref={ref}
37
+ sideOffset={sideOffset}
38
+ {...props}
39
+ />
40
+ </PopoverPrimitive.Portal>
41
+ );
42
+ }
43
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
44
+
45
+ export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };