@zentauri-ui/zentauri-components 1.8.0 → 1.8.1
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/README.md +8 -4
- package/cli/registry.json +2 -0
- package/dist/chunk-7TGUGTTQ.mjs +147 -0
- package/dist/chunk-7TGUGTTQ.mjs.map +1 -0
- package/dist/chunk-CQMV7BB6.js +50 -0
- package/dist/chunk-CQMV7BB6.js.map +1 -0
- package/dist/chunk-DN7TYUJ6.js +119 -0
- package/dist/chunk-DN7TYUJ6.js.map +1 -0
- package/dist/chunk-ODBG4Y6R.mjs +48 -0
- package/dist/chunk-ODBG4Y6R.mjs.map +1 -0
- package/dist/chunk-RKX5MERK.js +150 -0
- package/dist/chunk-RKX5MERK.js.map +1 -0
- package/dist/chunk-VYI3GS2C.mjs +115 -0
- package/dist/chunk-VYI3GS2C.mjs.map +1 -0
- package/dist/design-system/copy-button.d.ts +43 -0
- package/dist/design-system/copy-button.d.ts.map +1 -0
- package/dist/design-system/index.d.ts +2 -0
- package/dist/design-system/index.d.ts.map +1 -1
- package/dist/design-system/kbd.d.ts +44 -0
- package/dist/design-system/kbd.d.ts.map +1 -0
- package/dist/hooks/useClipboard.js +6 -44
- package/dist/hooks/useClipboard.js.map +1 -1
- package/dist/hooks/useClipboard.mjs +1 -46
- package/dist/hooks/useClipboard.mjs.map +1 -1
- package/dist/ui/copy-button/animated/animations.d.ts +3 -0
- package/dist/ui/copy-button/animated/animations.d.ts.map +1 -0
- package/dist/ui/copy-button/animated/copy-button-animated.d.ts +6 -0
- package/dist/ui/copy-button/animated/copy-button-animated.d.ts.map +1 -0
- package/dist/ui/copy-button/animated/index.d.ts +4 -0
- package/dist/ui/copy-button/animated/index.d.ts.map +1 -0
- package/dist/ui/copy-button/animated/types.d.ts +26 -0
- package/dist/ui/copy-button/animated/types.d.ts.map +1 -0
- package/dist/ui/copy-button/animated.js +59 -0
- package/dist/ui/copy-button/animated.js.map +1 -0
- package/dist/ui/copy-button/animated.mjs +56 -0
- package/dist/ui/copy-button/animated.mjs.map +1 -0
- package/dist/ui/copy-button/copy-button-base.d.ts +6 -0
- package/dist/ui/copy-button/copy-button-base.d.ts.map +1 -0
- package/dist/ui/copy-button/copy-button.d.ts +6 -0
- package/dist/ui/copy-button/copy-button.d.ts.map +1 -0
- package/dist/ui/copy-button/index.d.ts +4 -0
- package/dist/ui/copy-button/index.d.ts.map +1 -0
- package/dist/ui/copy-button/types.d.ts +32 -0
- package/dist/ui/copy-button/types.d.ts.map +1 -0
- package/dist/ui/copy-button/variants.d.ts +6 -0
- package/dist/ui/copy-button/variants.d.ts.map +1 -0
- package/dist/ui/copy-button.js +20 -0
- package/dist/ui/copy-button.js.map +1 -0
- package/dist/ui/copy-button.mjs +15 -0
- package/dist/ui/copy-button.mjs.map +1 -0
- package/dist/ui/kbd/animated/animations.d.ts +3 -0
- package/dist/ui/kbd/animated/animations.d.ts.map +1 -0
- package/dist/ui/kbd/animated/index.d.ts +4 -0
- package/dist/ui/kbd/animated/index.d.ts.map +1 -0
- package/dist/ui/kbd/animated/kbd-animated.d.ts +6 -0
- package/dist/ui/kbd/animated/kbd-animated.d.ts.map +1 -0
- package/dist/ui/kbd/animated/types.d.ts +10 -0
- package/dist/ui/kbd/animated/types.d.ts.map +1 -0
- package/dist/ui/kbd/animated.js +42 -0
- package/dist/ui/kbd/animated.js.map +1 -0
- package/dist/ui/kbd/animated.mjs +39 -0
- package/dist/ui/kbd/animated.mjs.map +1 -0
- package/dist/ui/kbd/index.d.ts +4 -0
- package/dist/ui/kbd/index.d.ts.map +1 -0
- package/dist/ui/kbd/kbd-base.d.ts +6 -0
- package/dist/ui/kbd/kbd-base.d.ts.map +1 -0
- package/dist/ui/kbd/kbd.d.ts +6 -0
- package/dist/ui/kbd/kbd.d.ts.map +1 -0
- package/dist/ui/kbd/types.d.ts +17 -0
- package/dist/ui/kbd/types.d.ts.map +1 -0
- package/dist/ui/kbd/variants.d.ts +8 -0
- package/dist/ui/kbd/variants.d.ts.map +1 -0
- package/dist/ui/kbd.js +23 -0
- package/dist/ui/kbd.js.map +1 -0
- package/dist/ui/kbd.mjs +14 -0
- package/dist/ui/kbd.mjs.map +1 -0
- package/package.json +1 -1
- package/src/design-system/copy-button.ts +81 -0
- package/src/design-system/index.ts +2 -0
- package/src/design-system/kbd.ts +83 -0
- package/src/ui/copy-button/animated/animations.ts +22 -0
- package/src/ui/copy-button/animated/copy-button-animated.tsx +39 -0
- package/src/ui/copy-button/animated/index.ts +10 -0
- package/src/ui/copy-button/animated/types.ts +21 -0
- package/src/ui/copy-button/copy-button-base.tsx +88 -0
- package/src/ui/copy-button/copy-button.test.tsx +82 -0
- package/src/ui/copy-button/copy-button.tsx +9 -0
- package/src/ui/copy-button/index.ts +10 -0
- package/src/ui/copy-button/types.ts +37 -0
- package/src/ui/copy-button/variants.ts +29 -0
- package/src/ui/kbd/animated/animations.ts +15 -0
- package/src/ui/kbd/animated/index.ts +9 -0
- package/src/ui/kbd/animated/kbd-animated.tsx +26 -0
- package/src/ui/kbd/animated/types.ts +16 -0
- package/src/ui/kbd/index.ts +5 -0
- package/src/ui/kbd/kbd-base.tsx +50 -0
- package/src/ui/kbd/kbd.test.tsx +48 -0
- package/src/ui/kbd/kbd.tsx +9 -0
- package/src/ui/kbd/types.ts +21 -0
- package/src/ui/kbd/variants.ts +31 -0
package/dist/ui/kbd.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var chunkDN7TYUJ6_js = require('../chunk-DN7TYUJ6.js');
|
|
5
|
+
require('../chunk-ZS5756ZC.js');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
function Kbd(props) {
|
|
9
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkDN7TYUJ6_js.KbdBase, { ...props });
|
|
10
|
+
}
|
|
11
|
+
Kbd.displayName = "Kbd";
|
|
12
|
+
|
|
13
|
+
Object.defineProperty(exports, "kbdKeyVariants", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return chunkDN7TYUJ6_js.kbdKeyVariants; }
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(exports, "kbdSeparatorVariants", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () { return chunkDN7TYUJ6_js.kbdSeparatorVariants; }
|
|
20
|
+
});
|
|
21
|
+
exports.Kbd = Kbd;
|
|
22
|
+
//# sourceMappingURL=kbd.js.map
|
|
23
|
+
//# sourceMappingURL=kbd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/ui/kbd/kbd.tsx"],"names":["jsx","KbdBase"],"mappings":";;;;;;AAIO,SAAS,IAAI,KAAA,EAAiB;AACnC,EAAA,uBAAOA,cAAA,CAACC,wBAAA,EAAA,EAAS,GAAG,KAAA,EAAO,CAAA;AAC7B;AAEA,GAAA,CAAI,WAAA,GAAc,KAAA","file":"kbd.js","sourcesContent":["// kbd.tsx — default static entry (no framer-motion)\nimport { KbdBase } from \"./kbd-base\";\nimport type { KbdProps } from \"./types\";\n\nexport function Kbd(props: KbdProps) {\n return <KbdBase {...props} />;\n}\n\nKbd.displayName = \"Kbd\";\n"]}
|
package/dist/ui/kbd.mjs
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { KbdBase } from '../chunk-VYI3GS2C.mjs';
|
|
3
|
+
export { kbdKeyVariants, kbdSeparatorVariants } from '../chunk-VYI3GS2C.mjs';
|
|
4
|
+
import '../chunk-4D54YOL6.mjs';
|
|
5
|
+
import { jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function Kbd(props) {
|
|
8
|
+
return /* @__PURE__ */ jsx(KbdBase, { ...props });
|
|
9
|
+
}
|
|
10
|
+
Kbd.displayName = "Kbd";
|
|
11
|
+
|
|
12
|
+
export { Kbd };
|
|
13
|
+
//# sourceMappingURL=kbd.mjs.map
|
|
14
|
+
//# sourceMappingURL=kbd.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/ui/kbd/kbd.tsx"],"names":[],"mappings":";;;;;AAIO,SAAS,IAAI,KAAA,EAAiB;AACnC,EAAA,uBAAO,GAAA,CAAC,OAAA,EAAA,EAAS,GAAG,KAAA,EAAO,CAAA;AAC7B;AAEA,GAAA,CAAI,WAAA,GAAc,KAAA","file":"kbd.mjs","sourcesContent":["// kbd.tsx — default static entry (no framer-motion)\nimport { KbdBase } from \"./kbd-base\";\nimport type { KbdProps } from \"./types\";\n\nexport function Kbd(props: KbdProps) {\n return <KbdBase {...props} />;\n}\n\nKbd.displayName = \"Kbd\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zentauri-ui/zentauri-components",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "React + Tailwind UI kit with charts, ESM/CJS builds, per-entry exports, and a zentauri-components / zentauri-ui CLI to vendor UI or hook source into your app",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export const zuiCopyButtonBase = [
|
|
2
|
+
"relative inline-flex items-center justify-center gap-2 whitespace-nowrap",
|
|
3
|
+
"rounded-[var(--zui-copy-button-radius,0.75rem)] font-medium",
|
|
4
|
+
"ring-offset-[var(--zui-copy-button-ring-offset,#f8fafc)] dark:ring-offset-[var(--zui-copy-button-ring-offset-dark,#020617)]",
|
|
5
|
+
"transition-colors select-none",
|
|
6
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--zui-copy-button-focus-ring,#475569)] dark:focus-visible:ring-[var(--zui-copy-button-focus-ring-dark,#cbd5e1)] focus-visible:ring-offset-2",
|
|
7
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
8
|
+
] as const;
|
|
9
|
+
|
|
10
|
+
export const zuiCopyButtonAppearances = {
|
|
11
|
+
default:
|
|
12
|
+
"bg-[var(--zui-copy-button-default-bg,#0f172a)] dark:bg-[var(--zui-copy-button-default-bg-dark,#f8fafc)] text-[color:var(--zui-copy-button-default-fg,#f8fafc)] dark:text-[color:var(--zui-copy-button-default-fg-dark,#020617)] shadow-[var(--zui-copy-button-default-shadow,0_1px_2px_#0f172a14)] dark:shadow-[var(--zui-copy-button-default-shadow-dark,0_1px_2px_#0f172a1f)] hover:bg-[var(--zui-copy-button-default-bg-hover,#000000)] dark:hover:bg-[var(--zui-copy-button-default-bg-hover-dark,#ffffff)]",
|
|
13
|
+
secondary:
|
|
14
|
+
"bg-[var(--zui-copy-button-secondary-bg,#e2e8f0)] dark:bg-[var(--zui-copy-button-secondary-bg-dark,#1e293b)] text-[color:var(--zui-copy-button-secondary-fg,#0f172a)] dark:text-[color:var(--zui-copy-button-secondary-fg-dark,#f8fafc)] hover:bg-[var(--zui-copy-button-secondary-bg-hover,#cbd5e1)] dark:hover:bg-[var(--zui-copy-button-secondary-bg-hover-dark,#334155)]",
|
|
15
|
+
destructive:
|
|
16
|
+
"bg-[var(--zui-copy-button-destructive-bg,#f43f5e)] dark:bg-[var(--zui-copy-button-destructive-bg-dark,#be123c)] text-[color:var(--zui-copy-button-destructive-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-destructive-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-destructive-bg-hover,#f43f5e)] dark:hover:bg-[var(--zui-copy-button-destructive-bg-hover-dark,#9f1239)]",
|
|
17
|
+
outline:
|
|
18
|
+
"border border-[color:var(--zui-copy-button-outline-border,#0000001a)] dark:border-[color:var(--zui-copy-button-outline-border-dark,#ffffff1a)] bg-[var(--zui-copy-button-outline-bg,#0000000d)] dark:bg-[var(--zui-copy-button-outline-bg-dark,#ffffff0d)] text-[color:var(--zui-copy-button-outline-fg,#0f172a)] dark:text-[color:var(--zui-copy-button-outline-fg-dark,#f8fafc)] hover:bg-[var(--zui-copy-button-outline-bg-hover,#0000001a)] dark:hover:bg-[var(--zui-copy-button-outline-bg-hover-dark,#ffffff1a)]",
|
|
19
|
+
ghost:
|
|
20
|
+
"bg-transparent text-[color:var(--zui-copy-button-ghost-fg,#334155)] dark:text-[color:var(--zui-copy-button-ghost-fg-dark,#e2e8f0)] hover:bg-[var(--zui-copy-button-ghost-bg-hover,#0000000d)] dark:hover:bg-[var(--zui-copy-button-ghost-bg-hover-dark,#ffffff0d)]",
|
|
21
|
+
glass:
|
|
22
|
+
"border border-[color:var(--zui-copy-button-glass-border,#00000026)] dark:border-[color:var(--zui-copy-button-glass-border-dark,#ffffff26)] bg-[var(--zui-copy-button-glass-bg,#0000001a)] dark:bg-[var(--zui-copy-button-glass-bg-dark,#ffffff1a)] text-[color:var(--zui-copy-button-glass-fg,#0f172a)] dark:text-[color:var(--zui-copy-button-glass-fg-dark,#ffffff)] backdrop-blur-md hover:bg-[var(--zui-copy-button-glass-bg-hover,#00000026)] dark:hover:bg-[var(--zui-copy-button-glass-bg-hover-dark,#ffffff26)]",
|
|
23
|
+
emerald:
|
|
24
|
+
"bg-[var(--zui-copy-button-emerald-bg,#10b981)] dark:bg-[var(--zui-copy-button-emerald-bg-dark,#065f46)] text-[color:var(--zui-copy-button-emerald-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-emerald-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-emerald-bg-hover,#10b981)] dark:hover:bg-[var(--zui-copy-button-emerald-bg-hover-dark,#064e3b)]",
|
|
25
|
+
indigo:
|
|
26
|
+
"bg-[var(--zui-copy-button-indigo-bg,#3730a3)] dark:bg-[var(--zui-copy-button-indigo-bg-dark,#4f46e5)] text-[color:var(--zui-copy-button-indigo-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-indigo-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-indigo-bg-hover,#3730a3)] dark:hover:bg-[var(--zui-copy-button-indigo-bg-hover-dark,#4f46e5)]",
|
|
27
|
+
purple:
|
|
28
|
+
"bg-[var(--zui-copy-button-purple-bg,#6b21a8)] dark:bg-[var(--zui-copy-button-purple-bg-dark,#9333ea)] text-[color:var(--zui-copy-button-purple-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-purple-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-purple-bg-hover,#6b21a8)] dark:hover:bg-[var(--zui-copy-button-purple-bg-hover-dark,#9333ea)]",
|
|
29
|
+
pink:
|
|
30
|
+
"bg-[var(--zui-copy-button-pink-bg,#9d174d)] dark:bg-[var(--zui-copy-button-pink-bg-dark,#db2777)] text-[color:var(--zui-copy-button-pink-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-pink-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-pink-bg-hover,#9d174d)] dark:hover:bg-[var(--zui-copy-button-pink-bg-hover-dark,#db2777)]",
|
|
31
|
+
rose:
|
|
32
|
+
"bg-[var(--zui-copy-button-rose-bg,#9f1239)] dark:bg-[var(--zui-copy-button-rose-bg-dark,#e11d48)] text-[color:var(--zui-copy-button-rose-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-rose-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-rose-bg-hover,#9f1239)] dark:hover:bg-[var(--zui-copy-button-rose-bg-hover-dark,#e11d48)]",
|
|
33
|
+
sky:
|
|
34
|
+
"bg-[var(--zui-copy-button-sky-bg,#0ea5e9)] dark:bg-[var(--zui-copy-button-sky-bg-dark,#0369a1)] text-[color:var(--zui-copy-button-sky-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-sky-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-sky-bg-hover,#0ea5e9)] dark:hover:bg-[var(--zui-copy-button-sky-bg-hover-dark,#075985)]",
|
|
35
|
+
teal:
|
|
36
|
+
"bg-[var(--zui-copy-button-teal-bg,#14b8a6)] dark:bg-[var(--zui-copy-button-teal-bg-dark,#0f766e)] text-[color:var(--zui-copy-button-teal-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-teal-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-teal-bg-hover,#14b8a6)] dark:hover:bg-[var(--zui-copy-button-teal-bg-hover-dark,#115e59)]",
|
|
37
|
+
yellow:
|
|
38
|
+
"bg-[var(--zui-copy-button-yellow-bg,#eab308)] dark:bg-[var(--zui-copy-button-yellow-bg-dark,#854d0e)] text-[color:var(--zui-copy-button-yellow-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-yellow-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-yellow-bg-hover,#eab308)] dark:hover:bg-[var(--zui-copy-button-yellow-bg-hover-dark,#713f12)]",
|
|
39
|
+
orange:
|
|
40
|
+
"bg-[var(--zui-copy-button-orange-bg,#f97316)] dark:bg-[var(--zui-copy-button-orange-bg-dark,#9a3412)] text-[color:var(--zui-copy-button-orange-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-orange-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-orange-bg-hover,#f97316)] dark:hover:bg-[var(--zui-copy-button-orange-bg-hover-dark,#7c2d12)]",
|
|
41
|
+
gray:
|
|
42
|
+
"bg-[var(--zui-copy-button-gray-bg,#6b7280)] dark:bg-[var(--zui-copy-button-gray-bg-dark,#374151)] text-[color:var(--zui-copy-button-gray-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-gray-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-gray-bg-hover,#6b7280)] dark:hover:bg-[var(--zui-copy-button-gray-bg-hover-dark,#1f2937)]",
|
|
43
|
+
amber:
|
|
44
|
+
"bg-[var(--zui-copy-button-amber-bg,#f59e0b)] dark:bg-[var(--zui-copy-button-amber-bg-dark,#92400e)] text-[color:var(--zui-copy-button-amber-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-amber-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-amber-bg-hover,#f59e0b)] dark:hover:bg-[var(--zui-copy-button-amber-bg-hover-dark,#78350f)]",
|
|
45
|
+
violet:
|
|
46
|
+
"bg-[var(--zui-copy-button-violet-bg,#5b21b6)] dark:bg-[var(--zui-copy-button-violet-bg-dark,#7c3aed)] text-[color:var(--zui-copy-button-violet-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-violet-fg-dark,#ffffff)] hover:bg-[var(--zui-copy-button-violet-bg-hover,#5b21b6)] dark:hover:bg-[var(--zui-copy-button-violet-bg-hover-dark,#7c3aed)]",
|
|
47
|
+
"gradient-blue":
|
|
48
|
+
"bg-linear-to-r from-[var(--zui-copy-button-gradient-blue-from,#1e40af)] dark:from-[var(--zui-copy-button-gradient-blue-from-dark,#2563eb)] to-[var(--zui-copy-button-gradient-blue-to,#6b21a8)] dark:to-[var(--zui-copy-button-gradient-blue-to-dark,#9333ea)] text-[color:var(--zui-copy-button-gradient-blue-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-gradient-blue-fg-dark,#ffffff)] hover:from-[var(--zui-copy-button-gradient-blue-from-hover,#1e40af)] dark:hover:from-[var(--zui-copy-button-gradient-blue-from-hover-dark,#2563eb)] hover:to-[var(--zui-copy-button-gradient-blue-to-hover,#6b21a8)] dark:hover:to-[var(--zui-copy-button-gradient-blue-to-hover-dark,#9333ea)]",
|
|
49
|
+
"gradient-green":
|
|
50
|
+
"bg-linear-to-r from-[var(--zui-copy-button-gradient-green-from,#166534)] dark:from-[var(--zui-copy-button-gradient-green-from-dark,#16a34a)] to-[var(--zui-copy-button-gradient-green-to,#3f6212)] dark:to-[var(--zui-copy-button-gradient-green-to-dark,#65a30d)] text-[color:var(--zui-copy-button-gradient-green-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-gradient-green-fg-dark,#ffffff)] hover:from-[var(--zui-copy-button-gradient-green-from-hover,#166534)] dark:hover:from-[var(--zui-copy-button-gradient-green-from-hover-dark,#16a34a)] hover:to-[var(--zui-copy-button-gradient-green-to-hover,#3f6212)] dark:hover:to-[var(--zui-copy-button-gradient-green-to-hover-dark,#65a30d)]",
|
|
51
|
+
"gradient-red":
|
|
52
|
+
"bg-linear-to-r from-[var(--zui-copy-button-gradient-red-from,#991b1b)] dark:from-[var(--zui-copy-button-gradient-red-from-dark,#dc2626)] to-[var(--zui-copy-button-gradient-red-to,#9d174d)] dark:to-[var(--zui-copy-button-gradient-red-to-dark,#db2777)] text-[color:var(--zui-copy-button-gradient-red-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-gradient-red-fg-dark,#ffffff)] hover:from-[var(--zui-copy-button-gradient-red-from-hover,#991b1b)] dark:hover:from-[var(--zui-copy-button-gradient-red-from-hover-dark,#dc2626)] hover:to-[var(--zui-copy-button-gradient-red-to-hover,#9d174d)] dark:hover:to-[var(--zui-copy-button-gradient-red-to-hover-dark,#db2777)]",
|
|
53
|
+
"gradient-yellow":
|
|
54
|
+
"bg-linear-to-r from-[var(--zui-copy-button-gradient-yellow-from,#854d0e)] dark:from-[var(--zui-copy-button-gradient-yellow-from-dark,#ca8a04)] to-[var(--zui-copy-button-gradient-yellow-to,#9a3412)] dark:to-[var(--zui-copy-button-gradient-yellow-to-dark,#ea580c)] text-[color:var(--zui-copy-button-gradient-yellow-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-gradient-yellow-fg-dark,#ffffff)] hover:from-[var(--zui-copy-button-gradient-yellow-from-hover,#854d0e)] dark:hover:from-[var(--zui-copy-button-gradient-yellow-from-hover-dark,#ca8a04)] hover:to-[var(--zui-copy-button-gradient-yellow-to-hover,#9a3412)] dark:hover:to-[var(--zui-copy-button-gradient-yellow-to-hover-dark,#ea580c)]",
|
|
55
|
+
"gradient-purple":
|
|
56
|
+
"bg-linear-to-r from-[var(--zui-copy-button-gradient-purple-from,#6b21a8)] dark:from-[var(--zui-copy-button-gradient-purple-from-dark,#9333ea)] to-[var(--zui-copy-button-gradient-purple-to,#9d174d)] dark:to-[var(--zui-copy-button-gradient-purple-to-dark,#db2777)] text-[color:var(--zui-copy-button-gradient-purple-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-gradient-purple-fg-dark,#ffffff)] hover:from-[var(--zui-copy-button-gradient-purple-from-hover,#6b21a8)] dark:hover:from-[var(--zui-copy-button-gradient-purple-from-hover-dark,#9333ea)] hover:to-[var(--zui-copy-button-gradient-purple-to-hover,#9d174d)] dark:hover:to-[var(--zui-copy-button-gradient-purple-to-hover-dark,#db2777)]",
|
|
57
|
+
"gradient-teal":
|
|
58
|
+
"bg-linear-to-r from-[var(--zui-copy-button-gradient-teal-from,#115e59)] dark:from-[var(--zui-copy-button-gradient-teal-from-dark,#0d9488)] to-[var(--zui-copy-button-gradient-teal-to,#155e75)] dark:to-[var(--zui-copy-button-gradient-teal-to-dark,#0891b2)] text-[color:var(--zui-copy-button-gradient-teal-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-gradient-teal-fg-dark,#ffffff)] hover:from-[var(--zui-copy-button-gradient-teal-from-hover,#115e59)] dark:hover:from-[var(--zui-copy-button-gradient-teal-from-hover-dark,#0d9488)] hover:to-[var(--zui-copy-button-gradient-teal-to-hover,#155e75)] dark:hover:to-[var(--zui-copy-button-gradient-teal-to-hover-dark,#0891b2)]",
|
|
59
|
+
"gradient-indigo":
|
|
60
|
+
"bg-linear-to-r from-[var(--zui-copy-button-gradient-indigo-from,#3730a3)] dark:from-[var(--zui-copy-button-gradient-indigo-from-dark,#4f46e5)] to-[var(--zui-copy-button-gradient-indigo-to,#6b21a8)] dark:to-[var(--zui-copy-button-gradient-indigo-to-dark,#9333ea)] text-[color:var(--zui-copy-button-gradient-indigo-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-gradient-indigo-fg-dark,#ffffff)] hover:from-[var(--zui-copy-button-gradient-indigo-from-hover,#3730a3)] dark:hover:from-[var(--zui-copy-button-gradient-indigo-from-hover-dark,#4f46e5)] hover:to-[var(--zui-copy-button-gradient-indigo-to-hover,#6b21a8)] dark:hover:to-[var(--zui-copy-button-gradient-indigo-to-hover-dark,#9333ea)]",
|
|
61
|
+
"gradient-pink":
|
|
62
|
+
"bg-linear-to-r from-[var(--zui-copy-button-gradient-pink-from,#9d174d)] dark:from-[var(--zui-copy-button-gradient-pink-from-dark,#db2777)] to-[var(--zui-copy-button-gradient-pink-to,#9f1239)] dark:to-[var(--zui-copy-button-gradient-pink-to-dark,#e11d48)] text-[color:var(--zui-copy-button-gradient-pink-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-gradient-pink-fg-dark,#ffffff)] hover:from-[var(--zui-copy-button-gradient-pink-from-hover,#9d174d)] dark:hover:from-[var(--zui-copy-button-gradient-pink-from-hover-dark,#db2777)] hover:to-[var(--zui-copy-button-gradient-pink-to-hover,#9f1239)] dark:hover:to-[var(--zui-copy-button-gradient-pink-to-hover-dark,#e11d48)]",
|
|
63
|
+
"gradient-orange":
|
|
64
|
+
"bg-linear-to-r from-[var(--zui-copy-button-gradient-orange-from,#9a3412)] dark:from-[var(--zui-copy-button-gradient-orange-from-dark,#ea580c)] to-[var(--zui-copy-button-gradient-orange-to,#991b1b)] dark:to-[var(--zui-copy-button-gradient-orange-to-dark,#dc2626)] text-[color:var(--zui-copy-button-gradient-orange-fg,#ffffff)] dark:text-[color:var(--zui-copy-button-gradient-orange-fg-dark,#ffffff)] hover:from-[var(--zui-copy-button-gradient-orange-from-hover,#9a3412)] dark:hover:from-[var(--zui-copy-button-gradient-orange-from-hover-dark,#ea580c)] hover:to-[var(--zui-copy-button-gradient-orange-to-hover,#991b1b)] dark:hover:to-[var(--zui-copy-button-gradient-orange-to-hover-dark,#dc2626)]",
|
|
65
|
+
} as const;
|
|
66
|
+
|
|
67
|
+
export type ZuiCopyButtonAppearance = keyof typeof zuiCopyButtonAppearances;
|
|
68
|
+
|
|
69
|
+
export const zuiCopyButtonSizes = {
|
|
70
|
+
sm: "h-8 px-2.5 text-xs [&_svg]:size-3.5",
|
|
71
|
+
md: "h-9 px-3 text-sm [&_svg]:size-4",
|
|
72
|
+
lg: "h-11 px-4 text-base [&_svg]:size-5",
|
|
73
|
+
} as const;
|
|
74
|
+
|
|
75
|
+
export type ZuiCopyButtonSize = keyof typeof zuiCopyButtonSizes;
|
|
76
|
+
|
|
77
|
+
export const zuiCopyButtonIconOnlySizes = {
|
|
78
|
+
sm: "w-8 px-0",
|
|
79
|
+
md: "w-9 px-0",
|
|
80
|
+
lg: "w-11 px-0",
|
|
81
|
+
} as const;
|
|
@@ -9,6 +9,7 @@ export * from "./card";
|
|
|
9
9
|
export * from "./checkbox";
|
|
10
10
|
export * from "./command";
|
|
11
11
|
export * from "./context-menu";
|
|
12
|
+
export * from "./copy-button";
|
|
12
13
|
export * from "./divider";
|
|
13
14
|
export * from "./drawer";
|
|
14
15
|
export * from "./dropdown";
|
|
@@ -16,6 +17,7 @@ export * from "./dynamic-stepper";
|
|
|
16
17
|
export * from "./empty-state";
|
|
17
18
|
export * from "./file-upload";
|
|
18
19
|
export * from "./inputs";
|
|
20
|
+
export * from "./kbd";
|
|
19
21
|
export * from "./marquee";
|
|
20
22
|
export * from "./modal";
|
|
21
23
|
export * from "./otp-input";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export const zuiKbdBase = [
|
|
2
|
+
"inline-flex items-center justify-center gap-1 align-middle",
|
|
3
|
+
"rounded-[var(--zui-kbd-radius,0.375rem)] font-mono font-medium leading-none select-none",
|
|
4
|
+
] as const;
|
|
5
|
+
|
|
6
|
+
export const zuiKbdKeyAppearances = {
|
|
7
|
+
default:
|
|
8
|
+
"bg-[var(--zui-kbd-default-bg,#0f172a)] dark:bg-[var(--zui-kbd-default-bg-dark,#f8fafc)] text-[color:var(--zui-kbd-default-fg,#f8fafc)] dark:text-[color:var(--zui-kbd-default-fg-dark,#020617)] shadow-[var(--zui-kbd-default-shadow,0_1px_2px_#0f172a14)] dark:shadow-[var(--zui-kbd-default-shadow-dark,0_1px_2px_#0f172a1f)]",
|
|
9
|
+
secondary:
|
|
10
|
+
"bg-[var(--zui-kbd-secondary-bg,#e2e8f0)] dark:bg-[var(--zui-kbd-secondary-bg-dark,#1e293b)] text-[color:var(--zui-kbd-secondary-fg,#0f172a)] dark:text-[color:var(--zui-kbd-secondary-fg-dark,#f8fafc)]",
|
|
11
|
+
destructive:
|
|
12
|
+
"bg-[var(--zui-kbd-destructive-bg,#f43f5e)] dark:bg-[var(--zui-kbd-destructive-bg-dark,#be123c)] text-[color:var(--zui-kbd-destructive-fg,#ffffff)] dark:text-[color:var(--zui-kbd-destructive-fg-dark,#ffffff)]",
|
|
13
|
+
outline:
|
|
14
|
+
"border border-[color:var(--zui-kbd-outline-border,#0000001a)] dark:border-[color:var(--zui-kbd-outline-border-dark,#ffffff1a)] bg-[var(--zui-kbd-outline-bg,#0000000d)] dark:bg-[var(--zui-kbd-outline-bg-dark,#ffffff0d)] text-[color:var(--zui-kbd-outline-fg,#0f172a)] dark:text-[color:var(--zui-kbd-outline-fg-dark,#f8fafc)]",
|
|
15
|
+
ghost:
|
|
16
|
+
"bg-transparent text-[color:var(--zui-kbd-ghost-fg,#334155)] dark:text-[color:var(--zui-kbd-ghost-fg-dark,#e2e8f0)]",
|
|
17
|
+
glass:
|
|
18
|
+
"border border-[color:var(--zui-kbd-glass-border,#00000026)] dark:border-[color:var(--zui-kbd-glass-border-dark,#ffffff26)] bg-[var(--zui-kbd-glass-bg,#0000001a)] dark:bg-[var(--zui-kbd-glass-bg-dark,#ffffff1a)] text-[color:var(--zui-kbd-glass-fg,#0f172a)] dark:text-[color:var(--zui-kbd-glass-fg-dark,#ffffff)] backdrop-blur-md",
|
|
19
|
+
emerald:
|
|
20
|
+
"bg-[var(--zui-kbd-emerald-bg,#10b981)] dark:bg-[var(--zui-kbd-emerald-bg-dark,#065f46)] text-[color:var(--zui-kbd-emerald-fg,#ffffff)] dark:text-[color:var(--zui-kbd-emerald-fg-dark,#ffffff)]",
|
|
21
|
+
indigo:
|
|
22
|
+
"bg-[var(--zui-kbd-indigo-bg,#3730a3)] dark:bg-[var(--zui-kbd-indigo-bg-dark,#4f46e5)] text-[color:var(--zui-kbd-indigo-fg,#ffffff)] dark:text-[color:var(--zui-kbd-indigo-fg-dark,#ffffff)]",
|
|
23
|
+
purple:
|
|
24
|
+
"bg-[var(--zui-kbd-purple-bg,#6b21a8)] dark:bg-[var(--zui-kbd-purple-bg-dark,#9333ea)] text-[color:var(--zui-kbd-purple-fg,#ffffff)] dark:text-[color:var(--zui-kbd-purple-fg-dark,#ffffff)]",
|
|
25
|
+
pink:
|
|
26
|
+
"bg-[var(--zui-kbd-pink-bg,#9d174d)] dark:bg-[var(--zui-kbd-pink-bg-dark,#db2777)] text-[color:var(--zui-kbd-pink-fg,#ffffff)] dark:text-[color:var(--zui-kbd-pink-fg-dark,#ffffff)]",
|
|
27
|
+
rose:
|
|
28
|
+
"bg-[var(--zui-kbd-rose-bg,#9f1239)] dark:bg-[var(--zui-kbd-rose-bg-dark,#e11d48)] text-[color:var(--zui-kbd-rose-fg,#ffffff)] dark:text-[color:var(--zui-kbd-rose-fg-dark,#ffffff)]",
|
|
29
|
+
sky:
|
|
30
|
+
"bg-[var(--zui-kbd-sky-bg,#0ea5e9)] dark:bg-[var(--zui-kbd-sky-bg-dark,#0369a1)] text-[color:var(--zui-kbd-sky-fg,#ffffff)] dark:text-[color:var(--zui-kbd-sky-fg-dark,#ffffff)]",
|
|
31
|
+
teal:
|
|
32
|
+
"bg-[var(--zui-kbd-teal-bg,#14b8a6)] dark:bg-[var(--zui-kbd-teal-bg-dark,#0f766e)] text-[color:var(--zui-kbd-teal-fg,#ffffff)] dark:text-[color:var(--zui-kbd-teal-fg-dark,#ffffff)]",
|
|
33
|
+
yellow:
|
|
34
|
+
"bg-[var(--zui-kbd-yellow-bg,#eab308)] dark:bg-[var(--zui-kbd-yellow-bg-dark,#854d0e)] text-[color:var(--zui-kbd-yellow-fg,#ffffff)] dark:text-[color:var(--zui-kbd-yellow-fg-dark,#ffffff)]",
|
|
35
|
+
orange:
|
|
36
|
+
"bg-[var(--zui-kbd-orange-bg,#f97316)] dark:bg-[var(--zui-kbd-orange-bg-dark,#9a3412)] text-[color:var(--zui-kbd-orange-fg,#ffffff)] dark:text-[color:var(--zui-kbd-orange-fg-dark,#ffffff)]",
|
|
37
|
+
gray:
|
|
38
|
+
"bg-[var(--zui-kbd-gray-bg,#6b7280)] dark:bg-[var(--zui-kbd-gray-bg-dark,#374151)] text-[color:var(--zui-kbd-gray-fg,#ffffff)] dark:text-[color:var(--zui-kbd-gray-fg-dark,#ffffff)]",
|
|
39
|
+
amber:
|
|
40
|
+
"bg-[var(--zui-kbd-amber-bg,#f59e0b)] dark:bg-[var(--zui-kbd-amber-bg-dark,#92400e)] text-[color:var(--zui-kbd-amber-fg,#ffffff)] dark:text-[color:var(--zui-kbd-amber-fg-dark,#ffffff)]",
|
|
41
|
+
violet:
|
|
42
|
+
"bg-[var(--zui-kbd-violet-bg,#5b21b6)] dark:bg-[var(--zui-kbd-violet-bg-dark,#7c3aed)] text-[color:var(--zui-kbd-violet-fg,#ffffff)] dark:text-[color:var(--zui-kbd-violet-fg-dark,#ffffff)]",
|
|
43
|
+
"gradient-blue":
|
|
44
|
+
"bg-linear-to-r from-[var(--zui-kbd-gradient-blue-from,#1e40af)] dark:from-[var(--zui-kbd-gradient-blue-from-dark,#2563eb)] to-[var(--zui-kbd-gradient-blue-to,#6b21a8)] dark:to-[var(--zui-kbd-gradient-blue-to-dark,#9333ea)] text-[color:var(--zui-kbd-gradient-blue-fg,#ffffff)] dark:text-[color:var(--zui-kbd-gradient-blue-fg-dark,#ffffff)]",
|
|
45
|
+
"gradient-green":
|
|
46
|
+
"bg-linear-to-r from-[var(--zui-kbd-gradient-green-from,#166534)] dark:from-[var(--zui-kbd-gradient-green-from-dark,#16a34a)] to-[var(--zui-kbd-gradient-green-to,#3f6212)] dark:to-[var(--zui-kbd-gradient-green-to-dark,#65a30d)] text-[color:var(--zui-kbd-gradient-green-fg,#ffffff)] dark:text-[color:var(--zui-kbd-gradient-green-fg-dark,#ffffff)]",
|
|
47
|
+
"gradient-red":
|
|
48
|
+
"bg-linear-to-r from-[var(--zui-kbd-gradient-red-from,#991b1b)] dark:from-[var(--zui-kbd-gradient-red-from-dark,#dc2626)] to-[var(--zui-kbd-gradient-red-to,#9d174d)] dark:to-[var(--zui-kbd-gradient-red-to-dark,#db2777)] text-[color:var(--zui-kbd-gradient-red-fg,#ffffff)] dark:text-[color:var(--zui-kbd-gradient-red-fg-dark,#ffffff)]",
|
|
49
|
+
"gradient-yellow":
|
|
50
|
+
"bg-linear-to-r from-[var(--zui-kbd-gradient-yellow-from,#854d0e)] dark:from-[var(--zui-kbd-gradient-yellow-from-dark,#ca8a04)] to-[var(--zui-kbd-gradient-yellow-to,#9a3412)] dark:to-[var(--zui-kbd-gradient-yellow-to-dark,#ea580c)] text-[color:var(--zui-kbd-gradient-yellow-fg,#ffffff)] dark:text-[color:var(--zui-kbd-gradient-yellow-fg-dark,#ffffff)]",
|
|
51
|
+
"gradient-purple":
|
|
52
|
+
"bg-linear-to-r from-[var(--zui-kbd-gradient-purple-from,#6b21a8)] dark:from-[var(--zui-kbd-gradient-purple-from-dark,#9333ea)] to-[var(--zui-kbd-gradient-purple-to,#9d174d)] dark:to-[var(--zui-kbd-gradient-purple-to-dark,#db2777)] text-[color:var(--zui-kbd-gradient-purple-fg,#ffffff)] dark:text-[color:var(--zui-kbd-gradient-purple-fg-dark,#ffffff)]",
|
|
53
|
+
"gradient-teal":
|
|
54
|
+
"bg-linear-to-r from-[var(--zui-kbd-gradient-teal-from,#115e59)] dark:from-[var(--zui-kbd-gradient-teal-from-dark,#0d9488)] to-[var(--zui-kbd-gradient-teal-to,#155e75)] dark:to-[var(--zui-kbd-gradient-teal-to-dark,#0891b2)] text-[color:var(--zui-kbd-gradient-teal-fg,#ffffff)] dark:text-[color:var(--zui-kbd-gradient-teal-fg-dark,#ffffff)]",
|
|
55
|
+
"gradient-indigo":
|
|
56
|
+
"bg-linear-to-r from-[var(--zui-kbd-gradient-indigo-from,#3730a3)] dark:from-[var(--zui-kbd-gradient-indigo-from-dark,#4f46e5)] to-[var(--zui-kbd-gradient-indigo-to,#6b21a8)] dark:to-[var(--zui-kbd-gradient-indigo-to-dark,#9333ea)] text-[color:var(--zui-kbd-gradient-indigo-fg,#ffffff)] dark:text-[color:var(--zui-kbd-gradient-indigo-fg-dark,#ffffff)]",
|
|
57
|
+
"gradient-pink":
|
|
58
|
+
"bg-linear-to-r from-[var(--zui-kbd-gradient-pink-from,#9d174d)] dark:from-[var(--zui-kbd-gradient-pink-from-dark,#db2777)] to-[var(--zui-kbd-gradient-pink-to,#9f1239)] dark:to-[var(--zui-kbd-gradient-pink-to-dark,#e11d48)] text-[color:var(--zui-kbd-gradient-pink-fg,#ffffff)] dark:text-[color:var(--zui-kbd-gradient-pink-fg-dark,#ffffff)]",
|
|
59
|
+
"gradient-orange":
|
|
60
|
+
"bg-linear-to-r from-[var(--zui-kbd-gradient-orange-from,#9a3412)] dark:from-[var(--zui-kbd-gradient-orange-from-dark,#ea580c)] to-[var(--zui-kbd-gradient-orange-to,#991b1b)] dark:to-[var(--zui-kbd-gradient-orange-to-dark,#dc2626)] text-[color:var(--zui-kbd-gradient-orange-fg,#ffffff)] dark:text-[color:var(--zui-kbd-gradient-orange-fg-dark,#ffffff)]",
|
|
61
|
+
} as const;
|
|
62
|
+
|
|
63
|
+
export type ZuiKbdAppearance = keyof typeof zuiKbdKeyAppearances;
|
|
64
|
+
|
|
65
|
+
export const zuiKbdKeyBase = [
|
|
66
|
+
"inline-flex items-center justify-center font-mono font-medium leading-none",
|
|
67
|
+
"rounded-[var(--zui-kbd-radius,0.375rem)]",
|
|
68
|
+
"shadow-[var(--zui-kbd-shadow,inset_0_-1px_0_#0000001f)] dark:shadow-[var(--zui-kbd-shadow-dark,inset_0_-1px_0_#0000004d)]",
|
|
69
|
+
] as const;
|
|
70
|
+
|
|
71
|
+
export const zuiKbdKeySizes = {
|
|
72
|
+
sm: "h-5 min-w-5 px-1 text-[0.7rem]",
|
|
73
|
+
md: "h-6 min-w-6 px-1.5 text-xs",
|
|
74
|
+
lg: "h-7 min-w-7 px-2 text-sm",
|
|
75
|
+
} as const;
|
|
76
|
+
|
|
77
|
+
export type ZuiKbdSize = keyof typeof zuiKbdKeySizes;
|
|
78
|
+
|
|
79
|
+
export const zuiKbdSeparatorSizes = {
|
|
80
|
+
sm: "text-[0.7rem]",
|
|
81
|
+
md: "text-xs",
|
|
82
|
+
lg: "text-sm",
|
|
83
|
+
} as const;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CopyButtonAnimationPresets } from "./types";
|
|
2
|
+
|
|
3
|
+
export const copyButtonAnimationPresets: CopyButtonAnimationPresets = {
|
|
4
|
+
swap: {
|
|
5
|
+
initial: { opacity: 0, scale: 0.6, rotate: -45 },
|
|
6
|
+
animate: { opacity: 1, scale: 1, rotate: 0 },
|
|
7
|
+
exit: { opacity: 0, scale: 0.6, rotate: 45 },
|
|
8
|
+
transition: { type: "spring", stiffness: 520, damping: 24 },
|
|
9
|
+
},
|
|
10
|
+
pop: {
|
|
11
|
+
initial: { opacity: 0, scale: 0.4, rotate: 0 },
|
|
12
|
+
animate: { opacity: 1, scale: 1, rotate: 0 },
|
|
13
|
+
exit: { opacity: 0, scale: 0.4, rotate: 0 },
|
|
14
|
+
transition: { type: "spring", stiffness: 600, damping: 20 },
|
|
15
|
+
},
|
|
16
|
+
fade: {
|
|
17
|
+
initial: { opacity: 0, scale: 1, rotate: 0 },
|
|
18
|
+
animate: { opacity: 1, scale: 1, rotate: 0 },
|
|
19
|
+
exit: { opacity: 0, scale: 1, rotate: 0 },
|
|
20
|
+
transition: { duration: 0.16 },
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { AnimatePresence, motion } from "framer-motion";
|
|
4
|
+
|
|
5
|
+
import { CopyButtonBase } from "../copy-button-base";
|
|
6
|
+
import type { CopyButtonIconRenderer } from "../types";
|
|
7
|
+
|
|
8
|
+
import { copyButtonAnimationPresets } from "./animations";
|
|
9
|
+
import type { CopyButtonAnimatedProps } from "./types";
|
|
10
|
+
|
|
11
|
+
export function CopyButtonAnimated({
|
|
12
|
+
animation = "swap",
|
|
13
|
+
...props
|
|
14
|
+
}: CopyButtonAnimatedProps) {
|
|
15
|
+
const preset = copyButtonAnimationPresets[animation];
|
|
16
|
+
|
|
17
|
+
const renderIcon: CopyButtonIconRenderer = ({
|
|
18
|
+
copied,
|
|
19
|
+
copyIcon,
|
|
20
|
+
copiedIcon,
|
|
21
|
+
}) => (
|
|
22
|
+
<AnimatePresence initial={false} mode="wait">
|
|
23
|
+
<motion.span
|
|
24
|
+
key={copied ? "copied" : "idle"}
|
|
25
|
+
className="inline-flex items-center justify-center"
|
|
26
|
+
initial={preset.initial}
|
|
27
|
+
animate={preset.animate}
|
|
28
|
+
exit={preset.exit}
|
|
29
|
+
transition={preset.transition}
|
|
30
|
+
>
|
|
31
|
+
{copied ? copiedIcon : copyIcon}
|
|
32
|
+
</motion.span>
|
|
33
|
+
</AnimatePresence>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return <CopyButtonBase {...props} renderIcon={renderIcon} />;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
CopyButtonAnimated.displayName = "CopyButtonAnimated";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export { CopyButtonAnimated } from "./copy-button-animated";
|
|
4
|
+
export { copyButtonAnimationPresets } from "./animations";
|
|
5
|
+
export type {
|
|
6
|
+
CopyButtonAnimatedProps,
|
|
7
|
+
CopyButtonAnimation,
|
|
8
|
+
CopyButtonAnimationPreset,
|
|
9
|
+
CopyButtonAnimationPresets,
|
|
10
|
+
} from "./types";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Transition } from "framer-motion";
|
|
2
|
+
|
|
3
|
+
import type { CopyButtonProps } from "../types";
|
|
4
|
+
|
|
5
|
+
export type CopyButtonAnimation = "swap" | "pop" | "fade";
|
|
6
|
+
|
|
7
|
+
export type CopyButtonAnimatedProps = CopyButtonProps & {
|
|
8
|
+
animation?: CopyButtonAnimation;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type CopyButtonAnimationPreset = {
|
|
12
|
+
initial: { opacity: number; scale: number; rotate: number };
|
|
13
|
+
animate: { opacity: number; scale: number; rotate: number };
|
|
14
|
+
exit: { opacity: number; scale: number; rotate: number };
|
|
15
|
+
transition: Transition;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type CopyButtonAnimationPresets = Record<
|
|
19
|
+
CopyButtonAnimation,
|
|
20
|
+
CopyButtonAnimationPreset
|
|
21
|
+
>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FiCheck, FiCopy } from "react-icons/fi";
|
|
4
|
+
|
|
5
|
+
import { useClipboard } from "../../hooks/useClipboard/useClipboard";
|
|
6
|
+
import { cn } from "../../lib/utils";
|
|
7
|
+
|
|
8
|
+
import type { CopyButtonBaseProps, CopyButtonIconRenderer } from "./types";
|
|
9
|
+
import { copyButtonVariants } from "./variants";
|
|
10
|
+
|
|
11
|
+
const defaultRenderIcon: CopyButtonIconRenderer = ({
|
|
12
|
+
copied,
|
|
13
|
+
copyIcon,
|
|
14
|
+
copiedIcon,
|
|
15
|
+
}) => (copied ? copiedIcon : copyIcon);
|
|
16
|
+
|
|
17
|
+
export function CopyButtonBase({
|
|
18
|
+
value,
|
|
19
|
+
timeout = 2000,
|
|
20
|
+
appearance,
|
|
21
|
+
size,
|
|
22
|
+
iconOnly = true,
|
|
23
|
+
label = "Copy",
|
|
24
|
+
copiedLabel = "Copied",
|
|
25
|
+
copyIcon = <FiCopy aria-hidden />,
|
|
26
|
+
copiedIcon = <FiCheck aria-hidden />,
|
|
27
|
+
onCopy,
|
|
28
|
+
renderIcon = defaultRenderIcon,
|
|
29
|
+
className,
|
|
30
|
+
type = "button",
|
|
31
|
+
disabled,
|
|
32
|
+
onClick,
|
|
33
|
+
"aria-label": ariaLabel,
|
|
34
|
+
ref,
|
|
35
|
+
...rest
|
|
36
|
+
}: CopyButtonBaseProps) {
|
|
37
|
+
const { copied, copy } = useClipboard(timeout);
|
|
38
|
+
|
|
39
|
+
const handleClick: NonNullable<CopyButtonBaseProps["onClick"]> = async (
|
|
40
|
+
event,
|
|
41
|
+
) => {
|
|
42
|
+
onClick?.(event);
|
|
43
|
+
if (event.defaultPrevented) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const ok = await copy(value);
|
|
47
|
+
if (ok) {
|
|
48
|
+
onCopy?.(value);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const text = copied ? copiedLabel : label;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<button
|
|
56
|
+
ref={ref}
|
|
57
|
+
type={type}
|
|
58
|
+
data-slot="copy-button"
|
|
59
|
+
data-copied={copied ? "true" : undefined}
|
|
60
|
+
disabled={disabled}
|
|
61
|
+
aria-label={ariaLabel ?? (iconOnly ? text : undefined)}
|
|
62
|
+
onClick={handleClick}
|
|
63
|
+
className={cn(
|
|
64
|
+
copyButtonVariants({ appearance, size, iconOnly }),
|
|
65
|
+
className,
|
|
66
|
+
)}
|
|
67
|
+
{...rest}
|
|
68
|
+
>
|
|
69
|
+
<span
|
|
70
|
+
data-slot="copy-button-icon"
|
|
71
|
+
className="relative inline-flex items-center justify-center"
|
|
72
|
+
>
|
|
73
|
+
{renderIcon({ copied, copyIcon, copiedIcon })}
|
|
74
|
+
</span>
|
|
75
|
+
{!iconOnly ? (
|
|
76
|
+
<span data-slot="copy-button-label" aria-live="polite">
|
|
77
|
+
{text}
|
|
78
|
+
</span>
|
|
79
|
+
) : (
|
|
80
|
+
<span className="sr-only" aria-live="polite">
|
|
81
|
+
{text}
|
|
82
|
+
</span>
|
|
83
|
+
)}
|
|
84
|
+
</button>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
CopyButtonBase.displayName = "CopyButton";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createRef } from "react";
|
|
2
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { CopyButton } from "./copy-button";
|
|
7
|
+
|
|
8
|
+
describe("CopyButton", () => {
|
|
9
|
+
const originalClipboard = navigator.clipboard;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
Object.defineProperty(navigator, "clipboard", {
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: { writeText: vi.fn().mockResolvedValue(undefined) },
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
Object.defineProperty(navigator, "clipboard", {
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: originalClipboard,
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should expose displayName", () => {
|
|
28
|
+
expect(CopyButton.displayName).toBe("CopyButton");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should stamp data-slot", () => {
|
|
32
|
+
render(<CopyButton value="npm i zentauri" />);
|
|
33
|
+
expect(
|
|
34
|
+
document.querySelector('[data-slot="copy-button"]'),
|
|
35
|
+
).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should write the value to the clipboard on click", async () => {
|
|
39
|
+
render(<CopyButton value="copy me" />);
|
|
40
|
+
fireEvent.click(screen.getByRole("button"));
|
|
41
|
+
await waitFor(() =>
|
|
42
|
+
expect(navigator.clipboard.writeText).toHaveBeenCalledWith("copy me"),
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should call onCopy after a successful copy", async () => {
|
|
47
|
+
const user = userEvent.setup();
|
|
48
|
+
const onCopy = vi.fn();
|
|
49
|
+
render(<CopyButton value="token-123" onCopy={onCopy} />);
|
|
50
|
+
await user.click(screen.getByRole("button"));
|
|
51
|
+
await waitFor(() => expect(onCopy).toHaveBeenCalledWith("token-123"));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("should flip to the copied state and mark data-copied", async () => {
|
|
55
|
+
const user = userEvent.setup();
|
|
56
|
+
render(<CopyButton value="x" timeout={0} copiedLabel="Copied!" />);
|
|
57
|
+
const button = screen.getByRole("button");
|
|
58
|
+
await user.click(button);
|
|
59
|
+
await waitFor(() =>
|
|
60
|
+
expect(button.getAttribute("data-copied")).toBe("true"),
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should render the label when iconOnly is false", () => {
|
|
65
|
+
render(<CopyButton value="x" iconOnly={false} label="Copy code" />);
|
|
66
|
+
expect(screen.getByText("Copy code")).toBeInTheDocument();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should apply the secondary appearance token", () => {
|
|
70
|
+
render(<CopyButton value="x" appearance="secondary" />);
|
|
71
|
+
const button = document.querySelector(
|
|
72
|
+
'[data-slot="copy-button"]',
|
|
73
|
+
) as HTMLElement;
|
|
74
|
+
expect(button.className).toMatch(/--zui-copy-button-secondary-bg/);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should forward ref", () => {
|
|
78
|
+
const ref = createRef<HTMLButtonElement>();
|
|
79
|
+
render(<CopyButton ref={ref} value="x" />);
|
|
80
|
+
expect(ref.current?.getAttribute("data-slot")).toBe("copy-button");
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// copy-button.tsx — default static entry (no framer-motion)
|
|
2
|
+
import { CopyButtonBase } from "./copy-button-base";
|
|
3
|
+
import type { CopyButtonProps } from "./types";
|
|
4
|
+
|
|
5
|
+
export function CopyButton(props: CopyButtonProps) {
|
|
6
|
+
return <CopyButtonBase {...props} />;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
CopyButton.displayName = "CopyButton";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { VariantProps } from "class-variance-authority";
|
|
2
|
+
import type { ComponentPropsWithRef, ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import type { copyButtonVariants } from "./variants";
|
|
5
|
+
|
|
6
|
+
export type CopyButtonVariantProps = VariantProps<typeof copyButtonVariants>;
|
|
7
|
+
|
|
8
|
+
/** Renders the icon region for a given copied state. Lets the animated entry swap the static icons for motion ones. */
|
|
9
|
+
export type CopyButtonIconRenderer = (state: {
|
|
10
|
+
copied: boolean;
|
|
11
|
+
copyIcon: ReactNode;
|
|
12
|
+
copiedIcon: ReactNode;
|
|
13
|
+
}) => ReactNode;
|
|
14
|
+
|
|
15
|
+
export interface CopyButtonBaseProps
|
|
16
|
+
extends Omit<ComponentPropsWithRef<"button">, "value" | "onCopy"> {
|
|
17
|
+
/** Text written to the clipboard when the button is pressed. */
|
|
18
|
+
value: string;
|
|
19
|
+
/** Milliseconds the copied state stays active before resetting. `0` keeps it until re-copied. */
|
|
20
|
+
timeout?: number;
|
|
21
|
+
appearance?: CopyButtonVariantProps["appearance"];
|
|
22
|
+
size?: CopyButtonVariantProps["size"];
|
|
23
|
+
/** Render only the icon (default). Pass `false` to show the label text alongside the icon. */
|
|
24
|
+
iconOnly?: boolean;
|
|
25
|
+
/** Label shown (and used for `aria-label`) in the idle state. */
|
|
26
|
+
label?: string;
|
|
27
|
+
/** Label shown (and used for `aria-label`) after a successful copy. */
|
|
28
|
+
copiedLabel?: string;
|
|
29
|
+
copyIcon?: ReactNode;
|
|
30
|
+
copiedIcon?: ReactNode;
|
|
31
|
+
/** Called with `value` after the clipboard write succeeds. */
|
|
32
|
+
onCopy?: (value: string) => void;
|
|
33
|
+
/** Overrides how the icon region renders; the animated entry uses this for motion. */
|
|
34
|
+
renderIcon?: CopyButtonIconRenderer;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type CopyButtonProps = Omit<CopyButtonBaseProps, "renderIcon">;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
zuiCopyButtonAppearances,
|
|
5
|
+
zuiCopyButtonBase,
|
|
6
|
+
zuiCopyButtonIconOnlySizes,
|
|
7
|
+
zuiCopyButtonSizes,
|
|
8
|
+
} from "../../design-system/copy-button";
|
|
9
|
+
|
|
10
|
+
export const copyButtonVariants = cva(zuiCopyButtonBase, {
|
|
11
|
+
variants: {
|
|
12
|
+
appearance: zuiCopyButtonAppearances,
|
|
13
|
+
size: zuiCopyButtonSizes,
|
|
14
|
+
iconOnly: {
|
|
15
|
+
true: "",
|
|
16
|
+
false: "",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
compoundVariants: [
|
|
20
|
+
{ iconOnly: true, size: "sm", class: zuiCopyButtonIconOnlySizes.sm },
|
|
21
|
+
{ iconOnly: true, size: "md", class: zuiCopyButtonIconOnlySizes.md },
|
|
22
|
+
{ iconOnly: true, size: "lg", class: zuiCopyButtonIconOnlySizes.lg },
|
|
23
|
+
],
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
appearance: "default",
|
|
26
|
+
size: "md",
|
|
27
|
+
iconOnly: true,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { KbdAnimationPresets } from "./types";
|
|
2
|
+
|
|
3
|
+
export const kbdAnimationPresets: KbdAnimationPresets = {
|
|
4
|
+
none: {},
|
|
5
|
+
press: {
|
|
6
|
+
whileHover: { y: -1 },
|
|
7
|
+
whileTap: { y: 1, scale: 0.96 },
|
|
8
|
+
transition: { type: "spring", stiffness: 600, damping: 22 },
|
|
9
|
+
},
|
|
10
|
+
pop: {
|
|
11
|
+
initial: { scale: 0.85, opacity: 0 },
|
|
12
|
+
animate: { scale: 1, opacity: 1 },
|
|
13
|
+
transition: { type: "spring", stiffness: 520, damping: 26 },
|
|
14
|
+
},
|
|
15
|
+
};
|