create-ec-app 1.8.0 → 1.10.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 (104) hide show
  1. package/README.md +72 -17
  2. package/dist/cssScope.js +3 -5
  3. package/dist/cssScope.js.map +1 -1
  4. package/dist/index.d.ts +46 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +129 -53
  7. package/dist/index.js.map +1 -1
  8. package/dist/libFunctions.d.ts +13 -6
  9. package/dist/libFunctions.d.ts.map +1 -1
  10. package/dist/libFunctions.js +24 -9
  11. package/dist/libFunctions.js.map +1 -1
  12. package/dist/pcf.d.ts.map +1 -1
  13. package/dist/pcf.js +4 -1
  14. package/dist/pcf.js.map +1 -1
  15. package/dist/portalContainers.js +7 -5
  16. package/dist/portalContainers.js.map +1 -1
  17. package/package.json +18 -11
  18. package/scripts/build-generated.mjs +59 -0
  19. package/scripts/refresh-shadcn-template.ts +406 -0
  20. package/scripts/smoke-scaffold.mjs +245 -0
  21. package/templates/base/eslint.config.js +1 -1
  22. package/templates/base/package-lock.json +380 -476
  23. package/templates/base/package.json +14 -19
  24. package/templates/pcf/base/package-lock.json +35 -53
  25. package/templates/targets/code-apps/AGENTS.md +43 -1
  26. package/templates/targets/code-apps/CLAUDE.md +1 -0
  27. package/templates/targets/code-apps/package.patch.json +1 -1
  28. package/templates/targets/power-pages/AGENTS.md +43 -1
  29. package/templates/targets/power-pages/CLAUDE.md +1 -0
  30. package/templates/targets/power-pages/README.md +22 -2
  31. package/templates/targets/power-pages/src/App.patch.tsx +3 -1
  32. package/templates/targets/power-pages/src/components/shared/AuthError.tsx +18 -0
  33. package/templates/targets/power-pages/src/context/AuthContext.tsx +0 -4
  34. package/templates/targets/swa/AGENTS.md +42 -0
  35. package/templates/targets/swa/CLAUDE.md +1 -0
  36. package/templates/targets/webresource/AGENTS.md +47 -4
  37. package/templates/targets/webresource/CLAUDE.md +1 -0
  38. package/templates/targets/webresource/README.md +5 -5
  39. package/templates/ui/kendo/package.patch.json +2 -2
  40. package/templates/ui/shadcn-ui/SHADCN_TEMPLATE.md +20 -0
  41. package/templates/ui/shadcn-ui/package.patch.json +18 -9
  42. package/templates/ui/shadcn-ui/src/components/ui/accordion.tsx +79 -0
  43. package/templates/ui/shadcn-ui/src/components/ui/alert-dialog.tsx +199 -0
  44. package/templates/ui/shadcn-ui/src/components/ui/alert.tsx +76 -0
  45. package/templates/ui/shadcn-ui/src/components/ui/aspect-ratio.tsx +11 -0
  46. package/templates/ui/shadcn-ui/src/components/ui/attachment.tsx +206 -0
  47. package/templates/ui/shadcn-ui/src/components/ui/avatar.tsx +110 -0
  48. package/templates/ui/shadcn-ui/src/components/ui/badge.tsx +49 -0
  49. package/templates/ui/shadcn-ui/src/components/ui/breadcrumb.tsx +122 -0
  50. package/templates/ui/shadcn-ui/src/components/ui/bubble.tsx +125 -0
  51. package/templates/ui/shadcn-ui/src/components/ui/button-group.tsx +83 -0
  52. package/templates/ui/shadcn-ui/src/components/ui/button.tsx +67 -0
  53. package/templates/ui/shadcn-ui/src/components/ui/calendar.tsx +222 -0
  54. package/templates/ui/shadcn-ui/src/components/ui/card.tsx +103 -0
  55. package/templates/ui/shadcn-ui/src/components/ui/carousel.tsx +240 -0
  56. package/templates/ui/shadcn-ui/src/components/ui/chart.tsx +373 -0
  57. package/templates/ui/shadcn-ui/src/components/ui/checkbox.tsx +31 -0
  58. package/templates/ui/shadcn-ui/src/components/ui/collapsible.tsx +33 -0
  59. package/templates/ui/shadcn-ui/src/components/ui/combobox.tsx +299 -0
  60. package/templates/ui/shadcn-ui/src/components/ui/command.tsx +195 -0
  61. package/templates/ui/shadcn-ui/src/components/ui/context-menu.tsx +264 -0
  62. package/templates/ui/shadcn-ui/src/components/ui/dialog.tsx +170 -0
  63. package/templates/ui/shadcn-ui/src/components/ui/direction.tsx +22 -0
  64. package/templates/ui/shadcn-ui/src/components/ui/drawer.tsx +134 -0
  65. package/templates/ui/shadcn-ui/src/components/ui/dropdown-menu.tsx +272 -0
  66. package/templates/ui/shadcn-ui/src/components/ui/empty.tsx +104 -0
  67. package/templates/ui/shadcn-ui/src/components/ui/field.tsx +236 -0
  68. package/templates/ui/shadcn-ui/src/components/ui/hover-card.tsx +44 -0
  69. package/templates/ui/shadcn-ui/src/components/ui/input-group.tsx +156 -0
  70. package/templates/ui/shadcn-ui/src/components/ui/input-otp.tsx +87 -0
  71. package/templates/ui/shadcn-ui/src/components/ui/input.tsx +19 -0
  72. package/templates/ui/shadcn-ui/src/components/ui/item.tsx +196 -0
  73. package/templates/ui/shadcn-ui/src/components/ui/kbd.tsx +26 -0
  74. package/templates/ui/shadcn-ui/src/components/ui/label.tsx +22 -0
  75. package/templates/ui/shadcn-ui/src/components/ui/marker.tsx +69 -0
  76. package/templates/ui/shadcn-ui/src/components/ui/menubar.tsx +282 -0
  77. package/templates/ui/shadcn-ui/src/components/ui/message-scroller.tsx +129 -0
  78. package/templates/ui/shadcn-ui/src/components/ui/message.tsx +92 -0
  79. package/templates/ui/shadcn-ui/src/components/ui/native-select.tsx +61 -0
  80. package/templates/ui/shadcn-ui/src/components/ui/navigation-menu.tsx +164 -0
  81. package/templates/ui/shadcn-ui/src/components/ui/pagination.tsx +129 -0
  82. package/templates/ui/shadcn-ui/src/components/ui/popover.tsx +89 -0
  83. package/templates/ui/shadcn-ui/src/components/ui/progress.tsx +31 -0
  84. package/templates/ui/shadcn-ui/src/components/ui/radio-group.tsx +42 -0
  85. package/templates/ui/shadcn-ui/src/components/ui/resizable.tsx +50 -0
  86. package/templates/ui/shadcn-ui/src/components/ui/scroll-area.tsx +53 -0
  87. package/templates/ui/shadcn-ui/src/components/ui/select.tsx +194 -0
  88. package/templates/ui/shadcn-ui/src/components/ui/separator.tsx +26 -0
  89. package/templates/ui/shadcn-ui/src/components/ui/sheet.tsx +149 -0
  90. package/templates/ui/shadcn-ui/src/components/ui/sidebar.tsx +702 -0
  91. package/templates/ui/shadcn-ui/src/components/ui/skeleton.tsx +13 -0
  92. package/templates/ui/shadcn-ui/src/components/ui/slider.tsx +59 -0
  93. package/templates/ui/shadcn-ui/src/components/ui/sonner.tsx +47 -0
  94. package/templates/ui/shadcn-ui/src/components/ui/spinner.tsx +10 -0
  95. package/templates/ui/shadcn-ui/src/components/ui/switch.tsx +33 -0
  96. package/templates/ui/shadcn-ui/src/components/ui/table.tsx +114 -0
  97. package/templates/ui/shadcn-ui/src/components/ui/tabs.tsx +90 -0
  98. package/templates/ui/shadcn-ui/src/components/ui/textarea.tsx +18 -0
  99. package/templates/ui/shadcn-ui/src/components/ui/toggle-group.tsx +87 -0
  100. package/templates/ui/shadcn-ui/src/components/ui/toggle.tsx +45 -0
  101. package/templates/ui/shadcn-ui/src/components/ui/tooltip.tsx +59 -0
  102. package/templates/ui/shadcn-ui/src/index.patch.css +0 -118
  103. package/templates/ui/shadcn-ui/src/runtime/PortalContainer.ts +8 -0
  104. package/templates/base/biome.json +0 -54
