myoperator-ui 0.0.95 → 0.0.97
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/dist/index.js +712 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -289,9 +289,9 @@ const buttonVariants = cva(
|
|
|
289
289
|
"border border-dashed border-[#D5D7DA] bg-transparent text-[#717680] hover:border-[#343E55] hover:text-[#343E55] hover:bg-[#FAFAFA]",
|
|
290
290
|
},
|
|
291
291
|
size: {
|
|
292
|
-
default: "py-2.5 px-4 [&_svg]:size-4",
|
|
293
|
-
sm: "py-2 px-3 text-xs [&_svg]:size-3.5",
|
|
294
|
-
lg: "py-3 px-6 [&_svg]:size-5",
|
|
292
|
+
default: "min-w-20 py-2.5 px-4 [&_svg]:size-4",
|
|
293
|
+
sm: "min-w-16 py-2 px-3 text-xs [&_svg]:size-3.5",
|
|
294
|
+
lg: "min-w-24 py-3 px-6 [&_svg]:size-5",
|
|
295
295
|
icon: "h-8 w-8 rounded-md",
|
|
296
296
|
"icon-sm": "h-7 w-7 rounded-md",
|
|
297
297
|
"icon-lg": "h-10 w-10 rounded-md",
|
|
@@ -671,7 +671,7 @@ const Typography = React.forwardRef<HTMLElement, TypographyProps>(
|
|
|
671
671
|
ref
|
|
672
672
|
) => {
|
|
673
673
|
const key: Key = \`\${kind}-\${variant}\`;
|
|
674
|
-
const Tag = tag || mapTagName[key];
|
|
674
|
+
const Tag = (tag || mapTagName[key]) as React.ElementType;
|
|
675
675
|
|
|
676
676
|
const classes = cn(
|
|
677
677
|
"m-0", // Reset margin
|
|
@@ -682,11 +682,13 @@ const Typography = React.forwardRef<HTMLElement, TypographyProps>(
|
|
|
682
682
|
className
|
|
683
683
|
);
|
|
684
684
|
|
|
685
|
+
const tagName = tag || mapTagName[key];
|
|
686
|
+
|
|
685
687
|
return (
|
|
686
688
|
<Tag
|
|
687
|
-
ref={ref
|
|
689
|
+
ref={ref}
|
|
688
690
|
className={classes}
|
|
689
|
-
htmlFor={
|
|
691
|
+
htmlFor={tagName === "label" ? htmlFor : undefined}
|
|
690
692
|
{...props}
|
|
691
693
|
>
|
|
692
694
|
{children}
|
|
@@ -2699,6 +2701,169 @@ export {
|
|
|
2699
2701
|
TableToggle,
|
|
2700
2702
|
tableVariants,
|
|
2701
2703
|
};
|
|
2704
|
+
`, prefix)
|
|
2705
|
+
}
|
|
2706
|
+
]
|
|
2707
|
+
},
|
|
2708
|
+
"dialog": {
|
|
2709
|
+
name: "dialog",
|
|
2710
|
+
description: "A modal dialog component built on Radix UI Dialog with size variants and animations",
|
|
2711
|
+
dependencies: [
|
|
2712
|
+
"@radix-ui/react-dialog",
|
|
2713
|
+
"class-variance-authority",
|
|
2714
|
+
"clsx",
|
|
2715
|
+
"tailwind-merge",
|
|
2716
|
+
"lucide-react"
|
|
2717
|
+
],
|
|
2718
|
+
files: [
|
|
2719
|
+
{
|
|
2720
|
+
name: "dialog.tsx",
|
|
2721
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
2722
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
2723
|
+
import { X } from "lucide-react";
|
|
2724
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2725
|
+
|
|
2726
|
+
import { cn } from "../../lib/utils";
|
|
2727
|
+
|
|
2728
|
+
const Dialog = DialogPrimitive.Root;
|
|
2729
|
+
|
|
2730
|
+
const DialogTrigger = DialogPrimitive.Trigger;
|
|
2731
|
+
|
|
2732
|
+
const DialogPortal = DialogPrimitive.Portal;
|
|
2733
|
+
|
|
2734
|
+
const DialogClose = DialogPrimitive.Close;
|
|
2735
|
+
|
|
2736
|
+
const DialogOverlay = React.forwardRef<
|
|
2737
|
+
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
|
2738
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
|
2739
|
+
>(({ className, ...props }, ref) => (
|
|
2740
|
+
<DialogPrimitive.Overlay
|
|
2741
|
+
ref={ref}
|
|
2742
|
+
className={cn(
|
|
2743
|
+
"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
2744
|
+
className
|
|
2745
|
+
)}
|
|
2746
|
+
{...props}
|
|
2747
|
+
/>
|
|
2748
|
+
));
|
|
2749
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
2750
|
+
|
|
2751
|
+
const dialogContentVariants = cva(
|
|
2752
|
+
"fixed left-[50%] top-[50%] z-50 grid translate-x-[-50%] translate-y-[-50%] gap-4 border border-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%] rounded-lg",
|
|
2753
|
+
{
|
|
2754
|
+
variants: {
|
|
2755
|
+
size: {
|
|
2756
|
+
sm: "w-full max-w-sm",
|
|
2757
|
+
default: "w-full max-w-lg",
|
|
2758
|
+
lg: "w-full max-w-2xl",
|
|
2759
|
+
xl: "w-full max-w-4xl",
|
|
2760
|
+
full: "w-[calc(100%-2rem)] h-[calc(100%-2rem)] max-w-none",
|
|
2761
|
+
},
|
|
2762
|
+
},
|
|
2763
|
+
defaultVariants: {
|
|
2764
|
+
size: "default",
|
|
2765
|
+
},
|
|
2766
|
+
}
|
|
2767
|
+
);
|
|
2768
|
+
|
|
2769
|
+
export interface DialogContentProps
|
|
2770
|
+
extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>,
|
|
2771
|
+
VariantProps<typeof dialogContentVariants> {
|
|
2772
|
+
/** Hide the default close button in the top-right corner */
|
|
2773
|
+
hideCloseButton?: boolean;
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
const DialogContent = React.forwardRef<
|
|
2777
|
+
React.ElementRef<typeof DialogPrimitive.Content>,
|
|
2778
|
+
DialogContentProps
|
|
2779
|
+
>(({ className, children, size, hideCloseButton = false, ...props }, ref) => (
|
|
2780
|
+
<DialogPortal>
|
|
2781
|
+
<DialogOverlay />
|
|
2782
|
+
<DialogPrimitive.Content
|
|
2783
|
+
ref={ref}
|
|
2784
|
+
className={cn(dialogContentVariants({ size, className }))}
|
|
2785
|
+
{...props}
|
|
2786
|
+
>
|
|
2787
|
+
{children}
|
|
2788
|
+
{!hideCloseButton && (
|
|
2789
|
+
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
|
2790
|
+
<X className="h-4 w-4" />
|
|
2791
|
+
<span className="sr-only">Close</span>
|
|
2792
|
+
</DialogPrimitive.Close>
|
|
2793
|
+
)}
|
|
2794
|
+
</DialogPrimitive.Content>
|
|
2795
|
+
</DialogPortal>
|
|
2796
|
+
));
|
|
2797
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
2798
|
+
|
|
2799
|
+
const DialogHeader = ({
|
|
2800
|
+
className,
|
|
2801
|
+
...props
|
|
2802
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
2803
|
+
<div
|
|
2804
|
+
className={cn(
|
|
2805
|
+
"flex flex-col space-y-1.5 text-center sm:text-left",
|
|
2806
|
+
className
|
|
2807
|
+
)}
|
|
2808
|
+
{...props}
|
|
2809
|
+
/>
|
|
2810
|
+
);
|
|
2811
|
+
DialogHeader.displayName = "DialogHeader";
|
|
2812
|
+
|
|
2813
|
+
const DialogFooter = ({
|
|
2814
|
+
className,
|
|
2815
|
+
...props
|
|
2816
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
2817
|
+
<div
|
|
2818
|
+
className={cn(
|
|
2819
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
2820
|
+
className
|
|
2821
|
+
)}
|
|
2822
|
+
{...props}
|
|
2823
|
+
/>
|
|
2824
|
+
);
|
|
2825
|
+
DialogFooter.displayName = "DialogFooter";
|
|
2826
|
+
|
|
2827
|
+
const DialogTitle = React.forwardRef<
|
|
2828
|
+
React.ElementRef<typeof DialogPrimitive.Title>,
|
|
2829
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
|
2830
|
+
>(({ className, ...props }, ref) => (
|
|
2831
|
+
<DialogPrimitive.Title
|
|
2832
|
+
ref={ref}
|
|
2833
|
+
className={cn(
|
|
2834
|
+
"text-lg font-semibold leading-none tracking-tight text-foreground",
|
|
2835
|
+
className
|
|
2836
|
+
)}
|
|
2837
|
+
{...props}
|
|
2838
|
+
/>
|
|
2839
|
+
));
|
|
2840
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
2841
|
+
|
|
2842
|
+
const DialogDescription = React.forwardRef<
|
|
2843
|
+
React.ElementRef<typeof DialogPrimitive.Description>,
|
|
2844
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
|
2845
|
+
>(({ className, ...props }, ref) => (
|
|
2846
|
+
<DialogPrimitive.Description
|
|
2847
|
+
ref={ref}
|
|
2848
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
2849
|
+
{...props}
|
|
2850
|
+
/>
|
|
2851
|
+
));
|
|
2852
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
2853
|
+
|
|
2854
|
+
export {
|
|
2855
|
+
Dialog,
|
|
2856
|
+
DialogPortal,
|
|
2857
|
+
DialogOverlay,
|
|
2858
|
+
DialogClose,
|
|
2859
|
+
DialogTrigger,
|
|
2860
|
+
DialogContent,
|
|
2861
|
+
DialogHeader,
|
|
2862
|
+
DialogFooter,
|
|
2863
|
+
DialogTitle,
|
|
2864
|
+
DialogDescription,
|
|
2865
|
+
dialogContentVariants,
|
|
2866
|
+
};
|
|
2702
2867
|
`, prefix)
|
|
2703
2868
|
}
|
|
2704
2869
|
]
|
|
@@ -2977,6 +3142,331 @@ export {
|
|
|
2977
3142
|
TooltipArrow,
|
|
2978
3143
|
TooltipProvider,
|
|
2979
3144
|
};
|
|
3145
|
+
`, prefix)
|
|
3146
|
+
}
|
|
3147
|
+
]
|
|
3148
|
+
},
|
|
3149
|
+
"delete-confirmation-modal": {
|
|
3150
|
+
name: "delete-confirmation-modal",
|
|
3151
|
+
description: "A confirmation modal requiring text input to confirm deletion",
|
|
3152
|
+
dependencies: [
|
|
3153
|
+
"clsx",
|
|
3154
|
+
"tailwind-merge"
|
|
3155
|
+
],
|
|
3156
|
+
files: [
|
|
3157
|
+
{
|
|
3158
|
+
name: "delete-confirmation-modal.tsx",
|
|
3159
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
3160
|
+
|
|
3161
|
+
import { cn } from "../../lib/utils";
|
|
3162
|
+
import {
|
|
3163
|
+
Dialog,
|
|
3164
|
+
DialogContent,
|
|
3165
|
+
DialogHeader,
|
|
3166
|
+
DialogFooter,
|
|
3167
|
+
DialogTitle,
|
|
3168
|
+
DialogDescription,
|
|
3169
|
+
DialogTrigger,
|
|
3170
|
+
} from "./dialog";
|
|
3171
|
+
import { Button } from "./button";
|
|
3172
|
+
import { Input } from "./input";
|
|
3173
|
+
|
|
3174
|
+
/**
|
|
3175
|
+
* Props for the DeleteConfirmationModal component
|
|
3176
|
+
*/
|
|
3177
|
+
export interface DeleteConfirmationModalProps {
|
|
3178
|
+
/** Controls modal visibility (controlled mode) */
|
|
3179
|
+
open?: boolean;
|
|
3180
|
+
/** Callback when open state changes */
|
|
3181
|
+
onOpenChange?: (open: boolean) => void;
|
|
3182
|
+
/** The name of the item being deleted (shown in title) */
|
|
3183
|
+
itemName?: string;
|
|
3184
|
+
/** Custom title (overrides default) */
|
|
3185
|
+
title?: React.ReactNode;
|
|
3186
|
+
/** Additional description text */
|
|
3187
|
+
description?: React.ReactNode;
|
|
3188
|
+
/** Text user must type to confirm (default: "DELETE") */
|
|
3189
|
+
confirmText?: string;
|
|
3190
|
+
/** Called when user confirms deletion */
|
|
3191
|
+
onConfirm?: () => void;
|
|
3192
|
+
/** Called when user cancels */
|
|
3193
|
+
onCancel?: () => void;
|
|
3194
|
+
/** Loading state for delete button */
|
|
3195
|
+
loading?: boolean;
|
|
3196
|
+
/** Text for delete button (default: "Delete") */
|
|
3197
|
+
deleteButtonText?: string;
|
|
3198
|
+
/** Text for cancel button (default: "Cancel") */
|
|
3199
|
+
cancelButtonText?: string;
|
|
3200
|
+
/** Trigger element for uncontrolled usage */
|
|
3201
|
+
trigger?: React.ReactNode;
|
|
3202
|
+
/** Additional className for the dialog content */
|
|
3203
|
+
className?: string;
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
/**
|
|
3207
|
+
* A confirmation modal that requires the user to type a specific text to confirm deletion.
|
|
3208
|
+
*
|
|
3209
|
+
* @example
|
|
3210
|
+
* \`\`\`tsx
|
|
3211
|
+
* // Controlled usage
|
|
3212
|
+
* <DeleteConfirmationModal
|
|
3213
|
+
* open={isOpen}
|
|
3214
|
+
* onOpenChange={setIsOpen}
|
|
3215
|
+
* itemName="webhook"
|
|
3216
|
+
* onConfirm={handleDelete}
|
|
3217
|
+
* />
|
|
3218
|
+
*
|
|
3219
|
+
* // Uncontrolled with trigger
|
|
3220
|
+
* <DeleteConfirmationModal
|
|
3221
|
+
* trigger={<Button variant="destructive">Delete</Button>}
|
|
3222
|
+
* itemName="user"
|
|
3223
|
+
* onConfirm={handleDelete}
|
|
3224
|
+
* />
|
|
3225
|
+
* \`\`\`
|
|
3226
|
+
*/
|
|
3227
|
+
const DeleteConfirmationModal = React.forwardRef<
|
|
3228
|
+
HTMLDivElement,
|
|
3229
|
+
DeleteConfirmationModalProps
|
|
3230
|
+
>(
|
|
3231
|
+
(
|
|
3232
|
+
{
|
|
3233
|
+
open,
|
|
3234
|
+
onOpenChange,
|
|
3235
|
+
itemName = "item",
|
|
3236
|
+
title,
|
|
3237
|
+
description,
|
|
3238
|
+
confirmText = "DELETE",
|
|
3239
|
+
onConfirm,
|
|
3240
|
+
onCancel,
|
|
3241
|
+
loading = false,
|
|
3242
|
+
deleteButtonText = "Delete",
|
|
3243
|
+
cancelButtonText = "Cancel",
|
|
3244
|
+
trigger,
|
|
3245
|
+
className,
|
|
3246
|
+
},
|
|
3247
|
+
ref
|
|
3248
|
+
) => {
|
|
3249
|
+
const [inputValue, setInputValue] = React.useState("");
|
|
3250
|
+
const isConfirmEnabled = inputValue === confirmText;
|
|
3251
|
+
|
|
3252
|
+
// Reset input when modal closes
|
|
3253
|
+
React.useEffect(() => {
|
|
3254
|
+
if (!open) {
|
|
3255
|
+
setInputValue("");
|
|
3256
|
+
}
|
|
3257
|
+
}, [open]);
|
|
3258
|
+
|
|
3259
|
+
const handleConfirm = () => {
|
|
3260
|
+
if (isConfirmEnabled) {
|
|
3261
|
+
onConfirm?.();
|
|
3262
|
+
}
|
|
3263
|
+
};
|
|
3264
|
+
|
|
3265
|
+
const handleCancel = () => {
|
|
3266
|
+
onCancel?.();
|
|
3267
|
+
onOpenChange?.(false);
|
|
3268
|
+
};
|
|
3269
|
+
|
|
3270
|
+
const handleOpenChange = (newOpen: boolean) => {
|
|
3271
|
+
if (!newOpen) {
|
|
3272
|
+
setInputValue("");
|
|
3273
|
+
}
|
|
3274
|
+
onOpenChange?.(newOpen);
|
|
3275
|
+
};
|
|
3276
|
+
|
|
3277
|
+
const defaultTitle = \`Are you sure you want to delete this \${itemName}?\`;
|
|
3278
|
+
|
|
3279
|
+
return (
|
|
3280
|
+
<Dialog open={open} onOpenChange={handleOpenChange}>
|
|
3281
|
+
{trigger && <DialogTrigger asChild>{trigger}</DialogTrigger>}
|
|
3282
|
+
<DialogContent ref={ref} size="sm" className={cn(className)}>
|
|
3283
|
+
<DialogHeader>
|
|
3284
|
+
<DialogTitle>{title || defaultTitle}</DialogTitle>
|
|
3285
|
+
{description && (
|
|
3286
|
+
<DialogDescription>{description}</DialogDescription>
|
|
3287
|
+
)}
|
|
3288
|
+
</DialogHeader>
|
|
3289
|
+
<div className="grid gap-2 py-4">
|
|
3290
|
+
<label
|
|
3291
|
+
htmlFor="delete-confirmation-input"
|
|
3292
|
+
className="text-sm text-muted-foreground"
|
|
3293
|
+
>
|
|
3294
|
+
Enter "{confirmText}" in uppercase to confirm
|
|
3295
|
+
</label>
|
|
3296
|
+
<Input
|
|
3297
|
+
id="delete-confirmation-input"
|
|
3298
|
+
value={inputValue}
|
|
3299
|
+
onChange={(e) => setInputValue(e.target.value)}
|
|
3300
|
+
placeholder={confirmText}
|
|
3301
|
+
autoComplete="off"
|
|
3302
|
+
autoFocus
|
|
3303
|
+
/>
|
|
3304
|
+
</div>
|
|
3305
|
+
<DialogFooter className="gap-2 sm:gap-0">
|
|
3306
|
+
<Button variant="outline" onClick={handleCancel} disabled={loading}>
|
|
3307
|
+
{cancelButtonText}
|
|
3308
|
+
</Button>
|
|
3309
|
+
<Button
|
|
3310
|
+
variant="destructive"
|
|
3311
|
+
onClick={handleConfirm}
|
|
3312
|
+
disabled={!isConfirmEnabled || loading}
|
|
3313
|
+
loading={loading}
|
|
3314
|
+
>
|
|
3315
|
+
{deleteButtonText}
|
|
3316
|
+
</Button>
|
|
3317
|
+
</DialogFooter>
|
|
3318
|
+
</DialogContent>
|
|
3319
|
+
</Dialog>
|
|
3320
|
+
);
|
|
3321
|
+
}
|
|
3322
|
+
);
|
|
3323
|
+
DeleteConfirmationModal.displayName = "DeleteConfirmationModal";
|
|
3324
|
+
|
|
3325
|
+
export { DeleteConfirmationModal };
|
|
3326
|
+
`, prefix)
|
|
3327
|
+
}
|
|
3328
|
+
]
|
|
3329
|
+
},
|
|
3330
|
+
"confirmation-modal": {
|
|
3331
|
+
name: "confirmation-modal",
|
|
3332
|
+
description: "A simple confirmation modal for yes/no decisions",
|
|
3333
|
+
dependencies: [
|
|
3334
|
+
"clsx",
|
|
3335
|
+
"tailwind-merge"
|
|
3336
|
+
],
|
|
3337
|
+
files: [
|
|
3338
|
+
{
|
|
3339
|
+
name: "confirmation-modal.tsx",
|
|
3340
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
3341
|
+
|
|
3342
|
+
import { cn } from "../../lib/utils";
|
|
3343
|
+
import {
|
|
3344
|
+
Dialog,
|
|
3345
|
+
DialogContent,
|
|
3346
|
+
DialogHeader,
|
|
3347
|
+
DialogFooter,
|
|
3348
|
+
DialogTitle,
|
|
3349
|
+
DialogDescription,
|
|
3350
|
+
DialogTrigger,
|
|
3351
|
+
} from "./dialog";
|
|
3352
|
+
import { Button } from "./button";
|
|
3353
|
+
|
|
3354
|
+
/**
|
|
3355
|
+
* Props for the ConfirmationModal component
|
|
3356
|
+
*/
|
|
3357
|
+
export interface ConfirmationModalProps {
|
|
3358
|
+
/** Controls modal visibility (controlled mode) */
|
|
3359
|
+
open?: boolean;
|
|
3360
|
+
/** Callback when open state changes */
|
|
3361
|
+
onOpenChange?: (open: boolean) => void;
|
|
3362
|
+
/** Modal title */
|
|
3363
|
+
title: React.ReactNode;
|
|
3364
|
+
/** Modal description/message */
|
|
3365
|
+
description?: React.ReactNode;
|
|
3366
|
+
/** Visual style of confirm button */
|
|
3367
|
+
variant?: "default" | "destructive";
|
|
3368
|
+
/** Called when user confirms */
|
|
3369
|
+
onConfirm?: () => void;
|
|
3370
|
+
/** Called when user cancels */
|
|
3371
|
+
onCancel?: () => void;
|
|
3372
|
+
/** Loading state for confirm button */
|
|
3373
|
+
loading?: boolean;
|
|
3374
|
+
/** Text for confirm button (default: "Yes") */
|
|
3375
|
+
confirmButtonText?: string;
|
|
3376
|
+
/** Text for cancel button (default: "Cancel") */
|
|
3377
|
+
cancelButtonText?: string;
|
|
3378
|
+
/** Trigger element for uncontrolled usage */
|
|
3379
|
+
trigger?: React.ReactNode;
|
|
3380
|
+
/** Additional className for the dialog content */
|
|
3381
|
+
className?: string;
|
|
3382
|
+
}
|
|
3383
|
+
|
|
3384
|
+
/**
|
|
3385
|
+
* A simple confirmation modal for yes/no decisions.
|
|
3386
|
+
*
|
|
3387
|
+
* @example
|
|
3388
|
+
* \`\`\`tsx
|
|
3389
|
+
* // Controlled usage
|
|
3390
|
+
* <ConfirmationModal
|
|
3391
|
+
* open={isOpen}
|
|
3392
|
+
* onOpenChange={setIsOpen}
|
|
3393
|
+
* title="Disable Webhook"
|
|
3394
|
+
* description="Are you sure you want to disable this webhook?"
|
|
3395
|
+
* onConfirm={handleDisable}
|
|
3396
|
+
* />
|
|
3397
|
+
*
|
|
3398
|
+
* // Destructive variant
|
|
3399
|
+
* <ConfirmationModal
|
|
3400
|
+
* open={isOpen}
|
|
3401
|
+
* onOpenChange={setIsOpen}
|
|
3402
|
+
* title="Archive Project"
|
|
3403
|
+
* variant="destructive"
|
|
3404
|
+
* confirmButtonText="Archive"
|
|
3405
|
+
* onConfirm={handleArchive}
|
|
3406
|
+
* />
|
|
3407
|
+
* \`\`\`
|
|
3408
|
+
*/
|
|
3409
|
+
const ConfirmationModal = React.forwardRef<
|
|
3410
|
+
HTMLDivElement,
|
|
3411
|
+
ConfirmationModalProps
|
|
3412
|
+
>(
|
|
3413
|
+
(
|
|
3414
|
+
{
|
|
3415
|
+
open,
|
|
3416
|
+
onOpenChange,
|
|
3417
|
+
title,
|
|
3418
|
+
description,
|
|
3419
|
+
variant = "default",
|
|
3420
|
+
onConfirm,
|
|
3421
|
+
onCancel,
|
|
3422
|
+
loading = false,
|
|
3423
|
+
confirmButtonText = "Yes",
|
|
3424
|
+
cancelButtonText = "Cancel",
|
|
3425
|
+
trigger,
|
|
3426
|
+
className,
|
|
3427
|
+
},
|
|
3428
|
+
ref
|
|
3429
|
+
) => {
|
|
3430
|
+
const handleConfirm = () => {
|
|
3431
|
+
onConfirm?.();
|
|
3432
|
+
};
|
|
3433
|
+
|
|
3434
|
+
const handleCancel = () => {
|
|
3435
|
+
onCancel?.();
|
|
3436
|
+
onOpenChange?.(false);
|
|
3437
|
+
};
|
|
3438
|
+
|
|
3439
|
+
return (
|
|
3440
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
3441
|
+
{trigger && <DialogTrigger asChild>{trigger}</DialogTrigger>}
|
|
3442
|
+
<DialogContent ref={ref} size="sm" className={cn(className)}>
|
|
3443
|
+
<DialogHeader>
|
|
3444
|
+
<DialogTitle>{title}</DialogTitle>
|
|
3445
|
+
{description && (
|
|
3446
|
+
<DialogDescription>{description}</DialogDescription>
|
|
3447
|
+
)}
|
|
3448
|
+
</DialogHeader>
|
|
3449
|
+
<DialogFooter className="gap-2 sm:gap-0">
|
|
3450
|
+
<Button variant="outline" onClick={handleCancel} disabled={loading}>
|
|
3451
|
+
{cancelButtonText}
|
|
3452
|
+
</Button>
|
|
3453
|
+
<Button
|
|
3454
|
+
variant={variant === "destructive" ? "destructive" : "default"}
|
|
3455
|
+
onClick={handleConfirm}
|
|
3456
|
+
disabled={loading}
|
|
3457
|
+
loading={loading}
|
|
3458
|
+
>
|
|
3459
|
+
{confirmButtonText}
|
|
3460
|
+
</Button>
|
|
3461
|
+
</DialogFooter>
|
|
3462
|
+
</DialogContent>
|
|
3463
|
+
</Dialog>
|
|
3464
|
+
);
|
|
3465
|
+
}
|
|
3466
|
+
);
|
|
3467
|
+
ConfirmationModal.displayName = "ConfirmationModal";
|
|
3468
|
+
|
|
3469
|
+
export { ConfirmationModal };
|
|
2980
3470
|
`, prefix)
|
|
2981
3471
|
}
|
|
2982
3472
|
]
|
|
@@ -3127,6 +3617,222 @@ const TagGroup = ({
|
|
|
3127
3617
|
TagGroup.displayName = "TagGroup";
|
|
3128
3618
|
|
|
3129
3619
|
export { Tag, TagGroup, tagVariants };
|
|
3620
|
+
`, prefix)
|
|
3621
|
+
}
|
|
3622
|
+
]
|
|
3623
|
+
},
|
|
3624
|
+
"alert": {
|
|
3625
|
+
name: "alert",
|
|
3626
|
+
description: "A dismissible alert component for notifications, errors, warnings, and success messages with icons, actions, and controlled visibility",
|
|
3627
|
+
dependencies: [
|
|
3628
|
+
"class-variance-authority",
|
|
3629
|
+
"clsx",
|
|
3630
|
+
"tailwind-merge",
|
|
3631
|
+
"lucide-react"
|
|
3632
|
+
],
|
|
3633
|
+
files: [
|
|
3634
|
+
{
|
|
3635
|
+
name: "alert.tsx",
|
|
3636
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
3637
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3638
|
+
import { CheckCircle2, XCircle, AlertTriangle, Info, X } from "lucide-react";
|
|
3639
|
+
|
|
3640
|
+
import { cn } from "../../lib/utils";
|
|
3641
|
+
|
|
3642
|
+
/**
|
|
3643
|
+
* Alert variants for different notification types.
|
|
3644
|
+
* Colors are hardcoded for Bootstrap compatibility.
|
|
3645
|
+
*/
|
|
3646
|
+
const alertVariants = cva(
|
|
3647
|
+
"relative w-full rounded border p-4 text-sm text-[#181D27] [&>svg~*]:pl-8 [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4",
|
|
3648
|
+
{
|
|
3649
|
+
variants: {
|
|
3650
|
+
variant: {
|
|
3651
|
+
default: "bg-[#F5F5F5] border-[#E9EAEB] [&>svg]:text-[#181D27]",
|
|
3652
|
+
success: "bg-[#ECFDF3] border-[#17B26A]/20 [&>svg]:text-[#17B26A]",
|
|
3653
|
+
error: "bg-[#FEF3F2] border-[#F04438]/20 [&>svg]:text-[#F04438]",
|
|
3654
|
+
destructive: "bg-[#FEF3F2] border-[#F04438]/20 [&>svg]:text-[#F04438]",
|
|
3655
|
+
warning: "bg-[#FFFAEB] border-[#F79009]/20 [&>svg]:text-[#F79009]",
|
|
3656
|
+
info: "bg-[#EBF5FF] border-[#4275D6]/20 [&>svg]:text-[#4275D6]",
|
|
3657
|
+
},
|
|
3658
|
+
},
|
|
3659
|
+
defaultVariants: {
|
|
3660
|
+
variant: "default",
|
|
3661
|
+
},
|
|
3662
|
+
}
|
|
3663
|
+
);
|
|
3664
|
+
|
|
3665
|
+
/**
|
|
3666
|
+
* Default icons for each alert variant
|
|
3667
|
+
*/
|
|
3668
|
+
const defaultIcons: Record<string, React.ReactNode> = {
|
|
3669
|
+
default: <Info className="size-5" />,
|
|
3670
|
+
success: <CheckCircle2 className="size-5" />,
|
|
3671
|
+
error: <XCircle className="size-5" />,
|
|
3672
|
+
destructive: <XCircle className="size-5" />,
|
|
3673
|
+
warning: <AlertTriangle className="size-5" />,
|
|
3674
|
+
info: <Info className="size-5" />,
|
|
3675
|
+
};
|
|
3676
|
+
|
|
3677
|
+
/**
|
|
3678
|
+
* Dismissible alert banners for notifications, errors, warnings, and success messages.
|
|
3679
|
+
*
|
|
3680
|
+
* @example
|
|
3681
|
+
* \`\`\`tsx
|
|
3682
|
+
* // Simple alert
|
|
3683
|
+
* <Alert variant="success">
|
|
3684
|
+
* <AlertTitle>Success!</AlertTitle>
|
|
3685
|
+
* <AlertDescription>Your changes have been saved.</AlertDescription>
|
|
3686
|
+
* </Alert>
|
|
3687
|
+
*
|
|
3688
|
+
* // With close button and actions
|
|
3689
|
+
* <Alert
|
|
3690
|
+
* variant="error"
|
|
3691
|
+
* closable
|
|
3692
|
+
* onClose={() => console.log('closed')}
|
|
3693
|
+
* action={<Button size="sm">Retry</Button>}
|
|
3694
|
+
* >
|
|
3695
|
+
* <AlertTitle>Error</AlertTitle>
|
|
3696
|
+
* <AlertDescription>Something went wrong.</AlertDescription>
|
|
3697
|
+
* </Alert>
|
|
3698
|
+
*
|
|
3699
|
+
* // Controlled visibility
|
|
3700
|
+
* const [open, setOpen] = useState(true)
|
|
3701
|
+
* <Alert open={open} onClose={() => setOpen(false)} closable>...</Alert>
|
|
3702
|
+
* \`\`\`
|
|
3703
|
+
*/
|
|
3704
|
+
export interface AlertProps
|
|
3705
|
+
extends
|
|
3706
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
3707
|
+
VariantProps<typeof alertVariants> {
|
|
3708
|
+
/** Custom icon to display. Set to null to hide icon. */
|
|
3709
|
+
icon?: React.ReactNode | null;
|
|
3710
|
+
/** Whether to show the default variant icon (default: true) */
|
|
3711
|
+
showIcon?: boolean;
|
|
3712
|
+
/** Show close button */
|
|
3713
|
+
closable?: boolean;
|
|
3714
|
+
/** Callback when close button is clicked */
|
|
3715
|
+
onClose?: () => void;
|
|
3716
|
+
/** Primary action element (e.g., button) */
|
|
3717
|
+
action?: React.ReactNode;
|
|
3718
|
+
/** Secondary action element */
|
|
3719
|
+
secondaryAction?: React.ReactNode;
|
|
3720
|
+
/** Controlled visibility state */
|
|
3721
|
+
open?: boolean;
|
|
3722
|
+
/** Initial visibility for uncontrolled mode (default: true) */
|
|
3723
|
+
defaultOpen?: boolean;
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
|
|
3727
|
+
(
|
|
3728
|
+
{
|
|
3729
|
+
className,
|
|
3730
|
+
variant = "default",
|
|
3731
|
+
icon,
|
|
3732
|
+
showIcon = true,
|
|
3733
|
+
closable = false,
|
|
3734
|
+
onClose,
|
|
3735
|
+
action,
|
|
3736
|
+
secondaryAction,
|
|
3737
|
+
open: controlledOpen,
|
|
3738
|
+
defaultOpen = true,
|
|
3739
|
+
children,
|
|
3740
|
+
...props
|
|
3741
|
+
},
|
|
3742
|
+
ref
|
|
3743
|
+
) => {
|
|
3744
|
+
const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
|
|
3745
|
+
const isControlled = controlledOpen !== undefined;
|
|
3746
|
+
const isOpen = isControlled ? controlledOpen : internalOpen;
|
|
3747
|
+
|
|
3748
|
+
const handleClose = React.useCallback(() => {
|
|
3749
|
+
if (!isControlled) {
|
|
3750
|
+
setInternalOpen(false);
|
|
3751
|
+
}
|
|
3752
|
+
onClose?.();
|
|
3753
|
+
}, [isControlled, onClose]);
|
|
3754
|
+
|
|
3755
|
+
if (!isOpen) {
|
|
3756
|
+
return null;
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3759
|
+
const renderIcon = () => {
|
|
3760
|
+
if (icon === null || !showIcon) return null;
|
|
3761
|
+
if (icon) return icon;
|
|
3762
|
+
return defaultIcons[variant || "default"];
|
|
3763
|
+
};
|
|
3764
|
+
|
|
3765
|
+
const hasActions = action || secondaryAction;
|
|
3766
|
+
|
|
3767
|
+
return (
|
|
3768
|
+
<div
|
|
3769
|
+
ref={ref}
|
|
3770
|
+
role="alert"
|
|
3771
|
+
aria-live="polite"
|
|
3772
|
+
className={cn(alertVariants({ variant, className }))}
|
|
3773
|
+
{...props}
|
|
3774
|
+
>
|
|
3775
|
+
{renderIcon()}
|
|
3776
|
+
<div className="flex flex-1 items-center justify-between gap-4">
|
|
3777
|
+
<div className="flex-1">{children}</div>
|
|
3778
|
+
{(hasActions || closable) && (
|
|
3779
|
+
<div className="flex shrink-0 items-center gap-2">
|
|
3780
|
+
{secondaryAction}
|
|
3781
|
+
{action}
|
|
3782
|
+
{closable && (
|
|
3783
|
+
<button
|
|
3784
|
+
type="button"
|
|
3785
|
+
onClick={handleClose}
|
|
3786
|
+
className={cn(
|
|
3787
|
+
"rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2",
|
|
3788
|
+
variant === "default" && "focus:ring-[#181D27]",
|
|
3789
|
+
variant === "success" && "focus:ring-[#17B26A]",
|
|
3790
|
+
(variant === "error" || variant === "destructive") &&
|
|
3791
|
+
"focus:ring-[#F04438]",
|
|
3792
|
+
variant === "warning" && "focus:ring-[#F79009]",
|
|
3793
|
+
variant === "info" && "focus:ring-[#4275D6]"
|
|
3794
|
+
)}
|
|
3795
|
+
aria-label="Close alert"
|
|
3796
|
+
>
|
|
3797
|
+
<X className="size-5" />
|
|
3798
|
+
</button>
|
|
3799
|
+
)}
|
|
3800
|
+
</div>
|
|
3801
|
+
)}
|
|
3802
|
+
</div>
|
|
3803
|
+
</div>
|
|
3804
|
+
);
|
|
3805
|
+
}
|
|
3806
|
+
);
|
|
3807
|
+
Alert.displayName = "Alert";
|
|
3808
|
+
|
|
3809
|
+
/**
|
|
3810
|
+
* Alert title component for the heading text.
|
|
3811
|
+
*/
|
|
3812
|
+
const AlertTitle = React.forwardRef<
|
|
3813
|
+
HTMLHeadingElement,
|
|
3814
|
+
React.HTMLAttributes<HTMLHeadingElement>
|
|
3815
|
+
>(({ className, ...props }, ref) => (
|
|
3816
|
+
<h5
|
|
3817
|
+
ref={ref}
|
|
3818
|
+
className={cn("font-semibold leading-tight tracking-tight", className)}
|
|
3819
|
+
{...props}
|
|
3820
|
+
/>
|
|
3821
|
+
));
|
|
3822
|
+
AlertTitle.displayName = "AlertTitle";
|
|
3823
|
+
|
|
3824
|
+
/**
|
|
3825
|
+
* Alert description component for the body text.
|
|
3826
|
+
*/
|
|
3827
|
+
const AlertDescription = React.forwardRef<
|
|
3828
|
+
HTMLParagraphElement,
|
|
3829
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
3830
|
+
>(({ className, ...props }, ref) => (
|
|
3831
|
+
<p ref={ref} className={cn("mt-1 text-sm", className)} {...props} />
|
|
3832
|
+
));
|
|
3833
|
+
AlertDescription.displayName = "AlertDescription";
|
|
3834
|
+
|
|
3835
|
+
export { Alert, AlertTitle, AlertDescription, alertVariants };
|
|
3130
3836
|
`, prefix)
|
|
3131
3837
|
}
|
|
3132
3838
|
]
|