@izumisy-tailor/tailor-data-viewer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +255 -0
  2. package/package.json +47 -0
  3. package/src/component/column-selector.tsx +264 -0
  4. package/src/component/data-table.tsx +428 -0
  5. package/src/component/data-view-tab-content.tsx +324 -0
  6. package/src/component/data-viewer.tsx +280 -0
  7. package/src/component/hooks/use-accessible-tables.ts +22 -0
  8. package/src/component/hooks/use-column-state.ts +281 -0
  9. package/src/component/hooks/use-relation-data.ts +387 -0
  10. package/src/component/hooks/use-table-data.ts +317 -0
  11. package/src/component/index.ts +15 -0
  12. package/src/component/pagination.tsx +56 -0
  13. package/src/component/relation-content.tsx +250 -0
  14. package/src/component/saved-view-context.tsx +145 -0
  15. package/src/component/search-filter.tsx +319 -0
  16. package/src/component/single-record-tab-content.tsx +676 -0
  17. package/src/component/table-selector.tsx +102 -0
  18. package/src/component/types.ts +20 -0
  19. package/src/component/view-save-load.tsx +112 -0
  20. package/src/generator/metadata-generator.ts +461 -0
  21. package/src/lib/utils.ts +6 -0
  22. package/src/providers/graphql-client.ts +31 -0
  23. package/src/styles/theme.css +105 -0
  24. package/src/types/table-metadata.ts +73 -0
  25. package/src/ui/alert.tsx +66 -0
  26. package/src/ui/badge.tsx +46 -0
  27. package/src/ui/button.tsx +62 -0
  28. package/src/ui/card.tsx +92 -0
  29. package/src/ui/checkbox.tsx +30 -0
  30. package/src/ui/collapsible.tsx +31 -0
  31. package/src/ui/dialog.tsx +143 -0
  32. package/src/ui/dropdown-menu.tsx +255 -0
  33. package/src/ui/input.tsx +21 -0
  34. package/src/ui/label.tsx +24 -0
  35. package/src/ui/select.tsx +188 -0
  36. package/src/ui/table.tsx +116 -0
  37. package/src/utils/query-builder.ts +190 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ * GraphQL Client for dynamic query execution
