@zentauri-ui/zentauri-components 2.2.0 → 2.2.2

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 (144) hide show
  1. package/README.md +9 -7
  2. package/cli/props.json +1495 -798
  3. package/cli/registry.json +12 -0
  4. package/dist/chunk-46HJFCP7.js +87 -0
  5. package/dist/chunk-46HJFCP7.js.map +1 -0
  6. package/dist/{chunk-EPJYLBXV.js → chunk-5I4GAURE.js} +6 -6
  7. package/dist/{chunk-EPJYLBXV.js.map → chunk-5I4GAURE.js.map} +1 -1
  8. package/dist/chunk-ATE5SCTR.mjs +39 -0
  9. package/dist/chunk-ATE5SCTR.mjs.map +1 -0
  10. package/dist/chunk-DIAA5VH4.mjs +64 -0
  11. package/dist/chunk-DIAA5VH4.mjs.map +1 -0
  12. package/dist/{chunk-AMNJ35TT.mjs → chunk-GFE6ZX5Y.mjs} +25 -6
  13. package/dist/chunk-GFE6ZX5Y.mjs.map +1 -0
  14. package/dist/chunk-H3BJOK22.js +74 -0
  15. package/dist/chunk-H3BJOK22.js.map +1 -0
  16. package/dist/chunk-HR6MGXNI.mjs +212 -0
  17. package/dist/chunk-HR6MGXNI.mjs.map +1 -0
  18. package/dist/chunk-ILPPXWR3.js +19 -0
  19. package/dist/{chunk-VQJXOJ7G.js.map → chunk-ILPPXWR3.js.map} +1 -1
  20. package/dist/{chunk-YTCVWOBC.js → chunk-IY72Z65Z.js} +12 -12
  21. package/dist/{chunk-YTCVWOBC.js.map → chunk-IY72Z65Z.js.map} +1 -1
  22. package/dist/chunk-LOEKM3FL.mjs +78 -0
  23. package/dist/chunk-LOEKM3FL.mjs.map +1 -0
  24. package/dist/{chunk-5XSW5JYA.mjs → chunk-N2G7IWHS.mjs} +4 -4
  25. package/dist/{chunk-5XSW5JYA.mjs.map → chunk-N2G7IWHS.mjs.map} +1 -1
  26. package/dist/{chunk-37KMH77M.mjs → chunk-NUV2I337.mjs} +3 -3
  27. package/dist/{chunk-37KMH77M.mjs.map → chunk-NUV2I337.mjs.map} +1 -1
  28. package/dist/chunk-PQ2XTY3M.js +44 -0
  29. package/dist/chunk-PQ2XTY3M.js.map +1 -0
  30. package/dist/{chunk-F3G3RL2N.js → chunk-UZ6Y5CSV.js} +25 -6
  31. package/dist/chunk-UZ6Y5CSV.js.map +1 -0
  32. package/dist/{chunk-TACF7MJE.mjs → chunk-W5MTZJPE.mjs} +3 -3
  33. package/dist/{chunk-TACF7MJE.mjs.map → chunk-W5MTZJPE.mjs.map} +1 -1
  34. package/dist/chunk-WMM42MAC.js +222 -0
  35. package/dist/chunk-WMM42MAC.js.map +1 -0
  36. package/dist/design-system/facade.js +8 -6
  37. package/dist/design-system/facade.js.map +1 -1
  38. package/dist/design-system/facade.mjs +7 -5
  39. package/dist/design-system/facade.mjs.map +1 -1
  40. package/dist/design-system/index.d.ts +2 -0
  41. package/dist/design-system/index.d.ts.map +1 -1
  42. package/dist/design-system/password-strength-meter.d.ts +74 -0
  43. package/dist/design-system/password-strength-meter.d.ts.map +1 -0
  44. package/dist/design-system/secret-reveal.d.ts +57 -0
  45. package/dist/design-system/secret-reveal.d.ts.map +1 -0
  46. package/dist/ui/buttons/animated.js +10 -8
  47. package/dist/ui/buttons/animated.js.map +1 -1
  48. package/dist/ui/buttons/animated.mjs +8 -6
  49. package/dist/ui/buttons/animated.mjs.map +1 -1
  50. package/dist/ui/buttons.js +11 -9
  51. package/dist/ui/buttons.mjs +9 -7
  52. package/dist/ui/data-table.js +21 -19
  53. package/dist/ui/data-table.js.map +1 -1
  54. package/dist/ui/data-table.mjs +11 -9
  55. package/dist/ui/data-table.mjs.map +1 -1
  56. package/dist/ui/dynamic-stepper.js +20 -18
  57. package/dist/ui/dynamic-stepper.js.map +1 -1
  58. package/dist/ui/dynamic-stepper.mjs +9 -7
  59. package/dist/ui/dynamic-stepper.mjs.map +1 -1
  60. package/dist/ui/pagination.js +12 -10
  61. package/dist/ui/pagination.mjs +9 -7
  62. package/dist/ui/password-strength-meter/animated/animations.d.ts +3 -0
  63. package/dist/ui/password-strength-meter/animated/animations.d.ts.map +1 -0
  64. package/dist/ui/password-strength-meter/animated/index.d.ts +4 -0
  65. package/dist/ui/password-strength-meter/animated/index.d.ts.map +1 -0
  66. package/dist/ui/password-strength-meter/animated/password-strength-meter-animated.d.ts +14 -0
  67. package/dist/ui/password-strength-meter/animated/password-strength-meter-animated.d.ts.map +1 -0
  68. package/dist/ui/password-strength-meter/animated/types.d.ts +21 -0
  69. package/dist/ui/password-strength-meter/animated/types.d.ts.map +1 -0
  70. package/dist/ui/password-strength-meter/animated.js +169 -0
  71. package/dist/ui/password-strength-meter/animated.js.map +1 -0
  72. package/dist/ui/password-strength-meter/animated.mjs +165 -0
  73. package/dist/ui/password-strength-meter/animated.mjs.map +1 -0
  74. package/dist/ui/password-strength-meter/index.d.ts +5 -0
  75. package/dist/ui/password-strength-meter/index.d.ts.map +1 -0
  76. package/dist/ui/password-strength-meter/password-strength-meter-base.d.ts +17 -0
  77. package/dist/ui/password-strength-meter/password-strength-meter-base.d.ts.map +1 -0
  78. package/dist/ui/password-strength-meter/password-strength-meter.d.ts +6 -0
  79. package/dist/ui/password-strength-meter/password-strength-meter.d.ts.map +1 -0
  80. package/dist/ui/password-strength-meter/types.d.ts +33 -0
  81. package/dist/ui/password-strength-meter/types.d.ts.map +1 -0
  82. package/dist/ui/password-strength-meter/variants.d.ts +13 -0
  83. package/dist/ui/password-strength-meter/variants.d.ts.map +1 -0
  84. package/dist/ui/password-strength-meter.js +37 -0
  85. package/dist/ui/password-strength-meter.js.map +1 -0
  86. package/dist/ui/password-strength-meter.mjs +16 -0
  87. package/dist/ui/password-strength-meter.mjs.map +1 -0
  88. package/dist/ui/secret-reveal/animated/animations.d.ts +8 -0
  89. package/dist/ui/secret-reveal/animated/animations.d.ts.map +1 -0
  90. package/dist/ui/secret-reveal/animated/index.d.ts +4 -0
  91. package/dist/ui/secret-reveal/animated/index.d.ts.map +1 -0
  92. package/dist/ui/secret-reveal/animated/secret-reveal-animated.d.ts +6 -0
  93. package/dist/ui/secret-reveal/animated/secret-reveal-animated.d.ts.map +1 -0
  94. package/dist/ui/secret-reveal/animated/types.d.ts +9 -0
  95. package/dist/ui/secret-reveal/animated/types.d.ts.map +1 -0
  96. package/dist/ui/secret-reveal/animated.js +194 -0
  97. package/dist/ui/secret-reveal/animated.js.map +1 -0
  98. package/dist/ui/secret-reveal/animated.mjs +191 -0
  99. package/dist/ui/secret-reveal/animated.mjs.map +1 -0
  100. package/dist/ui/secret-reveal/index.d.ts +4 -0
  101. package/dist/ui/secret-reveal/index.d.ts.map +1 -0
  102. package/dist/ui/secret-reveal/secret-reveal-base.d.ts +6 -0
  103. package/dist/ui/secret-reveal/secret-reveal-base.d.ts.map +1 -0
  104. package/dist/ui/secret-reveal/secret-reveal.d.ts +2 -0
  105. package/dist/ui/secret-reveal/secret-reveal.d.ts.map +1 -0
  106. package/dist/ui/secret-reveal/types.d.ts +15 -0
  107. package/dist/ui/secret-reveal/types.d.ts.map +1 -0
  108. package/dist/ui/secret-reveal/variants.d.ts +15 -0
  109. package/dist/ui/secret-reveal/variants.d.ts.map +1 -0
  110. package/dist/ui/secret-reveal.js +136 -0
  111. package/dist/ui/secret-reveal.js.map +1 -0
  112. package/dist/ui/secret-reveal.mjs +119 -0
  113. package/dist/ui/secret-reveal.mjs.map +1 -0
  114. package/dist/ui/split-button.js +22 -20
  115. package/dist/ui/split-button.js.map +1 -1
  116. package/dist/ui/split-button.mjs +9 -7
  117. package/dist/ui/split-button.mjs.map +1 -1
  118. package/package.json +1 -1
  119. package/src/design-system/index.ts +2 -0
  120. package/src/design-system/password-strength-meter.ts +115 -0
  121. package/src/design-system/secret-reveal.ts +75 -0
  122. package/src/ui/password-strength-meter/animated/animations.ts +10 -0
  123. package/src/ui/password-strength-meter/animated/index.ts +14 -0
  124. package/src/ui/password-strength-meter/animated/password-strength-meter-animated.tsx +186 -0
  125. package/src/ui/password-strength-meter/animated/types.ts +35 -0
  126. package/src/ui/password-strength-meter/index.ts +18 -0
  127. package/src/ui/password-strength-meter/password-strength-meter-base.tsx +202 -0
  128. package/src/ui/password-strength-meter/password-strength-meter.test.tsx +103 -0
  129. package/src/ui/password-strength-meter/password-strength-meter.tsx +8 -0
  130. package/src/ui/password-strength-meter/types.ts +40 -0
  131. package/src/ui/password-strength-meter/variants.ts +49 -0
  132. package/src/ui/secret-reveal/animated/animations.ts +74 -0
  133. package/src/ui/secret-reveal/animated/index.ts +5 -0
  134. package/src/ui/secret-reveal/animated/secret-reveal-animated.tsx +132 -0
  135. package/src/ui/secret-reveal/animated/types.ts +11 -0
  136. package/src/ui/secret-reveal/index.ts +14 -0
  137. package/src/ui/secret-reveal/secret-reveal-base.tsx +116 -0
  138. package/src/ui/secret-reveal/secret-reveal.test.tsx +75 -0
  139. package/src/ui/secret-reveal/secret-reveal.tsx +2 -0
  140. package/src/ui/secret-reveal/types.ts +21 -0
  141. package/src/ui/secret-reveal/variants.ts +49 -0
  142. package/dist/chunk-AMNJ35TT.mjs.map +0 -1
  143. package/dist/chunk-F3G3RL2N.js.map +0 -1
  144. package/dist/chunk-VQJXOJ7G.js +0 -19
