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.
- package/README.md +72 -17
- package/dist/cssScope.js +3 -5
- package/dist/cssScope.js.map +1 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +129 -53
- package/dist/index.js.map +1 -1
- package/dist/libFunctions.d.ts +13 -6
- package/dist/libFunctions.d.ts.map +1 -1
- package/dist/libFunctions.js +24 -9
- package/dist/libFunctions.js.map +1 -1
- package/dist/pcf.d.ts.map +1 -1
- package/dist/pcf.js +4 -1
- package/dist/pcf.js.map +1 -1
- package/dist/portalContainers.js +7 -5
- package/dist/portalContainers.js.map +1 -1
- package/package.json +18 -11
- package/scripts/build-generated.mjs +59 -0
- package/scripts/refresh-shadcn-template.ts +406 -0
- package/scripts/smoke-scaffold.mjs +245 -0
- package/templates/base/eslint.config.js +1 -1
- package/templates/base/package-lock.json +380 -476
- package/templates/base/package.json +14 -19
- package/templates/pcf/base/package-lock.json +35 -53
- package/templates/targets/code-apps/AGENTS.md +43 -1
- package/templates/targets/code-apps/CLAUDE.md +1 -0
- package/templates/targets/code-apps/package.patch.json +1 -1
- package/templates/targets/power-pages/AGENTS.md +43 -1
- package/templates/targets/power-pages/CLAUDE.md +1 -0
- package/templates/targets/power-pages/README.md +22 -2
- package/templates/targets/power-pages/src/App.patch.tsx +3 -1
- package/templates/targets/power-pages/src/components/shared/AuthError.tsx +18 -0
- package/templates/targets/power-pages/src/context/AuthContext.tsx +0 -4
- package/templates/targets/swa/AGENTS.md +42 -0
- package/templates/targets/swa/CLAUDE.md +1 -0
- package/templates/targets/webresource/AGENTS.md +47 -4
- package/templates/targets/webresource/CLAUDE.md +1 -0
- package/templates/targets/webresource/README.md +5 -5
- package/templates/ui/kendo/package.patch.json +2 -2
- package/templates/ui/shadcn-ui/SHADCN_TEMPLATE.md +20 -0
- package/templates/ui/shadcn-ui/package.patch.json +18 -9
- package/templates/ui/shadcn-ui/src/components/ui/accordion.tsx +79 -0
- package/templates/ui/shadcn-ui/src/components/ui/alert-dialog.tsx +199 -0
- package/templates/ui/shadcn-ui/src/components/ui/alert.tsx +76 -0
- package/templates/ui/shadcn-ui/src/components/ui/aspect-ratio.tsx +11 -0
- package/templates/ui/shadcn-ui/src/components/ui/attachment.tsx +206 -0
- package/templates/ui/shadcn-ui/src/components/ui/avatar.tsx +110 -0
- package/templates/ui/shadcn-ui/src/components/ui/badge.tsx +49 -0
- package/templates/ui/shadcn-ui/src/components/ui/breadcrumb.tsx +122 -0
- package/templates/ui/shadcn-ui/src/components/ui/bubble.tsx +125 -0
- package/templates/ui/shadcn-ui/src/components/ui/button-group.tsx +83 -0
- package/templates/ui/shadcn-ui/src/components/ui/button.tsx +67 -0
- package/templates/ui/shadcn-ui/src/components/ui/calendar.tsx +222 -0
- package/templates/ui/shadcn-ui/src/components/ui/card.tsx +103 -0
- package/templates/ui/shadcn-ui/src/components/ui/carousel.tsx +240 -0
- package/templates/ui/shadcn-ui/src/components/ui/chart.tsx +373 -0
- package/templates/ui/shadcn-ui/src/components/ui/checkbox.tsx +31 -0
- package/templates/ui/shadcn-ui/src/components/ui/collapsible.tsx +33 -0
- package/templates/ui/shadcn-ui/src/components/ui/combobox.tsx +299 -0
- package/templates/ui/shadcn-ui/src/components/ui/command.tsx +195 -0
- package/templates/ui/shadcn-ui/src/components/ui/context-menu.tsx +264 -0
- package/templates/ui/shadcn-ui/src/components/ui/dialog.tsx +170 -0
- package/templates/ui/shadcn-ui/src/components/ui/direction.tsx +22 -0
- package/templates/ui/shadcn-ui/src/components/ui/drawer.tsx +134 -0
- package/templates/ui/shadcn-ui/src/components/ui/dropdown-menu.tsx +272 -0
- package/templates/ui/shadcn-ui/src/components/ui/empty.tsx +104 -0
- package/templates/ui/shadcn-ui/src/components/ui/field.tsx +236 -0
- package/templates/ui/shadcn-ui/src/components/ui/hover-card.tsx +44 -0
- package/templates/ui/shadcn-ui/src/components/ui/input-group.tsx +156 -0
- package/templates/ui/shadcn-ui/src/components/ui/input-otp.tsx +87 -0
- package/templates/ui/shadcn-ui/src/components/ui/input.tsx +19 -0
- package/templates/ui/shadcn-ui/src/components/ui/item.tsx +196 -0
- package/templates/ui/shadcn-ui/src/components/ui/kbd.tsx +26 -0
- package/templates/ui/shadcn-ui/src/components/ui/label.tsx +22 -0
- package/templates/ui/shadcn-ui/src/components/ui/marker.tsx +69 -0
- package/templates/ui/shadcn-ui/src/components/ui/menubar.tsx +282 -0
- package/templates/ui/shadcn-ui/src/components/ui/message-scroller.tsx +129 -0
- package/templates/ui/shadcn-ui/src/components/ui/message.tsx +92 -0
- package/templates/ui/shadcn-ui/src/components/ui/native-select.tsx +61 -0
- package/templates/ui/shadcn-ui/src/components/ui/navigation-menu.tsx +164 -0
- package/templates/ui/shadcn-ui/src/components/ui/pagination.tsx +129 -0
- package/templates/ui/shadcn-ui/src/components/ui/popover.tsx +89 -0
- package/templates/ui/shadcn-ui/src/components/ui/progress.tsx +31 -0
- package/templates/ui/shadcn-ui/src/components/ui/radio-group.tsx +42 -0
- package/templates/ui/shadcn-ui/src/components/ui/resizable.tsx +50 -0
- package/templates/ui/shadcn-ui/src/components/ui/scroll-area.tsx +53 -0
- package/templates/ui/shadcn-ui/src/components/ui/select.tsx +194 -0
- package/templates/ui/shadcn-ui/src/components/ui/separator.tsx +26 -0
- package/templates/ui/shadcn-ui/src/components/ui/sheet.tsx +149 -0
- package/templates/ui/shadcn-ui/src/components/ui/sidebar.tsx +702 -0
- package/templates/ui/shadcn-ui/src/components/ui/skeleton.tsx +13 -0
- package/templates/ui/shadcn-ui/src/components/ui/slider.tsx +59 -0
- package/templates/ui/shadcn-ui/src/components/ui/sonner.tsx +47 -0
- package/templates/ui/shadcn-ui/src/components/ui/spinner.tsx +10 -0
- package/templates/ui/shadcn-ui/src/components/ui/switch.tsx +33 -0
- package/templates/ui/shadcn-ui/src/components/ui/table.tsx +114 -0
- package/templates/ui/shadcn-ui/src/components/ui/tabs.tsx +90 -0
- package/templates/ui/shadcn-ui/src/components/ui/textarea.tsx +18 -0
- package/templates/ui/shadcn-ui/src/components/ui/toggle-group.tsx +87 -0
- package/templates/ui/shadcn-ui/src/components/ui/toggle.tsx +45 -0
- package/templates/ui/shadcn-ui/src/components/ui/tooltip.tsx +59 -0
- package/templates/ui/shadcn-ui/src/index.patch.css +0 -118
- package/templates/ui/shadcn-ui/src/runtime/PortalContainer.ts +8 -0
- 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 `
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
|
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";
|
|
@@ -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
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
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 }
|