UNPKG

app-walkthrough

Version:

An intuitive guided walkthrough library with UI highlighting and voice narration for web apps.

81 lines (80 loc) 2.75 kB
import { debounce } from "es-toolkit"; import { addRecordingButton, recordEvent } from "./utils"; import { RECORDABLE_EVENT_TYPES, RECORDER_UI_CONTAINER_CLASS, } from "./constants"; export class ActionRecorder { reset() { if (this.state.isRecording) { this.stopRecording(); } } constructor() { const wasRecording = localStorage.getItem("isRecording") === "true"; this.state = { isInitialized: false, isRecording: wasRecording, events: [], recordingStartTime: 0, pageId: window.location.pathname, }; this.uiContainer = undefined; const boundRecordEvent = this._recordEvent.bind(this); this.recordEvent = debounce(boundRecordEvent, 500); this.startRecording = this.startRecording.bind(this); this.stopRecording = this.stopRecording.bind(this); this.updateUI = this.updateUI.bind(this); } static getInstance() { if (!this.instance) { this.instance = new ActionRecorder(); this.instance.init(); } return this.instance; } getRecordingState() { return this.state.isRecording; } _setRecordingState(isRecording) { this.state.isRecording = isRecording; localStorage.setItem("isRecording", isRecording ? "true" : "false"); } _recordEvent(e) { recordEvent(e, this.state); } startRecording() { if (this.state.isRecording) return; this.state.isRecording = true; this.state.events = []; this.state.recordingStartTime = performance.now(); RECORDABLE_EVENT_TYPES.forEach((type) => { document.addEventListener(type, this.recordEvent, { capture: true }); }); this._setRecordingState(true); this.updateUI(); } stopRecording() { this._setRecordingState(false); RECORDABLE_EVENT_TYPES.forEach((type) => document.removeEventListener(type, this.recordEvent, { capture: true })); if (this.uiContainer) { this.uiContainer.innerHTML = ""; } } getEvents() { return this.state.events; } updateUI() { if (!this.uiContainer) { this.uiContainer = document.createElement("div"); this.uiContainer.id = "recorder-ui-container"; this.uiContainer.className = RECORDER_UI_CONTAINER_CLASS; document.body.appendChild(this.uiContainer); } addRecordingButton(this.uiContainer, this.state, this.stopRecording); } init() { if (this.state.isInitialized) return; this.state.isInitialized = true; this.updateUI(); } }