@windrun-huaiin/third-ui 20.0.0 → 21.0.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 (54) hide show
  1. package/LICENSE +1 -1
  2. package/dist/clerk/clerk-page-generator.d.ts +0 -8
  3. package/dist/clerk/fingerprint/fingerprint-provider.js +58 -49
  4. package/dist/clerk/fingerprint/fingerprint-provider.mjs +58 -49
  5. package/dist/fuma/fuma-page-genarator.d.ts +2 -2
  6. package/dist/fuma/fuma-page-genarator.js +21 -8
  7. package/dist/fuma/fuma-page-genarator.mjs +21 -8
  8. package/dist/fuma/llm-copy-handler.js +3 -2
  9. package/dist/fuma/llm-copy-handler.mjs +3 -2
  10. package/dist/fuma/mdx/index.d.ts +1 -0
  11. package/dist/fuma/mdx/index.js +3 -0
  12. package/dist/fuma/mdx/index.mjs +1 -0
  13. package/dist/fuma/mdx/math.d.ts +17 -0
  14. package/dist/fuma/mdx/math.js +60 -0
  15. package/dist/fuma/mdx/math.mjs +57 -0
  16. package/dist/fuma/mdx/zia-card.js +1 -1
  17. package/dist/fuma/mdx/zia-card.mjs +1 -1
  18. package/dist/main/{ads-alert-dialog.d.ts → alert-dialog/ads-alert-dialog.d.ts} +1 -1
  19. package/dist/main/alert-dialog/ads-alert-dialog.js +24 -0
  20. package/dist/main/alert-dialog/ads-alert-dialog.mjs +22 -0
  21. package/dist/main/alert-dialog/confirm-dialog.d.ts +15 -0
  22. package/dist/main/alert-dialog/confirm-dialog.js +40 -0
  23. package/dist/main/alert-dialog/confirm-dialog.mjs +38 -0
  24. package/dist/main/alert-dialog/dialog-styles.d.ts +14 -0
  25. package/dist/main/alert-dialog/dialog-styles.js +35 -0
  26. package/dist/main/alert-dialog/dialog-styles.mjs +20 -0
  27. package/dist/main/alert-dialog/high-priority-confirm-dialog.d.ts +12 -0
  28. package/dist/main/alert-dialog/high-priority-confirm-dialog.js +23 -0
  29. package/dist/main/alert-dialog/high-priority-confirm-dialog.mjs +21 -0
  30. package/dist/main/alert-dialog/index.d.ts +4 -0
  31. package/dist/main/alert-dialog/info-dialog.d.ts +13 -0
  32. package/dist/main/alert-dialog/info-dialog.js +50 -0
  33. package/dist/main/alert-dialog/info-dialog.mjs +48 -0
  34. package/dist/main/index.d.ts +1 -1
  35. package/dist/main/index.js +7 -1
  36. package/dist/main/index.mjs +4 -1
  37. package/package.json +4 -4
  38. package/src/clerk/clerk-page-generator.tsx +0 -9
  39. package/src/clerk/fingerprint/fingerprint-provider.tsx +155 -62
  40. package/src/fuma/fuma-page-genarator.tsx +26 -9
  41. package/src/fuma/llm-copy-handler.ts +3 -3
  42. package/src/fuma/mdx/index.ts +1 -0
  43. package/src/fuma/mdx/math.tsx +130 -0
  44. package/src/fuma/mdx/zia-card.tsx +1 -0
  45. package/src/main/{ads-alert-dialog.tsx → alert-dialog/ads-alert-dialog.tsx} +46 -29
  46. package/src/main/alert-dialog/confirm-dialog.tsx +131 -0
  47. package/src/main/alert-dialog/dialog-styles.ts +73 -0
  48. package/src/main/alert-dialog/high-priority-confirm-dialog.tsx +94 -0
  49. package/src/main/alert-dialog/index.ts +7 -0
  50. package/src/main/alert-dialog/info-dialog.tsx +139 -0
  51. package/src/main/index.ts +1 -1
  52. package/src/main/language-detector.tsx +0 -8
  53. package/dist/main/ads-alert-dialog.js +0 -21
  54. package/dist/main/ads-alert-dialog.mjs +0 -19