@@ -34,13 +34,13 @@ The app supports two modes.
34
34
  - Never commit real token values.
35
35
  - Never bundle `token.json` into deployment output.
36
36
 
37
- Do not mix the two modes. Do not duplicate runtime detection. Reuse `authService.ts`.
37
+ Do not mix the two modes. Do not duplicate runtime detection. Reuse `AuthService.ts`.
38
38
 
39
39
  ## Critical Files
40
40
 
41
41
  | File | Rule |
42
42
  |---|---|
43
- | `src/services/authService.ts` | Single source of truth for runtime detection, base URL, and auth headers. |
43
+ | `src/services/AuthService.ts` | Single source of truth for runtime detection, base URL, and auth headers. |
44
44
  | `src/main.tsx` | Preserve bootstrap, providers, and global theme/style imports. |
45
45
  | `vite.config.ts` | Preserve Dynamics-friendly output: `base: "./"`, predictable filenames, and `main.css`. |
46
46
  | `index.html` | Treat as the Dynamics integration boundary. Preserve `ClientGlobalContext.js.aspx` where present. |
@@ -102,6 +102,7 @@ Preferred shape:
102
102
  - one TanStack Query hook when components need it
103
103
  - one mutation hook when mutation state or invalidation is needed
104
104
  - query keys colocated with the hook when reused for invalidation
