create-warlock 5.34.3 → 5.35.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 (68) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +8 -0
  3. package/esm/commands/create-new-app/index.mjs +4 -3
  4. package/esm/commands/create-new-app/index.mjs.map +1 -1
  5. package/esm/commands/create-warlock-app/index.mjs +7 -1
  6. package/esm/commands/create-warlock-app/index.mjs.map +1 -1
  7. package/esm/features/features-map.mjs +8 -2
  8. package/esm/features/features-map.mjs.map +1 -1
  9. package/esm/helpers/app.mjs +1 -0
  10. package/esm/helpers/app.mjs.map +1 -1
  11. package/esm/prompts/ask-stack.mjs +1 -1
  12. package/esm/prompts/ask-stack.mjs.map +1 -1
  13. package/llms-full.txt +58 -15
  14. package/package.json +2 -2
  15. package/skills/SKILL.md +58 -15
  16. package/templates/design-system/SOURCES.md +23 -0
  17. package/templates/design-system/package.json +14 -0
  18. package/templates/design-system/src/web/components/ui/LICENSE.md +21 -0
  19. package/templates/design-system/src/web/components/ui/button.tsx +61 -0
  20. package/templates/design-system/src/web/components/ui/calendar.tsx +178 -0
  21. package/templates/design-system/src/web/components/ui/checkbox.tsx +28 -0
  22. package/templates/design-system/src/web/components/ui/command.tsx +161 -0
  23. package/templates/design-system/src/web/components/ui/dialog.tsx +144 -0
  24. package/templates/design-system/src/web/components/ui/input.tsx +20 -0
  25. package/templates/design-system/src/web/components/ui/label.tsx +20 -0
  26. package/templates/design-system/src/web/components/ui/popover.tsx +73 -0
  27. package/templates/design-system/src/web/components/ui/radio-group.tsx +44 -0
  28. package/templates/design-system/src/web/components/ui/switch.tsx +34 -0
  29. package/templates/design-system/src/web/components/ui/textarea.tsx +17 -0
  30. package/templates/design-system/src/web/design-system/components/checkbox-input.tsx +19 -0
  31. package/templates/design-system/src/web/design-system/components/date-picker-input.tsx +59 -0
  32. package/templates/design-system/src/web/design-system/components/date-time-picker-input.tsx +67 -0
  33. package/templates/design-system/src/web/design-system/components/email-input.tsx +22 -0
  34. package/templates/design-system/src/web/design-system/components/field-frame.tsx +36 -0
  35. package/templates/design-system/src/web/design-system/components/file-input.tsx +24 -0
  36. package/templates/design-system/src/web/design-system/components/multi-select-input.tsx +94 -0
  37. package/templates/design-system/src/web/design-system/components/password-input.tsx +22 -0
  38. package/templates/design-system/src/web/design-system/components/radio-input.tsx +41 -0
  39. package/templates/design-system/src/web/design-system/components/select-input.tsx +94 -0
  40. package/templates/design-system/src/web/design-system/components/submit-button.tsx +20 -0
  41. package/templates/design-system/src/web/design-system/components/switch-input.tsx +19 -0
  42. package/templates/design-system/src/web/design-system/components/text-input.tsx +22 -0
  43. package/templates/design-system/src/web/design-system/components/textarea-input.tsx +22 -0
  44. package/templates/design-system/src/web/design-system/components/time-picker-input.tsx +22 -0
  45. package/templates/design-system/src/web/design-system/hooks/use-calendar-day-button.ts +9 -0
  46. package/templates/design-system/src/web/design-system/hooks/use-control.ts +29 -0
  47. package/templates/design-system/src/web/design-system/hooks/use-date-control.ts +42 -0
  48. package/templates/design-system/src/web/design-system/hooks/use-file-control.ts +24 -0
  49. package/templates/design-system/src/web/design-system/hooks/use-select-control.ts +78 -0
  50. package/templates/design-system/src/web/design-system/hooks/use-submit-control.ts +13 -0
  51. package/templates/design-system/src/web/design-system/types/control-props.ts +46 -0
  52. package/templates/warlock/e2e/home.e2e.ts +19 -0
  53. package/templates/warlock/package.json +1 -1
  54. package/templates/warlock/skills/api-design/SKILL.md +72 -9
  55. package/templates/warlock/skills/testing-strategy/SKILL.md +46 -5
  56. package/templates/warlock/skills/web-code-standards/SKILL.md +38 -0
  57. package/templates/warlock/src/app/auth/controllers/forgot-password.controller.ts +6 -0
  58. package/templates/warlock/src/app/auth/controllers/login.controller.ts +17 -0
  59. package/templates/warlock/src/app/auth/controllers/logout-all.controller.ts +6 -0
  60. package/templates/warlock/src/app/auth/controllers/logout.controller.ts +6 -0
  61. package/templates/warlock/src/app/auth/controllers/me.controller.ts +7 -0
  62. package/templates/warlock/src/app/auth/controllers/refresh-token.controller.ts +12 -0
  63. package/templates/warlock/src/app/auth/controllers/reset-password.controller.ts +6 -0
  64. package/templates/warlock/src/app/home/controllers/home-page.controller.ts +4 -0
  65. package/templates/warlock/src/app/posts/controllers/update-post.controller.ts +1 -0
  66. package/templates/warlock/src/app/shared/utils/format-http-error-response.ts +13 -1
  67. package/templates/warlock/src/app/users/controllers/create-new-user.controller.ts +1 -0
  68. package/templates/warlock/warlock.config.ts +1 -0