@@ -0,0 +1,130 @@
1
+ import katex from 'katex';
2
+ import { cn } from '@windrun-huaiin/lib/utils';
3
+ import type { HTMLAttributes, ReactNode } from 'react';
4
+
5
+ type MathSourceProps = {
6
+ children?: ReactNode;
7
+ math?: string;
8
+ formula?: string;
9
+ };
10
+
11
+ type Align = 'left' | 'center' | 'right';
12
+
13
+ export type MathBlockProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> &
14
+ MathSourceProps & {
15
+ title?: ReactNode;
16
+ titleAlign?: Align;
17
+ };
18
+
19
+ export type InlineMathProps = Omit<HTMLAttributes<HTMLSpanElement>, 'children'> &
20
+ MathSourceProps & {
21
+ align?: Align;
22
+ };
23
+
24
+ const alignClassMap: Record<Align, string> = {
25
+ left: 'text-left justify-start',
26
+ center: 'text-center justify-center',
27
+ right: 'text-right justify-end',
28
+ };
29
+
30
+ const textAlignClassMap: Record<Align, string> = {
31
+ left: 'text-left',
32
+ center: 'text-center',
33
+ right: 'text-right',
34
+ };
35
+
36
+ function getMathSource({ children, math, formula }: MathSourceProps): string {
37
+ if (typeof math === 'string' && math.trim() !== '') return math.trim();
38
+ if (typeof formula === 'string' && formula.trim() !== '') return formula.trim();
39
+ if (typeof children === 'string' && children.trim() !== '') return children.trim();
40
+
41
+ if (Array.isArray(children)) {
42
+ const text = children
43
+ .map((item) => (typeof item === 'string' ? item : ''))
44
+ .join('')
45
+ .trim();
46
+
47
+ if (text !== '') return text;
48
+ }
49
+
50
+ return '';
51
+ }
52
+
53
+ function renderMath(source: string, displayMode: boolean) {
54
+ if (source === '') return '';
55
+
56
+ return katex.renderToString(source, {
57
+ displayMode,
58
+ throwOnError: false,
59
+ output: 'html',
60
+ strict: 'ignore',
61
+ trust: false,
62
+ });
63
+ }
64
+
65
+ export function MathBlock({
66
+ title,
67
+ titleAlign = 'center',
68
+ children,
69
+ math,
70
+ formula,
71
+ className,
72
+ ...props
73
+ }: MathBlockProps) {
74
+ const source = getMathSource({ children, math, formula });
75
+ const html = renderMath(source, true);
76
+
77
+ return (
78
+ <div
79
+ {...props}
80
+ className={cn(
81
+ 'not-prose my-6 overflow-x-auto rounded-xl border bg-fd-card p-4 text-fd-card-foreground',
82
+ className,
83
+ )}
84
+ >
85
+ {title ? (
86
+ <div
87
+ className={cn(
88
+ 'mb-3 text-sm font-medium text-fd-muted-foreground',
89
+ alignClassMap[titleAlign].split(' ')[0],
90
+ )}
91
+ >
92
+ {title}
93
+ </div>
94
+ ) : null}
95
+ {html ? (
96
+ <div
97
+ className="min-w-fit [&_.katex-display]:my-0 [&_.katex-display]:overflow-x-auto [&_.katex-display]:overflow-y-hidden"
98
+ dangerouslySetInnerHTML={{ __html: html }}
99
+ />
100
+ ) : (
101
+ <div className="text-sm text-fd-muted-foreground">Empty math block.</div>
102
+ )}
103
+ </div>
104
+ );
105
+ }
106
+
107
+ export function InlineMath({
108
+ children,
109
+ math,
110
+ formula,
111
+ align = 'center',
112
+ className,
113
+ ...props
114
+ }: InlineMathProps) {
115
+ const source = getMathSource({ children, math, formula });
116
+ const html = renderMath(source, false);
117
+
118
+ return (
119
+ <span
120
+ {...props}
121
+ className={cn(
122
+ 'mx-1 inline-flex max-w-full align-middle rounded-md bg-neutral-200 px-2 py-0.5 text-sm leading-none dark:bg-white/20 [&_.katex]:text-inherit',
123
+ textAlignClassMap[align],
124
+ className,
125
+ )}
126
+ >
127
+ <span dangerouslySetInnerHTML={{ __html: html }} />
128
+ </span>
129
+ );
130
+ }
@@ -22,6 +22,7 @@ export function ZiaCard({ icon, title, description, ...props }: ZiaCardProps) {
22
22
  return (
23
23
  <Link
24
24
  href={props.href!}
25
+ prefetch={false}
25
26
  data-card
26
27
  className={cn(
27
28
  'block rounded-lg border bg-fd-card p-4 text-fd-card-foreground shadow-md transition-colors @max-lg:col-span-full',
@@ -1,15 +1,28 @@
1
1
  'use client';
2
2
 
3
- import React, { useState } from "react";
4
- import Image from "next/image";
5
- import { ImageOffIcon, InfoIcon, XIcon } from "@windrun-huaiin/base-ui/icons";
3
+ import React, { useState } from 'react';
4
+ import Image from 'next/image';
5
+ import { BellIcon, ImageOffIcon, XIcon } from '@windrun-huaiin/base-ui/icons';
6
6
  import {
7
7
  AlertDialog,
8
+ AlertDialogAction,
8
9
  AlertDialogContent,
9
- AlertDialogTitle,
10
10
  AlertDialogDescription,
11
- AlertDialogAction,
12
- } from "@windrun-huaiin/base-ui/ui";
11
+ AlertDialogTitle,
12
+ } from '@windrun-huaiin/base-ui/ui';
13
+ import { cn } from '@windrun-huaiin/lib/utils';
14
+ import {
15
+ closeButtonClass,
16
+ dialogContentClass,
17
+ dialogDescriptionClass,
18
+ dialogFooterClass,
19
+ dialogHeaderClass,
20
+ dialogThemedOverlayClass,
21
+ dialogTitleClass,
22
+ primaryButtonClass,
23
+ secondaryButtonClass,
24
+ subtlePrimaryButtonClass,
25
+ } from './dialog-styles';
13
26
 
14
27
  interface AdsAlertDialogProps {
15
28
  open: boolean;
@@ -37,49 +50,52 @@ export function AdsAlertDialog({
37
50
  onConfirm,
38
51
  }: AdsAlertDialogProps) {
39
52
  const [imgError, setImgError] = useState(false);
53
+ const handleClose = () => onOpenChange(false);
40
54
 
41
55
  return (
42
56
  <AlertDialog open={open} onOpenChange={onOpenChange}>
43
57
  <AlertDialogContent
44
- className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-white dark:bg-neutral-900 rounded-2xl shadow-2xl border border-neutral-200 dark:border-neutral-700 max-w-md w-full min-w-[320px] p-4 flex flex-col items-stretch"
58
+ className={cn(dialogContentClass, 'max-w-md p-4')}
59
+ overlayClassName={dialogThemedOverlayClass}
60
+ onOverlayClick={handleClose}
45
61
  >
46
- {/* Header: left icon + title, right X close */}
47
- <div className="flex flex-row items-center justify-between mb-2">
62
+ <div className={dialogHeaderClass}>
48
63
  <AlertDialogTitle asChild>
49
- <div className="flex flex-row items-center gap-1 min-w-0 text-xl font-semibold">
50
- <InfoIcon className="w-5 h-5" />
64
+ <div className={dialogTitleClass}>
65
+ <span className="inline-flex size-9 shrink-0 items-center justify-center rounded-full bg-neutral-100 text-neutral-600 ring-1 ring-neutral-200 dark:bg-neutral-900 dark:text-neutral-300 dark:ring-neutral-800">
66
+ <BellIcon className="size-5" />
67
+ </span>
51
68
  <span className="truncate">{title}</span>
52
69
  </div>
53
70
  </AlertDialogTitle>
54
71
  <button
55
- className="text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-200 text-xl ml-4"
56
- onClick={() => onOpenChange(false)}
72
+ type="button"
73
+ className={closeButtonClass}
74
+ onClick={handleClose}
57
75
  aria-label="Close"
58
- tabIndex={0}
59
76
  >
60
- <XIcon className="w-5 h-5" />
77
+ <XIcon className="size-4" />
61
78
  </button>
62
79
  </div>
63
-
64
- {/* description area */}
65
- <AlertDialogDescription className="text-base font-medium text-neutral-800 dark:text-neutral-100 mb-2">
80
+
81
+ <AlertDialogDescription className={cn(dialogDescriptionClass, 'mb-3 text-base text-neutral-800 dark:text-neutral-100')}>
66
82
  {description}
67
83
  </AlertDialogDescription>
68
- {/* image area (optional) */}
84
+
69
85
  {imgSrc && (
70
- <div className="w-full max-w-[400px] h-[220px] relative flex items-center justify-center mb-2">
86
+ <div className="relative mb-2 flex h-[220px] w-full max-w-[400px] items-center justify-center overflow-hidden rounded-xl border border-neutral-200 bg-neutral-50 dark:border-neutral-800 dark:bg-neutral-900">
71
87
  {imgError ? (
72
- <div className="absolute inset-0 flex flex-col items-center justify-center bg-gray-100 dark:bg-neutral-800 border border-dashed border-neutral-300 dark:border-neutral-700 rounded-lg text-neutral-400 text-sm">
73
- <ImageOffIcon className="w-12 h-12 mb-2" />
88
+ <div className="absolute inset-0 flex flex-col items-center justify-center border border-dashed border-neutral-300 text-sm text-neutral-400 dark:border-neutral-700">
89
+ <ImageOffIcon className="mb-2 size-12" />
74
90
  <span>Image loading failed</span>
75
91
  </div>
76
92
  ) : imgHref ? (
77
- <a href={imgHref} target="_blank" rel="noopener noreferrer" className="block w-full h-full">
93
+ <a href={imgHref} target="_blank" rel="noopener noreferrer" className="block h-full w-full">
78
94
  <Image
79
95
  src={imgSrc}
80
96
  alt="image"
81
97
  fill
82
- className="object-contain rounded-lg"
98
+ className="rounded-lg object-contain"
83
99
  priority={false}
84
100
  placeholder="empty"
85
101
  unoptimized
@@ -92,7 +108,7 @@ export function AdsAlertDialog({
92
108
  src={imgSrc}
93
109
  alt="image"
94
110
  fill
95
- className="object-contain rounded-lg"
111
+ className="rounded-lg object-contain"
96
112
  priority={false}
97
113
  placeholder="empty"
98
114
  unoptimized
@@ -102,16 +118,17 @@ export function AdsAlertDialog({
102
118
  )}
103
119
  </div>
104
120
  )}
105
- {/* button area (optional) */}
121
+
106
122
  {(cancelText || confirmText) && (
107
- <div className="flex justify-end gap-2 mt-2">
123
+ <div className={dialogFooterClass}>
108
124
  {cancelText && (
109
125
  <button
126
+ type="button"
110
127
  onClick={() => {
111
128
  onOpenChange(false);
112
129
  onCancel?.();
113
130
  }}
114
- className="px-6 py-2 rounded-lg border border-neutral-300 dark:border-neutral-600 bg-white dark:bg-neutral-800 text-neutral-700 dark:text-neutral-200 font-semibold hover:bg-neutral-100 dark:hover:bg-neutral-700 transition"
131
+ className={secondaryButtonClass}
115
132
  >
116
133
  {cancelText}
117
134
  </button>
@@ -122,7 +139,7 @@ export function AdsAlertDialog({
122
139
  onOpenChange(false);
123
140
  onConfirm?.();
124
141
  }}
125
- className="px-6 py-2 rounded-lg bg-purple-500 text-white font-semibold hover:bg-purple-600 transition"
142
+ className={confirmText && !cancelText ? subtlePrimaryButtonClass : primaryButtonClass}
126
143
  >
127
144
  {confirmText}
128
145
  </AlertDialogAction>
@@ -0,0 +1,131 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { CircleAlertIcon, CircleQuestionMarkIcon, XIcon } from '@windrun-huaiin/base-ui/icons';
5
+ import { themeBgColor, themeIconColor } from '@windrun-huaiin/base-ui/lib';
6
+ import {
7
+ AlertDialog,
8
+ AlertDialogAction,
9
+ AlertDialogCancel,
10
+ AlertDialogContent,
11
+ AlertDialogDescription,
12
+ AlertDialogTitle,
13
+ } from '@windrun-huaiin/base-ui/ui';
14
+ import { cn } from '@windrun-huaiin/lib/utils';
15
+ import {
16
+ closeButtonClass,
17
+ dangerButtonClass,
18
+ dialogContentClass,
19
+ dialogDescriptionClass,
20
+ dialogFooterClass,
21
+ dialogHeaderClass,
22
+ dialogThemedOverlayClass,
23
+ dialogTitleClass,
24
+ primaryButtonClass,
25
+ secondaryButtonClass,
26
+ } from './dialog-styles';
27
+
28
+ export type ConfirmDialogType = 'normal' | 'danger';
29
+
30
+ interface ConfirmDialogProps {
31
+ open: boolean;
32
+ onOpenChange: (open: boolean) => void;
33
+ type?: ConfirmDialogType;
34
+ title: React.ReactNode;
35
+ description: React.ReactNode;
36
+ cancelText?: string;
37
+ confirmText?: string;
38
+ onCancel?: () => void;
39
+ onConfirm?: () => void;
40
+ }
41
+
42
+ const confirmTypeClassMap: Record<ConfirmDialogType, {
43
+ content: string;
44
+ iconWrap: string;
45
+ icon: string;
46
+ action: string;
47
+ Icon: typeof CircleQuestionMarkIcon;
48
+ }> = {
49
+ normal: {
50
+ content: '',
51
+ iconWrap: cn(themeBgColor, 'ring-0'),
52
+ icon: themeIconColor,
53
+ action: primaryButtonClass,
54
+ Icon: CircleQuestionMarkIcon,
55
+ },
56
+ danger: {
57
+ content: 'border-red-300 dark:border-red-700',
58
+ iconWrap: 'bg-red-100 text-red-600 ring-red-200 dark:bg-red-950 dark:text-red-300 dark:ring-red-900',
59
+ icon: 'text-red-600 dark:text-red-300',
60
+ action: dangerButtonClass,
61
+ Icon: CircleAlertIcon,
62
+ },
63
+ };
64
+
65
+ export function ConfirmDialog({
66
+ open,
67
+ onOpenChange,
68
+ type = 'normal',
69
+ title,
70
+ description,
71
+ cancelText = 'Cancel',
72
+ confirmText = 'Confirm',
73
+ onCancel,
74
+ onConfirm,
75
+ }: ConfirmDialogProps) {
76
+ const typeClass = confirmTypeClassMap[type];
77
+ const Icon = typeClass.Icon;
78
+
79
+ const handleCancel = () => {
80
+ onOpenChange(false);
81
+ onCancel?.();
82
+ };
83
+
84
+ return (
85
+ <AlertDialog open={open} onOpenChange={onOpenChange}>
86
+ <AlertDialogContent
87
+ className={cn(dialogContentClass, typeClass.content)}
88
+ overlayClassName={dialogThemedOverlayClass}
89
+ onOverlayClick={handleCancel}
90
+ >
91
+ <div className={dialogHeaderClass}>
92
+ <AlertDialogTitle asChild>
93
+ <div className={dialogTitleClass}>
94
+ <span className={cn('inline-flex size-9 shrink-0 items-center justify-center rounded-full ring-1', typeClass.iconWrap)}>
95
+ <Icon className={cn('size-5', typeClass.icon)} />
96
+ </span>
97
+ <span className="min-w-0 truncate">{title}</span>
98
+ </div>
99
+ </AlertDialogTitle>
100
+ <button
101
+ type="button"
102
+ className={closeButtonClass}
103
+ onClick={handleCancel}
104
+ aria-label="Close"
105
+ >
106
+ <XIcon className="size-4" />
107
+ </button>
108
+ </div>
109
+
110
+ <AlertDialogDescription className={dialogDescriptionClass}>
111
+ {description}
112
+ </AlertDialogDescription>
113
+
114
+ <div className={dialogFooterClass}>
115
+ <AlertDialogCancel className={secondaryButtonClass} onClick={handleCancel}>
116
+ {cancelText}
117
+ </AlertDialogCancel>
118
+ <AlertDialogAction
119
+ className={typeClass.action}
120
+ onClick={() => {
121
+ onOpenChange(false);
122
+ onConfirm?.();
123
+ }}
124
+ >
125
+ {confirmText}
126
+ </AlertDialogAction>
127
+ </div>
128
+ </AlertDialogContent>
129
+ </AlertDialog>
130
+ );
131
+ }
@@ -0,0 +1,73 @@
1
+ 'use client';
2
+
3
+ import {
4
+ themeBgColor,
5
+ themeBorderColor,
6
+ themeButtonGradientClass,
7
+ themeButtonGradientHoverClass,
8
+ themeIconColor,
9
+ themeMainBgColor,
10
+ themeRingColor,
11
+ } from '@windrun-huaiin/base-ui/lib';
12
+ import { cn } from '@windrun-huaiin/lib/utils';
13
+
14
+ export const dialogSurfaceClass = cn(
15
+ 'w-[calc(100vw-2rem)] max-w-md rounded-2xl border bg-white p-5 text-neutral-950 shadow-2xl outline-none dark:bg-neutral-950 dark:text-neutral-50',
16
+ 'border-neutral-200 dark:border-neutral-800'
17
+ );
18
+
19
+ export const dialogThemedOverlayClass = cn(
20
+ themeMainBgColor,
21
+ 'opacity-90 backdrop-blur-[2px] dark:opacity-85'
22
+ );
23
+
24
+ export const dialogContentClass = cn(
25
+ 'fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2',
26
+ dialogSurfaceClass
27
+ );
28
+
29
+ export const dialogHeaderClass = 'flex items-start justify-between gap-4';
30
+
31
+ export const dialogTitleClass =
32
+ 'flex min-w-0 items-center gap-2 text-lg font-bold leading-tight text-neutral-950 dark:text-neutral-50';
33
+
34
+ export const dialogDescriptionClass =
35
+ 'mt-3 text-sm font-medium leading-relaxed text-neutral-600 dark:text-neutral-300';
36
+
37
+ export const dialogFooterClass = 'mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end';
38
+
39
+ export const closeButtonClass =
40
+ 'inline-flex size-8 shrink-0 items-center justify-center rounded-full text-neutral-400 transition hover:bg-neutral-100 hover:text-neutral-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-neutral-200';
41
+
42
+ export const secondaryButtonClass =
43
+ 'inline-flex min-h-10 items-center justify-center rounded-full border border-neutral-300 bg-white px-5 py-2 text-sm font-semibold text-neutral-700 transition hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-400 disabled:pointer-events-none disabled:opacity-60 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-200 dark:hover:bg-neutral-800';
44
+
45
+ export const primaryButtonClass = cn(
46
+ 'inline-flex min-h-10 items-center justify-center rounded-full px-5 py-2 text-sm font-bold text-white shadow-sm transition hover:shadow-md focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-60',
47
+ themeButtonGradientClass,
48
+ themeButtonGradientHoverClass,
49
+ themeRingColor
50
+ );
51
+
52
+ export const subtlePrimaryButtonClass = cn(
53
+ 'inline-flex min-h-10 items-center justify-center rounded-full border px-5 py-2 text-sm font-bold transition hover:brightness-95 focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-60',
54
+ themeBgColor,
55
+ themeBorderColor,
56
+ themeIconColor,
57
+ themeRingColor
58
+ );
59
+
60
+ export const dangerButtonClass =
61
+ 'inline-flex min-h-10 items-center justify-center rounded-full bg-red-600 px-5 py-2 text-sm font-bold text-white shadow-sm transition hover:bg-red-700 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500 disabled:pointer-events-none disabled:opacity-60 dark:bg-red-600 dark:hover:bg-red-500';
62
+
63
+ export const highPriorityTitleClass = cn(
64
+ 'flex min-w-0 items-center gap-2 text-lg font-bold leading-tight',
65
+ themeIconColor
66
+ );
67
+
68
+ export const highPrioritySurfaceClass = cn(
69
+ dialogSurfaceClass,
70
+ 'backdrop-blur-md ring-4 animate-in zoom-in-95 duration-300',
71
+ themeBorderColor,
72
+ themeRingColor
73
+ );
@@ -0,0 +1,94 @@
1
+ "use client";
2
+
3
+ import React, { useEffect, useState } from "react";
4
+ import { createPortal } from "react-dom";
5
+ import { FAQSIcon, XIcon } from "@windrun-huaiin/base-ui/icons";
6
+ import { cn } from "@windrun-huaiin/lib/utils";
7
+ import {
8
+ closeButtonClass,
9
+ dialogDescriptionClass,
10
+ dialogFooterClass,
11
+ dialogHeaderClass,
12
+ highPrioritySurfaceClass,
13
+ highPriorityTitleClass,
14
+ primaryButtonClass,
15
+ secondaryButtonClass,
16
+ } from "./dialog-styles";
17
+ import { themeBgColor, themeBorderColor, themeIconColor } from "@windrun-huaiin/base-ui/lib";
18
+
19
+ interface HighPriorityConfirmDialogProps {
20
+ open: boolean;
21
+ onCancel: () => void;
22
+ onConfirm: () => void;
23
+ title: string;
24
+ description: React.ReactNode;
25
+ confirmText?: string;
26
+ cancelText?: string;
27
+ }
28
+
29
+ export function HighPriorityConfirmDialog({
30
+ open,
31
+ onCancel,
32
+ onConfirm,
33
+ title,
34
+ description,
35
+ confirmText = "Confirm",
36
+ cancelText = "Cancel",
37
+ }: HighPriorityConfirmDialogProps) {
38
+ const [mounted, setMounted] = useState(false);
39
+
40
+ useEffect(() => {
41
+ // Ensure portal target exists and prevent hydration mismatch
42
+ setTimeout(() => setMounted(true), 0);
43
+ }, []);
44
+
45
+ if (!open || !mounted) return null;
46
+
47
+ return createPortal(
48
+ <div className="fixed inset-0 z-10000 flex items-center justify-center bg-black/60 backdrop-blur-sm animate-in fade-in duration-300">
49
+ <div
50
+ className={cn(highPrioritySurfaceClass, "scale-100")}
51
+ role="dialog"
52
+ aria-modal="true"
53
+ onClick={(e) => e.stopPropagation()}
54
+ >
55
+ <div className={dialogHeaderClass}>
56
+ <h3 className={highPriorityTitleClass}>
57
+ <span className={cn('inline-flex size-9 shrink-0 items-center justify-center rounded-full ring-1', themeBgColor, themeBorderColor)}>
58
+ <FAQSIcon className={cn('size-5', themeIconColor)} />
59
+ </span>
60
+ <span className="min-w-0 truncate">{title}</span>
61
+ </h3>
62
+ <button
63
+ type="button"
64
+ className={closeButtonClass}
65
+ onClick={onCancel}
66
+ aria-label="Close"
67
+ >
68
+ <XIcon className="size-4" />
69
+ </button>
70
+ </div>
71
+ <div className={dialogDescriptionClass}>
72
+ {description}
73
+ </div>
74
+ <div className={dialogFooterClass}>
75
+ <button
76
+ type="button"
77
+ onClick={onCancel}
78
+ className={secondaryButtonClass}
79
+ >
80
+ {cancelText}
81
+ </button>
82
+ <button
83
+ type="button"
84
+ onClick={onConfirm}
85
+ className={cn(primaryButtonClass, "hover:scale-105 active:scale-95")}
86
+ >
87
+ {confirmText}
88
+ </button>
89
+ </div>
90
+ </div>
91
+ </div>,
92
+ document.body
93
+ );
94
+ }
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+
3
+ export * from './ads-alert-dialog';
4
+ export * from './confirm-dialog';
5
+ export * from './high-priority-confirm-dialog';
6
+ export * from './info-dialog';
7
+