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.
- package/CHANGELOG.md +22 -0
- package/README.md +8 -0
- package/esm/commands/create-new-app/index.mjs +4 -3
- package/esm/commands/create-new-app/index.mjs.map +1 -1
- package/esm/commands/create-warlock-app/index.mjs +7 -1
- package/esm/commands/create-warlock-app/index.mjs.map +1 -1
- package/esm/features/features-map.mjs +8 -2
- package/esm/features/features-map.mjs.map +1 -1
- package/esm/helpers/app.mjs +1 -0
- package/esm/helpers/app.mjs.map +1 -1
- package/esm/prompts/ask-stack.mjs +1 -1
- package/esm/prompts/ask-stack.mjs.map +1 -1
- package/llms-full.txt +58 -15
- package/package.json +2 -2
- package/skills/SKILL.md +58 -15
- package/templates/design-system/SOURCES.md +23 -0
- package/templates/design-system/package.json +14 -0
- package/templates/design-system/src/web/components/ui/LICENSE.md +21 -0
- package/templates/design-system/src/web/components/ui/button.tsx +61 -0
- package/templates/design-system/src/web/components/ui/calendar.tsx +178 -0
- package/templates/design-system/src/web/components/ui/checkbox.tsx +28 -0
- package/templates/design-system/src/web/components/ui/command.tsx +161 -0
- package/templates/design-system/src/web/components/ui/dialog.tsx +144 -0
- package/templates/design-system/src/web/components/ui/input.tsx +20 -0
- package/templates/design-system/src/web/components/ui/label.tsx +20 -0
- package/templates/design-system/src/web/components/ui/popover.tsx +73 -0
- package/templates/design-system/src/web/components/ui/radio-group.tsx +44 -0
- package/templates/design-system/src/web/components/ui/switch.tsx +34 -0
- package/templates/design-system/src/web/components/ui/textarea.tsx +17 -0
- package/templates/design-system/src/web/design-system/components/checkbox-input.tsx +19 -0
- package/templates/design-system/src/web/design-system/components/date-picker-input.tsx +59 -0
- package/templates/design-system/src/web/design-system/components/date-time-picker-input.tsx +67 -0
- package/templates/design-system/src/web/design-system/components/email-input.tsx +22 -0
- package/templates/design-system/src/web/design-system/components/field-frame.tsx +36 -0
- package/templates/design-system/src/web/design-system/components/file-input.tsx +24 -0
- package/templates/design-system/src/web/design-system/components/multi-select-input.tsx +94 -0
- package/templates/design-system/src/web/design-system/components/password-input.tsx +22 -0
- package/templates/design-system/src/web/design-system/components/radio-input.tsx +41 -0
- package/templates/design-system/src/web/design-system/components/select-input.tsx +94 -0
- package/templates/design-system/src/web/design-system/components/submit-button.tsx +20 -0
- package/templates/design-system/src/web/design-system/components/switch-input.tsx +19 -0
- package/templates/design-system/src/web/design-system/components/text-input.tsx +22 -0
- package/templates/design-system/src/web/design-system/components/textarea-input.tsx +22 -0
- package/templates/design-system/src/web/design-system/components/time-picker-input.tsx +22 -0
- package/templates/design-system/src/web/design-system/hooks/use-calendar-day-button.ts +9 -0
- package/templates/design-system/src/web/design-system/hooks/use-control.ts +29 -0
- package/templates/design-system/src/web/design-system/hooks/use-date-control.ts +42 -0
- package/templates/design-system/src/web/design-system/hooks/use-file-control.ts +24 -0
- package/templates/design-system/src/web/design-system/hooks/use-select-control.ts +78 -0
- package/templates/design-system/src/web/design-system/hooks/use-submit-control.ts +13 -0
- package/templates/design-system/src/web/design-system/types/control-props.ts +46 -0
- package/templates/warlock/e2e/home.e2e.ts +19 -0
- package/templates/warlock/package.json +1 -1
- package/templates/warlock/skills/api-design/SKILL.md +72 -9
- package/templates/warlock/skills/testing-strategy/SKILL.md +46 -5
- package/templates/warlock/skills/web-code-standards/SKILL.md +38 -0
- package/templates/warlock/src/app/auth/controllers/forgot-password.controller.ts +6 -0
- package/templates/warlock/src/app/auth/controllers/login.controller.ts +17 -0
- package/templates/warlock/src/app/auth/controllers/logout-all.controller.ts +6 -0
- package/templates/warlock/src/app/auth/controllers/logout.controller.ts +6 -0
- package/templates/warlock/src/app/auth/controllers/me.controller.ts +7 -0
- package/templates/warlock/src/app/auth/controllers/refresh-token.controller.ts +12 -0
- package/templates/warlock/src/app/auth/controllers/reset-password.controller.ts +6 -0
- package/templates/warlock/src/app/home/controllers/home-page.controller.ts +4 -0
- package/templates/warlock/src/app/posts/controllers/update-post.controller.ts +1 -0
- package/templates/warlock/src/app/shared/utils/format-http-error-response.ts +13 -1
- package/templates/warlock/src/app/users/controllers/create-new-user.controller.ts +1 -0
- 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
|
|
76
|
-
| ------------------------------ | ----------- |
|
|
77
|
-
| `<positional>` / `--name` | yes | — (required)
|
|
78
|
-
| `--stack` | yes | `api`
|
|
79
|
-
| `--db` | yes | `mongodb`
|
|
80
|
-
| `--no-db` | no | —
|
|
81
|
-
| `--pm` | yes | inferred (`npm_config_user_agent` / system)
|
|
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`
|
|
84
|
-
| `--ai` | yes (CSV) | none
|
|
85
|
-
| `--agents` | yes (CSV) | `claude`
|
|
86
|
-
| `--git` / `--no-git` | no | off
|
|
87
|
-
| `--jwt` / `--no-jwt` | no | off
|
|
88
|
-
| `--yes` / `-y` | no | off
|
|
89
|
-
| `--interactive`, `--customize` | no | off
|
|
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.
|
|
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.
|
|
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
|
|
68
|
-
| ------------------------------ | ----------- |
|
|
69
|
-
| `<positional>` / `--name` | yes | — (required)
|
|
70
|
-
| `--stack` | yes | `api`
|
|
71
|
-
| `--db` | yes | `mongodb`
|
|
72
|
-
| `--no-db` | no | —
|
|
73
|
-
| `--pm` | yes | inferred (`npm_config_user_agent` / system)
|
|
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`
|
|
76
|
-
| `--ai` | yes (CSV) | none
|
|
77
|
-
| `--agents` | yes (CSV) | `claude`
|
|
78
|
-
| `--git` / `--no-git` | no | off
|
|
79
|
-
| `--jwt` / `--no-jwt` | no | off
|
|
80
|
-
| `--yes` / `-y` | no | off
|
|
81
|
-
| `--interactive`, `--customize` | no | off
|
|
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 };
|