@mieweb/ui 0.1.0 → 0.1.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/LICENSE +39 -15
- package/README.md +9 -1
- package/dist/{chunk-CLNOI5J7.js → chunk-4SMSH4OY.js} +4 -4
- package/dist/chunk-4SMSH4OY.js.map +1 -0
- package/dist/chunk-5T3AWNHG.cjs +471 -0
- package/dist/chunk-5T3AWNHG.cjs.map +1 -0
- package/dist/chunk-74K3RRU7.cjs +4 -0
- package/dist/{chunk-ZO46CFVN.cjs.map → chunk-74K3RRU7.cjs.map} +1 -1
- package/dist/{chunk-VWXGUNBR.cjs → chunk-AKTUXJPI.cjs} +107 -18
- package/dist/chunk-AKTUXJPI.cjs.map +1 -0
- package/dist/{chunk-NH2JVQ6V.cjs → chunk-I7L6CQXR.cjs} +21 -9
- package/dist/chunk-I7L6CQXR.cjs.map +1 -0
- package/dist/{chunk-KJOFWJHV.js → chunk-MFB4FS7D.js} +120 -81
- package/dist/chunk-MFB4FS7D.js.map +1 -0
- package/dist/{chunk-LEE3NMNP.cjs → chunk-NL3CZNBH.cjs} +120 -81
- package/dist/chunk-NL3CZNBH.cjs.map +1 -0
- package/dist/{chunk-BR2XGATJ.cjs → chunk-NNEFAUHV.cjs} +4 -4
- package/dist/chunk-NNEFAUHV.cjs.map +1 -0
- package/dist/chunk-SCV7C55E.cjs +11 -0
- package/dist/chunk-SCV7C55E.cjs.map +1 -0
- package/dist/{chunk-D5IBXXF2.js → chunk-SD44QJIP.js} +20 -8
- package/dist/chunk-SD44QJIP.js.map +1 -0
- package/dist/{chunk-QBWVTJKF.js → chunk-UBRDKNLQ.js} +107 -18
- package/dist/chunk-UBRDKNLQ.js.map +1 -0
- package/dist/chunk-V2DF2GUE.js +3 -0
- package/dist/{chunk-ZQ4XMJH7.js.map → chunk-V2DF2GUE.js.map} +1 -1
- package/dist/chunk-VSQF22GL.js +9 -0
- package/dist/chunk-VSQF22GL.js.map +1 -0
- package/dist/chunk-XVZ4SLQB.js +447 -0
- package/dist/chunk-XVZ4SLQB.js.map +1 -0
- package/dist/components/AudioPlayer/index.cjs +6 -6
- package/dist/components/AudioPlayer/index.d.cts +5 -1
- package/dist/components/AudioPlayer/index.d.ts +5 -1
- package/dist/components/AudioPlayer/index.js +1 -1
- package/dist/components/Modal/index.cjs +11 -10
- package/dist/components/Modal/index.js +3 -2
- package/dist/components/RecordButton/index.cjs +4 -8
- package/dist/components/RecordButton/index.d.cts +57 -44
- package/dist/components/RecordButton/index.d.ts +57 -44
- package/dist/components/RecordButton/index.js +1 -1
- package/dist/components/Select/index.cjs +3 -4
- package/dist/components/Select/index.js +1 -2
- package/dist/components/Spinner/index.d.cts +1 -1
- package/dist/components/Spinner/index.d.ts +1 -1
- package/dist/hooks/index.cjs +3 -2
- package/dist/hooks/index.js +2 -1
- package/dist/index.cjs +880 -667
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -11
- package/dist/index.d.ts +16 -11
- package/dist/index.js +801 -588
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils/index.cjs +6 -1
- package/dist/utils/index.d.cts +14 -1
- package/dist/utils/index.d.ts +14 -1
- package/dist/utils/index.js +2 -1
- package/package.json +7 -7
- package/dist/chunk-BR2XGATJ.cjs.map +0 -1
- package/dist/chunk-CLNOI5J7.js.map +0 -1
- package/dist/chunk-D5IBXXF2.js.map +0 -1
- package/dist/chunk-FQ5G7J24.js +0 -297
- package/dist/chunk-FQ5G7J24.js.map +0 -1
- package/dist/chunk-HLW3XD5R.cjs +0 -322
- package/dist/chunk-HLW3XD5R.cjs.map +0 -1
- package/dist/chunk-KJOFWJHV.js.map +0 -1
- package/dist/chunk-LEE3NMNP.cjs.map +0 -1
- package/dist/chunk-NH2JVQ6V.cjs.map +0 -1
- package/dist/chunk-QBWVTJKF.js.map +0 -1
- package/dist/chunk-VWXGUNBR.cjs.map +0 -1
- package/dist/chunk-ZO46CFVN.cjs +0 -4
- package/dist/chunk-ZQ4XMJH7.js +0 -3
package/LICENSE
CHANGED
|
@@ -1,21 +1,45 @@
|
|
|
1
|
-
|
|
1
|
+
@mieweb/ui - Source Available License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 Medical Informatics Engineering, LLC. All rights reserved.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
proprietary property of Medical Informatics Engineering, LLC. ("MIE").
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
OPEN SOURCE / NON-COMMERCIAL USE
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person or organization
|
|
11
|
+
obtaining a copy of this Software, to use, copy, modify, merge, and distribute
|
|
12
|
+
the Software for non-commercial purposes, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
1. Attribution Required: The above copyright notice, this permission notice,
|
|
15
|
+
and clear attribution to Medical Informatics Engineering, LLC. shall be
|
|
16
|
+
included in all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
2. Open Source Requirement: If you distribute the Software or any derivative
|
|
19
|
+
works, you must do so under an open source license approved by the Open
|
|
20
|
+
Source Initiative (OSI) and make the source code publicly available.
|
|
21
|
+
|
|
22
|
+
3. Non-Commercial Use Only: This license does not grant permission to use the
|
|
23
|
+
Software for commercial purposes. "Commercial purposes" means any use
|
|
24
|
+
intended for or directed toward commercial advantage or monetary
|
|
25
|
+
compensation.
|
|
26
|
+
|
|
27
|
+
COMMERCIAL USE
|
|
28
|
+
|
|
29
|
+
For commercial use, including but not limited to:
|
|
30
|
+
- Use in proprietary/closed-source products
|
|
31
|
+
- Use in products or services sold for profit
|
|
32
|
+
- Use by for-profit organizations for internal business operations
|
|
33
|
+
|
|
34
|
+
You must obtain a separate commercial license from Medical Informatics
|
|
35
|
+
Engineering, LLC. Contact: https://www.mieweb.com or helpdesk@mieweb.com
|
|
36
|
+
|
|
37
|
+
NO WARRANTY
|
|
14
38
|
|
|
15
39
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
40
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
18
|
-
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
|
21
|
-
SOFTWARE.
|
|
41
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
42
|
+
MEDICAL INFORMATICS ENGINEERING, LLC. OR ITS AFFILIATES BE LIABLE FOR ANY
|
|
43
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
44
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
|
45
|
+
OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -693,4 +693,12 @@ We welcome contributions! Here's how to get started:
|
|
|
693
693
|
|
|
694
694
|
## License
|
|
695
695
|
|
|
696
|
-
|
|
696
|
+
Copyright © 2026 Medical Informatics Engineering, Inc. All rights reserved.
|
|
697
|
+
|
|
698
|
+
This software is **source available** with the following terms:
|
|
699
|
+
|
|
700
|
+
- ✅ **Free for open source projects** - Use, modify, and distribute freely in open source projects with attribution
|
|
701
|
+
- ✅ **Free for non-commercial use** - Personal projects, education, research
|
|
702
|
+
- 💼 **Commercial license required** - For proprietary products or commercial use, contact [licensing@mieweb.com](mailto:licensing@mieweb.com)
|
|
703
|
+
|
|
704
|
+
See the [LICENSE](LICENSE) file for full details.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { isStorybookDocsMode } from './chunk-VSQF22GL.js';
|
|
1
2
|
import { useRef, useEffect } from 'react';
|
|
2
3
|
|
|
3
|
-
// src/hooks/useFocusTrap.ts
|
|
4
4
|
function useFocusTrap(enabled = true) {
|
|
5
5
|
const containerRef = useRef(null);
|
|
6
6
|
useEffect(() => {
|
|
7
|
-
if (!enabled || !containerRef.current) return;
|
|
7
|
+
if (!enabled || !containerRef.current || isStorybookDocsMode()) return;
|
|
8
8
|
const container = containerRef.current;
|
|
9
9
|
const focusableElements = container.querySelectorAll(
|
|
10
10
|
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
@@ -34,5 +34,5 @@ function useFocusTrap(enabled = true) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export { useFocusTrap };
|
|
37
|
-
//# sourceMappingURL=chunk-
|
|
38
|
-
//# sourceMappingURL=chunk-
|
|
37
|
+
//# sourceMappingURL=chunk-4SMSH4OY.js.map
|
|
38
|
+
//# sourceMappingURL=chunk-4SMSH4OY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useFocusTrap.ts"],"names":[],"mappings":";;;AAuBO,SAAS,YAAA,CACd,UAAU,IAAA,EACW;AACrB,EAAA,MAAM,YAAA,GAAe,OAAU,IAAI,CAAA;AAEnC,EAAA,SAAA,CAAU,MAAM;AAEd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,YAAA,CAAa,OAAA,IAAW,qBAAoB,EAAG;AAEhE,IAAA,MAAM,YAAY,YAAA,CAAa,OAAA;AAC/B,IAAA,MAAM,oBAAoB,SAAA,CAAU,gBAAA;AAAA,MAClC;AAAA,KACF;AAEA,IAAA,IAAI,iBAAA,CAAkB,WAAW,CAAA,EAAG;AAEpC,IAAA,MAAM,YAAA,GAAe,kBAAkB,CAAC,CAAA;AACxC,IAAA,MAAM,WAAA,GAAc,iBAAA,CAAkB,iBAAA,CAAkB,MAAA,GAAS,CAAC,CAAA;AAGlE,IAAA,YAAA,CAAa,KAAA,EAAM;AAEnB,IAAA,MAAM,aAAA,GAAgB,CAAC,KAAA,KAAoC;AACzD,MAAA,IAAI,KAAA,CAAM,QAAQ,KAAA,EAAO;AAEzB,MAAA,IAAI,MAAM,QAAA,EAAU;AAElB,QAAA,IAAI,QAAA,CAAS,kBAAkB,YAAA,EAAc;AAC3C,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,WAAA,CAAY,KAAA,EAAM;AAAA,QACpB;AAAA,MACF,CAAA,MAAO;AAEL,QAAA,IAAI,QAAA,CAAS,kBAAkB,WAAA,EAAa;AAC1C,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,YAAA,CAAa,KAAA,EAAM;AAAA,QACrB;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,SAAA,CAAU,gBAAA,CAAiB,WAAW,aAAa,CAAA;AACnD,IAAA,OAAO,MAAM,SAAA,CAAU,mBAAA,CAAoB,SAAA,EAAW,aAAa,CAAA;AAAA,EACrE,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,OAAO,YAAA;AACT","file":"chunk-4SMSH4OY.js","sourcesContent":["import { useEffect, useRef, type RefObject } from 'react';\nimport { isStorybookDocsMode } from '../utils/environment';\n\n/**\n * Hook that traps focus within a container element.\n * Essential for accessibility in modals and dialogs.\n *\n * @param enabled - Whether focus trapping is active\n * @returns ref to attach to the container element\n *\n * @example\n * ```tsx\n * function Modal({ isOpen }: { isOpen: boolean }) {\n * const containerRef = useFocusTrap<HTMLDivElement>(isOpen);\n * return (\n * <div ref={containerRef} role=\"dialog\" aria-modal=\"true\">\n * <button>First focusable</button>\n * <button>Last focusable</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFocusTrap<T extends HTMLElement>(\n enabled = true\n): RefObject<T | null> {\n const containerRef = useRef<T>(null);\n\n useEffect(() => {\n // Skip focus trap in Storybook docs mode to prevent auto-scroll\n if (!enabled || !containerRef.current || isStorybookDocsMode()) return;\n\n const container = containerRef.current;\n const focusableElements = container.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n );\n\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n // Focus the first element when trap is enabled\n firstElement.focus();\n\n const handleKeyDown = (event: globalThis.KeyboardEvent) => {\n if (event.key !== 'Tab') return;\n\n if (event.shiftKey) {\n // Shift + Tab\n if (document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n }\n } else {\n // Tab\n if (document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }\n };\n\n container.addEventListener('keydown', handleKeyDown);\n return () => container.removeEventListener('keydown', handleKeyDown);\n }, [enabled]);\n\n return containerRef;\n}\n"]}
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkOR5DRJCW_cjs = require('./chunk-OR5DRJCW.cjs');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var classVarianceAuthority = require('class-variance-authority');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
function _interopNamespace(e) {
|
|
9
|
+
if (e && e.__esModule) return e;
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
27
|
+
|
|
28
|
+
function MicIcon({ className }) {
|
|
29
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
30
|
+
"svg",
|
|
31
|
+
{
|
|
32
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
33
|
+
viewBox: "0 0 24 24",
|
|
34
|
+
fill: "none",
|
|
35
|
+
stroke: "currentColor",
|
|
36
|
+
strokeWidth: "2",
|
|
37
|
+
strokeLinecap: "round",
|
|
38
|
+
strokeLinejoin: "round",
|
|
39
|
+
className,
|
|
40
|
+
"aria-hidden": "true",
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
|
|
43
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
|
|
44
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
function MicOffIcon({ className }) {
|
|
50
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51
|
+
"svg",
|
|
52
|
+
{
|
|
53
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
54
|
+
viewBox: "0 0 24 24",
|
|
55
|
+
fill: "none",
|
|
56
|
+
stroke: "currentColor",
|
|
57
|
+
strokeWidth: "2",
|
|
58
|
+
strokeLinecap: "round",
|
|
59
|
+
strokeLinejoin: "round",
|
|
60
|
+
className,
|
|
61
|
+
"aria-hidden": "true",
|
|
62
|
+
children: [
|
|
63
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "2", x2: "22", y1: "2", y2: "22" }),
|
|
64
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18.89 13.23A7.12 7.12 0 0 0 19 12v-2" }),
|
|
65
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 10v2a7 7 0 0 0 12 5" }),
|
|
66
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33" }),
|
|
67
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }),
|
|
68
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
function StopIcon({ className }) {
|
|
74
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
75
|
+
"svg",
|
|
76
|
+
{
|
|
77
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
78
|
+
viewBox: "0 0 24 24",
|
|
79
|
+
fill: "currentColor",
|
|
80
|
+
className,
|
|
81
|
+
"aria-hidden": "true",
|
|
82
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" })
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
function CheckIcon({ className }) {
|
|
87
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
88
|
+
"svg",
|
|
89
|
+
{
|
|
90
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
91
|
+
viewBox: "0 0 24 24",
|
|
92
|
+
fill: "none",
|
|
93
|
+
stroke: "currentColor",
|
|
94
|
+
strokeWidth: "2.5",
|
|
95
|
+
strokeLinecap: "round",
|
|
96
|
+
strokeLinejoin: "round",
|
|
97
|
+
className,
|
|
98
|
+
"aria-hidden": "true",
|
|
99
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" })
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
function LoadingSpinner({ className }) {
|
|
104
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
105
|
+
"svg",
|
|
106
|
+
{
|
|
107
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
108
|
+
viewBox: "0 0 24 24",
|
|
109
|
+
fill: "none",
|
|
110
|
+
stroke: "currentColor",
|
|
111
|
+
strokeWidth: "2",
|
|
112
|
+
strokeLinecap: "round",
|
|
113
|
+
strokeLinejoin: "round",
|
|
114
|
+
className: chunkOR5DRJCW_cjs.cn("animate-spin", className),
|
|
115
|
+
"aria-hidden": "true",
|
|
116
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
function PulseRings({ variant }) {
|
|
121
|
+
const ringColor = variant === "minimal" ? "bg-red-500/30" : "bg-red-400/40";
|
|
122
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
123
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
124
|
+
"span",
|
|
125
|
+
{
|
|
126
|
+
className: chunkOR5DRJCW_cjs.cn("absolute inset-0 animate-ping rounded-full", ringColor),
|
|
127
|
+
style: { animationDuration: "1.5s" }
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
131
|
+
"span",
|
|
132
|
+
{
|
|
133
|
+
className: chunkOR5DRJCW_cjs.cn("absolute inset-0 animate-ping rounded-full", ringColor),
|
|
134
|
+
style: { animationDuration: "1.5s", animationDelay: "0.5s" }
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
] });
|
|
138
|
+
}
|
|
139
|
+
function WaveformBars({ size }) {
|
|
140
|
+
const barHeight = size === "sm" ? "h-2" : size === "md" ? "h-3" : "h-4";
|
|
141
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-0.5", children: [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
142
|
+
"span",
|
|
143
|
+
{
|
|
144
|
+
className: chunkOR5DRJCW_cjs.cn(
|
|
145
|
+
"animate-waveform w-0.5 rounded-full bg-current",
|
|
146
|
+
barHeight
|
|
147
|
+
),
|
|
148
|
+
style: {
|
|
149
|
+
animationDelay: `${i * 0.1}s`
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
i
|
|
153
|
+
)) });
|
|
154
|
+
}
|
|
155
|
+
var recordButtonVariants = classVarianceAuthority.cva(
|
|
156
|
+
[
|
|
157
|
+
"relative inline-flex items-center justify-center rounded-full",
|
|
158
|
+
"transition-all duration-200",
|
|
159
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
160
|
+
],
|
|
161
|
+
{
|
|
162
|
+
variants: {
|
|
163
|
+
variant: {
|
|
164
|
+
default: "",
|
|
165
|
+
outline: "border-2",
|
|
166
|
+
ghost: "",
|
|
167
|
+
minimal: ""
|
|
168
|
+
},
|
|
169
|
+
size: {
|
|
170
|
+
sm: "size-10",
|
|
171
|
+
md: "size-12",
|
|
172
|
+
lg: "size-14"
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
defaultVariants: {
|
|
176
|
+
variant: "default",
|
|
177
|
+
size: "md"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
var iconSizes = {
|
|
182
|
+
sm: "size-4",
|
|
183
|
+
md: "size-5",
|
|
184
|
+
lg: "size-6"
|
|
185
|
+
};
|
|
186
|
+
function getStateStyles(state, variant) {
|
|
187
|
+
const styles = {
|
|
188
|
+
default: {
|
|
189
|
+
idle: "bg-primary/10 text-primary hover:bg-primary/20",
|
|
190
|
+
recording: "bg-red-500/10 text-red-500 hover:bg-red-500/20",
|
|
191
|
+
processing: "bg-primary/10 text-primary cursor-wait",
|
|
192
|
+
disabled: "bg-muted text-muted-foreground cursor-not-allowed opacity-50",
|
|
193
|
+
error: "bg-destructive/10 text-destructive",
|
|
194
|
+
success: "bg-success/10 text-success"
|
|
195
|
+
},
|
|
196
|
+
outline: {
|
|
197
|
+
idle: "border-primary/50 text-primary bg-transparent hover:bg-primary/10 hover:border-primary",
|
|
198
|
+
recording: "border-red-500/50 text-red-500 bg-transparent hover:bg-red-500/10 hover:border-red-500",
|
|
199
|
+
processing: "border-primary/50 text-primary bg-transparent cursor-wait",
|
|
200
|
+
disabled: "border-muted text-muted-foreground bg-transparent cursor-not-allowed opacity-50",
|
|
201
|
+
error: "border-destructive/50 text-destructive bg-transparent",
|
|
202
|
+
success: "border-success/50 text-success bg-transparent"
|
|
203
|
+
},
|
|
204
|
+
ghost: {
|
|
205
|
+
idle: "text-primary hover:bg-primary/10",
|
|
206
|
+
recording: "text-red-500 hover:bg-red-500/10",
|
|
207
|
+
processing: "text-primary bg-primary/5 cursor-wait",
|
|
208
|
+
disabled: "text-muted-foreground cursor-not-allowed opacity-50",
|
|
209
|
+
error: "text-destructive",
|
|
210
|
+
success: "text-success"
|
|
211
|
+
},
|
|
212
|
+
minimal: {
|
|
213
|
+
idle: "text-primary hover:text-primary/80",
|
|
214
|
+
recording: "text-red-500 hover:text-red-500/80",
|
|
215
|
+
processing: "text-primary cursor-wait",
|
|
216
|
+
disabled: "text-muted-foreground/40 cursor-not-allowed",
|
|
217
|
+
error: "text-destructive",
|
|
218
|
+
success: "text-success"
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
return styles[variant][state];
|
|
222
|
+
}
|
|
223
|
+
function formatDuration(seconds) {
|
|
224
|
+
const mins = Math.floor(seconds / 60);
|
|
225
|
+
const secs = Math.floor(seconds % 60);
|
|
226
|
+
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
227
|
+
}
|
|
228
|
+
var RecordButton = React__namespace.forwardRef(
|
|
229
|
+
({
|
|
230
|
+
className,
|
|
231
|
+
variant = "default",
|
|
232
|
+
size = "md",
|
|
233
|
+
state: controlledState,
|
|
234
|
+
showWaveform = false,
|
|
235
|
+
showPulse = true,
|
|
236
|
+
disabled,
|
|
237
|
+
showDuration = false,
|
|
238
|
+
idleIcon,
|
|
239
|
+
recordingIcon,
|
|
240
|
+
transcriptionState,
|
|
241
|
+
showTranscriptionState = false,
|
|
242
|
+
onRecordingComplete,
|
|
243
|
+
onRecordingStart,
|
|
244
|
+
onRecordingError,
|
|
245
|
+
maxDuration = 0,
|
|
246
|
+
mimeType = "audio/webm",
|
|
247
|
+
onClick,
|
|
248
|
+
...props
|
|
249
|
+
}, ref) => {
|
|
250
|
+
const [internalState, setInternalState] = React__namespace.useState("idle");
|
|
251
|
+
const [duration, setDuration] = React__namespace.useState(0);
|
|
252
|
+
const mediaRecorderRef = React__namespace.useRef(null);
|
|
253
|
+
const streamRef = React__namespace.useRef(null);
|
|
254
|
+
const chunksRef = React__namespace.useRef([]);
|
|
255
|
+
const timerRef = React__namespace.useRef(void 0);
|
|
256
|
+
const startTimeRef = React__namespace.useRef(0);
|
|
257
|
+
const timeoutsRef = React__namespace.useRef([]);
|
|
258
|
+
const addTimeout = (callback, delay) => {
|
|
259
|
+
const id = setTimeout(() => {
|
|
260
|
+
callback();
|
|
261
|
+
timeoutsRef.current = timeoutsRef.current.filter((t) => t !== id);
|
|
262
|
+
}, delay);
|
|
263
|
+
timeoutsRef.current.push(id);
|
|
264
|
+
return id;
|
|
265
|
+
};
|
|
266
|
+
const clearAllTimeouts = () => {
|
|
267
|
+
timeoutsRef.current.forEach(clearTimeout);
|
|
268
|
+
timeoutsRef.current = [];
|
|
269
|
+
};
|
|
270
|
+
const isControlled = controlledState !== void 0;
|
|
271
|
+
const currentState = isControlled ? controlledState : internalState;
|
|
272
|
+
const effectiveState = disabled ? "disabled" : transcriptionState === "error" ? "error" : transcriptionState === "transcribing" || transcriptionState === "streaming" ? "processing" : transcriptionState === "complete" ? "success" : currentState;
|
|
273
|
+
React__namespace.useEffect(() => {
|
|
274
|
+
if (typeof window === "undefined") return;
|
|
275
|
+
if (disabled && (controlledState && controlledState !== "disabled" || transcriptionState)) {
|
|
276
|
+
console.warn(
|
|
277
|
+
"[RecordButton]: `disabled` prop takes precedence over both `state` and `transcriptionState`. When `disabled` is true, the button will always appear disabled."
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
if (controlledState !== void 0 && transcriptionState !== void 0) {
|
|
281
|
+
const mappedTranscriptionState = transcriptionState === "error" ? "error" : transcriptionState === "transcribing" || transcriptionState === "streaming" ? "processing" : transcriptionState === "complete" ? "success" : void 0;
|
|
282
|
+
if (mappedTranscriptionState !== void 0 && mappedTranscriptionState !== controlledState) {
|
|
283
|
+
console.warn(
|
|
284
|
+
`[RecordButton]: \`transcriptionState\` takes precedence over \`state\`. Received state="${controlledState}" and transcriptionState="${transcriptionState}". This may lead to unexpected visual states.`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}, [disabled, controlledState, transcriptionState]);
|
|
289
|
+
const iconSize = iconSizes[size];
|
|
290
|
+
const isRecording = effectiveState === "recording";
|
|
291
|
+
const isDisabled = effectiveState === "disabled" || effectiveState === "processing";
|
|
292
|
+
React__namespace.useEffect(() => {
|
|
293
|
+
return () => {
|
|
294
|
+
if (timerRef.current) {
|
|
295
|
+
clearInterval(timerRef.current);
|
|
296
|
+
}
|
|
297
|
+
clearAllTimeouts();
|
|
298
|
+
if (streamRef.current) {
|
|
299
|
+
streamRef.current.getTracks().forEach((track) => track.stop());
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}, []);
|
|
303
|
+
const stopRecording = React__namespace.useCallback(() => {
|
|
304
|
+
if (timerRef.current) {
|
|
305
|
+
clearInterval(timerRef.current);
|
|
306
|
+
}
|
|
307
|
+
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
|
|
308
|
+
mediaRecorderRef.current.stop();
|
|
309
|
+
}
|
|
310
|
+
if (streamRef.current) {
|
|
311
|
+
streamRef.current.getTracks().forEach((track) => track.stop());
|
|
312
|
+
}
|
|
313
|
+
}, []);
|
|
314
|
+
const startRecording = React__namespace.useCallback(async () => {
|
|
315
|
+
if (disabled || effectiveState === "recording" || effectiveState === "processing")
|
|
316
|
+
return;
|
|
317
|
+
try {
|
|
318
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
319
|
+
audio: true
|
|
320
|
+
});
|
|
321
|
+
streamRef.current = stream;
|
|
322
|
+
const options = { mimeType };
|
|
323
|
+
if (!MediaRecorder.isTypeSupported(mimeType)) {
|
|
324
|
+
mediaRecorderRef.current = new MediaRecorder(stream);
|
|
325
|
+
} else {
|
|
326
|
+
mediaRecorderRef.current = new MediaRecorder(stream, options);
|
|
327
|
+
}
|
|
328
|
+
chunksRef.current = [];
|
|
329
|
+
mediaRecorderRef.current.ondataavailable = (e) => {
|
|
330
|
+
if (e.data.size > 0) {
|
|
331
|
+
chunksRef.current.push(e.data);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
mediaRecorderRef.current.onstop = () => {
|
|
335
|
+
if (!isControlled) {
|
|
336
|
+
setInternalState("processing");
|
|
337
|
+
}
|
|
338
|
+
const blob = new Blob(chunksRef.current, { type: mimeType });
|
|
339
|
+
const finalDuration = duration;
|
|
340
|
+
addTimeout(() => {
|
|
341
|
+
onRecordingComplete?.(blob, finalDuration);
|
|
342
|
+
if (!isControlled) {
|
|
343
|
+
setInternalState("success");
|
|
344
|
+
addTimeout(() => {
|
|
345
|
+
setInternalState("idle");
|
|
346
|
+
}, 1500);
|
|
347
|
+
}
|
|
348
|
+
setDuration(0);
|
|
349
|
+
}, 200);
|
|
350
|
+
};
|
|
351
|
+
mediaRecorderRef.current.start(100);
|
|
352
|
+
startTimeRef.current = Date.now();
|
|
353
|
+
if (!isControlled) {
|
|
354
|
+
setInternalState("recording");
|
|
355
|
+
}
|
|
356
|
+
onRecordingStart?.();
|
|
357
|
+
timerRef.current = window.setInterval(() => {
|
|
358
|
+
const elapsed = (Date.now() - startTimeRef.current) / 1e3;
|
|
359
|
+
setDuration(elapsed);
|
|
360
|
+
if (maxDuration > 0 && elapsed >= maxDuration) {
|
|
361
|
+
stopRecording();
|
|
362
|
+
}
|
|
363
|
+
}, 100);
|
|
364
|
+
} catch (error) {
|
|
365
|
+
onRecordingError?.(error);
|
|
366
|
+
if (!isControlled) {
|
|
367
|
+
setInternalState("error");
|
|
368
|
+
addTimeout(() => {
|
|
369
|
+
setInternalState("idle");
|
|
370
|
+
}, 2e3);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}, [
|
|
374
|
+
disabled,
|
|
375
|
+
effectiveState,
|
|
376
|
+
isControlled,
|
|
377
|
+
mimeType,
|
|
378
|
+
maxDuration,
|
|
379
|
+
duration,
|
|
380
|
+
onRecordingComplete,
|
|
381
|
+
onRecordingStart,
|
|
382
|
+
onRecordingError,
|
|
383
|
+
stopRecording
|
|
384
|
+
]);
|
|
385
|
+
const handleClick = React__namespace.useCallback(
|
|
386
|
+
(e) => {
|
|
387
|
+
onClick?.(e);
|
|
388
|
+
if (!isControlled) {
|
|
389
|
+
if (effectiveState === "recording") {
|
|
390
|
+
stopRecording();
|
|
391
|
+
} else if (effectiveState === "idle") {
|
|
392
|
+
startRecording();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
[onClick, isControlled, effectiveState, startRecording, stopRecording]
|
|
397
|
+
);
|
|
398
|
+
const renderIcon = () => {
|
|
399
|
+
switch (effectiveState) {
|
|
400
|
+
case "recording":
|
|
401
|
+
if (showWaveform) {
|
|
402
|
+
return /* @__PURE__ */ jsxRuntime.jsx(WaveformBars, { size });
|
|
403
|
+
}
|
|
404
|
+
return recordingIcon || /* @__PURE__ */ jsxRuntime.jsx(StopIcon, { className: iconSize });
|
|
405
|
+
case "processing":
|
|
406
|
+
return /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { className: iconSize });
|
|
407
|
+
case "disabled":
|
|
408
|
+
case "error":
|
|
409
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MicOffIcon, { className: iconSize });
|
|
410
|
+
case "success":
|
|
411
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: iconSize });
|
|
412
|
+
default:
|
|
413
|
+
return idleIcon || /* @__PURE__ */ jsxRuntime.jsx(MicIcon, { className: iconSize });
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
const getAriaLabel = () => {
|
|
417
|
+
switch (effectiveState) {
|
|
418
|
+
case "recording":
|
|
419
|
+
return "Stop recording";
|
|
420
|
+
case "processing":
|
|
421
|
+
return "Processing recording";
|
|
422
|
+
case "disabled":
|
|
423
|
+
return "Recording unavailable";
|
|
424
|
+
case "error":
|
|
425
|
+
return "Recording failed";
|
|
426
|
+
case "success":
|
|
427
|
+
return "Recording complete";
|
|
428
|
+
default:
|
|
429
|
+
return "Start recording";
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
const getTranscriptionLabel = () => {
|
|
433
|
+
if (transcriptionState === "streaming") return "Listening...";
|
|
434
|
+
if (transcriptionState === "transcribing") return "Transcribing...";
|
|
435
|
+
return null;
|
|
436
|
+
};
|
|
437
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative inline-flex items-center gap-2", children: [
|
|
438
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
439
|
+
"button",
|
|
440
|
+
{
|
|
441
|
+
ref,
|
|
442
|
+
type: "button",
|
|
443
|
+
disabled: isDisabled,
|
|
444
|
+
onClick: handleClick,
|
|
445
|
+
...props,
|
|
446
|
+
className: chunkOR5DRJCW_cjs.cn(
|
|
447
|
+
recordButtonVariants({ variant, size }),
|
|
448
|
+
getStateStyles(effectiveState, variant),
|
|
449
|
+
className
|
|
450
|
+
),
|
|
451
|
+
"aria-label": getAriaLabel(),
|
|
452
|
+
"aria-pressed": effectiveState === "recording" ? true : void 0,
|
|
453
|
+
"aria-busy": effectiveState === "processing" ? true : void 0,
|
|
454
|
+
children: [
|
|
455
|
+
effectiveState === "recording" && showPulse && /* @__PURE__ */ jsxRuntime.jsx(PulseRings, { variant }),
|
|
456
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10", children: renderIcon() })
|
|
457
|
+
]
|
|
458
|
+
}
|
|
459
|
+
),
|
|
460
|
+
showDuration && isRecording && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive font-mono text-xs tabular-nums", children: formatDuration(duration) }),
|
|
461
|
+
showTranscriptionState && getTranscriptionLabel() && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary text-xs font-medium", children: getTranscriptionLabel() })
|
|
462
|
+
] });
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
RecordButton.displayName = "RecordButton";
|
|
466
|
+
|
|
467
|
+
exports.RecordButton = RecordButton;
|
|
468
|
+
exports.formatDuration = formatDuration;
|
|
469
|
+
exports.recordButtonVariants = recordButtonVariants;
|
|
470
|
+
//# sourceMappingURL=chunk-5T3AWNHG.cjs.map
|
|
471
|
+
//# sourceMappingURL=chunk-5T3AWNHG.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/RecordButton/RecordButton.tsx"],"names":["jsxs","jsx","cn","Fragment","cva","React"],"mappings":"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","file":"chunk-5T3AWNHG.cjs","sourcesContent":["import * as React from 'react';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type RecordButtonState =\n | 'idle'\n | 'recording'\n | 'processing'\n | 'disabled'\n | 'error'\n | 'success';\n\nexport type RecordButtonVariant = 'default' | 'outline' | 'ghost' | 'minimal';\nexport type RecordButtonSize = 'sm' | 'md' | 'lg';\n\n/** Transcription state for integration with transcription services */\nexport type TranscriptionState =\n | 'idle'\n | 'recording'\n | 'transcribing'\n | 'streaming'\n | 'complete'\n | 'error';\n\nexport interface TranscriptionResult {\n /** The transcribed text */\n text: string;\n /** Whether this is a partial (streaming) or final result */\n isFinal: boolean;\n /** Confidence score (0-1) if available */\n confidence?: number;\n}\n\nexport interface RecordButtonProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'children'\n> {\n /** Current state of the button */\n state?: RecordButtonState;\n /** Size of the button */\n size?: RecordButtonSize;\n /** Visual style variant */\n variant?: RecordButtonVariant;\n /** Show waveform bars when recording (instead of stop icon) */\n showWaveform?: boolean;\n /** Show pulse rings when recording */\n showPulse?: boolean;\n /** Show recording duration while recording */\n showDuration?: boolean;\n /** Custom idle icon */\n idleIcon?: React.ReactNode;\n /** Custom recording icon */\n recordingIcon?: React.ReactNode;\n /** Current transcription state (for external control) */\n transcriptionState?: TranscriptionState;\n /** Show transcription state indicator */\n showTranscriptionState?: boolean;\n\n // Recording callbacks (for uncontrolled usage)\n /** Callback when recording is complete with the audio blob */\n onRecordingComplete?: (blob: Blob, duration: number) => void;\n /** Callback when recording starts */\n onRecordingStart?: () => void;\n /** Callback when a recording error occurs */\n onRecordingError?: (error: Error) => void;\n /** Maximum recording duration in seconds (0 for unlimited) */\n maxDuration?: number;\n /** Audio MIME type */\n mimeType?: string;\n}\n\n// ============================================================================\n// Icons\n// ============================================================================\n\nfunction MicIcon({ className }: { className?: string }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\" />\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" />\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\" />\n </svg>\n );\n}\n\nfunction MicOffIcon({ className }: { className?: string }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\" />\n <path d=\"M18.89 13.23A7.12 7.12 0 0 0 19 12v-2\" />\n <path d=\"M5 10v2a7 7 0 0 0 12 5\" />\n <path d=\"M15 9.34V5a3 3 0 0 0-5.68-1.33\" />\n <path d=\"M9 9v3a3 3 0 0 0 5.12 2.12\" />\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\" />\n </svg>\n );\n}\n\nfunction StopIcon({ className }: { className?: string }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className={className}\n aria-hidden=\"true\"\n >\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\" />\n </svg>\n );\n}\n\nfunction CheckIcon({ className }: { className?: string }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n );\n}\n\nfunction LoadingSpinner({ className }: { className?: string }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn('animate-spin', className)}\n aria-hidden=\"true\"\n >\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n );\n}\n\n// ============================================================================\n// Pulse Ring Animation (for recording state)\n// ============================================================================\n\nfunction PulseRings({ variant }: { variant: RecordButtonVariant }) {\n const ringColor = variant === 'minimal' ? 'bg-red-500/30' : 'bg-red-400/40';\n\n return (\n <>\n <span\n className={cn('absolute inset-0 animate-ping rounded-full', ringColor)}\n style={{ animationDuration: '1.5s' }}\n />\n <span\n className={cn('absolute inset-0 animate-ping rounded-full', ringColor)}\n style={{ animationDuration: '1.5s', animationDelay: '0.5s' }}\n />\n </>\n );\n}\n\n// ============================================================================\n// Waveform Animation (for recording state)\n// ============================================================================\n\nfunction WaveformBars({ size }: { size: RecordButtonSize }) {\n const barHeight = size === 'sm' ? 'h-2' : size === 'md' ? 'h-3' : 'h-4';\n\n return (\n <div className=\"flex items-center gap-0.5\">\n {[0, 1, 2, 3, 4].map((i) => (\n <span\n key={i}\n className={cn(\n 'animate-waveform w-0.5 rounded-full bg-current',\n barHeight\n )}\n style={{\n animationDelay: `${i * 0.1}s`,\n }}\n />\n ))}\n </div>\n );\n}\n\n// ============================================================================\n// Style Variants\n// ============================================================================\n\nconst recordButtonVariants = cva(\n [\n 'relative inline-flex items-center justify-center rounded-full',\n 'transition-all duration-200',\n 'outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n ],\n {\n variants: {\n variant: {\n default: '',\n outline: 'border-2',\n ghost: '',\n minimal: '',\n },\n size: {\n sm: 'size-10',\n md: 'size-12',\n lg: 'size-14',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n },\n }\n);\n\nconst iconSizes: Record<RecordButtonSize, string> = {\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n};\n\n// ============================================================================\n// State Styles\n// ============================================================================\n\nfunction getStateStyles(\n state: RecordButtonState,\n variant: RecordButtonVariant\n): string {\n const styles: Record<\n RecordButtonVariant,\n Record<RecordButtonState, string>\n > = {\n default: {\n idle: 'bg-primary/10 text-primary hover:bg-primary/20',\n recording: 'bg-red-500/10 text-red-500 hover:bg-red-500/20',\n processing: 'bg-primary/10 text-primary cursor-wait',\n disabled: 'bg-muted text-muted-foreground cursor-not-allowed opacity-50',\n error: 'bg-destructive/10 text-destructive',\n success: 'bg-success/10 text-success',\n },\n outline: {\n idle: 'border-primary/50 text-primary bg-transparent hover:bg-primary/10 hover:border-primary',\n recording:\n 'border-red-500/50 text-red-500 bg-transparent hover:bg-red-500/10 hover:border-red-500',\n processing: 'border-primary/50 text-primary bg-transparent cursor-wait',\n disabled:\n 'border-muted text-muted-foreground bg-transparent cursor-not-allowed opacity-50',\n error: 'border-destructive/50 text-destructive bg-transparent',\n success: 'border-success/50 text-success bg-transparent',\n },\n ghost: {\n idle: 'text-primary hover:bg-primary/10',\n recording: 'text-red-500 hover:bg-red-500/10',\n processing: 'text-primary bg-primary/5 cursor-wait',\n disabled: 'text-muted-foreground cursor-not-allowed opacity-50',\n error: 'text-destructive',\n success: 'text-success',\n },\n minimal: {\n idle: 'text-primary hover:text-primary/80',\n recording: 'text-red-500 hover:text-red-500/80',\n processing: 'text-primary cursor-wait',\n disabled: 'text-muted-foreground/40 cursor-not-allowed',\n error: 'text-destructive',\n success: 'text-success',\n },\n };\n\n return styles[variant][state];\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nfunction formatDuration(seconds: number): string {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\n/**\n * A voice recording button with 6 states and 4 visual variants.\n * Supports pulse animations, waveform visualization, and transcription integration.\n *\n * ## Controlled vs Uncontrolled Mode\n *\n * **Uncontrolled mode** (default): The component manages its own recording state.\n * Use `onRecordingComplete`, `onRecordingStart`, and `onRecordingError` callbacks.\n *\n * **Controlled mode**: When the `state` prop is provided, the component becomes\n * controlled and you must manage state changes externally. Note: In controlled mode,\n * the internal MediaRecorder functionality is disabled - you must implement your own\n * recording logic.\n *\n * ## State Precedence\n *\n * When multiple state-controlling props are provided, they follow this precedence:\n * 1. `disabled` prop (highest priority)\n * 2. `transcriptionState` prop\n * 3. `state` prop\n * 4. Internal state (uncontrolled)\n *\n * @example\n * ```tsx\n * // Uncontrolled with recording callbacks\n * <RecordButton\n * onRecordingComplete={(blob, duration) => console.log('Recorded:', blob)}\n * onRecordingError={(error) => console.error('Recording failed:', error)}\n * />\n *\n * // Controlled state (requires external recording implementation)\n * <RecordButton state=\"idle\" onClick={handleClick} />\n *\n * // Different variants\n * <RecordButton variant=\"outline\" size=\"lg\" />\n *\n * // With waveform animation\n * <RecordButton state=\"recording\" showWaveform showPulse />\n * ```\n */\nconst RecordButton = React.forwardRef<HTMLButtonElement, RecordButtonProps>(\n (\n {\n className,\n variant = 'default',\n size = 'md',\n state: controlledState,\n showWaveform = false,\n showPulse = true,\n disabled,\n showDuration = false,\n idleIcon,\n recordingIcon,\n transcriptionState,\n showTranscriptionState = false,\n onRecordingComplete,\n onRecordingStart,\n onRecordingError,\n maxDuration = 0,\n mimeType = 'audio/webm',\n onClick,\n ...props\n },\n ref\n ) => {\n // Internal state for uncontrolled usage\n const [internalState, setInternalState] =\n React.useState<RecordButtonState>('idle');\n const [duration, setDuration] = React.useState(0);\n\n const mediaRecorderRef = React.useRef<MediaRecorder | null>(null);\n const streamRef = React.useRef<MediaStream | null>(null);\n const chunksRef = React.useRef<Blob[]>([]);\n const timerRef = React.useRef<number | undefined>(undefined);\n const startTimeRef = React.useRef<number>(0);\n const timeoutsRef = React.useRef<NodeJS.Timeout[]>([]);\n\n // Helper to track and manage timeouts\n const addTimeout = (callback: () => void, delay: number) => {\n const id = setTimeout(() => {\n callback();\n // Remove from tracking after execution\n timeoutsRef.current = timeoutsRef.current.filter((t) => t !== id);\n }, delay);\n timeoutsRef.current.push(id);\n return id;\n };\n\n const clearAllTimeouts = () => {\n timeoutsRef.current.forEach(clearTimeout);\n timeoutsRef.current = [];\n };\n\n // Use controlled state if provided, otherwise internal state\n const isControlled = controlledState !== undefined;\n const currentState = isControlled ? controlledState : internalState;\n\n // Map transcription state to button state if provided\n // Precedence: disabled prop → transcriptionState → state prop → internal state\n const effectiveState: RecordButtonState = disabled\n ? 'disabled'\n : transcriptionState === 'error'\n ? 'error'\n : transcriptionState === 'transcribing' ||\n transcriptionState === 'streaming'\n ? 'processing'\n : transcriptionState === 'complete'\n ? 'success'\n : currentState;\n\n // Dev mode warnings for conflicting states\n React.useEffect(() => {\n // Only warn in development\n if (typeof window === 'undefined') return;\n\n // Warn when disabled is true but other state props suggest a different visual state\n if (\n disabled &&\n ((controlledState && controlledState !== 'disabled') ||\n transcriptionState)\n ) {\n console.warn(\n '[RecordButton]: `disabled` prop takes precedence over both `state` and `transcriptionState`. ' +\n 'When `disabled` is true, the button will always appear disabled.'\n );\n }\n\n // Warn when both controlled state and transcriptionState are provided and conflict\n if (controlledState !== undefined && transcriptionState !== undefined) {\n const mappedTranscriptionState: RecordButtonState | undefined =\n transcriptionState === 'error'\n ? 'error'\n : transcriptionState === 'transcribing' ||\n transcriptionState === 'streaming'\n ? 'processing'\n : transcriptionState === 'complete'\n ? 'success'\n : undefined;\n\n if (\n mappedTranscriptionState !== undefined &&\n mappedTranscriptionState !== controlledState\n ) {\n console.warn(\n '[RecordButton]: `transcriptionState` takes precedence over `state`. ' +\n `Received state=\"${controlledState}\" and transcriptionState=\"${transcriptionState}\". ` +\n 'This may lead to unexpected visual states.'\n );\n }\n }\n }, [disabled, controlledState, transcriptionState]);\n\n const iconSize = iconSizes[size];\n const isRecording = effectiveState === 'recording';\n const isDisabled =\n effectiveState === 'disabled' || effectiveState === 'processing';\n\n // Cleanup on unmount\n React.useEffect(() => {\n return () => {\n if (timerRef.current) {\n clearInterval(timerRef.current);\n }\n clearAllTimeouts();\n if (streamRef.current) {\n streamRef.current.getTracks().forEach((track) => track.stop());\n }\n };\n }, []);\n\n const stopRecording = React.useCallback(() => {\n if (timerRef.current) {\n clearInterval(timerRef.current);\n }\n\n if (\n mediaRecorderRef.current &&\n mediaRecorderRef.current.state !== 'inactive'\n ) {\n mediaRecorderRef.current.stop();\n }\n\n if (streamRef.current) {\n streamRef.current.getTracks().forEach((track) => track.stop());\n }\n }, []);\n\n const startRecording = React.useCallback(async () => {\n // Guard: don't start if disabled, already recording, or processing\n if (\n disabled ||\n effectiveState === 'recording' ||\n effectiveState === 'processing'\n )\n return;\n\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n });\n streamRef.current = stream;\n\n const options = { mimeType };\n if (!MediaRecorder.isTypeSupported(mimeType)) {\n mediaRecorderRef.current = new MediaRecorder(stream);\n } else {\n mediaRecorderRef.current = new MediaRecorder(stream, options);\n }\n\n chunksRef.current = [];\n\n mediaRecorderRef.current.ondataavailable = (e) => {\n if (e.data.size > 0) {\n chunksRef.current.push(e.data);\n }\n };\n\n mediaRecorderRef.current.onstop = () => {\n if (!isControlled) {\n setInternalState('processing');\n }\n\n const blob = new Blob(chunksRef.current, { type: mimeType });\n const finalDuration = duration;\n\n // Small delay to show processing state\n addTimeout(() => {\n onRecordingComplete?.(blob, finalDuration);\n if (!isControlled) {\n setInternalState('success');\n // Reset to idle after showing success\n addTimeout(() => {\n setInternalState('idle');\n }, 1500);\n }\n setDuration(0);\n }, 200);\n };\n\n mediaRecorderRef.current.start(100);\n startTimeRef.current = Date.now();\n\n if (!isControlled) {\n setInternalState('recording');\n }\n onRecordingStart?.();\n\n timerRef.current = window.setInterval(() => {\n const elapsed = (Date.now() - startTimeRef.current) / 1000;\n setDuration(elapsed);\n\n if (maxDuration > 0 && elapsed >= maxDuration) {\n stopRecording();\n }\n }, 100);\n } catch (error) {\n onRecordingError?.(error as Error);\n if (!isControlled) {\n setInternalState('error');\n // Reset to idle after showing error\n addTimeout(() => {\n setInternalState('idle');\n }, 2000);\n }\n }\n }, [\n disabled,\n effectiveState,\n isControlled,\n mimeType,\n maxDuration,\n duration,\n onRecordingComplete,\n onRecordingStart,\n onRecordingError,\n stopRecording,\n ]);\n\n const handleClick = React.useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n // Call external onClick if provided\n onClick?.(e);\n\n // Handle internal recording logic only if not fully controlled\n if (!isControlled) {\n if (effectiveState === 'recording') {\n stopRecording();\n } else if (effectiveState === 'idle') {\n startRecording();\n }\n }\n },\n [onClick, isControlled, effectiveState, startRecording, stopRecording]\n );\n\n // Determine which icon to show\n const renderIcon = () => {\n switch (effectiveState) {\n case 'recording':\n if (showWaveform) {\n return <WaveformBars size={size} />;\n }\n return recordingIcon || <StopIcon className={iconSize} />;\n case 'processing':\n return <LoadingSpinner className={iconSize} />;\n case 'disabled':\n case 'error':\n return <MicOffIcon className={iconSize} />;\n case 'success':\n return <CheckIcon className={iconSize} />;\n default:\n return idleIcon || <MicIcon className={iconSize} />;\n }\n };\n\n const getAriaLabel = () => {\n switch (effectiveState) {\n case 'recording':\n return 'Stop recording';\n case 'processing':\n return 'Processing recording';\n case 'disabled':\n return 'Recording unavailable';\n case 'error':\n return 'Recording failed';\n case 'success':\n return 'Recording complete';\n default:\n return 'Start recording';\n }\n };\n\n const getTranscriptionLabel = () => {\n if (transcriptionState === 'streaming') return 'Listening...';\n if (transcriptionState === 'transcribing') return 'Transcribing...';\n return null;\n };\n\n return (\n <div className=\"relative inline-flex items-center gap-2\">\n <button\n ref={ref}\n type=\"button\"\n disabled={isDisabled}\n onClick={handleClick}\n {...props}\n className={cn(\n recordButtonVariants({ variant, size }),\n getStateStyles(effectiveState, variant),\n className\n )}\n aria-label={getAriaLabel()}\n aria-pressed={effectiveState === 'recording' ? true : undefined}\n aria-busy={effectiveState === 'processing' ? true : undefined}\n >\n {/* Pulse animation for recording state */}\n {effectiveState === 'recording' && showPulse && (\n <PulseRings variant={variant} />\n )}\n\n {/* Icon */}\n <span className=\"relative z-10\">{renderIcon()}</span>\n </button>\n\n {/* Duration display */}\n {showDuration && isRecording && (\n <span className=\"text-destructive font-mono text-xs tabular-nums\">\n {formatDuration(duration)}\n </span>\n )}\n\n {/* Transcription state label */}\n {showTranscriptionState && getTranscriptionLabel() && (\n <span className=\"text-primary text-xs font-medium\">\n {getTranscriptionLabel()}\n </span>\n )}\n </div>\n );\n }\n);\n\nRecordButton.displayName = 'RecordButton';\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { RecordButton, recordButtonVariants, formatDuration };\n"]}
|