105
+ - UI that consumes async data should handle loading, error, empty, and success states when each state affects the workflow
105
106
 
106
107
  Do not create wrapper chains such as:
107
108
 
@@ -134,6 +135,13 @@ Stay consistent with the project's existing UI system.
134
135
  - Do not hand-roll custom CSS unless component props and Tailwind are not enough.
135
136
  - Keep layouts compact, scannable, responsive, and suitable for embedded Dynamics screens.
136
137
 
138
+ ## Accessibility
139
+
140
+ - Use semantic HTML.
141
+ - Use buttons for actions and links for navigation.
142
+ - Keep input labels, accessible names, dialog titles, and keyboard support intact.
143
+ - Do not remove shadcn/Radix or Kendo accessibility behavior while restyling.
144
+
137
145
  ## Code Shape
138
146
 
139
147
  Prefer:
@@ -143,6 +151,7 @@ Prefer:
143
151
  - existing services and components
144
152
  - small local helpers only when they remove real duplication or name non-obvious domain logic
145
153
  - explicit Dataverse table/field handling over generic frameworks
154
+ - feature folders only when a feature owns multiple pieces such as API, hooks, components, types, or schemas
146
155
 
147
156
  Avoid:
148
157
 
@@ -152,6 +161,39 @@ Avoid:
152
161
  - excessive configuration
153
162
  - defensive wrappers around every value
154
163
  - broad refactors while adding a feature