package/llms-full.txt CHANGED
@@ -72,21 +72,21 @@ Features: none
72
72
 
73
73
  ## Flags
74
74
 
75
- | Flag | Takes value | Default | Purpose |
76
- | ------------------------------ | ----------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
77
- | `<positional>` / `--name` | yes | — (required) | Project name + target directory. |
78
- | `--stack` | yes | `api` | `api` or `web` — the structural choice; `web` seeds the `web` feature by default. |
79
- | `--db` | yes | `mongodb` | Database driver key (`mongodb`, `postgres`, or `none`). |
80
- | `--no-db` | no | — | Opt out of a database — shorthand for `--db=none`. |
81
- | `--pm` | yes | inferred (`npm_config_user_agent` / system) | Package manager — use `npm` for the commands in this guide. |
82
- | `--features` | yes (CSV) | none, or `["web"]` when `--stack=web` and `--features` is not given | Comma-separated optional feature keys. An explicit `--features` always wins over the `--stack` default. |
83
- | `--lint` | yes | `oxc` | `oxc` (Oxlint/Oxfmt); removed or invalid values fail before writes. |
84
- | `--ai` | yes (CSV) | none | Comma-separated AI provider keys (auto-pulls `@warlock.js/ai`). |
85
- | `--agents` | yes (CSV) | `claude` | Comma-separated `agent-kit` targets — see below. |
86
- | `--git` / `--no-git` | no | off | Initialize a Git repo (`git init` + `main` branch + initial commit). |
87
- | `--jwt` / `--no-jwt` | no | off | Generate JWT secrets via the project's `jwt` script. |
88
- | `--yes` / `-y` | no | off | Fully headless mode. |
89
- | `--interactive`, `--customize` | no | off | Restore the full long-form wizard. |
75
+ | Flag | Takes value | Default | Purpose |
76
+ | ------------------------------ | ----------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
77
+ | `<positional>` / `--name` | yes | — (required) | Project name + target directory. |
78
+ | `--stack` | yes | `api` | `api` or `web` — the structural choice; `web` seeds `web`, `shadcn`, and `design-system` by default. |
79
+ | `--db` | yes | `mongodb` | Database driver key (`mongodb`, `postgres`, or `none`). |
80
+ | `--no-db` | no | — | Opt out of a database — shorthand for `--db=none`. |
81
+ | `--pm` | yes | inferred (`npm_config_user_agent` / system) | Package manager — use `npm` for the commands in this guide. |
82
+ | `--features` | yes (CSV) | none, or `["web", "shadcn", "design-system"]` when `--stack=web` and `--features` is not given | Comma-separated optional feature keys. An explicit `--features` always wins over the `--stack` default. |
83
+ | `--lint` | yes | `oxc` | `oxc` (Oxlint/Oxfmt); removed or invalid values fail before writes. |
84
+ | `--ai` | yes (CSV) | none | Comma-separated AI provider keys (auto-pulls `@warlock.js/ai`). |
85
+ | `--agents` | yes (CSV) | `claude` | Comma-separated `agent-kit` targets — see below. |
86
+ | `--git` / `--no-git` | no | off | Initialize a Git repo (`git init` + `main` branch + initial commit). |
87
+ | `--jwt` / `--no-jwt` | no | off | Generate JWT secrets via the project's `jwt` script. |
88
+ | `--yes` / `-y` | no | off | Fully headless mode. |
89
+ | `--interactive`, `--customize` | no | off | Restore the full long-form wizard. |
90
90
 
