@exode-team/react-recorder 1.1.14 → 1.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/components/AudioRecorder.js +50 -0
  2. package/dist/components/AudioRecorder.js.map +1 -0
  3. package/dist/components/AudioRecorder.messages.js +47 -0
  4. package/dist/components/AudioRecorder.messages.js.map +1 -0
  5. package/dist/components/AudioRecorder.styled.js +16 -0
  6. package/dist/components/AudioRecorder.styled.js.map +1 -0
  7. package/dist/components/IdleControl/IdleControl.js +12 -0
  8. package/dist/components/IdleControl/IdleControl.js.map +1 -0
  9. package/dist/components/IdleControl/IdleControl.styled.js +68 -0
  10. package/dist/components/IdleControl/IdleControl.styled.js.map +1 -0
  11. package/dist/components/RecordingShell/RecordingShell.js +80 -0
  12. package/dist/components/RecordingShell/RecordingShell.js.map +1 -0
  13. package/dist/components/RecordingShell/RecordingShell.styled.js +159 -0
  14. package/dist/components/RecordingShell/RecordingShell.styled.js.map +1 -0
  15. package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.js +151 -0
  16. package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.js.map +1 -0
  17. package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.styled.js +92 -0
  18. package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.styled.js.map +1 -0
  19. package/dist/components/VoiceRecorderInput/PreviewBars.js +52 -0
  20. package/dist/components/VoiceRecorderInput/PreviewBars.js.map +1 -0
  21. package/dist/components/VoiceRecorderInput/TimelineBars.js +31 -0
  22. package/dist/components/VoiceRecorderInput/TimelineBars.js.map +1 -0
  23. package/dist/components/VoiceRecorderInput/VoiceRecorderInput.js +51 -0
  24. package/dist/components/VoiceRecorderInput/VoiceRecorderInput.js.map +1 -0
  25. package/dist/components/VoiceRecorderInput/VoiceRecorderInput.styled.js +175 -0
  26. package/dist/components/VoiceRecorderInput/VoiceRecorderInput.styled.js.map +1 -0
  27. package/dist/components/WaveformBars/WaveformBars.js +29 -0
  28. package/dist/components/WaveformBars/WaveformBars.js.map +1 -0
  29. package/dist/components/WaveformBars/WaveformBars.styled.js +20 -0
  30. package/dist/components/WaveformBars/WaveformBars.styled.js.map +1 -0
  31. package/dist/constants.js +17 -0
  32. package/dist/constants.js.map +1 -0
  33. package/dist/hooks/useAudioBlobLoader.js +175 -0
  34. package/dist/hooks/useAudioBlobLoader.js.map +1 -0
  35. package/dist/hooks/useAudioPlayback.js +119 -0
  36. package/dist/hooks/useAudioPlayback.js.map +1 -0
  37. package/dist/hooks/useAudioRecorderHandlers.js +137 -0
  38. package/dist/hooks/useAudioRecorderHandlers.js.map +1 -0
  39. package/dist/hooks/useMediaRecorder.js +127 -0
  40. package/dist/hooks/useMediaRecorder.js.map +1 -0
  41. package/dist/hooks/useRecorderVisualization.js +94 -0
  42. package/dist/hooks/useRecorderVisualization.js.map +1 -0
  43. package/dist/hooks/useRecordingSender.js +53 -0
  44. package/dist/hooks/useRecordingSender.js.map +1 -0
  45. package/dist/hooks/useRecordingTiming.js +93 -0
  46. package/dist/hooks/useRecordingTiming.js.map +1 -0
  47. package/dist/hooks/useVoiceMessagePlayer.js +177 -0
  48. package/dist/hooks/useVoiceMessagePlayer.js.map +1 -0
  49. package/dist/hooks/useVoiceRecorder.js +153 -0
  50. package/dist/hooks/useVoiceRecorder.js.map +1 -0
  51. package/dist/hooks/useVoiceRecorderInput.js +163 -0
  52. package/dist/hooks/useVoiceRecorderInput.js.map +1 -0
  53. package/dist/index.js +13 -2659
  54. package/dist/index.js.map +1 -1
  55. package/dist/platform/animation-frame-adapter.js +14 -0
  56. package/dist/platform/animation-frame-adapter.js.map +1 -0
  57. package/dist/platform/audio-analyzer-adapter.js +74 -0
  58. package/dist/platform/audio-analyzer-adapter.js.map +1 -0
  59. package/dist/platform/audio-blob-fetcher.js +19 -0
  60. package/dist/platform/audio-blob-fetcher.js.map +1 -0
  61. package/dist/platform/audio-playback-adapter.js +114 -0
  62. package/dist/platform/audio-playback-adapter.js.map +1 -0
  63. package/dist/platform/audio-playback-manager.js +26 -0
  64. package/dist/platform/audio-playback-manager.js.map +1 -0
  65. package/dist/platform/media-recorder-adapter.js +23 -0
  66. package/dist/platform/media-recorder-adapter.js.map +1 -0
  67. package/dist/platform/microphone.js +24 -0
  68. package/dist/platform/microphone.js.map +1 -0
  69. package/dist/platform/mpeg-frame.js +60 -0
  70. package/dist/platform/mpeg-frame.js.map +1 -0
  71. package/dist/utils/format.js +13 -0
  72. package/dist/utils/format.js.map +1 -0
  73. package/dist/utils/math.js +16 -0
  74. package/dist/utils/math.js.map +1 -0
  75. package/dist/utils/pseudo-waveform.js +40 -0
  76. package/dist/utils/pseudo-waveform.js.map +1 -0
  77. package/dist/utils/voice-blob-cache.js +35 -0
  78. package/dist/utils/voice-blob-cache.js.map +1 -0
  79. package/dist/utils/voice-playback-chain.js +42 -0
  80. package/dist/utils/voice-playback-chain.js.map +1 -0
  81. package/package.json +2 -1