@@ -0,0 +1,40 @@
1
+ import type { VariantProps } from "class-variance-authority";
2
+ import type { ComponentPropsWithRef, ElementType, ReactNode } from "react";
3
+
4
+ import type { passwordStrengthMeterVariants } from "./variants";
5
+
6
+ export type PasswordStrengthMeterVariantProps = VariantProps<
7
+ typeof passwordStrengthMeterVariants
8
+ >;
9
+
10
+ export type PasswordStrengthMeterProps = PasswordStrengthMeterVariantProps &
11
+ (Omit<ComponentPropsWithRef<"div">, "children"> & {
12
+ value: number;
13
+ min?: number;
14
+ max?: number;
15
+ label?: string;
16
+ scoreLabel?: string;
17
+ showScoreLabel?: boolean;
18
+ animated?: boolean;
19
+ segmented?: boolean;
20
+ children?: ReactNode;
21
+ as?: ElementType;
22
+ });
23
+
24
+ export type PasswordStrengthMeterSectionProps = {
25
+ className?: string;
26
+ children?: ReactNode;
27
+ as?: ElementType;
28
+ };
29
+
30
+ export type PasswordStrengthMeterCtx = {
31
+ value: number;
32
+ min: number;
33
+ max: number;
34
+ size: NonNullable<PasswordStrengthMeterProps["size"]>;
35
+ shape: NonNullable<PasswordStrengthMeterProps["shape"]>;
36
+ animated: boolean;
37
+ segmented: boolean;
38
+ appearance: NonNullable<PasswordStrengthMeterProps["appearance"]>;
39
+ labelSlotId: string;
40
+ };
@@ -0,0 +1,49 @@
1
+ import { cva } from "class-variance-authority";
2
+
3
+ import {
4
+ zuiPasswordStrengthMeterAppearances,
5
+ zuiPasswordStrengthMeterBarBase,
6
+ zuiPasswordStrengthMeterBarSegmented,
7
+ zuiPasswordStrengthMeterBase,
8
+ zuiPasswordStrengthMeterShapes,
9
+ zuiPasswordStrengthMeterSizes,
10
+ zuiPasswordStrengthMeterTrackBase,
11
+ zuiPasswordStrengthMeterTrackSizes,
12
+ } from "../../design-system/password-strength-meter";
13
+
14
+ export const passwordStrengthMeterVariants = cva(zuiPasswordStrengthMeterBase, {
15
+ variants: {
16
+ appearance: zuiPasswordStrengthMeterAppearances,
17
+ size: zuiPasswordStrengthMeterSizes,
18
+ shape: zuiPasswordStrengthMeterShapes,
19
+ },
20
+ defaultVariants: {
21
+ appearance: "default",
22
+ size: "md",
23
+ shape: "rounded",
24
+ },
25
+ });
26
+
27
+ export const passwordStrengthMeterTrackVariants = cva(
28
+ zuiPasswordStrengthMeterTrackBase,
29
+ {
30
+ variants: {
31
+ size: zuiPasswordStrengthMeterTrackSizes,
32
+ shape: zuiPasswordStrengthMeterShapes,
33
+ },
34
+ defaultVariants: {
35
+ size: "md",
36
+ shape: "rounded",
37
+ },
38
+ },
39
+ );
40
+
41
+ export const passwordStrengthMeterBarVariants = cva(
42
+ zuiPasswordStrengthMeterBarBase,
43
+ {
44
+ variants: {
45
+ segmented: zuiPasswordStrengthMeterBarSegmented,
46
+ },
47
+ defaultVariants: { segmented: false },
48
+ },
49
+ );
@@ -0,0 +1,74 @@
1
+ import type { Transition, Variants } from "framer-motion";
2
+
3
+ export type SecretRevealAnimation =
4
+ | "none"
5
+ | "fade"
6
+ | "slide-up"
7
+ | "scale"
8
+ | "flip";
9
+
10
+ export type SecretRevealAnimationPresets = Record<
11
+ SecretRevealAnimation,
12
+ {
13
+ transition: Transition;
14
+ variants: Variants;
15
+ }
16
+ >;
17
+
18
+ export const secretRevealAnimationPresets: SecretRevealAnimationPresets = {
19
+ none: {
20
+ transition: { duration: 0 },
21
+ variants: {
22
+ initial: { opacity: 1 },
23
+ animate: { opacity: 1 },
24
+ exit: { opacity: 1 },
25
+ },
26
+ },
27
+ fade: {
28
+ transition: { duration: 0.25, ease: "easeInOut" },
29
+ variants: {
30
+ initial: { opacity: 0 },
31
+ animate: { opacity: 1 },
32
+ exit: { opacity: 0 },
33
+ },
34
+ },
35
+ "slide-up": {
36
+ transition: {
37
+ type: "spring",
38
+ stiffness: 300,
39
+ damping: 25,
40
+ mass: 0.5,
41
+ },
42
+ variants: {
43
+ initial: { opacity: 0, y: 8 },
44
+ animate: { opacity: 1, y: 0 },
45
+ exit: { opacity: 0, y: -8 },
46
+ },
47
+ },
48
+ scale: {
49
+ transition: {
50
+ type: "spring",
51
+ stiffness: 400,
52
+ damping: 20,
53
+ mass: 0.4,
54
+ },
55
+ variants: {
56
+ initial: { opacity: 0, scale: 0.9 },
57
+ animate: { opacity: 1, scale: 1 },
58
+ exit: { opacity: 0, scale: 0.9 },
59
+ },
60
+ },
61
+ flip: {
62
+ transition: {
63
+ type: "spring",
64
+ stiffness: 200,
65
+ damping: 18,
66
+ mass: 0.6,
67
+ },
68
+ variants: {
69
+ initial: { opacity: 0, rotateX: 90 },
70
+ animate: { opacity: 1, rotateX: 0 },
71
+ exit: { opacity: 0, rotateX: -90 },
72
+ },
73
+ },
74
+ };
@@ -0,0 +1,5 @@
1
+ "use client";
2
+
3
+ export { SecretRevealAnimated } from "./secret-reveal-animated";
4
+ export type { SecretRevealAnimation, SecretRevealAnimatedProps } from "./types";
5
+ export { secretRevealAnimationPresets } from "./animations";
@@ -0,0 +1,132 @@
1
+ "use client";
2
+
3
+ import { AnimatePresence, motion } from "framer-motion";
4
+ import { useCallback, useState } from "react";
5
+
6
+ import { cn } from "../../../lib/utils";
7
+
8
+ import { secretRevealAnimationPresets } from "./animations";
9
+ import type { SecretRevealAnimatedProps } from "./types";
10
+ import {
11
+ secretRevealLabelVariants,
12
+ secretRevealToggleVariants,
13
+ secretRevealValueVariants,
14
+ secretRevealVariants,
15
+ } from "../variants";
16
+
17
+ export function SecretRevealAnimated({
18
+ appearance,
19
+ size,
20
+ value,
21
+ children,
22
+ label,
23
+ labelPosition = "top",
24
+ initiallyRevealed = false,
25
+ onVisibilityChange,
26
+ animation = "fade",
27
+ muteChar = "•",
28
+ className,
29
+ ref,
30
+ ...rest
31
+ }: SecretRevealAnimatedProps) {
32
+ const [revealed, setRevealed] = useState(initiallyRevealed);
33
+
34
+ const toggle = useCallback(() => {
35
+ const next = !revealed;
36
+ setRevealed(next);
37
+ onVisibilityChange?.(next);
38
+ }, [onVisibilityChange, revealed]);
39
+
40
+ const preset = secretRevealAnimationPresets[animation];
41
+ const displayText = value ?? (typeof children === "string" ? children : "");
42
+
43
+ return (
44
+ <div
45
+ ref={ref}
46
+ data-slot="secret-reveal"
47
+ className={cn(secretRevealVariants({ appearance, size }), className)}
48
+ {...rest}
49
+ >
50
+ {label && labelPosition === "top" && (
51
+ <span
52
+ data-slot="secret-reveal-label"
53
+ className={secretRevealLabelVariants({ size })}
54
+ >
55
+ {label}
56
+ </span>
57
+ )}
58
+ <span className="flex items-center gap-2 flex-1 min-w-0">
59
+ {label && labelPosition === "side" && (
60
+ <span
61
+ data-slot="secret-reveal-label"
62
+ className={secretRevealLabelVariants({ size })}
63
+ >
64
+ {label}
65
+ </span>
66
+ )}
67
+ <span
68
+ data-slot="secret-reveal-value"
69
+ className={cn(secretRevealValueVariants({ size }), "flex-1 truncate")}
70
+ >
71
+ <AnimatePresence mode="wait">
72
+ <motion.span
73
+ key={revealed ? "revealed" : "hidden"}
74
+ initial="initial"
75
+ animate="animate"
76
+ exit="exit"
77
+ variants={preset.variants}
78
+ transition={preset.transition}
79
+ className="inline-block"
80
+ >
81
+ {revealed
82
+ ? (value ?? children)
83
+ : muteChar.repeat(displayText.length || 8)}
84
+ </motion.span>
85
+ </AnimatePresence>
86
+ </span>
87
+ <button
88
+ type="button"
89
+ data-slot="secret-reveal-toggle"
90
+ className={secretRevealToggleVariants({ size, appearance })}
91
+ onClick={toggle}
92
+ aria-label={revealed ? "Hide secret" : "Reveal secret"}
93
+ >
94
+ {revealed ? (
95
+ <svg
96
+ xmlns="http://www.w3.org/2000/svg"
97
+ width="16"
98
+ height="16"
99
+ viewBox="0 0 24 24"
100
+ fill="none"
101
+ stroke="currentColor"
102
+ strokeWidth="2"
103
+ strokeLinecap="round"
104
+ strokeLinejoin="round"
105
+ >
106
+ <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94" />
107
+ <path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19" />
108
+ <line x1="1" y1="1" x2="23" y2="23" />
109
+ </svg>
110
+ ) : (
111
+ <svg
112
+ xmlns="http://www.w3.org/2000/svg"
113
+ width="16"
114
+ height="16"
115
+ viewBox="0 0 24 24"
116
+ fill="none"
117
+ stroke="currentColor"
118
+ strokeWidth="2"
119
+ strokeLinecap="round"
120
+ strokeLinejoin="round"
121
+ >
122
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
123
+ <circle cx="12" cy="12" r="3" />
124
+ </svg>
125
+ )}
126
+ </button>
127
+ </span>
128
+ </div>
129
+ );
130
+ }
131
+
132
+ SecretRevealAnimated.displayName = "SecretRevealAnimated";
@@ -0,0 +1,11 @@
1
+ import type { Ref } from "react";
2
+
3
+ import type { SecretRevealBaseProps } from "../types";
4
+ import type { SecretRevealAnimation } from "./animations";
5
+
6
+ export type { SecretRevealAnimation };
7
+
8
+ export type SecretRevealAnimatedProps = SecretRevealBaseProps & {
9
+ animation?: SecretRevealAnimation;
10
+ ref?: Ref<HTMLDivElement>;
11
+ };
@@ -0,0 +1,14 @@
1
+ "use client";
2
+
3
+ export { SecretReveal } from "./secret-reveal";
4
+ export type {
5
+ SecretRevealBaseProps,
6
+ SecretRevealProps,
7
+ SecretRevealVariantProps,
8
+ } from "./types";
9
+ export {
10
+ secretRevealLabelVariants,
11
+ secretRevealToggleVariants,
12
+ secretRevealValueVariants,
13
+ secretRevealVariants,
14
+ } from "./variants";
@@ -0,0 +1,116 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ import type { SecretRevealBaseProps } from "./types";
8
+ import {
9
+ secretRevealLabelVariants,
10
+ secretRevealToggleVariants,
11
+ secretRevealValueVariants,
12
+ secretRevealVariants,
13
+ } from "./variants";
14
+
15
+ export function SecretRevealBase({
16
+ appearance,
17
+ size,
18
+ value,
19
+ children,
20
+ label,
21
+ labelPosition = "top",
22
+ initiallyRevealed = false,
23
+ onVisibilityChange,
24
+ muteChar = "•",
25
+ className,
26
+ ref,
27
+ ...rest
28
+ }: SecretRevealBaseProps) {
29
+ const [revealed, setRevealed] = useState(initiallyRevealed);
30
+
31
+ const toggle = useCallback(() => {
32
+ const next = !revealed;
33
+ setRevealed(next);
34
+ onVisibilityChange?.(next);
35
+ }, [onVisibilityChange, revealed]);
36
+
37
+ const displayText = value ?? (typeof children === "string" ? children : "");
38
+
39
+ return (
40
+ <div
41
+ ref={ref}
42
+ data-slot="secret-reveal"
43
+ className={cn(secretRevealVariants({ appearance, size }), className)}
44
+ {...rest}
45
+ >
46
+ {label && labelPosition === "top" && (
47
+ <span
48
+ data-slot="secret-reveal-label"
49
+ className={secretRevealLabelVariants({ size })}
50
+ >
51
+ {label}
52
+ </span>
53
+ )}
54
+ <span className="flex items-center gap-2 flex-1 min-w-0">
55
+ {label && labelPosition === "side" && (
56
+ <span
57
+ data-slot="secret-reveal-label"
58
+ className={secretRevealLabelVariants({ size })}
59
+ >
60
+ {label}
61
+ </span>
62
+ )}
63
+ <span
64
+ data-slot="secret-reveal-value"
65
+ className={cn(secretRevealValueVariants({ size }), "flex-1 truncate")}
66
+ >
67
+ {revealed
68
+ ? (value ?? children)
69
+ : muteChar.repeat(displayText.length || 8)}
70
+ </span>
71
+ <button
72
+ type="button"
73
+ data-slot="secret-reveal-toggle"
74
+ className={secretRevealToggleVariants({ size, appearance })}
75
+ onClick={toggle}
76
+ aria-label={revealed ? "Hide secret" : "Reveal secret"}
77
+ >
78
+ {revealed ? (
79
+ <svg
80
+ xmlns="http://www.w3.org/2000/svg"
81
+ width="16"
82
+ height="16"
83
+ viewBox="0 0 24 24"
84
+ fill="none"
85
+ stroke="currentColor"
86
+ strokeWidth="2"
87
+ strokeLinecap="round"
88
+ strokeLinejoin="round"
89
+ >
90
+ <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94" />
91
+ <path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19" />
92
+ <line x1="1" y1="1" x2="23" y2="23" />
93
+ </svg>
94
+ ) : (
95
+ <svg
96
+ xmlns="http://www.w3.org/2000/svg"
97
+ width="16"
98
+ height="16"
99
+ viewBox="0 0 24 24"
100
+ fill="none"
101
+ stroke="currentColor"
102
+ strokeWidth="2"
103
+ strokeLinecap="round"
104
+ strokeLinejoin="round"
105
+ >
106
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
107
+ <circle cx="12" cy="12" r="3" />
108
+ </svg>
109
+ )}
110
+ </button>
111
+ </span>
112
+ </div>
113
+ );
114
+ }
115
+
116
+ SecretRevealBase.displayName = "SecretReveal";
@@ -0,0 +1,75 @@
1
+ import { createRef } from "react";
2
+ import { render } from "@testing-library/react";
3
+ import { describe, expect, it } from "vitest";
4
+
5
+ import { SecretReveal } from "./secret-reveal";
6
+
7
+ describe("SecretReveal", () => {
8
+ it("should set displayName", () => {
9
+ expect(SecretReveal.displayName).toBe("SecretReveal");
10
+ });
11
+
12
+ it("should stamp data-slot on the root container", () => {
13
+ const { container } = render(<SecretReveal value="sk-test" />);
14
+ const root = container.querySelector('[data-slot="secret-reveal"]');
15
+ expect(root).toBeTruthy();
16
+ });
17
+
18
+ it("should render masked value by default", () => {
19
+ const { container } = render(<SecretReveal value="sk-test" />);
20
+ const valueEl = container.querySelector(
21
+ '[data-slot="secret-reveal-value"]',
22
+ );
23
+ expect(valueEl?.textContent).toBe("•••••••");
24
+ });
25
+
26
+ it("should render a label", () => {
27
+ const { container } = render(
28
+ <SecretReveal value="sk-test" label="API Key" />,
29
+ );
30
+ const label = container.querySelector('[data-slot="secret-reveal-label"]');
31
+ expect(label).toBeTruthy();
32
+ expect(label?.textContent).toBe("API Key");
33
+ });
34
+
35
+ it("should render a toggle button", () => {
36
+ const { container } = render(<SecretReveal value="sk-test" />);
37
+ const toggle = container.querySelector(
38
+ '[data-slot="secret-reveal-toggle"]',
39
+ );
40
+ expect(toggle).toBeTruthy();
41
+ });
42
+
43
+ it("should render initial revealed state", () => {
44
+ const { container } = render(
45
+ <SecretReveal value="sk-test" initiallyRevealed />,
46
+ );
47
+ const valueEl = container.querySelector(
48
+ '[data-slot="secret-reveal-value"]',
49
+ );
50
+ expect(valueEl?.textContent).toBe("sk-test");
51
+ });
52
+
53
+ it("should render with children instead of value", () => {
54
+ const { container } = render(<SecretReveal>my-secret-key</SecretReveal>);
55
+ const valueEl = container.querySelector(
56
+ '[data-slot="secret-reveal-value"]',
57
+ );
58
+ expect(valueEl?.textContent).toBe("•••••••••••••");
59
+ });
60
+
61
+ it("should forward refs to the root element", () => {
62
+ const ref = createRef<HTMLDivElement>();
63
+ render(<SecretReveal value="test" ref={ref} />);
64
+ expect(ref.current).toBeInstanceOf(HTMLElement);
65
+ expect(ref.current?.getAttribute("data-slot")).toBe("secret-reveal");
66
+ });
67
+
68
+ it("should apply custom className", () => {
69
+ const { container } = render(
70
+ <SecretReveal value="test" className="custom-class" />,
71
+ );
72
+ const root = container.querySelector('[data-slot="secret-reveal"]');
73
+ expect(root?.className).toMatch(/custom-class/);
74
+ });
75
+ });
@@ -0,0 +1,2 @@
1
+ // secret-reveal.tsx — default static entry (no framer-motion)
2
+ export { SecretRevealBase as SecretReveal } from "./secret-reveal-base";
@@ -0,0 +1,21 @@
1
+ import type { VariantProps } from "class-variance-authority";
2
+ import type { ComponentPropsWithRef, ReactNode } from "react";
3
+
4
+ import type { secretRevealVariants } from "./variants";
5
+
6
+ export type SecretRevealVariantProps = VariantProps<
7
+ typeof secretRevealVariants
8
+ >;
9
+
10
+ export type SecretRevealBaseProps = SecretRevealVariantProps &
11
+ ComponentPropsWithRef<"div"> & {
12
+ value?: string;
13
+ children?: ReactNode;
14
+ label?: ReactNode;
15
+ labelPosition?: "top" | "side";
16
+ initiallyRevealed?: boolean;
17
+ onVisibilityChange?: (revealed: boolean) => void;
18
+ muteChar?: string;
19
+ };
20
+
21
+ export type SecretRevealProps = SecretRevealBaseProps;
@@ -0,0 +1,49 @@
1
+ import { cva } from "class-variance-authority";
2
+
3
+ import {
4
+ zuiSecretRevealAppearances,
5
+ zuiSecretRevealContainerBase,
6
+ zuiSecretRevealLabelBase,
7
+ zuiSecretRevealLabelSizes,
8
+ zuiSecretRevealSizes,
9
+ zuiSecretRevealToggleBase,
10
+ zuiSecretRevealToggleSizes,
11
+ zuiSecretRevealValueBase,
12
+ zuiSecretRevealValueSizes,
13
+ } from "../../design-system/secret-reveal";
14
+
15
+ export const secretRevealVariants = cva(zuiSecretRevealContainerBase, {
16
+ variants: {
17
+ appearance: zuiSecretRevealAppearances,
18
+ size: zuiSecretRevealSizes,
19
+ },
20
+ defaultVariants: {
21
+ appearance: "default",
22
+ size: "md",
23
+ },
24
+ });
25
+
26
+ export const secretRevealLabelVariants = cva(zuiSecretRevealLabelBase, {
27
+ variants: {
28
+ size: zuiSecretRevealLabelSizes,
29
+ },
30
+ defaultVariants: { size: "md" },
31
+ });
32
+
33
+ export const secretRevealValueVariants = cva(zuiSecretRevealValueBase, {
34
+ variants: {
35
+ size: zuiSecretRevealValueSizes,
36
+ },
37
+ defaultVariants: { size: "md" },
38
+ });
39
+
40
+ export const secretRevealToggleVariants = cva(zuiSecretRevealToggleBase, {
41
+ variants: {
42
+ size: zuiSecretRevealToggleSizes,
43
+ appearance: zuiSecretRevealAppearances,
44
+ },
45
+ defaultVariants: {
46
+ size: "md",
47
+ appearance: "default",
48
+ },
49
+ });