UNPKG

@atlaskit/editor-plugin-interactivity

Version:

Interactivity plugin for @atlaskit/editor-core

90 lines (85 loc) 3.45 kB
import { REPORTING_THRESHOLD_MS } from './bucket-boundaries'; /** `performance.interactionCount` is Chromium-only and absent from the DOM typings. */ const interactionCount = () => performance.interactionCount; /** `durationThreshold` is absent from the DOM typings for `PerformanceObserverInit`. */ /** * Reports the interactions the browser observes to `onEntries`. * * `drain` and `stop` are safe to call before `start` and after each other, so a session * that never started collecting needs no special handling. */ export class InteractionObserver { /** * Whether the browser reports both things a session needs: `interactionId`, which groups * entries into interactions, and `performance.interactionCount`, which counts the ones * below the reporting threshold. Both are Chromium-only, and without the count * `totalCount` would be indistinguishable from `observedCount`. */ static isSupported() { if (typeof window === 'undefined' || typeof PerformanceObserver === 'undefined') { return false; } if (!('PerformanceEventTiming' in window) || !('interactionId' in PerformanceEventTiming.prototype)) { return false; } if (!PerformanceObserver.supportedEntryTypes.includes('event')) { return false; } return typeof interactionCount() === 'number'; } /** * Total interactions on the page since page load, including those below the reporting * threshold that no observer ever sees. A property of the page, not of an observer. */ static readPageInteractionCount() { var _interactionCount; return (_interactionCount = interactionCount()) !== null && _interactionCount !== void 0 ? _interactionCount : 0; } constructor(onEntries) { this.onEntries = onEntries; } /** * Starts reporting interactions from this point on. Does nothing when already started, so * a second call cannot leave an observer running with nobody to disconnect it. * * `buffered` is `false`: the entries the browser collected earlier are interactions with * the page while the editor was still loading, and they belong to no session of ours. */ start() { if (this.observer) { return; } this.observer = new PerformanceObserver(list => { // Delay by a microtask to work around a Safari bug where the callback is // invoked synchronously rather than in a separate task. // See: https://github.com/GoogleChrome/web-vitals/issues/277 Promise.resolve().then(() => { this.onEntries(list.getEntries()); }); }); const init = { type: 'event', buffered: false, // 16 ms is also the smallest value the spec honours; lower values are clamped. durationThreshold: REPORTING_THRESHOLD_MS }; this.observer.observe(init); } /** * Synchronously reports the entries the browser has produced but not yet dispatched to * the callback. A snapshot taken because the page is going away has to include them, * because there is no later chance to. */ drain() { var _this$observer; const entries = (_this$observer = this.observer) === null || _this$observer === void 0 ? void 0 : _this$observer.takeRecords(); if (entries) { this.onEntries(entries); } } stop() { var _this$observer2; (_this$observer2 = this.observer) === null || _this$observer2 === void 0 ? void 0 : _this$observer2.disconnect(); this.observer = undefined; } }