@@ -0,0 +1,50 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useMemo, useState, useCallback } from 'react';
3
+ import { useVoiceRecorder } from '../hooks/useVoiceRecorder.js';
4
+ import { useAudioRecorderHandlers } from '../hooks/useAudioRecorderHandlers.js';
5
+ import { Container } from './AudioRecorder.styled.js';
6
+ import { IdleControl } from './IdleControl/IdleControl.js';
7
+ import { RecordingShell } from './RecordingShell/RecordingShell.js';
8
+ import { mergeMessages } from './AudioRecorder.messages.js';
9
+
10
+ const AudioRecorder = (props) => {
11
+ const { fileName, uploadId, mimeType, className, uploader, onUploaded, onUploadError, onRecordingStop, onRecordingError, onRecordingStart, onRecordingDelete, onPermissionDenied, autoResetAfterUpload, messages: customMessages, } = props;
12
+ const messages = useMemo(() => mergeMessages(customMessages), [customMessages]);
13
+ const [resolvedUploadId] = useState(() => uploadId !== null && uploadId !== void 0 ? uploadId : (typeof crypto !== 'undefined' && 'randomUUID' in crypto
14
+ ? crypto.randomUUID()
15
+ : `upload-${Date.now()}`));
16
+ const handleRecorderError = useCallback((error) => {
17
+ onRecordingError === null || onRecordingError === void 0 ? void 0 : onRecordingError(error);
18
+ onUploadError === null || onUploadError === void 0 ? void 0 : onUploadError(error);
19
+ }, [onRecordingError, onUploadError]);
20
+ const vibrate = useCallback((pattern) => {
21
+ if (typeof navigator !== 'undefined' && 'vibrate' in navigator) {
22
+ navigator.vibrate(pattern);
23
+ }
24
+ }, []);
25
+ const recorder = useVoiceRecorder({
26
+ fileName,
27
+ mimeType,
28
+ onRecordingStop,
29
+ onRecordingStart,
30
+ onRecordingDelete,
31
+ onPermissionDenied,
32
+ autoResetAfterUpload,
33
+ uploader,
34
+ uploadId: resolvedUploadId,
35
+ onRecordingError: handleRecorderError,
36
+ onUploaded: onUploaded !== null && onUploaded !== void 0 ? onUploaded : (() => { }),
37
+ });
38
+ const { isSending, handleCancel, handleSendRequest, handleRecordToggle, handlePlaybackToggle, } = useAudioRecorderHandlers({
39
+ vibrate,
40
+ recorder,
41
+ messages,
42
+ onUploadError,
43
+ });
44
+ const { isPaused, isPlaying, formatTime, audioLevels, isRecording, lastDuration, playbackTime, hasRecording, previewLevels, recordingTime, playbackDuration, } = recorder;
45
+ const showRecorderBar = isRecording || hasRecording || isSending;
46
+ return (jsx(Container, { className: className, children: showRecorderBar ? (jsx(RecordingShell, { isPaused: isPaused, isSending: isSending, isPlaying: isPlaying, formatTime: formatTime, onCancel: handleCancel, isRecording: isRecording, audioLevels: audioLevels, onSend: handleSendRequest, playbackTime: playbackTime, lastDuration: lastDuration, hasRecording: hasRecording, previewLevels: previewLevels, recordingTime: recordingTime, playbackDuration: playbackDuration, onRecordToggle: handleRecordToggle, sendAriaLabel: messages.buttons.send, onTogglePlayback: handlePlaybackToggle, cancelAriaLabel: messages.buttons.cancel, playAriaLabel: messages.aria.playRecording, stopAriaLabel: messages.aria.stopRecording, startAriaLabel: messages.aria.startRecording, pauseRecordingAriaLabel: messages.aria.pauseRecording, resumeRecordingAriaLabel: messages.aria.resumeRecording, pausePlaybackAriaLabel: messages.aria.pausePlayback })) : (jsx(IdleControl, { onClick: handleRecordToggle, startAriaLabel: messages.aria.startRecording, instruction: messages.instruction })) }));
47
+ };
48
+
49
+ export { AudioRecorder };
50
+ //# sourceMappingURL=AudioRecorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioRecorder.js","sources":["../../src/components/AudioRecorder.tsx"],"sourcesContent":["/**\n * AudioRecorder\n *\n * @author: exode <hello@exode.ru>\n */\n\nimport React, { useCallback, useMemo, useState } from 'react';\n\nimport { useVoiceRecorder } from '../hooks';\nimport { useAudioRecorderHandlers } from '../hooks/useAudioRecorderHandlers';\n\nimport { Container } from './AudioRecorder.styled';\nimport { IdleControl } from './IdleControl/IdleControl';\nimport { RecordingShell } from './RecordingShell/RecordingShell';\n\nimport { mergeMessages, type RecorderMessagesInput } from './AudioRecorder.messages';\n\nimport {\n type StorageFileEntity,\n type UploadStrategy,\n type UseVoiceRecorderOptions,\n} from '../types';\n\n\ntype RecorderOptionOverrides = Omit<\n UseVoiceRecorderOptions<Partial<StorageFileEntity>>,\n 'uploadId' | 'onUploaded' | 'uploader'\n>;\n\ninterface Props extends Partial<RecorderOptionOverrides> {\n uploader: UploadStrategy<Partial<StorageFileEntity>>;\n messages?: RecorderMessagesInput;\n uploadId?: string;\n className?: string;\n onUploadError?: (error: unknown) => void;\n onUploaded?: (file: Partial<StorageFileEntity>, duration: number) => void | Promise<void>;\n}\n\n\nconst AudioRecorder = (props: Props) => {\n\n const {\n fileName,\n uploadId,\n mimeType,\n className,\n uploader,\n onUploaded,\n onUploadError,\n onRecordingStop,\n onRecordingError,\n onRecordingStart,\n onRecordingDelete,\n onPermissionDenied,\n autoResetAfterUpload,\n messages: customMessages,\n } = props;\n\n const messages = useMemo(() => mergeMessages(customMessages), [ customMessages ]);\n\n const [ resolvedUploadId ] = useState(\n () =>\n uploadId ??\n (typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : `upload-${Date.now()}`),\n );\n\n const handleRecorderError = useCallback(\n (error: unknown) => {\n onRecordingError?.(error);\n onUploadError?.(error);\n },\n [ onRecordingError, onUploadError ],\n );\n\n const vibrate = useCallback((pattern: number | number[]) => {\n if (typeof navigator !== 'undefined' && 'vibrate' in navigator) {\n navigator.vibrate(pattern);\n }\n }, []);\n\n const recorder = useVoiceRecorder<Partial<StorageFileEntity>>({\n fileName,\n mimeType,\n onRecordingStop,\n onRecordingStart,\n onRecordingDelete,\n onPermissionDenied,\n autoResetAfterUpload,\n uploader,\n uploadId: resolvedUploadId,\n onRecordingError: handleRecorderError,\n onUploaded: onUploaded ?? (() => {}),\n });\n\n const {\n isSending,\n handleCancel,\n handleSendRequest,\n handleRecordToggle,\n handlePlaybackToggle,\n } = useAudioRecorderHandlers({\n vibrate,\n recorder,\n messages,\n onUploadError,\n });\n\n const {\n isPaused,\n isPlaying,\n formatTime,\n audioLevels,\n isRecording,\n lastDuration,\n playbackTime,\n hasRecording,\n previewLevels,\n recordingTime,\n playbackDuration,\n } = recorder;\n\n const showRecorderBar = isRecording || hasRecording || isSending;\n\n return (\n <Container className={className}>\n {showRecorderBar ? (\n <RecordingShell isPaused={isPaused}\n isSending={isSending}\n isPlaying={isPlaying}\n formatTime={formatTime}\n onCancel={handleCancel}\n isRecording={isRecording}\n audioLevels={audioLevels}\n onSend={handleSendRequest}\n playbackTime={playbackTime}\n lastDuration={lastDuration}\n hasRecording={hasRecording}\n previewLevels={previewLevels}\n recordingTime={recordingTime}\n playbackDuration={playbackDuration}\n onRecordToggle={handleRecordToggle}\n sendAriaLabel={messages.buttons.send}\n onTogglePlayback={handlePlaybackToggle}\n cancelAriaLabel={messages.buttons.cancel}\n playAriaLabel={messages.aria.playRecording}\n stopAriaLabel={messages.aria.stopRecording}\n startAriaLabel={messages.aria.startRecording}\n pauseRecordingAriaLabel={messages.aria.pauseRecording}\n resumeRecordingAriaLabel={messages.aria.resumeRecording}\n pausePlaybackAriaLabel={messages.aria.pausePlayback}/>\n ) : (\n <IdleControl onClick={handleRecordToggle}\n startAriaLabel={messages.aria.startRecording}\n instruction={messages.instruction}/>\n )}\n </Container>\n );\n};\n\n\nexport { AudioRecorder };\n"],"names":["_jsx"],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * AudioRecorder messages — i18n-ready message definitions and merge utility
3
+ *
4
+ * @author: exode <hello@exode.ru>
5
+ */
6
+ const DEFAULT_MESSAGES = {
7
+ buttons: {
8
+ cancel: 'Cancel',
9
+ send: 'Send',
10
+ },
11
+ instruction: 'Tap to start recording',
12
+ aria: {
13
+ startRecording: 'Start recording voice message',
14
+ stopRecording: 'Stop recording voice message',
15
+ pauseRecording: 'Pause recording',
16
+ resumeRecording: 'Resume recording',
17
+ playRecording: 'Play recording',
18
+ pausePlayback: 'Pause playback',
19
+ },
20
+ errors: {
21
+ loadFileFailed: 'Failed to upload audio file',
22
+ },
23
+ };
24
+ const mergeMessages = (custom) => {
25
+ var _a;
26
+ if (!custom) {
27
+ return DEFAULT_MESSAGES;
28
+ }
29
+ return {
30
+ buttons: {
31
+ ...DEFAULT_MESSAGES.buttons,
32
+ ...custom.buttons,
33
+ },
34
+ instruction: (_a = custom.instruction) !== null && _a !== void 0 ? _a : DEFAULT_MESSAGES.instruction,
35
+ aria: {
36
+ ...DEFAULT_MESSAGES.aria,
37
+ ...custom.aria,
38
+ },
39
+ errors: {
40
+ ...DEFAULT_MESSAGES.errors,
41
+ ...custom.errors,
42
+ },
43
+ };
44
+ };
45
+
46
+ export { DEFAULT_MESSAGES, mergeMessages };
47
+ //# sourceMappingURL=AudioRecorder.messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioRecorder.messages.js","sources":["../../src/components/AudioRecorder.messages.ts"],"sourcesContent":["/**\n * AudioRecorder messages — i18n-ready message definitions and merge utility\n *\n * @author: exode <hello@exode.ru>\n */\n\n\nexport interface RecorderMessages {\n buttons: {\n send: string;\n cancel: string;\n };\n instruction: string;\n aria: {\n startRecording: string;\n stopRecording: string;\n pauseRecording: string;\n resumeRecording: string;\n playRecording: string;\n pausePlayback: string;\n };\n errors: {\n loadFileFailed: string;\n };\n}\n\nexport type RecorderMessagesInput = Partial<{\n buttons: Partial<RecorderMessages['buttons']>;\n instruction: RecorderMessages['instruction'];\n aria: Partial<RecorderMessages['aria']>;\n errors: Partial<RecorderMessages['errors']>;\n}>;\n\nexport const DEFAULT_MESSAGES: RecorderMessages = {\n buttons: {\n cancel: 'Cancel',\n send: 'Send',\n },\n instruction: 'Tap to start recording',\n aria: {\n startRecording: 'Start recording voice message',\n stopRecording: 'Stop recording voice message',\n pauseRecording: 'Pause recording',\n resumeRecording: 'Resume recording',\n playRecording: 'Play recording',\n pausePlayback: 'Pause playback',\n },\n errors: {\n loadFileFailed: 'Failed to upload audio file',\n },\n};\n\nexport const mergeMessages = (custom?: RecorderMessagesInput): RecorderMessages => {\n if (!custom) {\n return DEFAULT_MESSAGES;\n }\n\n return {\n buttons: {\n ...DEFAULT_MESSAGES.buttons,\n ...custom.buttons,\n },\n instruction: custom.instruction ?? DEFAULT_MESSAGES.instruction,\n aria: {\n ...DEFAULT_MESSAGES.aria,\n ...custom.aria,\n },\n errors: {\n ...DEFAULT_MESSAGES.errors,\n ...custom.errors,\n },\n };\n};\n"],"names":[],"mappings":"AAAA;;;;AAIG;AA6BI,MAAM,gBAAgB,GAAqB;AAC9C,IAAA,OAAO,EAAE;AACL,QAAA,MAAM,EAAE,QAAQ;AAChB,QAAA,IAAI,EAAE,MAAM;AACf,KAAA;AACD,IAAA,WAAW,EAAE,wBAAwB;AACrC,IAAA,IAAI,EAAE;AACF,QAAA,cAAc,EAAE,+BAA+B;AAC/C,QAAA,aAAa,EAAE,8BAA8B;AAC7C,QAAA,cAAc,EAAE,iBAAiB;AACjC,QAAA,eAAe,EAAE,kBAAkB;AACnC,QAAA,aAAa,EAAE,gBAAgB;AAC/B,QAAA,aAAa,EAAE,gBAAgB;AAClC,KAAA;AACD,IAAA,MAAM,EAAE;AACJ,QAAA,cAAc,EAAE,6BAA6B;AAChD,KAAA;;AAGE,MAAM,aAAa,GAAG,CAAC,MAA8B,KAAsB;;IAC9E,IAAI,CAAC,MAAM,EAAE;AACT,QAAA,OAAO,gBAAgB;IAC3B;IAEA,OAAO;AACH,QAAA,OAAO,EAAE;YACL,GAAG,gBAAgB,CAAC,OAAO;YAC3B,GAAG,MAAM,CAAC,OAAO;AACpB,SAAA;QACD,WAAW,EAAE,MAAA,MAAM,CAAC,WAAW,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,EAAA,GAAI,gBAAgB,CAAC,WAAW;AAC/D,QAAA,IAAI,EAAE;YACF,GAAG,gBAAgB,CAAC,IAAI;YACxB,GAAG,MAAM,CAAC,IAAI;AACjB,SAAA;AACD,QAAA,MAAM,EAAE;YACJ,GAAG,gBAAgB,CAAC,MAAM;YAC1B,GAAG,MAAM,CAAC,MAAM;AACnB,SAAA;KACJ;AACL;;;;"}
@@ -0,0 +1,16 @@
1
+ import styled from 'styled-components';
2
+
3
+ /**
4
+ * AudioRecorder styled components
5
+ *
6
+ * @author: exode <hello@exode.ru>
7
+ */
8
+ const Container = styled.div `
9
+ position: relative;
10
+ width: 100%;
11
+ transition: color 200ms ease, background-color 200ms ease;
12
+ color: var(--text_primary, #0a0a0a);
13
+ `;
14
+
15
+ export { Container };
16
+ //# sourceMappingURL=AudioRecorder.styled.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioRecorder.styled.js","sources":["../../src/components/AudioRecorder.styled.ts"],"sourcesContent":["/**\n * AudioRecorder styled components\n *\n * @author: exode <hello@exode.ru>\n */\n\nimport styled from 'styled-components';\n\n\nexport const Container = styled.div`\n position: relative;\n width: 100%;\n transition: color 200ms ease, background-color 200ms ease;\n color: var(--text_primary, #0a0a0a);\n`;\n"],"names":[],"mappings":";;AAAA;;;;AAIG;AAKI,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;"}
@@ -0,0 +1,12 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import 'react';
3
+ import { Icon32Microphone } from '@vkontakte/icons';
4
+ import { IdleWrapper, IdleButton, IdleButtonOutline, IdleButtonGlow, IdleInstruction } from './IdleControl.styled.js';
5
+
6
+ const IdleControl = (props) => {
7
+ const { onClick, instruction, startAriaLabel, } = props;
8
+ return (jsxs(IdleWrapper, { children: [jsxs(IdleButton, { type: "button", onClick: onClick, "aria-label": startAriaLabel, children: [jsx(IdleButtonOutline, { "aria-hidden": true }), jsx(IdleButtonGlow, { "aria-hidden": true }), jsx(Icon32Microphone, { height: 48, width: 48 })] }), jsx(IdleInstruction, { children: instruction })] }));
9
+ };
10
+
11
+ export { IdleControl };
12
+ //# sourceMappingURL=IdleControl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IdleControl.js","sources":["../../../src/components/IdleControl/IdleControl.tsx"],"sourcesContent":["/**\n * IdleControl\n *\n * @author: exode <hello@exode.ru>\n */\n\nimport React from 'react';\n\nimport { Icon32Microphone } from '@vkontakte/icons';\n\nimport { IdleButton, IdleButtonGlow, IdleButtonOutline, IdleInstruction, IdleWrapper } from './IdleControl.styled';\n\n\ninterface Props {\n onClick: () => void;\n instruction: string;\n startAriaLabel: string;\n}\n\n\nconst IdleControl = (props: Props) => {\n\n const {\n onClick,\n instruction,\n startAriaLabel,\n } = props;\n\n return (\n <IdleWrapper>\n <IdleButton type=\"button\" onClick={onClick} aria-label={startAriaLabel}>\n <IdleButtonOutline aria-hidden/>\n <IdleButtonGlow aria-hidden/>\n\n <Icon32Microphone height={48} width={48}/>\n </IdleButton>\n\n <IdleInstruction>\n {instruction}\n </IdleInstruction>\n </IdleWrapper>\n );\n};\n\n\nexport { IdleControl };"],"names":["_jsxs","_jsx"],"mappings":";;;;;AAoBA,MAAM,WAAW,GAAG,CAAC,KAAY,KAAI;IAEjC,MAAM,EACF,OAAO,EACP,WAAW,EACX,cAAc,GACjB,GAAG,KAAK;IAET,QACIA,KAAC,WAAW,EAAA,EAAA,QAAA,EAAA,CACRA,KAAC,UAAU,EAAA,EAAC,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,OAAO,EAAA,YAAA,EAAc,cAAc,aAClEC,GAAA,CAAC,iBAAiB,0BAAc,EAChCA,GAAA,CAAC,cAAc,EAAA,EAAA,aAAA,EAAA,IAAA,EAAA,CAAc,EAE7BA,IAAC,gBAAgB,EAAA,EAAC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAA,CAAG,CAAA,EAAA,CACjC,EAEbA,GAAA,CAAC,eAAe,cACX,WAAW,EAAA,CACE,CAAA,EAAA,CACR;AAEtB;;;;"}
@@ -0,0 +1,68 @@
1
+ import styled from 'styled-components';
2
+
3
+ /**
4
+ * IdleControl styles
5
+ *
6
+ * @author: exode <hello@exode.ru>
7
+ */
8
+ const IdleWrapper = styled.div `
9
+ display: flex;
10
+ flex-direction: column;
11
+ align-items: center;
12
+ gap: 16px;
13
+ `;
14
+ const IdleButton = styled.button `
15
+ position: relative;
16
+ width: 72px;
17
+ height: 72px;
18
+ display: inline-flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ border-radius: 36px;
22
+ border: 0;
23
+ background: var(--background_content, #ffffff);
24
+ color: color-mix(in srgb, var(--accent, #3f8ae0) 80%, white 20%);
25
+ cursor: pointer;
26
+ transition: transform 180ms ease, box-shadow 180ms ease, background-color 180ms ease;
27
+
28
+ &:hover {
29
+ transform: translateY(-2px);
30
+ }
31
+
32
+ &:active {
33
+ transform: translateY(0);
34
+ }
35
+
36
+ &:focus-visible {
37
+ outline: 2px solid color-mix(in srgb, var(--accent, #3f8ae0) 70%, transparent);
38
+ outline-offset: 4px;
39
+ }
40
+ `;
41
+ const IdleButtonOutline = styled.span `
42
+ position: absolute;
43
+ inset: 0;
44
+ border-radius: 32px;
45
+ border: 1px solid color-mix(in srgb, var(--accent, #3f8ae0) 28%, transparent);
46
+ pointer-events: none;
47
+ `;
48
+ const IdleButtonGlow = styled.span `
49
+ position: absolute;
50
+ inset: 8px;
51
+ border-radius: 28px;
52
+ background: radial-gradient(
53
+ 120% 120% at 50% 50%,
54
+ color-mix(in srgb, var(--accent, #3f8ae0) 24%, transparent) 0%,
55
+ color-mix(in srgb, var(--accent, #3f8ae0) 10%, transparent) 65%,
56
+ transparent 100%
57
+ );
58
+ filter: blur(4px);
59
+ pointer-events: none;
60
+ `;
61
+ const IdleInstruction = styled.div `
62
+ font-size: 14px;
63
+ text-align: center;
64
+ color: color-mix(in srgb, var(--text_primary, #0a0a0a) 60%, transparent);
65
+ `;
66
+
67
+ export { IdleButton, IdleButtonGlow, IdleButtonOutline, IdleInstruction, IdleWrapper };
68
+ //# sourceMappingURL=IdleControl.styled.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IdleControl.styled.js","sources":["../../../src/components/IdleControl/IdleControl.styled.ts"],"sourcesContent":["/**\n * IdleControl styles\n *\n * @author: exode <hello@exode.ru>\n */\n\nimport styled from 'styled-components';\n\n\nexport const IdleWrapper = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n`;\n\nexport const IdleButton = styled.button`\n position: relative;\n width: 72px;\n height: 72px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 36px;\n border: 0;\n background: var(--background_content, #ffffff);\n color: color-mix(in srgb, var(--accent, #3f8ae0) 80%, white 20%);\n cursor: pointer;\n transition: transform 180ms ease, box-shadow 180ms ease, background-color 180ms ease;\n\n &:hover {\n transform: translateY(-2px);\n }\n\n &:active {\n transform: translateY(0);\n }\n\n &:focus-visible {\n outline: 2px solid color-mix(in srgb, var(--accent, #3f8ae0) 70%, transparent);\n outline-offset: 4px;\n }\n`;\n\nexport const IdleButtonOutline = styled.span`\n position: absolute;\n inset: 0;\n border-radius: 32px;\n border: 1px solid color-mix(in srgb, var(--accent, #3f8ae0) 28%, transparent);\n pointer-events: none;\n`;\n\nexport const IdleButtonGlow = styled.span`\n position: absolute;\n inset: 8px;\n border-radius: 28px;\n background: radial-gradient(\n 120% 120% at 50% 50%,\n color-mix(in srgb, var(--accent, #3f8ae0) 24%, transparent) 0%,\n color-mix(in srgb, var(--accent, #3f8ae0) 10%, transparent) 65%,\n transparent 100%\n );\n filter: blur(4px);\n pointer-events: none;\n`;\n\nexport const IdleInstruction = styled.div`\n font-size: 14px;\n text-align: center;\n color: color-mix(in srgb, var(--text_primary, #0a0a0a) 60%, transparent);\n`;\n"],"names":[],"mappings":";;AAAA;;;;AAIG;AAKI,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;AAO9B,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BhC,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAA;;;;;;;AAQrC,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAA;;;;;;;;;;;;;AAclC,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;"}
@@ -0,0 +1,80 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { Icon16Microphone, Icon16Pause, Icon16Play, Icon28DeleteOutlineAndroid, Icon28SendOutline } from '@vkontakte/icons';
4
+ import { WaveformBars } from '../WaveformBars/WaveformBars.js';
5
+ import { Wrapper, CancelButton, Track, CircleButton, Waveform, Timer, SendButton, SpinnerSlot, Spinner } from './RecordingShell.styled.js';
6
+
7
+ const RecordingShell = (props) => {
8
+ const { isPaused, isPlaying, isSending, isRecording, audioLevels, hasRecording, playbackTime, previewLevels, recordingTime, stopAriaLabel, playAriaLabel, sendAriaLabel, startAriaLabel, cancelAriaLabel, playbackDuration, pausePlaybackAriaLabel, pauseRecordingAriaLabel, resumeRecordingAriaLabel, onSend, onCancel, formatTime, onRecordToggle, onTogglePlayback, } = props;
9
+ const { lastDuration } = props;
10
+ const isPreview = !isRecording || isPaused;
11
+ const waveformValues = isRecording ? audioLevels : (hasRecording ? previewLevels : audioLevels);
12
+ const effectiveDuration = recordingTime
13
+ || playbackDuration
14
+ || lastDuration
15
+ || 1;
16
+ const progress = (isPreview && isPlaying)
17
+ ? Math.min(1, playbackTime / effectiveDuration)
18
+ : 0;
19
+ const displayTime = useMemo(() => {
20
+ if (isPlaying) {
21
+ return formatTime(playbackTime);
22
+ }
23
+ if (isRecording && !isPaused) {
24
+ return formatTime(recordingTime);
25
+ }
26
+ if (isRecording && isPaused) {
27
+ return formatTime(recordingTime);
28
+ }
29
+ if (lastDuration > 0) {
30
+ return formatTime(lastDuration);
31
+ }
32
+ if (hasRecording) {
33
+ if (playbackDuration > 0.5) {
34
+ return formatTime(playbackDuration);
35
+ }
36
+ if (recordingTime > 0) {
37
+ return formatTime(recordingTime);
38
+ }
39
+ }
40
+ return formatTime(0);
41
+ }, [
42
+ isPaused,
43
+ isPlaying,
44
+ formatTime,
45
+ isRecording,
46
+ lastDuration,
47
+ hasRecording,
48
+ playbackTime,
49
+ recordingTime,
50
+ playbackDuration,
51
+ ]);
52
+ const recordButtonDisabled = isSending;
53
+ const recordButtonIcon = !isRecording || isPaused
54
+ ? jsx(Icon16Microphone, { width: 24, height: 24 })
55
+ : jsx(Icon16Pause, { width: 24, height: 24 });
56
+ const recordButtonVariant = !isRecording || isPaused ? 'resume' : 'record';
57
+ const recordButtonAria = !isRecording
58
+ ? startAriaLabel
59
+ : isPaused
60
+ ? resumeRecordingAriaLabel
61
+ : pauseRecordingAriaLabel || stopAriaLabel;
62
+ const canShowPlayback = !isRecording || isPaused;
63
+ const playbackDisabled = !hasRecording && !isRecording;
64
+ const playbackIcon = isPlaying
65
+ ? jsx(Icon16Pause, { width: 24, height: 24 })
66
+ : jsx(Icon16Play, { width: 24, height: 24 });
67
+ const playbackAria = isPlaying ? pausePlaybackAriaLabel : playAriaLabel;
68
+ const sendDisabled = isSending || (!hasRecording && !isRecording);
69
+ return (jsxs(Wrapper, { children: [jsx(CancelButton, { type: "button", onClick: onCancel, "aria-label": cancelAriaLabel, children: jsx(Icon28DeleteOutlineAndroid, { fill: "var(--exode_red)", width: 24, height: 24, style: { minWidth: 24 } }) }), jsxs(Track, { children: [jsx(CircleButton, { type: "button", "$variant": recordButtonVariant, onClick: onRecordToggle, "aria-label": recordButtonAria, disabled: recordButtonDisabled, children: recordButtonIcon }), !canShowPlayback ? null : (jsx(CircleButton, { type: "button", "$variant": "play", onClick: onTogglePlayback, "aria-label": playbackAria, disabled: playbackDisabled, children: playbackIcon })), jsx(Waveform, { "$expanded": canShowPlayback, children: jsx(WaveformBars, { scale: 42, minHeight: 6, divider: 100, progress: progress, values: waveformValues, styleInterpolator: ({ normalized, isPlayed, progress: p }) => ({
70
+ height: `${12 + normalized / 2}px`,
71
+ maxHeight: '34px',
72
+ opacity: p > 0
73
+ ? (isPlayed ? 0.80 : 0.22)
74
+ : 0.38 + Math.min(0.5, normalized / 120),
75
+ transition: 'opacity 80ms ease',
76
+ }) }) }), jsx(Timer, { children: displayTime })] }), jsx(SendButton, { type: "button", onClick: onSend, disabled: sendDisabled, "aria-label": sendAriaLabel, children: isSending ? (jsx(SpinnerSlot, { children: jsx(Spinner, {}) })) : (jsx(Icon28SendOutline, { width: 24, height: 24 })) })] }));
77
+ };
78
+
79
+ export { RecordingShell };
80
+ //# sourceMappingURL=RecordingShell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecordingShell.js","sources":["../../../src/components/RecordingShell/RecordingShell.tsx"],"sourcesContent":["/**\n * RecordingShell\n *\n * @author: exode <hello@exode.ru>\n */\n\nimport React, { useMemo } from 'react';\n\nimport {\n Icon16Microphone,\n Icon16Pause,\n Icon16Play,\n Icon28DeleteOutlineAndroid,\n Icon28SendOutline,\n} from '@vkontakte/icons';\n\nimport { WaveformBars } from '../WaveformBars/WaveformBars';\n\nimport {\n CancelButton,\n CircleButton,\n SendButton,\n Spinner,\n SpinnerSlot,\n Timer,\n Track,\n Waveform,\n Wrapper,\n} from './RecordingShell.styled';\n\n\ninterface Props {\n isRecording: boolean;\n isPaused: boolean;\n hasRecording: boolean;\n isPlaying: boolean;\n lastDuration: number;\n recordingTime: number;\n playbackTime: number;\n playbackDuration: number;\n audioLevels: number[];\n previewLevels: number[];\n isSending: boolean;\n cancelAriaLabel: string;\n stopAriaLabel: string;\n startAriaLabel: string;\n pauseRecordingAriaLabel: string;\n resumeRecordingAriaLabel: string;\n playAriaLabel: string;\n pausePlaybackAriaLabel: string;\n sendAriaLabel: string;\n onSend: () => void;\n onCancel: () => void;\n onRecordToggle: () => void;\n onTogglePlayback: () => void;\n formatTime: (seconds: number) => string;\n}\n\n\nconst RecordingShell = (props: Props) => {\n\n const {\n isPaused,\n isPlaying,\n isSending,\n isRecording,\n audioLevels,\n hasRecording,\n playbackTime,\n previewLevels,\n recordingTime,\n stopAriaLabel,\n playAriaLabel,\n sendAriaLabel,\n startAriaLabel,\n cancelAriaLabel,\n playbackDuration,\n pausePlaybackAriaLabel,\n pauseRecordingAriaLabel,\n resumeRecordingAriaLabel,\n onSend,\n onCancel,\n formatTime,\n onRecordToggle,\n onTogglePlayback,\n } = props;\n\n const { lastDuration } = props;\n\n const isPreview = !isRecording || isPaused;\n const waveformValues = isRecording ? audioLevels : (hasRecording ? previewLevels : audioLevels);\n\n const effectiveDuration = recordingTime\n || playbackDuration\n || lastDuration\n || 1;\n\n const progress = (isPreview && isPlaying)\n ? Math.min(1, playbackTime / effectiveDuration)\n : 0;\n\n const displayTime = useMemo(() => {\n if (isPlaying) {\n return formatTime(playbackTime);\n }\n\n if (isRecording && !isPaused) {\n return formatTime(recordingTime);\n }\n\n if (isRecording && isPaused) {\n return formatTime(recordingTime);\n }\n\n if (lastDuration > 0) {\n return formatTime(lastDuration);\n }\n\n if (hasRecording) {\n if (playbackDuration > 0.5) {\n return formatTime(playbackDuration);\n }\n\n if (recordingTime > 0) {\n return formatTime(recordingTime);\n }\n }\n\n return formatTime(0);\n }, [\n isPaused,\n isPlaying,\n formatTime,\n isRecording,\n lastDuration,\n hasRecording,\n playbackTime,\n recordingTime,\n playbackDuration,\n ]);\n\n const recordButtonDisabled = isSending;\n const recordButtonIcon = !isRecording || isPaused\n ? <Icon16Microphone width={24} height={24}/>\n : <Icon16Pause width={24} height={24}/>;\n const recordButtonVariant: 'record' | 'resume' = !isRecording || isPaused ? 'resume' : 'record';\n const recordButtonAria = !isRecording\n ? startAriaLabel\n : isPaused\n ? resumeRecordingAriaLabel\n : pauseRecordingAriaLabel || stopAriaLabel;\n\n const canShowPlayback = !isRecording || isPaused;\n const playbackDisabled = !hasRecording && !isRecording;\n const playbackIcon = isPlaying\n ? <Icon16Pause width={24} height={24}/>\n : <Icon16Play width={24} height={24}/>;\n const playbackAria = isPlaying ? pausePlaybackAriaLabel : playAriaLabel;\n\n const sendDisabled = isSending || (!hasRecording && !isRecording);\n\n return (\n <Wrapper>\n <CancelButton type=\"button\" onClick={onCancel} aria-label={cancelAriaLabel}>\n <Icon28DeleteOutlineAndroid fill=\"var(--exode_red)\" width={24} height={24} style={{ minWidth: 24 }}/>\n </CancelButton>\n\n <Track>\n <CircleButton type=\"button\"\n $variant={recordButtonVariant}\n onClick={onRecordToggle}\n aria-label={recordButtonAria}\n disabled={recordButtonDisabled}>\n {recordButtonIcon}\n </CircleButton>\n\n {!canShowPlayback ? null : (\n <CircleButton type=\"button\"\n $variant=\"play\"\n onClick={onTogglePlayback}\n aria-label={playbackAria}\n disabled={playbackDisabled}>\n {playbackIcon}\n </CircleButton>\n )}\n\n <Waveform $expanded={canShowPlayback}>\n <WaveformBars scale={42}\n minHeight={6}\n divider={100}\n progress={progress}\n values={waveformValues}\n styleInterpolator={({ normalized, isPlayed, progress: p }) => ({\n height: `${12 + normalized / 2}px`,\n maxHeight: '34px',\n opacity: p > 0\n ? (isPlayed ? 0.80 : 0.22)\n : 0.38 + Math.min(0.5, normalized / 120),\n transition: 'opacity 80ms ease',\n })}/>\n </Waveform>\n\n <Timer>\n {displayTime}\n </Timer>\n </Track>\n\n <SendButton type=\"button\" onClick={onSend} disabled={sendDisabled} aria-label={sendAriaLabel}>\n {isSending ? (\n <SpinnerSlot>\n <Spinner/>\n </SpinnerSlot>\n ) : (\n <Icon28SendOutline width={24} height={24}/>\n )}\n </SendButton>\n </Wrapper>\n );\n};\n\n\nexport { RecordingShell };\n"],"names":["_jsx","_jsxs"],"mappings":";;;;;;AA2DA,MAAM,cAAc,GAAG,CAAC,KAAY,KAAI;IAEpC,MAAM,EACF,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,EACb,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,EACxB,MAAM,EACN,QAAQ,EACR,UAAU,EACV,cAAc,EACd,gBAAgB,GACnB,GAAG,KAAK;AAET,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,KAAK;AAE9B,IAAA,MAAM,SAAS,GAAG,CAAC,WAAW,IAAI,QAAQ;IAC1C,MAAM,cAAc,GAAG,WAAW,GAAG,WAAW,IAAI,YAAY,GAAG,aAAa,GAAG,WAAW,CAAC;IAE/F,MAAM,iBAAiB,GAAG;WACnB;WACA;AACA,WAAA,CAAC;AAER,IAAA,MAAM,QAAQ,GAAG,CAAC,SAAS,IAAI,SAAS;UAClC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,iBAAiB;UAC5C,CAAC;AAEP,IAAA,MAAM,WAAW,GAAG,OAAO,CAAC,MAAK;QAC7B,IAAI,SAAS,EAAE;AACX,YAAA,OAAO,UAAU,CAAC,YAAY,CAAC;QACnC;AAEA,QAAA,IAAI,WAAW,IAAI,CAAC,QAAQ,EAAE;AAC1B,YAAA,OAAO,UAAU,CAAC,aAAa,CAAC;QACpC;AAEA,QAAA,IAAI,WAAW,IAAI,QAAQ,EAAE;AACzB,YAAA,OAAO,UAAU,CAAC,aAAa,CAAC;QACpC;AAEA,QAAA,IAAI,YAAY,GAAG,CAAC,EAAE;AAClB,YAAA,OAAO,UAAU,CAAC,YAAY,CAAC;QACnC;QAEA,IAAI,YAAY,EAAE;AACd,YAAA,IAAI,gBAAgB,GAAG,GAAG,EAAE;AACxB,gBAAA,OAAO,UAAU,CAAC,gBAAgB,CAAC;YACvC;AAEA,YAAA,IAAI,aAAa,GAAG,CAAC,EAAE;AACnB,gBAAA,OAAO,UAAU,CAAC,aAAa,CAAC;YACpC;QACJ;AAEA,QAAA,OAAO,UAAU,CAAC,CAAC,CAAC;AACxB,IAAA,CAAC,EAAE;QACC,QAAQ;QACR,SAAS;QACT,UAAU;QACV,WAAW;QACX,YAAY;QACZ,YAAY;QACZ,YAAY;QACZ,aAAa;QACb,gBAAgB;AACnB,KAAA,CAAC;IAEF,MAAM,oBAAoB,GAAG,SAAS;AACtC,IAAA,MAAM,gBAAgB,GAAG,CAAC,WAAW,IAAI;UACnCA,GAAA,CAAC,gBAAgB,EAAA,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAA;AACzC,UAAEA,GAAA,CAAC,WAAW,EAAA,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAA,CAAG;AAC3C,IAAA,MAAM,mBAAmB,GAAwB,CAAC,WAAW,IAAI,QAAQ,GAAG,QAAQ,GAAG,QAAQ;IAC/F,MAAM,gBAAgB,GAAG,CAAC;AACtB,UAAE;AACF,UAAE;AACE,cAAE;AACF,cAAE,uBAAuB,IAAI,aAAa;AAElD,IAAA,MAAM,eAAe,GAAG,CAAC,WAAW,IAAI,QAAQ;AAChD,IAAA,MAAM,gBAAgB,GAAG,CAAC,YAAY,IAAI,CAAC,WAAW;IACtD,MAAM,YAAY,GAAG;UACfA,GAAA,CAAC,WAAW,EAAA,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAA;AACpC,UAAEA,GAAA,CAAC,UAAU,EAAA,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAA,CAAG;IAC1C,MAAM,YAAY,GAAG,SAAS,GAAG,sBAAsB,GAAG,aAAa;IAEvE,MAAM,YAAY,GAAG,SAAS,KAAK,CAAC,YAAY,IAAI,CAAC,WAAW,CAAC;AAEjE,IAAA,QACIC,IAAA,CAAC,OAAO,EAAA,EAAA,QAAA,EAAA,CACJD,IAAC,YAAY,EAAA,EAAC,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,QAAQ,EAAA,YAAA,EAAc,eAAe,EAAA,QAAA,EACtEA,GAAA,CAAC,0BAA0B,EAAA,EAAC,IAAI,EAAC,kBAAkB,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAA,CAAG,GAC1F,EAEfC,IAAA,CAAC,KAAK,EAAA,EAAA,QAAA,EAAA,CACFD,GAAA,CAAC,YAAY,EAAA,EAAC,IAAI,EAAC,QAAQ,EAAA,UAAA,EACH,mBAAmB,EAC7B,OAAO,EAAE,cAAc,EAAA,YAAA,EACX,gBAAgB,EAC5B,QAAQ,EAAE,oBAAoB,YACvC,gBAAgB,EAAA,CACN,EAEd,CAAC,eAAe,GAAG,IAAI,IACpBA,IAAC,YAAY,EAAA,EAAC,IAAI,EAAC,QAAQ,EAAA,UAAA,EACJ,MAAM,EACf,OAAO,EAAE,gBAAgB,EAAA,YAAA,EACb,YAAY,EACxB,QAAQ,EAAE,gBAAgB,YACnC,YAAY,EAAA,CACF,CAClB,EAEDA,IAAC,QAAQ,EAAA,EAAA,WAAA,EAAY,eAAe,EAAA,QAAA,EAChCA,IAAC,YAAY,EAAA,EAAC,KAAK,EAAE,EAAE,EACT,SAAS,EAAE,CAAC,EACZ,OAAO,EAAE,GAAG,EACZ,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,cAAc,EACtB,iBAAiB,EAAE,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM;AAC3D,gCAAA,MAAM,EAAE,CAAA,EAAG,EAAE,GAAG,UAAU,GAAG,CAAC,CAAA,EAAA,CAAI;AAClC,gCAAA,SAAS,EAAE,MAAM;gCACjB,OAAO,EAAE,CAAC,GAAG;uCACN,QAAQ,GAAG,IAAI,GAAG,IAAI;AACzB,sCAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,GAAG,CAAC;AAC5C,gCAAA,UAAU,EAAE,mBAAmB;6BAClC,CAAC,EAAA,CAAG,EAAA,CACZ,EAEXA,GAAA,CAAC,KAAK,EAAA,EAAA,QAAA,EACD,WAAW,EAAA,CACR,CAAA,EAAA,CACJ,EAERA,GAAA,CAAC,UAAU,EAAA,EAAC,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAA,YAAA,EAAc,aAAa,EAAA,QAAA,EACvF,SAAS,IACNA,GAAA,CAAC,WAAW,EAAA,EAAA,QAAA,EACRA,GAAA,CAAC,OAAO,EAAA,EAAA,CAAE,EAAA,CACA,KAEdA,GAAA,CAAC,iBAAiB,EAAA,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAA,CAAG,CAC9C,EAAA,CACQ,CAAA,EAAA,CACP;AAElB;;;;"}
@@ -0,0 +1,159 @@
1
+ import styled from 'styled-components';
2
+
3
+ /**
4
+ * RecordingShell styles
5
+ *
6
+ * @author: exode <hello@exode.ru>
7
+ */
8
+ const Wrapper$1 = styled.div `
9
+ display: flex;
10
+ align-items: center;
11
+ gap: 12px;
12
+ padding: 10px 14px;
13
+ border-radius: 9999px;
14
+ background: var(--background_hover, color-mix(in srgb, var(--text_primary, #0a0a0a) 6%, transparent));
15
+ `;
16
+ const CancelButton$1 = styled.button `
17
+ width: 38px;
18
+ height: 38px;
19
+ display: inline-flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ border-radius: 19px;
23
+ background: color-mix(in srgb, var(--exode_red) 10%, transparent);
24
+ border: 0;
25
+ flex-shrink: 0;
26
+ color: color-mix(in srgb, var(--text_primary, #0a0a0a) 54%, transparent);
27
+ cursor: pointer;
28
+ transition: transform 160ms ease;
29
+
30
+ &:hover:not(:disabled) {
31
+ transform: translateY(-1px);
32
+ }
33
+
34
+ &:active:not(:disabled) {
35
+ transform: translateY(0);
36
+ }
37
+ `;
38
+ const Track = styled.div `
39
+ flex: 1;
40
+ display: flex;
41
+ align-items: center;
42
+ gap: 12px;
43
+ padding: 8px 10px;
44
+ border-radius: 9999px;
45
+ background: color-mix(in srgb, var(--text_primary, #0a0a0a) 10%, transparent);
46
+ `;
47
+ const CircleButton = styled.button `
48
+ width: 38px;
49
+ height: 38px;
50
+ display: inline-flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ border-radius: 19px;
54
+ border: 0;
55
+ cursor: pointer;
56
+ transition: transform 160ms ease, color 160ms ease, background-color 160ms ease, box-shadow 160ms ease;
57
+ flex-shrink: 0;
58
+ background: var(--background_content, #ffffff);
59
+
60
+ color: ${({ $variant }) => {
61
+ if ($variant === 'record') {
62
+ return 'var(--exode_red, #ed1c24)';
63
+ }
64
+ if ($variant === 'resume') {
65
+ return 'color-mix(in srgb, var(--accent, #3f8ae0) 86%, white 14%)';
66
+ }
67
+ return 'color-mix(in srgb, var(--text_primary, #0a0a0a) 80%, transparent)';
68
+ }};
69
+
70
+ &:hover:not(:disabled) {
71
+ transform: translateY(-1px);
72
+ }
73
+
74
+ &:active:not(:disabled) {
75
+ transform: translateY(0);
76
+ }
77
+
78
+ &:focus-visible {
79
+ outline: 2px solid color-mix(in srgb, var(--accent, #3f8ae0) 70%, transparent);
80
+ outline-offset: 2px;
81
+ }
82
+
83
+ &:disabled {
84
+ cursor: not-allowed;
85
+ opacity: 0.45;
86
+ transform: none;
87
+ }
88
+ `;
89
+ const Waveform = styled.div `
90
+ position: relative;
91
+ flex: ${({ $expanded }) => ($expanded ? 1.4 : 1)};
92
+ height: 34px;
93
+ display: flex;
94
+ align-items: center;
95
+ overflow: hidden;
96
+ `;
97
+ const Timer$1 = styled.span `
98
+ text-align: right;
99
+ font-size: 13px;
100
+ font-weight: 600;
101
+ color: color-mix(in srgb, var(--text_primary, #0a0a0a) 86%, transparent);
102
+ `;
103
+ const SendButton$1 = styled.button `
104
+ width: 40px;
105
+ height: 40px;
106
+ display: inline-flex;
107
+ align-items: center;
108
+ justify-content: center;
109
+ border-radius: 20px;
110
+ border: 0;
111
+ cursor: pointer;
112
+ background: var(--accent, #3f8ae0);
113
+ color: var(--white, #ffffff);
114
+ transition: transform 160ms ease, opacity 160ms ease;
115
+
116
+ &:hover:not(:disabled) {
117
+ transform: translateY(-1px);
118
+ }
119
+
120
+ &:active:not(:disabled) {
121
+ transform: translateY(0);
122
+ }
123
+
124
+ &:disabled {
125
+ opacity: 0.45;
126
+ cursor: not-allowed;
127
+ }
128
+ `;
129
+ const SpinnerSlot = styled.div `
130
+ width: 22px;
131
+ height: 22px;
132
+ display: inline-flex;
133
+ align-items: center;
134
+ justify-content: center;
135
+ `;
136
+ const Spinner = styled.span `
137
+ width: 100%;
138
+ height: 100%;
139
+ display: inline-block;
140
+ box-sizing: border-box;
141
+ border-radius: 50%;
142
+ border: 2px solid rgba(255, 255, 255, 0.3);
143
+ border-top-color: var(--white, #ffffff);
144
+ border-right-color: var(--white, #ffffff);
145
+ animation: audioRecorderSpinner 0.75s linear infinite;
146
+
147
+ @keyframes audioRecorderSpinner {
148
+ from {
149
+ transform: rotate(0deg);
150
+ }
151
+
152
+ to {
153
+ transform: rotate(360deg);
154
+ }
155
+ }
156
+ `;
157
+
158
+ export { CancelButton$1 as CancelButton, CircleButton, SendButton$1 as SendButton, Spinner, SpinnerSlot, Timer$1 as Timer, Track, Waveform, Wrapper$1 as Wrapper };
159
+ //# sourceMappingURL=RecordingShell.styled.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecordingShell.styled.js","sources":["../../../src/components/RecordingShell/RecordingShell.styled.ts"],"sourcesContent":["/**\n * RecordingShell styles\n *\n * @author: exode <hello@exode.ru>\n */\n\nimport styled from 'styled-components';\n\n\nexport const Wrapper = styled.div`\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 14px;\n border-radius: 9999px;\n background: var(--background_hover, color-mix(in srgb, var(--text_primary, #0a0a0a) 6%, transparent));\n`;\n\nexport const CancelButton = styled.button`\n width: 38px;\n height: 38px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 19px;\n background: color-mix(in srgb, var(--exode_red) 10%, transparent);\n border: 0;\n flex-shrink: 0;\n color: color-mix(in srgb, var(--text_primary, #0a0a0a) 54%, transparent);\n cursor: pointer;\n transition: transform 160ms ease;\n\n &:hover:not(:disabled) {\n transform: translateY(-1px);\n }\n\n &:active:not(:disabled) {\n transform: translateY(0);\n }\n`;\n\nexport const Track = styled.div`\n flex: 1;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 10px;\n border-radius: 9999px;\n background: color-mix(in srgb, var(--text_primary, #0a0a0a) 10%, transparent);\n`;\n\nexport const CircleButton = styled.button<{ $variant: 'record' | 'resume' | 'play' }>`\n width: 38px;\n height: 38px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 19px;\n border: 0;\n cursor: pointer;\n transition: transform 160ms ease, color 160ms ease, background-color 160ms ease, box-shadow 160ms ease;\n flex-shrink: 0;\n background: var(--background_content, #ffffff);\n\n color: ${({ $variant }) => {\n if ($variant === 'record') {\n return 'var(--exode_red, #ed1c24)';\n }\n\n if ($variant === 'resume') {\n return 'color-mix(in srgb, var(--accent, #3f8ae0) 86%, white 14%)';\n }\n\n return 'color-mix(in srgb, var(--text_primary, #0a0a0a) 80%, transparent)';\n }};\n\n &:hover:not(:disabled) {\n transform: translateY(-1px);\n }\n\n &:active:not(:disabled) {\n transform: translateY(0);\n }\n\n &:focus-visible {\n outline: 2px solid color-mix(in srgb, var(--accent, #3f8ae0) 70%, transparent);\n outline-offset: 2px;\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.45;\n transform: none;\n }\n`;\n\nexport const Waveform = styled.div<{ $expanded: boolean }>`\n position: relative;\n flex: ${({ $expanded }) => ($expanded ? 1.4 : 1)};\n height: 34px;\n display: flex;\n align-items: center;\n overflow: hidden;\n`;\n\nexport const Timer = styled.span`\n text-align: right;\n font-size: 13px;\n font-weight: 600;\n color: color-mix(in srgb, var(--text_primary, #0a0a0a) 86%, transparent);\n`;\n\nexport const SendButton = styled.button`\n width: 40px;\n height: 40px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 20px;\n border: 0;\n cursor: pointer;\n background: var(--accent, #3f8ae0);\n color: var(--white, #ffffff);\n transition: transform 160ms ease, opacity 160ms ease;\n\n &:hover:not(:disabled) {\n transform: translateY(-1px);\n }\n\n &:active:not(:disabled) {\n transform: translateY(0);\n }\n\n &:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n }\n`;\n\nexport const SpinnerSlot = styled.div`\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n`;\n\nexport const Spinner = styled.span`\n width: 100%;\n height: 100%;\n display: inline-block;\n box-sizing: border-box;\n border-radius: 50%;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: var(--white, #ffffff);\n border-right-color: var(--white, #ffffff);\n animation: audioRecorderSpinner 0.75s linear infinite;\n\n @keyframes audioRecorderSpinner {\n from {\n transform: rotate(0deg);\n }\n\n to {\n transform: rotate(360deg);\n }\n }\n`;\n"],"names":["Wrapper","CancelButton","Timer","SendButton"],"mappings":";;AAAA;;;;AAIG;AAKI,MAAMA,SAAO,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;AAS1B,MAAMC,cAAY,GAAG,MAAM,CAAC,MAAM,CAAA;;;;;;;;;;;;;;;;;;;;;;AAuBlC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;AAUxB,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAA4C;;;;;;;;;;;;;AAaxE,WAAA,EAAA,CAAC,EAAE,QAAQ,EAAE,KAAI;AACtB,IAAA,IAAI,QAAQ,KAAK,QAAQ,EAAE;AACvB,QAAA,OAAO,2BAA2B;IACtC;AAEA,IAAA,IAAI,QAAQ,KAAK,QAAQ,EAAE;AACvB,QAAA,OAAO,2DAA2D;IACtE;AAEA,IAAA,OAAO,mEAAmE;AAC9E,CAAC,CAAA;;;;;;;;;;;;;;;;;;;;;AAsBE,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAwB;;AAE9C,UAAA,EAAA,CAAC,EAAE,SAAS,EAAE,MAAM,SAAS,GAAG,GAAG,GAAG,CAAC,CAAC,CAAA;;;;;;AAO7C,MAAMC,OAAK,GAAG,MAAM,CAAC,IAAI,CAAA;;;;;;AAOzB,MAAMC,YAAU,GAAG,MAAM,CAAC,MAAM,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BhC,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;AAQ9B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;"}