3
+ * Uses graphql-request for flexibility with dynamic queries
4
+ */
5
+
6
+ import { GraphQLClient } from "graphql-request";
7
+
8
+ /**
9
+ * Create a GraphQL client for dynamic queries
10
+ * Uses the same authentication setup as urql (cookie + X-Tailor-Nonce)
11
+ */
12
+ export function createGraphQLClient(appUri: string): GraphQLClient {
13
+ return new GraphQLClient(`${appUri}/query`, {
14
+ credentials: "include",
15
+ headers: {
16
+ "Content-Type": "application/json",
17
+ "X-Tailor-Nonce": crypto.randomUUID(),
18
+ },
19
+ });
20
+ }
21
+
22
+ /**
23
+ * Execute a dynamic GraphQL query
24
+ */
25
+ export async function executeQuery<T = unknown>(
26
+ client: GraphQLClient,
27
+ query: string,
28
+ variables?: Record<string, unknown>,
29
+ ): Promise<T> {
30
+ return client.request<T>(query, variables);
31
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Basic theme CSS for Data Viewer components
3
+ *
4
+ * This file provides CSS custom properties (variables) that the UI components expect.
5
+ * The design is based on shadcn/ui theme system.
6
+ *
7
+ * Note: This is a minimal theme. In production, you may want to:
8
+ * 1. Import your app's existing theme CSS
9
+ * 2. Use @tailor-platform/app-shell/theme.css if available
10
+ * 3. Customize these values to match your design system
11
+ */
12
+
13
+ @layer base {
14
+ :root {
15
+ /* Background colors */
16
+ --background: 0 0% 100%;
17
+ --foreground: 222.2 84% 4.9%;
18
+
19
+ /* Card colors */
20
+ --card: 0 0% 100%;
21
+ --card-foreground: 222.2 84% 4.9%;
22
+
23
+ /* Popover colors */
24
+ --popover: 0 0% 100%;
25
+ --popover-foreground: 222.2 84% 4.9%;
26
+
27
+ /* Primary colors */
28
+ --primary: 222.2 47.4% 11.2%;
29
+ --primary-foreground: 210 40% 98%;
30
+
31
+ /* Secondary colors */
32
+ --secondary: 210 40% 96.1%;
33
+ --secondary-foreground: 222.2 47.4% 11.2%;
34
+
35
+ /* Muted colors */
36
+ --muted: 210 40% 96.1%;
37
+ --muted-foreground: 215.4 16.3% 46.9%;
38
+
39
+ /* Accent colors */
40
+ --accent: 210 40% 96.1%;
41
+ --accent-foreground: 222.2 47.4% 11.2%;
42
+
43
+ /* Destructive colors */
44
+ --destructive: 0 84.2% 60.2%;
45
+ --destructive-foreground: 210 40% 98%;
46
+
47
+ /* Border and input colors */
48
+ --border: 214.3 31.8% 91.4%;
49
+ --input: 214.3 31.8% 91.4%;
50
+ --ring: 222.2 84% 4.9%;
51
+
52
+ /* Border radius */
53
+ --radius: 0.5rem;
54
+ }
55
+
56
+ .dark {
57
+ /* Dark mode background colors */
58
+ --background: 222.2 84% 4.9%;
59
+ --foreground: 210 40% 98%;
60
+
61
+ /* Dark mode card colors */
62
+ --card: 222.2 84% 4.9%;
63
+ --card-foreground: 210 40% 98%;
64
+
65
+ /* Dark mode popover colors */
66
+ --popover: 222.2 84% 4.9%;
67
+ --popover-foreground: 210 40% 98%;
68
+
69
+ /* Dark mode primary colors */
70
+ --primary: 210 40% 98%;
71
+ --primary-foreground: 222.2 47.4% 11.2%;
72
+
73
+ /* Dark mode secondary colors */
74
+ --secondary: 217.2 32.6% 17.5%;
75
+ --secondary-foreground: 210 40% 98%;
76
+
77
+ /* Dark mode muted colors */
78
+ --muted: 217.2 32.6% 17.5%;
79
+ --muted-foreground: 215 20.2% 65.1%;
80
+
81
+ /* Dark mode accent colors */
82
+ --accent: 217.2 32.6% 17.5%;
83
+ --accent-foreground: 210 40% 98%;
84
+
85
+ /* Dark mode destructive colors */
86
+ --destructive: 0 62.8% 30.6%;
87
+ --destructive-foreground: 210 40% 98%;
88
+
89
+ /* Dark mode border and input colors */
90
+ --border: 217.2 32.6% 17.5%;
91
+ --input: 217.2 32.6% 17.5%;
92
+ --ring: 212.7 26.8% 83.9%;
93
+ }
94
+ }
95
+
96
+ @layer base {
97
+ * {
98
+ border-color: hsl(var(--border));
99
+ }
100
+
101
+ body {
102
+ background-color: hsl(var(--background));
103
+ color: hsl(var(--foreground));
104
+ }
105
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Field type mapping for Data View
3
+ * Re-exported from supplier-portal-module generator for frontend usage
4
+ */
5
+ export type FieldType =
6
+ | "string"
7
+ | "number"
8
+ | "boolean"
9
+ | "uuid"
10
+ | "datetime"
11
+ | "date"
12
+ | "time"
13
+ | "enum"
14
+ | "array"
15
+ | "nested";
16
+
17
+ /**
18
+ * Metadata for a single field
19
+ */
20
+ export interface FieldMetadata {
21
+ name: string;
22
+ type: FieldType;
23
+ required: boolean;
24
+ enumValues?: readonly string[];
25
+ arrayItemType?: FieldType;
26
+ description?: string;
27
+ /** manyToOne relation info (if this field is a foreign key) */
28
+ relation?: {
29
+ /** GraphQL field name for the related object (e.g., "task") */
30
+ fieldName: string;
31
+ /** Target table name in camelCase (e.g., "task") */
32
+ targetTable: string;
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Metadata for a relation
38
+ */
39
+ export interface RelationMetadata {
40
+ /** GraphQL field name (e.g., "task" for manyToOne, "taskAssignments" for oneToMany) */
41
+ fieldName: string;
42
+ /** Target table name in camelCase (e.g., "task", "taskAssignment") */
43
+ targetTable: string;
44
+ /** Relation type */
45
+ relationType: "manyToOne" | "oneToMany";
46
+ /** For manyToOne: the FK field name (e.g., "taskId"). For oneToMany: the FK field on the child table */
47
+ foreignKeyField: string;
48
+ }
49
+
50
+ /**
51
+ * Metadata for a single table
52
+ */
53
+ export interface TableMetadata {
54
+ name: string;
55
+ pluralForm: string;
56
+ description?: string;
57
+ readAllowedRoles: string[];
58
+ fields: FieldMetadata[];
59
+ /** Relations (manyToOne and oneToMany) */
60
+ relations?: RelationMetadata[];
61
+ }
62
+
63
+ /**
64
+ * Map of all tables
65
+ */
66
+ export type TableMetadataMap = Record<string, TableMetadata>;
67
+
68
+ /**
69
+ * Expanded relation fields configuration
70
+ * Key: relation field name (e.g., "task")
71
+ * Value: array of selected field names from the related table (e.g., ["name", "status"])
72
+ */
73
+ export type ExpandedRelationFields = Record<string, string[]>;
@@ -0,0 +1,66 @@
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
+ "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-card text-card-foreground",
12
+ destructive:
13
+ "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
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
+ "col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
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-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
59
+ className,
60
+ )}
61
+ {...props}
62
+ />
63
+ );
64
+ }
65
+
66
+ export { Alert, AlertTitle, AlertDescription };
@@ -0,0 +1,46 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+
5
+ import { cn } from "../lib/utils";
6
+
7
+ const badgeVariants = cva(
8
+ "inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
14
+ secondary:
15
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
16
+ destructive:
17
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
18
+ outline:
19
+ "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ },
25
+ },
26
+ );
27
+
28
+ function Badge({
29
+ className,
30
+ variant,
31
+ asChild = false,
32
+ ...props
33
+ }: React.ComponentProps<"span"> &
34
+ VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
35
+ const Comp = asChild ? Slot : "span";
36
+
37
+ return (
38
+ <Comp
39
+ data-slot="badge"
40
+ className={cn(badgeVariants({ variant }), className)}
41
+ {...props}
42
+ />
43
+ );
44
+ }
45
+
46
+ export { Badge, badgeVariants };
@@ -0,0 +1,62 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+
5
+ import { cn } from "../lib/utils";
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ destructive:
14
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
15
+ outline:
16
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17
+ secondary:
18
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
+ ghost:
20
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ size: {
24
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
25
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
26
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
27
+ icon: "size-9",
28
+ "icon-sm": "size-8",
29
+ "icon-lg": "size-10",
30
+ },
31
+ },
32
+ defaultVariants: {
33
+ variant: "default",
34
+ size: "default",
35
+ },
36
+ },
37
+ );
38
+
39
+ function Button({
40
+ className,
41
+ variant = "default",
42
+ size = "default",
43
+ asChild = false,
44
+ ...props
45
+ }: React.ComponentProps<"button"> &
46
+ VariantProps<typeof buttonVariants> & {
47
+ asChild?: boolean;
48
+ }) {
49
+ const Comp = asChild ? Slot : "button";
50
+
51
+ return (
52
+ <Comp
53
+ data-slot="button"
54
+ data-variant={variant}
55
+ data-size={size}
56
+ className={cn(buttonVariants({ variant, size, className }))}
57
+ {...props}
58
+ />
59
+ );
60
+ }
61
+
62
+ export { Button, buttonVariants };
@@ -0,0 +1,92 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../lib/utils";
4
+
5
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="card"
9
+ className={cn(
10
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
11
+ className,
12
+ )}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <div
21
+ data-slot="card-header"
22
+ className={cn(
23
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
24
+ className,
25
+ )}
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32
+ return (
33
+ <div
34
+ data-slot="card-title"
35
+ className={cn("leading-none font-semibold", className)}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ data-slot="card-description"
45
+ className={cn("text-muted-foreground text-sm", className)}
46
+ {...props}
47
+ />
48
+ );
49
+ }
50
+
51
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52
+ return (
53
+ <div
54
+ data-slot="card-action"
55
+ className={cn(
56
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
57
+ className,
58
+ )}
59
+ {...props}
60
+ />
61
+ );
62
+ }
63
+
64
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65
+ return (
66
+ <div
67
+ data-slot="card-content"
68
+ className={cn("px-6", className)}
69
+ {...props}
70
+ />
71
+ );
72
+ }
73
+
74
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75
+ return (
76
+ <div
77
+ data-slot="card-footer"
78
+ className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
79
+ {...props}
80
+ />
81
+ );
82
+ }
83
+
84
+ export {
85
+ Card,
86
+ CardHeader,
87
+ CardFooter,
88
+ CardTitle,
89
+ CardAction,
90
+ CardDescription,
91
+ CardContent,
92
+ };
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
3
+ import { CheckIcon } from "lucide-react";
4
+
5
+ import { cn } from "../lib/utils";
6
+
7
+ function Checkbox({
8
+ className,
9
+ ...props
10
+ }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
11
+ return (
12
+ <CheckboxPrimitive.Root
13
+ data-slot="checkbox"
14
+ className={cn(
15
+ "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
16
+ className,
17
+ )}
18
+ {...props}
19
+ >
20
+ <CheckboxPrimitive.Indicator
21
+ data-slot="checkbox-indicator"
22
+ className="grid place-content-center text-current transition-none"
23
+ >
24
+ <CheckIcon className="size-3.5" />
25
+ </CheckboxPrimitive.Indicator>
26
+ </CheckboxPrimitive.Root>
27
+ );
28
+ }
29
+
30
+ export { Checkbox };
@@ -0,0 +1,31 @@
1
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
2
+
3
+ function Collapsible({
4
+ ...props
5
+ }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
6
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
7
+ }
8
+
9
+ function CollapsibleTrigger({
10
+ ...props
11
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
12
+ return (
13
+ <CollapsiblePrimitive.CollapsibleTrigger
14
+ data-slot="collapsible-trigger"
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+
20
+ function CollapsibleContent({
21
+ ...props
22
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
23
+ return (
24
+ <CollapsiblePrimitive.CollapsibleContent
25
+ data-slot="collapsible-content"
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent };
@@ -0,0 +1,143 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
5
+ import { XIcon } from "lucide-react";
6
+
7
+ import { cn } from "../lib/utils";
8
+
9
+ function Dialog({
10
+ ...props
11
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
12
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
13
+ }
14
+
15
+ function DialogTrigger({
16
+ ...props
17
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
18
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
19
+ }
20
+
21
+ function DialogPortal({
22
+ ...props
23
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
24
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
25
+ }
26
+
27
+ function DialogClose({
28
+ ...props
29
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
30
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
31
+ }
32
+
33
+ function DialogOverlay({
34
+ className,
35
+ ...props
36
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
37
+ return (
38
+ <DialogPrimitive.Overlay
39
+ data-slot="dialog-overlay"
40
+ className={cn(
41
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
42
+ className,
43
+ )}
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ function DialogContent({
50
+ className,
51
+ children,
52
+ showCloseButton = true,
53
+ ...props
54
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
55
+ showCloseButton?: boolean;
56
+ }) {
57
+ return (
58
+ <DialogPortal data-slot="dialog-portal">
59
+ <DialogOverlay />
60
+ <DialogPrimitive.Content
61
+ data-slot="dialog-content"
62
+ className={cn(
63
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
64
+ className,
65
+ )}
66
+ {...props}
67
+ >
68
+ {children}
69
+ {showCloseButton && (
70
+ <DialogPrimitive.Close
71
+ data-slot="dialog-close"
72
+ className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
73
+ >
74
+ <XIcon />
75
+ <span className="sr-only">Close</span>
76
+ </DialogPrimitive.Close>
77
+ )}
78
+ </DialogPrimitive.Content>
79
+ </DialogPortal>
80
+ );
81
+ }
82
+
83
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
84
+ return (
85
+ <div
86
+ data-slot="dialog-header"
87
+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
88
+ {...props}
89
+ />
90
+ );
91
+ }
92
+
93
+ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
94
+ return (
95
+ <div
96
+ data-slot="dialog-footer"
97
+ className={cn(
98
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
99
+ className,
100
+ )}
101
+ {...props}
102
+ />
103
+ );
104
+ }
105
+
106
+ function DialogTitle({
107
+ className,
108
+ ...props
109
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
110
+ return (
111
+ <DialogPrimitive.Title
112
+ data-slot="dialog-title"
113
+ className={cn("text-lg leading-none font-semibold", className)}
114
+ {...props}
115
+ />
116
+ );
117
+ }
118
+
119
+ function DialogDescription({
120
+ className,
121
+ ...props
122
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
123
+ return (
124
+ <DialogPrimitive.Description
125
+ data-slot="dialog-description"
126
+ className={cn("text-muted-foreground text-sm", className)}
127
+ {...props}
128
+ />
129
+ );
130
+ }
131
+
132
+ export {
133
+ Dialog,
134
+ DialogClose,
135
+ DialogContent,
136
+ DialogDescription,
137
+ DialogFooter,
138
+ DialogHeader,
139
+ DialogOverlay,
140
+ DialogPortal,
141
+ DialogTitle,
142
+ DialogTrigger,
143
+ };