{"version":3,"file":"AudioRecorder.cjs","sources":["../src/MediaRecorder/use-media-recorder.ts","../src/MediaRecorder/AudioRecorder.tsx"],"sourcesContent":["import { type MutableRefObject, useEffect, useRef, useState } from 'react';\n\nexport function useMediaStream(constraints: MediaStreamConstraints) {\n  const [mediaStream, setMediaStream] = useState<MediaStream | null>(null);\n  const [requireMediaStreamError, setRequireMediaStreamError] =\n    useState<Error | null>(null);\n  useEffect(() => {\n    if (!navigator.mediaDevices.getUserMedia) {\n      setMediaStream(null);\n      setRequireMediaStreamError(\n        new Error('getUserMedia not supported on your browser!'),\n      );\n    }\n    navigator.mediaDevices\n      .getUserMedia(constraints)\n      .then(setMediaStream)\n      .catch(setRequireMediaStreamError);\n  }, []);\n\n  return {\n    mediaStream,\n    requireMediaStreamError,\n  };\n}\n\nexport function useAudioRecorder(\n  options?: MediaRecorderOptions,\n): [MediaRecorder | null, Error | null, MutableRefObject<Blob[] | null>] {\n  const { mediaStream, requireMediaStreamError } = useMediaStream({\n    audio: true,\n  });\n  const [audioRecorder, setAudioRecorder] = useState<MediaRecorder | null>(\n    null,\n  );\n  const [recorderError, setRecorderError] = useState<Error | null>(null);\n  const recordedChunks = useRef<null | Blob[]>(null);\n  useEffect(\n    function setupRecorder() {\n      if (!mediaStream) return undefined;\n      const recorder = new MediaRecorder(mediaStream, options);\n      setAudioRecorder(recorder);\n      const onError = (event: MediaRecorderEventMap['error']) => {\n        // @ts-expect-error Typing error!\n        setRecorderError(event.error);\n      };\n      const onStart = () => {\n        recordedChunks.current = [];\n      };\n\n      const onDataavailable = (event: BlobEvent) => {\n        recordedChunks.current?.push(event.data);\n      };\n\n      recorder.addEventListener('dataavailable', onDataavailable);\n      recorder.addEventListener('error', onError);\n      recorder.addEventListener('start', onStart);\n\n      return () => {\n        recorder.removeEventListener('error', onError);\n        recorder.removeEventListener('start', onStart);\n        recorder.removeEventListener('dataavailable', onDataavailable);\n      };\n    },\n    [mediaStream],\n  );\n\n  return [\n    audioRecorder,\n    requireMediaStreamError || recorderError,\n    recordedChunks,\n  ];\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { IconButton } from '../Button/IconButton.tsx';\nimport type {\n  ComponentProps,\n  SlotComponentPropsWithoutOverride,\n} from '../components.ts';\nimport { MicrophoneIcon, StopIcon } from '../icons/solid.tsx';\nimport { assocDefaultStyle } from '../utils/assign-default-style.ts';\nimport { mergeRootSlotPropsToComponentProps } from '../utils/merge-root-slot-props-to-component-prop.ts';\nimport { useAudioRecorder } from './use-media-recorder.ts';\n\ninterface SlotProps {\n  errorMessage?: SlotComponentPropsWithoutOverride<'p', object>;\n  errorTitle?: SlotComponentPropsWithoutOverride<'h1', object>;\n  root?: SlotComponentPropsWithoutOverride<'button', object>;\n  start?: SlotComponentPropsWithoutOverride<'button', object>;\n  stop?: SlotComponentPropsWithoutOverride<'button', object>;\n}\n\nexport type StartRecordingHandler = () => void;\nexport type StopRecordingHandler = (e: CustomEvent<{ value: Blob[] }>) => void;\n\nexport type AudioRecordProps = ComponentProps<\n  SlotProps,\n  {\n    onStartRecording?: StartRecordingHandler;\n    onStopRecording: StopRecordingHandler;\n  }\n>;\n\nexport function AudioRecorder({\n  'data-testid': testId,\n  disableDefaultClasses,\n  onStartRecording,\n  onStopRecording,\n  slotProps: givenSlotProps,\n  ...rest\n}: AudioRecordProps) {\n  const [audioRecorder, recorderError, recordChunks] = useAudioRecorder({\n    mimeType: 'audio/webm',\n  });\n  const [isRecording, setIsRecording] = useState(false);\n  const recordingChunks = useRef<unknown[] | null>(null);\n  const toggleIsRecording = useCallback(() => {\n    if (isRecording) {\n      audioRecorder?.stop();\n      setIsRecording(false);\n    } else {\n      recordingChunks.current = [];\n      audioRecorder?.start();\n      onStartRecording?.();\n      setIsRecording(true);\n    }\n  }, [audioRecorder, isRecording, onStartRecording]);\n  useEffect(() => {\n    if (!audioRecorder) return undefined;\n    const onRecorderStop = () => {\n      onStopRecording(\n        new CustomEvent('finishRecording', {\n          detail: {\n            value: recordChunks.current!,\n          },\n        }),\n      );\n      recordChunks.current = null;\n    };\n    audioRecorder.addEventListener('stop', onRecorderStop);\n    return () => {\n      audioRecorder.removeEventListener('stop', onRecorderStop);\n    };\n  }, [audioRecorder, onStopRecording, recordChunks]);\n  let slotProps = givenSlotProps;\n\n  if (!disableDefaultClasses) {\n    slotProps = assocDefaultStyle<SlotProps>({\n      slotWithDefaultClasses: {},\n    })(givenSlotProps);\n  }\n  const rootProps = mergeRootSlotPropsToComponentProps()(slotProps, rest);\n  // if (!audioRecorder) return null;\n  if (recorderError)\n    return (\n      <div>\n        <h1 className={slotProps?.errorTitle?.className}>\n          {recorderError.name}\n        </h1>\n        <p className={slotProps?.errorMessage?.className}>\n          {recorderError.message}\n        </p>\n      </div>\n    );\n  return (\n    <IconButton\n      data-testid={testId}\n      disableDefaultClasses={disableDefaultClasses}\n      onClick={toggleIsRecording}\n      slotProps={{\n        root: slotProps?.root,\n      }}\n      {...rootProps}\n    >\n      {isRecording ? (\n        <StopIcon className={slotProps?.stop?.className} />\n      ) : (\n        <MicrophoneIcon className={slotProps?.start?.className} />\n      )}\n    </IconButton>\n  );\n}\n"],"names":["useMediaStream","constraints","mediaStream","setMediaStream","useState","requireMediaStreamError","setRequireMediaStreamError","useEffect","useAudioRecorder","options","audioRecorder","setAudioRecorder","recorderError","setRecorderError","recordedChunks","useRef","recorder","onError","event","onStart","onDataavailable","_a","AudioRecorder","testId","disableDefaultClasses","onStartRecording","onStopRecording","givenSlotProps","rest","recordChunks","isRecording","setIsRecording","recordingChunks","toggleIsRecording","useCallback","onRecorderStop","slotProps","assocDefaultStyle","rootProps","mergeRootSlotPropsToComponentProps","jsx","_b","IconButton","StopIcon","_c","MicrophoneIcon","_d"],"mappings":"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"}