164
+ - moving code into shared `common` or `lib` before there is a second caller
165
+
166
+ ## Feature and Routing Shape
167
+
168
+ Keep small apps flat. Use feature folders when a feature owns enough surface area to group its page, UI, data access, hooks, and types.
169
+
170
+ Example:
171
+
172
+ ```text
173
+ src/
174
+ features/
175
+ accounts/
176
+ pages/
177
+ accounts-page.tsx
178
+ components/
179
+ accounts-table.tsx
180
+ api/
181
+ accounts.api.ts
182
+ hooks/
183
+ use-accounts.ts
184
+ types/
185
+ account.types.ts
186
+ ```
187
+
188
+ Use only folders that contain real files. Flatten the example when the feature is small.
189
+
190
+ When React Router or another router is present:
191
+
192
+ - keep route definitions in one obvious place, such as `src/routes.tsx` or `src/router/routes.tsx`
193
+ - keep page components thin; they read route/search params and compose feature components
194
+ - parse URL params at the route/page boundary, then pass typed values down
195
+ - prefer hash/search-param routing for embedded webresources unless the Dynamics host URL is proven to support browser history routing
196
+ - do not add a router for a single screen that local state can handle
155
197
 
156
198
  ## Error Handling
157
199
 
@@ -183,7 +225,7 @@ Do not run broad expensive checks unless the change touches shared infrastructur
183
225
  ```ts
184
226
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
185
227
 
186
- import { getApiUrl, getAuthHeaders } from "@/services/authService";
228
+ import { getApiUrl, getAuthHeaders } from "@/services/AuthService";
187
229
 
188
230
  export interface Account {
189
231
  accountid: string;
@@ -234,9 +276,10 @@ export const useUpdateAccount = () => {
234
276
  });
235
277
  };
236
278
  ```
279
+
237
280
  ## Figma MCP
238
281
  When using the Figma MCP server, ensure that you are not just blindly copying the designs. Take note and always place a focus on the following:
239
282
 
240
283
  - Ensure responsiveness on all screen sizes
241
284
  - If there are icons as part of the design, use those, don't blindly look for Lucide-React equivalents.
242
- - Use the exact colours in the design. Don't make up your own.
285
+ - Use the exact colours in the design. Don't make up your own.
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -30,7 +30,7 @@ React + TypeScript template for Dynamics 365/Dataverse web resources. Generated
30
30
  ## Key Files
31
31
 
32
32
  - `src/main.tsx`: Sets up React, Tailwind, and TanStack Query. If Kendo UI was selected, imports the chosen theme CSS (`<theme>/dist/all.css`).
33
- - `src/services/authService.ts`: Utilities to build API URLs and headers based on environment (inside Dynamics vs. local dev).
33
+ - `src/services/AuthService.ts`: Utilities to build API URLs and headers based on environment (inside Dynamics vs. local dev).
34
34
  - `token.json`: Local development token store. Build is configured to treat this as external and not bundle it.
35
35
  - `index.html`: Injects `ClientGlobalContext.js.aspx` for Dynamics runtime.
36
36
  - `vite.config.ts`: Uses base `./`, disables code splitting, emits `main.css`, and places assets at the top of `dist`.
@@ -40,7 +40,7 @@ React + TypeScript template for Dynamics 365/Dataverse web resources. Generated
40
40
  The app auto-detects whether it runs inside Dynamics 365 (uses `window.Xrm` and does not add an Authorization header) or locally (reads a bearer token from `token.json`).
41
41
 
42
42
  ```ts
43
- // src/services/authService.ts
43
+ // src/services/AuthService.ts
44
44
  export const getApiUrl = (): string => {
45
45
  if (window.parent && window.parent.Xrm) {
46
46
  const clientUrl = window.Xrm.Utility.getGlobalContext().getClientUrl();
@@ -72,7 +72,7 @@ export const getAuthHeaders = async (): Promise<HeadersInit> => {
72
72
  Example usage:
73
73
 
74
74
  ```ts
75
- import { getApiUrl, getAuthHeaders } from "@/services/authService";
75
+ import { getApiUrl, getAuthHeaders } from "@/services/AuthService";
76
76
 