91
91
  If selected feature dependencies do not install, JWT generation is skipped and reported rather than retried through
92
92
  the package manager.
@@ -270,4 +270,47 @@ existing Oxc configs retain their categories. A real lockfile beside a workspace
270
270
  marker determines the installer: `package-lock.json` plus `pnpm-workspace.yaml`
271
271
  without `pnpm-lock.yaml` now produces `npm install`.
272
272
 
273
+ ## Starter browser checks
274
+
275
+ Full-stack web projects include `e2e/home.e2e.ts`. Run `npx warlock e2e` with
276
+ Chrome or Edge installed. The spec builds and starts an owned app copy, visits
277
+ `/` at 1280×800 and 390×844, then calls `page.assertAccessible()`,
278
+ `page.assertViewport()` and `page.screenshot("home")`. API-only scaffolds omit
279
+ this web spec. The fixture excludes dotenv files and production data directories;
280
+ provide disposable service configuration explicitly if you add database-backed
281
+ features. A database-free scaffold needs no external database.
282
+
283
+ ## Test projects
284
+
285
+ `npx create-warlock my-project --yes --features=test` delegates to `warlock add test`:
286
+ unit `*.spec.ts` outside `tests/` runs without Warlock setup, integration `*.test.ts`
287
+ and specs under `tests/` use framework setup and the generated cleanup runner.
288
+ The template pins Vitest 4.1.10. See the generated `testing-strategy` skill for
289
+ `setupTest({ mode: "unit", importModule })`, opt-in `tests.lifetime: "worker"`,
290
+ `tests.resetBetweenFiles`, profiling and the changed-tests release gate.
291
+
292
+ ## Shared form controls
293
+
294
+ `npx create-warlock app --yes --stack=web` includes Tailwind, shadcn primitives
295
+ and the app-owned design system. `--features=tailwind,shadcn` also includes it.
296
+ An explicit `--features` replaces the preset. Import the generated default
297
+ exports from `web/design-system/components/<kebab-name>`; use `Form` from
298
+ `@mongez/react-form` directly. For example:
299
+
300
+ ```tsx
301
+ import TextInput from "web/design-system/components/text-input";
302
+
303
+ <TextInput id="profile-name" name="name" label="Name" hint="Display name" />;
304
+ ```
305
+
306
+ All fourteen controls, props and hooks are documented by the Web
307
+ `build-accessible-controls` topic and the generated `web-code-standards` skill.
308
+ Existing apps use `npx warlock add tailwind`, then `npx warlock add design-system`.
309
+
310
+ Starter controllers include `responseSchema`. Keep these contracts aligned when changing returned bodies, for example `homePageController.responseSchema = { 200: { body: { message: "string", version: "string", docs: "string" } } };`.
311
+
312
+ Feature picker labels stay compact enough for a 40-column terminal; hints are shortened or omitted when necessary. The existing-Tailwind design controls choice keeps its feature value when its display label is shortened.
313
+
314
+ For generated form controls, return the save promise from `onSubmit` to finish the form's submitting lifecycle before a second submission. In jsdom control tests, provide a scoped ResizeObserver adapter because jsdom does not calculate layout.
315
+
273
316
 
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "dependencies": {
14
14
  "@clack/prompts": "^0.8.2",
15
15
  "@mongez/copper": "^2.1.3",
16
- "@warlock.js/fs": "5.34.3",
16
+ "@warlock.js/fs": "5.35.0",
17
17
  "@mongez/reinforcements": "^4.0.2",
18
18
  "cross-spawn": "^7.0.3",
19
19
  "rimraf": "^6.0.1",
@@ -22,7 +22,7 @@
22
22
  "engines": {
23
23
  "node": ">=22.15"
24
24
  },
25
- "version": "5.34.3",
25
+ "version": "5.35.0",
26
26
  "type": "module",
27
27
  "main": "./esm/index.mjs",
28
28
  "module": "./esm/index.mjs",
package/skills/SKILL.md CHANGED
@@ -64,21 +64,21 @@ Features: none
64
64
 
65
65
  ## Flags
66
66
 
67
- | Flag | Takes value | Default | Purpose |
68
- | ------------------------------ | ----------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
69
- | `<positional>` / `--name` | yes | — (required) | Project name + target directory. |
70
- | `--stack` | yes | `api` | `api` or `web` — the structural choice; `web` seeds the `web` feature by default. |
71
- | `--db` | yes | `mongodb` | Database driver key (`mongodb`, `postgres`, or `none`). |
72
- | `--no-db` | no | — | Opt out of a database — shorthand for `--db=none`. |
73
- | `--pm` | yes | inferred (`npm_config_user_agent` / system) | Package manager — use `npm` for the commands in this guide. |
74
- | `--features` | yes (CSV) | none, or `["web"]` when `--stack=web` and `--features` is not given | Comma-separated optional feature keys. An explicit `--features` always wins over the `--stack` default. |
75
- | `--lint` | yes | `oxc` | `oxc` (Oxlint/Oxfmt); removed or invalid values fail before writes. |
76
- | `--ai` | yes (CSV) | none | Comma-separated AI provider keys (auto-pulls `@warlock.js/ai`). |
77
- | `--agents` | yes (CSV) | `claude` | Comma-separated `agent-kit` targets — see below. |
78
- | `--git` / `--no-git` | no | off | Initialize a Git repo (`git init` + `main` branch + initial commit). |
79
- | `--jwt` / `--no-jwt` | no | off | Generate JWT secrets via the project's `jwt` script. |
80
- | `--yes` / `-y` | no | off | Fully headless mode. |
81
- | `--interactive`, `--customize` | no | off | Restore the full long-form wizard. |
67
+ | Flag | Takes value | Default | Purpose |
68
+ | ------------------------------ | ----------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
69
+ | `<positional>` / `--name` | yes | — (required) | Project name + target directory. |
70
+ | `--stack` | yes | `api` | `api` or `web` — the structural choice; `web` seeds `web`, `shadcn`, and `design-system` by default. |
71
+ | `--db` | yes | `mongodb` | Database driver key (`mongodb`, `postgres`, or `none`). |
72
+ | `--no-db` | no | — | Opt out of a database — shorthand for `--db=none`. |
73
+ | `--pm` | yes | inferred (`npm_config_user_agent` / system) | Package manager — use `npm` for the commands in this guide. |
74
+ | `--features` | yes (CSV) | none, or `["web", "shadcn", "design-system"]` when `--stack=web` and `--features` is not given | Comma-separated optional feature keys. An explicit `--features` always wins over the `--stack` default. |
75
+ | `--lint` | yes | `oxc` | `oxc` (Oxlint/Oxfmt); removed or invalid values fail before writes. |
76
+ | `--ai` | yes (CSV) | none | Comma-separated AI provider keys (auto-pulls `@warlock.js/ai`). |
77
+ | `--agents` | yes (CSV) | `claude` | Comma-separated `agent-kit` targets — see below. |
78
+ | `--git` / `--no-git` | no | off | Initialize a Git repo (`git init` + `main` branch + initial commit). |
79
+ | `--jwt` / `--no-jwt` | no | off | Generate JWT secrets via the project's `jwt` script. |
80
+ | `--yes` / `-y` | no | off | Fully headless mode. |
81
+ | `--interactive`, `--customize` | no | off | Restore the full long-form wizard. |
82
82
 
83
83
  If selected feature dependencies do not install, JWT generation is skipped and reported rather than retried through
84
84
  the package manager.
@@ -261,3 +261,46 @@ editor settings and nonstandard scripts can also require review.
261
261
  existing Oxc configs retain their categories. A real lockfile beside a workspace
262
262
  marker determines the installer: `package-lock.json` plus `pnpm-workspace.yaml`
263
263
  without `pnpm-lock.yaml` now produces `npm install`.
264
+
265
+ ## Starter browser checks
266
+
267
+ Full-stack web projects include `e2e/home.e2e.ts`. Run `npx warlock e2e` with
268
+ Chrome or Edge installed. The spec builds and starts an owned app copy, visits
269
+ `/` at 1280×800 and 390×844, then calls `page.assertAccessible()`,
270
+ `page.assertViewport()` and `page.screenshot("home")`. API-only scaffolds omit
271
+ this web spec. The fixture excludes dotenv files and production data directories;
272
+ provide disposable service configuration explicitly if you add database-backed
273
+ features. A database-free scaffold needs no external database.
274
+
275
+ ## Test projects
276
+
277
+ `npx create-warlock my-project --yes --features=test` delegates to `warlock add test`:
278
+ unit `*.spec.ts` outside `tests/` runs without Warlock setup, integration `*.test.ts`
279
+ and specs under `tests/` use framework setup and the generated cleanup runner.
280
+ The template pins Vitest 4.1.10. See the generated `testing-strategy` skill for
281
+ `setupTest({ mode: "unit", importModule })`, opt-in `tests.lifetime: "worker"`,
282
+ `tests.resetBetweenFiles`, profiling and the changed-tests release gate.
283
+
284
+ ## Shared form controls
285
+
286
+ `npx create-warlock app --yes --stack=web` includes Tailwind, shadcn primitives
287
+ and the app-owned design system. `--features=tailwind,shadcn` also includes it.
288
+ An explicit `--features` replaces the preset. Import the generated default
289
+ exports from `web/design-system/components/<kebab-name>`; use `Form` from
290
+ `@mongez/react-form` directly. For example:
291
+
292
+ ```tsx
293
+ import TextInput from "web/design-system/components/text-input";
294
+
295
+ <TextInput id="profile-name" name="name" label="Name" hint="Display name" />;
296
+ ```
297
+
298
+ All fourteen controls, props and hooks are documented by the Web
299
+ `build-accessible-controls` topic and the generated `web-code-standards` skill.
300
+ Existing apps use `npx warlock add tailwind`, then `npx warlock add design-system`.
301
+
302
+ Starter controllers include `responseSchema`. Keep these contracts aligned when changing returned bodies, for example `homePageController.responseSchema = { 200: { body: { message: "string", version: "string", docs: "string" } } };`.
303
+
304
+ Feature picker labels stay compact enough for a 40-column terminal; hints are shortened or omitted when necessary. The existing-Tailwind design controls choice keeps its feature value when its display label is shortened.
305
+
306
+ For generated form controls, return the save promise from `onSubmit` to finish the form's submitting lifecycle before a second submission. In jsdom control tests, provide a scoped ResizeObserver adapter because jsdom does not calculate layout.
@@ -0,0 +1,23 @@
1
+ # Design system source
2
+
3
+ The UI primitives are copied from the official shadcn/ui new-york-v4 registry:
4
+ https://ui.shadcn.com/r/styles/new-york-v4/{component}.json (retrieved 2026-10-21).
5
+ Their MIT license is retained in `src/web/components/ui/LICENSE.md`. Calendar's
6
+ focus effect is extracted to `use-calendar-day-button`.
7
+
8
+ The registry declares `radix-ui` for Button, Checkbox, RadioGroup, Switch,
9
+ Popover, Dialog and Label; `cmdk` for Command; and `react-day-picker` plus
10
+ `date-fns` for Calendar. The registry's floating day-picker dependency is pinned
11
+ to the verified 9.14.0 API (single selection, autoFocus, dir). All runtime versions
12
+ in package.json were verified with `npm view` on 2026-10-21. `cn`,
13
+ class-variance-authority and lucide-react are the registry's styling dependencies.
14
+
15
+ The form adapters use the installed Mongez React Form source contract:
16
+ `useFormControl`, `changeValue`, `setChecked`, `onBlur`, `inputRef`,
17
+ `visibleElementRef`, reset/clear subscriptions and `useSubmitButton`.
18
+ Forms use the library's `Form` directly; it supplies `noValidate`.
19
+
20
+ Edit this template, format its files, then run
21
+ `node packages/create-warlock/scripts/sync-design-system.mjs` from the repository
22
+ root and format the resulting Core payload. The integrity spec compares every
23
+ file and dependency so the add command and scaffolder cannot drift.
@@ -0,0 +1,14 @@
1
+ {
2
+ "dependencies": {
3
+ "@mongez/react-form": "4.0.0",
4
+ "radix-ui": "1.7.0",
5
+ "cmdk": "1.1.1",
6
+ "react-day-picker": "9.14.0",
7
+ "date-fns": "4.4.0",
8
+ "cn": "0.4.0",
9
+ "lucide-react": "1.52.0",
10
+ "class-variance-authority": "0.7.1",
11
+ "clsx": "2.1.1",
12
+ "tailwind-merge": "3.7.0"
13
+ }
14
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 shadcn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,61 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "cn";
4
+ import { Slot } from "radix-ui";
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
+ destructive:
13
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
14
+ outline:
15
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
16
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
17
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
18
+ link: "text-primary underline-offset-4 hover:underline",
19
+ },
20
+ size: {
21
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
22
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
23
+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
24
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
25
+ icon: "size-9",
26
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
27
+ "icon-sm": "size-8",
28
+ "icon-lg": "size-10",
29
+ },
30
+ },
31
+ defaultVariants: {
32
+ variant: "default",
33
+ size: "default",
34
+ },
35
+ },
36
+ );
37
+
38
+ function Button({
39
+ className,
40
+ variant = "default",
41
+ size = "default",
42
+ asChild = false,
43
+ ...props
44
+ }: React.ComponentProps<"button"> &
45
+ VariantProps<typeof buttonVariants> & {
46
+ asChild?: boolean;
47
+ }) {
48
+ const Comp = asChild ? Slot.Root : "button";
49
+
50
+ return (
51
+ <Comp
52
+ data-slot="button"
53
+ data-variant={variant}
54
+ data-size={size}
55
+ className={cn(buttonVariants({ variant, size, className }))}
56
+ {...props}
57
+ />
58
+ );
59
+ }
60
+
61
+ export { Button, buttonVariants };
@@ -0,0 +1,178 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { useCalendarDayButton } from "web/design-system/hooks/use-calendar-day-button";
5
+ import { cn } from "cn";
6
+ import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
7
+ import { DayPicker, getDefaultClassNames, type DayButton } from "react-day-picker";
8
+
9
+ import { Button, buttonVariants } from "web/components/ui/button";
10
+
11
+ function Calendar({
12
+ className,
13
+ classNames,
14
+ showOutsideDays = true,
15
+ captionLayout = "label",
16
+ buttonVariant = "ghost",
17
+ formatters,
18
+ components,
19
+ ...props
20
+ }: React.ComponentProps<typeof DayPicker> & {
21
+ buttonVariant?: React.ComponentProps<typeof Button>["variant"];
22
+ }) {
23
+ const defaultClassNames = getDefaultClassNames();
24
+
25
+ return (
26
+ <DayPicker
27
+ showOutsideDays={showOutsideDays}
28
+ className={cn(
29
+ "group/calendar bg-background p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
30
+ String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
31
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
32
+ className,
33
+ )}
34
+ captionLayout={captionLayout}
35
+ formatters={{
36
+ formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }),
37
+ ...formatters,
38
+ }}
39
+ classNames={{
40
+ root: cn("w-fit", defaultClassNames.root),
41
+ months: cn("relative flex flex-col gap-4 md:flex-row", defaultClassNames.months),
42
+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
43
+ nav: cn(
44
+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
45
+ defaultClassNames.nav,
46
+ ),
47
+ button_previous: cn(
48
+ buttonVariants({ variant: buttonVariant }),
49
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
50
+ defaultClassNames.button_previous,
51
+ ),
52
+ button_next: cn(
53
+ buttonVariants({ variant: buttonVariant }),
54
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
55
+ defaultClassNames.button_next,
56
+ ),
57
+ month_caption: cn(
58
+ "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
59
+ defaultClassNames.month_caption,
60
+ ),
61
+ dropdowns: cn(
62
+ "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
63
+ defaultClassNames.dropdowns,
64
+ ),
65
+ dropdown_root: cn(
66
+ "relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50",
67
+ defaultClassNames.dropdown_root,
68
+ ),
69
+ dropdown: cn("absolute inset-0 bg-popover opacity-0", defaultClassNames.dropdown),
70
+ caption_label: cn(
71
+ "font-medium select-none",
72
+ captionLayout === "label"
73
+ ? "text-sm"
74
+ : "flex h-8 items-center gap-1 rounded-md pr-1 pl-2 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
75
+ defaultClassNames.caption_label,
76
+ ),
77
+ month_grid: cn("w-full border-collapse", defaultClassNames.month_grid),
78
+ weekdays: cn("flex", defaultClassNames.weekdays),
79
+ weekday: cn(
80
+ "flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none",
81
+ defaultClassNames.weekday,
82
+ ),
83
+ week: cn("mt-2 flex w-full", defaultClassNames.week),
84
+ week_number_header: cn("w-(--cell-size) select-none", defaultClassNames.week_number_header),
85
+ week_number: cn(
86
+ "text-[0.8rem] text-muted-foreground select-none",
87
+ defaultClassNames.week_number,
88
+ ),
89
+ day: cn(
90
+ "group/day relative aspect-square h-full w-full p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-md",
91
+ props.showWeekNumber
92
+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
93
+ : "[&:first-child[data-selected=true]_button]:rounded-l-md",
94
+ defaultClassNames.day,
95
+ ),
96
+ range_start: cn("rounded-l-md bg-accent", defaultClassNames.range_start),
97
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
98
+ range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
99
+ today: cn(
100
+ "rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none",
101
+ defaultClassNames.today,
102
+ ),
103
+ outside: cn(
104
+ "text-muted-foreground aria-selected:text-muted-foreground",
105
+ defaultClassNames.outside,
106
+ ),
107
+ disabled: cn("text-muted-foreground opacity-50", defaultClassNames.disabled),
108
+ hidden: cn("invisible", defaultClassNames.hidden),
109
+ ...classNames,
110
+ }}
111
+ components={{
112
+ Root: ({ className, rootRef, ...props }) => {
113
+ return <div data-slot="calendar" ref={rootRef} className={cn(className)} {...props} />;
114
+ },
115
+ Chevron: ({ className, orientation, ...props }) => {
116
+ if (orientation === "left") {
117
+ return <ChevronLeftIcon className={cn("size-4", className)} {...props} />;
118
+ }
119
+
120
+ if (orientation === "right") {
121
+ return <ChevronRightIcon className={cn("size-4", className)} {...props} />;
122
+ }
123
+
124
+ return <ChevronDownIcon className={cn("size-4", className)} {...props} />;
125
+ },
126
+ DayButton: CalendarDayButton,
127
+ WeekNumber: ({ children, ...props }) => {
128
+ return (
129
+ <td {...props}>
130
+ <div className="flex size-(--cell-size) items-center justify-center text-center">
131
+ {children}
132
+ </div>
133
+ </td>
134
+ );
135
+ },
136
+ ...components,
137
+ }}
138
+ {...props}
139
+ />
140
+ );
141
+ }
142
+
143
+ function CalendarDayButton({
144
+ className,
145
+ day,
146
+ modifiers,
147
+ ...props
148
+ }: React.ComponentProps<typeof DayButton>) {
149
+ const defaultClassNames = getDefaultClassNames();
150
+
151
+ const ref = useCalendarDayButton(modifiers.focused);
152
+
153
+ return (
154
+ <Button
155
+ ref={ref}
156
+ variant="ghost"
157
+ size="icon"
158
+ data-day={day.date.toLocaleDateString()}
159
+ data-selected-single={
160
+ modifiers.selected &&
161
+ !modifiers.range_start &&
162
+ !modifiers.range_end &&
163
+ !modifiers.range_middle
164
+ }
165
+ data-range-start={modifiers.range_start}
166
+ data-range-end={modifiers.range_end}
167
+ data-range-middle={modifiers.range_middle}
168
+ className={cn(
169
+ "flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-accent-foreground [&>span]:text-xs [&>span]:opacity-70",
170
+ defaultClassNames.day,
171
+ className,
172
+ )}
173
+ {...props}
174
+ />
175
+ );
176
+ }
177
+
178
+ export { Calendar, CalendarDayButton };
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { cn } from "cn";
5
+ import { CheckIcon } from "lucide-react";
6
+ import { Checkbox as CheckboxPrimitive } from "radix-ui";
7
+
8
+ function Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
9
+ return (
10
+ <CheckboxPrimitive.Root
11
+ data-slot="checkbox"
12
+ className={cn(
13
+ "peer size-4 shrink-0 rounded-[4px] border border-input shadow-xs transition-shadow outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-[state=checked]:bg-primary",
14
+ className,
15
+ )}
16
+ {...props}
17
+ >
18
+ <CheckboxPrimitive.Indicator
19
+ data-slot="checkbox-indicator"
20
+ className="grid place-content-center text-current transition-none"
21
+ >
22
+ <CheckIcon className="size-3.5" />
23
+ </CheckboxPrimitive.Indicator>
24
+ </CheckboxPrimitive.Root>
25
+ );
26
+ }
27
+
28
+ export { Checkbox };