@orsetra/shared-ui 1.0.39 → 1.0.40

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.
@@ -18,7 +18,7 @@ const AlertDialogOverlay = React.forwardRef<
18
18
  >(({ className, ...props }, ref) => (
19
19
  <AlertDialogPrimitive.Overlay
20
20
  className={cn(
21
- "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
21
+ "fixed inset-0 z-50 bg-ibm-gray-100/50 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
22
22
  className
23
23
  )}
24
24
  {...props}
@@ -32,11 +32,10 @@ const AlertDialogContent = React.forwardRef<
32
32
  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
33
33
  >(({ className, ...props }, ref) => (
34
34
  <AlertDialogPortal>
35
- <AlertDialogOverlay />
36
35
  <AlertDialogPrimitive.Content
37
36
  ref={ref}
38
37
  className={cn(
39
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
38
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-ibm-gray-20 bg-white p-6 shadow-xl duration-200 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 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
40
39
  className
41
40
  )}
42
41
  {...props}
@@ -79,7 +78,7 @@ const AlertDialogTitle = React.forwardRef<
79
78
  >(({ className, ...props }, ref) => (
80
79
  <AlertDialogPrimitive.Title
81
80
  ref={ref}
82
- className={cn("text-lg font-semibold", className)}
81
+ className={cn("text-lg font-semibold text-ibm-gray-100", className)}
83
82
  {...props}
84
83
  />
85
84
  ))
@@ -91,7 +90,7 @@ const AlertDialogDescription = React.forwardRef<
91
90
  >(({ className, ...props }, ref) => (
92
91
  <AlertDialogPrimitive.Description
93
92
  ref={ref}
94
- className={cn("text-sm text-muted-foreground", className)}
93
+ className={cn("text-sm text-ibm-gray-70", className)}
95
94
  {...props}
96
95
  />
97
96
  ))
@@ -0,0 +1,147 @@
1
+ "use client"
2
+
3
+ import { useState, useEffect } from "react"
4
+ import {
5
+ AlertDialog,
6
+ AlertDialogAction,
7
+ AlertDialogCancel,
8
+ AlertDialogContent,
9
+ AlertDialogDescription,
10
+ AlertDialogFooter,
11
+ AlertDialogHeader,
12
+ AlertDialogTitle,
13
+ AlertDialogTrigger,
14
+ } from "./alert-dialog"
15
+ import { AlertBanner, useAlertBanner } from "./alert-banner"
16
+ import { Input } from "./input"
17
+ import { Label } from "./label"
18
+ import type { ReactNode } from "react"
19
+
20
+ export interface ConfirmationDialogProps {
21
+ trigger: ReactNode
22
+ title: string
23
+ description: string | ReactNode
24
+ confirmText?: string
25
+ cancelText?: string
26
+ confirmVariant?: "default" | "destructive"
27
+ onConfirm: () => void | Promise<void>
28
+ onCancel?: () => void
29
+ requireTextConfirmation?: {
30
+ expectedText: string
31
+ label?: string
32
+ placeholder?: string
33
+ }
34
+ open?: boolean
35
+ onOpenChange?: (open: boolean) => void
36
+ }
37
+
38
+ export function ConfirmationDialog({
39
+ trigger,
40
+ title,
41
+ description,
42
+ confirmText = "Confirm",
43
+ cancelText = "Cancel",
44
+ confirmVariant = "default",
45
+ onConfirm,
46
+ onCancel,
47
+ requireTextConfirmation,
48
+ open: controlledOpen,
49
+ onOpenChange: controlledOnOpenChange,
50
+ }: ConfirmationDialogProps) {
51
+ const { alert, showSuccess, showError, hideAlert } = useAlertBanner()
52
+ const [loading, setLoading] = useState(false)
53
+ const [internalOpen, setInternalOpen] = useState(false)
54
+ const [confirmationText, setConfirmationText] = useState("")
55
+
56
+ const isControlled = controlledOpen !== undefined
57
+ const open = isControlled ? controlledOpen : internalOpen
58
+ const setOpen = isControlled ? controlledOnOpenChange! : setInternalOpen
59
+
60
+ const isConfirmDisabled =
61
+ loading ||
62
+ (requireTextConfirmation &&
63
+ confirmationText !== requireTextConfirmation.expectedText)
64
+
65
+ useEffect(() => {
66
+ if (!open) {
67
+ setConfirmationText("")
68
+ hideAlert()
69
+ }
70
+ }, [open])
71
+
72
+ const handleConfirm = async () => {
73
+ setLoading(true)
74
+ hideAlert()
75
+ try {
76
+ await onConfirm()
77
+ setOpen(false)
78
+ } catch (error: any) {
79
+ showError(error?.message || "Operation failed")
80
+ } finally {
81
+ setLoading(false)
82
+ }
83
+ }
84
+
85
+ const handleCancel = () => {
86
+ onCancel?.()
87
+ setOpen(false)
88
+ }
89
+
90
+ const confirmButtonClass =
91
+ confirmVariant === "destructive"
92
+ ? "bg-red-600 hover:bg-red-700"
93
+ : undefined
94
+
95
+ return (
96
+ <AlertDialog open={open} onOpenChange={setOpen}>
97
+ <AlertDialogTrigger asChild>{trigger}</AlertDialogTrigger>
98
+ <AlertDialogContent>
99
+ <AlertDialogHeader>
100
+ <AlertDialogTitle>{title}</AlertDialogTitle>
101
+ <AlertDialogDescription>{description}</AlertDialogDescription>
102
+ </AlertDialogHeader>
103
+
104
+ {alert.show && (
105
+ <AlertBanner
106
+ variant={alert.variant}
107
+ message={alert.message}
108
+ onClose={hideAlert}
109
+ />
110
+ )}
111
+
112
+ {requireTextConfirmation && (
113
+ <div className="space-y-2">
114
+ <Label htmlFor="confirmation-input">
115
+ {requireTextConfirmation.label ||
116
+ `Type "${requireTextConfirmation.expectedText}" to confirm`}
117
+ </Label>
118
+ <Input
119
+ id="confirmation-input"
120
+ value={confirmationText}
121
+ onChange={(e) => setConfirmationText(e.target.value)}
122
+ placeholder={
123
+ requireTextConfirmation.placeholder ||
124
+ requireTextConfirmation.expectedText
125
+ }
126
+ disabled={loading}
127
+ autoComplete="off"
128
+ />
129
+ </div>
130
+ )}
131
+
132
+ <AlertDialogFooter className="mt-6">
133
+ <AlertDialogCancel disabled={loading} onClick={handleCancel}>
134
+ {cancelText}
135
+ </AlertDialogCancel>
136
+ <AlertDialogAction
137
+ className={confirmButtonClass}
138
+ onClick={handleConfirm}
139
+ disabled={isConfirmDisabled}
140
+ >
141
+ {confirmText}
142
+ </AlertDialogAction>
143
+ </AlertDialogFooter>
144
+ </AlertDialogContent>
145
+ </AlertDialog>
146
+ )
147
+ }
@@ -23,6 +23,7 @@ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './
23
23
  export { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel } from './alert-dialog'
24
24
  export { Alert, AlertTitle, AlertDescription } from './alert'
25
25
  export { AlertBanner, useAlertBanner, type AlertBannerProps, type AlertState } from './alert-banner'
26
+ export { ConfirmationDialog, type ConfirmationDialogProps } from './confirmation-dialog'
26
27
  export { AspectRatio } from './aspect-ratio'
27
28
  export { Badge } from './badge'
28
29
  export { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis } from './breadcrumb'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orsetra/shared-ui",
3
- "version": "1.0.39",
3
+ "version": "1.0.40",
4
4
  "description": "Shared UI components for Orsetra platform",
5
5
  "main": "./index.ts",
6
6
  "types": "./index.ts",