77
77
  const res = await fetch(`${getApiUrl()}/accounts?$top=10`, {
78
78
  headers: await getAuthHeaders(),
@@ -139,7 +139,7 @@ The following example shows a minimal data service and hooks to fetch and update
139
139
  Create `src/services/accounts.ts`:
140
140
 
141
141
  ```ts
142
- import { getApiUrl, getAuthHeaders } from "@/services/authService";
142
+ import { getApiUrl, getAuthHeaders } from "@/services/AuthService";
143
143
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
144
144
 
145
145
  export interface Account {
@@ -232,7 +232,7 @@ export function AccountsList() {
232
232
  <Button>Click me</Button>;
233
233
  ```
234
234
 
235
- - Shadcn/ui: Components are installed and available under the `@/components` alias. Example:
235
+ - Shadcn/ui: Components are copied from the committed template snapshot and available under the `@/components` alias. Example:
236
236
 
237
237
  ```tsx
238
238
  import { Button } from "@/components/ui/button";
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
3
  "@progress/kendo-licensing": "^1.11.2",
4
- "@progress/kendo-react-buttons": "^13.3.0",
5
- "@progress/kendo-theme-fluent": "^12.3.0"
4
+ "@progress/kendo-react-buttons": "^15.0.0",
5
+ "@progress/kendo-theme-fluent": "^14.1.0"
6
6
  }
7
7
  }
@@ -0,0 +1,20 @@
1
+ # shadcn Template Snapshot
2
+
3
+ This template contains committed shadcn component source.
4
+
5
+ Generated with:
6
+
7
+ - shadcn CLI: 4.12.0
8
+ - style: radix-nova
9
+ - base color: neutral
10
+ - Tailwind: v4
11
+ - generated by: `npm run refresh:shadcn-template`
12
+
13
+ Normal `create-ec-app` scaffolding does not run `npx shadcn`.
14
+ To refresh this snapshot, run:
15
+
16
+ ```bash
17
+ npm run refresh:shadcn-template
18
+ ```
19
+
20
+ After refreshing, generate a test app and run a build before committing.
@@ -1,13 +1,22 @@
1
1
  {
2
2
  "dependencies": {
3
- "class-variance-authority": "^0.7.1",
4
- "clsx": "^2.1.1",
5
- "lucide-react": "^1.17.0",
6
- "radix-ui": "^1.4.3",
7
- "shadcn": "^4.10.0",
8
- "tailwind-merge": "^3.6.0"
9
- },
10
- "devDependencies": {
11
- "tw-animate-css": "^1.4.0"
3
+ "@base-ui/react": "1.6.0",
4
+ "@shadcn/react": "0.1.0",
5
+ "class-variance-authority": "0.7.1",
6
+ "clsx": "2.1.1",
7
+ "cmdk": "1.1.1",
8
+ "date-fns": "4.4.0",
9
+ "embla-carousel-react": "8.6.0",
10
+ "input-otp": "1.4.2",
11
+ "lucide-react": "1.21.0",
12
+ "next-themes": "0.4.6",
13
+ "radix-ui": "1.6.0",
14
+ "react-day-picker": "10.0.1",
15
+ "react-resizable-panels": "4.12.0",
16
+ "recharts": "3.8.0",
17
+ "shadcn": "4.12.0",
18
+ "sonner": "2.0.7",
19
+ "tailwind-merge": "3.6.0",
20
+ "vaul": "1.1.2"
12
21
  }
13
22
  }
@@ -0,0 +1,79 @@
1
+ import * as React from "react"
2
+ import { Accordion as AccordionPrimitive } from "radix-ui"
3
+
4
+ import { cn } from "@/lib/utils"
5
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
6
+
7
+ function Accordion({
8
+ className,
9
+ ...props
10
+ }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
11
+ return (
12
+ <AccordionPrimitive.Root
13
+ data-slot="accordion"
14
+ className={cn("flex w-full flex-col", className)}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ function AccordionItem({
21
+ className,
22
+ ...props
23
+ }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
24
+ return (
25
+ <AccordionPrimitive.Item
26
+ data-slot="accordion-item"
27
+ className={cn("not-last:border-b", className)}
28
+ {...props}
29
+ />
30
+ )
31
+ }
32
+
33
+ function AccordionTrigger({
34
+ className,
35
+ children,
36
+ ...props
37
+ }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
38
+ return (
39
+ <AccordionPrimitive.Header className="flex">
40
+ <AccordionPrimitive.Trigger
41
+ data-slot="accordion-trigger"
42
+ className={cn(
43
+ "group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
44
+ className
45
+ )}
46
+ {...props}
47
+ >
48
+ {children}
49
+ <ChevronDownIcon data-slot="accordion-trigger-icon" className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
50
+ <ChevronUpIcon data-slot="accordion-trigger-icon" className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
51
+ </AccordionPrimitive.Trigger>
52
+ </AccordionPrimitive.Header>
53
+ )
54
+ }
55
+
56
+ function AccordionContent({
57
+ className,
58
+ children,
59
+ ...props
60
+ }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
61
+ return (
62
+ <AccordionPrimitive.Content
63
+ data-slot="accordion-content"
64
+ className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
65
+ {...props}
66
+ >
67
+ <div
68
+ className={cn(
69
+ "h-(--radix-accordion-content-height) pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
70
+ className
71
+ )}
72
+ >
73
+ {children}
74
+ </div>
75
+ </AccordionPrimitive.Content>
76
+ )
77
+ }
78
+
79
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
@@ -0,0 +1,199 @@
1
+ import * as React from "react"
2
+ import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
3
+
4
+ import { cn } from "@/lib/utils"
5
+ import { Button } from "@/components/ui/button"
6
+ import { usePortalContainer } from "@/runtime/PortalContainer"
7
+
8
+ function AlertDialog({
9
+ ...props
10
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
11
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
12
+ }
13
+
14
+ function AlertDialogTrigger({
15
+ ...props
16
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
17
+ return (
18
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
19
+ )
20
+ }
21
+
22
+ function AlertDialogPortal({
23
+ ...props
24
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
25
+ const portalContainer = usePortalContainer()
26
+ return (
27
+ <AlertDialogPrimitive.Portal container={portalContainer ?? undefined} data-slot="alert-dialog-portal" {...props} />
28
+ )
29
+ }
30
+
31
+ function AlertDialogOverlay({
32
+ className,
33
+ ...props
34
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
35
+ return (
36
+ <AlertDialogPrimitive.Overlay
37
+ data-slot="alert-dialog-overlay"
38
+ className={cn(
39
+ "fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
40
+ className
41
+ )}
42
+ {...props}
43
+ />
44
+ )
45
+ }
46
+
47
+ function AlertDialogContent({
48
+ className,
49
+ size = "default",
50
+ ...props
51
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
52
+ size?: "default" | "sm"
53
+ }) {
54
+ return (
55
+ <AlertDialogPortal>
56
+ <AlertDialogOverlay />
57
+ <AlertDialogPrimitive.Content
58
+ data-slot="alert-dialog-content"
59
+ data-size={size}
60
+ className={cn(
61
+ "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
62
+ className
63
+ )}
64
+ {...props}
65
+ />
66
+ </AlertDialogPortal>
67
+ )
68
+ }
69
+
70
+ function AlertDialogHeader({
71
+ className,
72
+ ...props
73
+ }: React.ComponentProps<"div">) {
74
+ return (
75
+ <div
76
+ data-slot="alert-dialog-header"
77
+ className={cn(
78
+ "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
79
+ className
80
+ )}
81
+ {...props}
82
+ />
83
+ )
84
+ }
85
+
86
+ function AlertDialogFooter({
87
+ className,
88
+ ...props
89
+ }: React.ComponentProps<"div">) {
90
+ return (
91
+ <div
92
+ data-slot="alert-dialog-footer"
93
+ className={cn(
94
+ "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
95
+ className
96
+ )}
97
+ {...props}
98
+ />
99
+ )
100
+ }
101
+
102
+ function AlertDialogMedia({
103
+ className,
104
+ ...props
105
+ }: React.ComponentProps<"div">) {
106
+ return (
107
+ <div
108
+ data-slot="alert-dialog-media"
109
+ className={cn(
110
+ "mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
111
+ className
112
+ )}
113
+ {...props}
114
+ />
115
+ )
116
+ }
117
+
118
+ function AlertDialogTitle({
119
+ className,
120
+ ...props
121
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
122
+ return (
123
+ <AlertDialogPrimitive.Title
124
+ data-slot="alert-dialog-title"
125
+ className={cn(
126
+ "text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
127
+ className
128
+ )}
129
+ {...props}
130
+ />
131
+ )
132
+ }
133
+
134
+ function AlertDialogDescription({
135
+ className,
136
+ ...props
137
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
138
+ return (
139
+ <AlertDialogPrimitive.Description
140
+ data-slot="alert-dialog-description"
141
+ className={cn(
142
+ "text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
143
+ className
144
+ )}
145
+ {...props}
146
+ />
147
+ )
148
+ }
149
+
150
+ function AlertDialogAction({
151
+ className,
152
+ variant = "default",
153
+ size = "default",
154
+ ...props
155
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
156
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
157
+ return (
158
+ <Button variant={variant} size={size} asChild>
159
+ <AlertDialogPrimitive.Action
160
+ data-slot="alert-dialog-action"
161
+ className={cn(className)}
162
+ {...props}
163
+ />
164
+ </Button>
165
+ )
166
+ }
167
+
168
+ function AlertDialogCancel({
169
+ className,
170
+ variant = "outline",
171
+ size = "default",
172
+ ...props
173
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
174
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
175
+ return (
176
+ <Button variant={variant} size={size} asChild>
177
+ <AlertDialogPrimitive.Cancel
178
+ data-slot="alert-dialog-cancel"
179
+ className={cn(className)}
180
+ {...props}
181
+ />
182
+ </Button>
183
+ )
184
+ }
185
+
186
+ export {
187
+ AlertDialog,
188
+ AlertDialogAction,
189
+ AlertDialogCancel,
190
+ AlertDialogContent,
191
+ AlertDialogDescription,
192
+ AlertDialogFooter,
193
+ AlertDialogHeader,
194
+ AlertDialogMedia,
195
+ AlertDialogOverlay,
196
+ AlertDialogPortal,
197
+ AlertDialogTitle,
198
+ AlertDialogTrigger,
199
+ }
@@ -0,0 +1,76 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const alertVariants = cva(
7
+ "group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-card text-card-foreground",
12
+ destructive:
13
+ "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "default",
18
+ },
19
+ }
20
+ )
21
+
22
+ function Alert({
23
+ className,
24
+ variant,
25
+ ...props
26
+ }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
27
+ return (
28
+ <div
29
+ data-slot="alert"
30
+ role="alert"
31
+ className={cn(alertVariants({ variant }), className)}
32
+ {...props}
33
+ />
34
+ )
35
+ }
36
+
37
+ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
38
+ return (
39
+ <div
40
+ data-slot="alert-title"
41
+ className={cn(
42
+ "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
43
+ className
44
+ )}
45
+ {...props}
46
+ />
47
+ )
48
+ }
49
+
50
+ function AlertDescription({
51
+ className,
52
+ ...props
53
+ }: React.ComponentProps<"div">) {
54
+ return (
55
+ <div
56
+ data-slot="alert-description"
57
+ className={cn(
58
+ "text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
59
+ className
60
+ )}
61
+ {...props}
62
+ />
63
+ )
64
+ }
65
+
66
+ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
67
+ return (
68
+ <div
69
+ data-slot="alert-action"
70
+ className={cn("absolute top-2 right-2", className)}
71
+ {...props}
72
+ />
73
+ )
74
+ }
75
+
76
+ export { Alert, AlertTitle, AlertDescription, AlertAction }
@@ -0,0 +1,11 @@
1
+ "use client"
2
+
3
+ import { AspectRatio as AspectRatioPrimitive } from "radix-ui"
4
+
5
+ function AspectRatio({
6
+ ...props
7
+ }: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
8
+ return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />
9
+ }
10
+
11
+ export { AspectRatio }