Commit cd2db0a7 authored by Eduardo Sanz García's avatar Eduardo Sanz García Committed by Eduardo

Add a `Destroyable` inteface

The purpose of this interface is to highlight developers that in order
to cleanly get ride of the classes that implement the interface, they
need to call the `destroy` method.

This interface doesn't actually enforce calling `destroy`, but hopefully
makes the omission more notorious.
parent 0e954e02
...@@ -66,6 +66,8 @@ function nearestPositionedAncestor(el) { ...@@ -66,6 +66,8 @@ function nearestPositionedAncestor(el) {
* @prop {() => any} onHighlight - Callback invoked when "Highlight" button is clicked * @prop {() => any} onHighlight - Callback invoked when "Highlight" button is clicked
* @prop {(annotations: Object[]) => any} onShowAnnotations - * @prop {(annotations: Object[]) => any} onShowAnnotations -
* Callback invoked when "Show" button is clicked * Callback invoked when "Show" button is clicked
*
* @typedef {import('../types/annotator').Destroyable} Destroyable
*/ */
/** /**
...@@ -76,6 +78,8 @@ function nearestPositionedAncestor(el) { ...@@ -76,6 +78,8 @@ function nearestPositionedAncestor(el) {
* the container for the toolbar that positions it on the page and isolates * the container for the toolbar that positions it on the page and isolates
* it from the page's styles using shadow DOM, and the `AdderToolbar` Preact * it from the page's styles using shadow DOM, and the `AdderToolbar` Preact
* component which actually renders the toolbar. * component which actually renders the toolbar.
*
* @implements Destroyable
*/ */
export class Adder { export class Adder {
/** /**
......
...@@ -9,11 +9,15 @@ import { ListenerCollection } from './util/listener-collection'; ...@@ -9,11 +9,15 @@ import { ListenerCollection } from './util/listener-collection';
* @prop {Element} [contentContainer] - The scrollable container element for the * @prop {Element} [contentContainer] - The scrollable container element for the
* document content. All of the highlights that the bucket bar's buckets point * document content. All of the highlights that the bucket bar's buckets point
* at should be contained within this element. * at should be contained within this element.
*
* @typedef {import('../types/annotator').Destroyable} Destroyable
*/ */
/** /**
* Controller for the "bucket bar" shown alongside the sidebar indicating where * Controller for the "bucket bar" shown alongside the sidebar indicating where
* annotations are in the document. * annotations are in the document.
*
* @implements Destroyable
*/ */
export default class BucketBar { export default class BucketBar {
/** /**
......
...@@ -6,6 +6,7 @@ import FrameObserver from './frame-observer'; ...@@ -6,6 +6,7 @@ import FrameObserver from './frame-observer';
/** /**
* @typedef {import('../types/annotator').AnnotationData} AnnotationData * @typedef {import('../types/annotator').AnnotationData} AnnotationData
* @typedef {import('../types/annotator').Destroyable} Destroyable
*/ */
/** /**
...@@ -16,6 +17,8 @@ import FrameObserver from './frame-observer'; ...@@ -16,6 +17,8 @@ import FrameObserver from './frame-observer';
* This class also has logic for injecting Hypothesis into iframes that * This class also has logic for injecting Hypothesis into iframes that
* are added to the page if they have the `enable-annotation` attribute set * are added to the page if they have the `enable-annotation` attribute set
* and are same-origin with the current document. * and are same-origin with the current document.
*
* @implements Destroyable
*/ */
export class CrossFrame { export class CrossFrame {
/** /**
......
...@@ -21,6 +21,7 @@ import { ListenerCollection } from './util/listener-collection'; ...@@ -21,6 +21,7 @@ import { ListenerCollection } from './util/listener-collection';
* @typedef {import('./util/emitter').EventBus} EventBus * @typedef {import('./util/emitter').EventBus} EventBus
* @typedef {import('../types/annotator').AnnotationData} AnnotationData * @typedef {import('../types/annotator').AnnotationData} AnnotationData
* @typedef {import('../types/annotator').Anchor} Anchor * @typedef {import('../types/annotator').Anchor} Anchor
* @typedef {import('../types/annotator').Destroyable} Destroyable
* @typedef {import('../types/annotator').Integration} Integration * @typedef {import('../types/annotator').Integration} Integration
* @typedef {import('../types/annotator').SidebarLayout} SidebarLayout * @typedef {import('../types/annotator').SidebarLayout} SidebarLayout
* @typedef {import('../types/api').Target} Target * @typedef {import('../types/api').Target} Target
...@@ -101,6 +102,8 @@ function resolveAnchor(anchor) { ...@@ -101,6 +102,8 @@ function resolveAnchor(anchor) {
* *
* The anchoring implementation defaults to a generic one for HTML documents and * The anchoring implementation defaults to a generic one for HTML documents and
* can be overridden to handle different document types. * can be overridden to handle different document types.
*
* @implements Destroyable
*/ */
export default class Guest { export default class Guest {
/** /**
......
...@@ -6,6 +6,7 @@ import { HTMLMetadata } from './html-metadata'; ...@@ -6,6 +6,7 @@ import { HTMLMetadata } from './html-metadata';
/** /**
* @typedef {import('../../types/annotator').Anchor} Anchor * @typedef {import('../../types/annotator').Anchor} Anchor
* @typedef {import('../../types/annotator').Destroyable} Destroyable
* @typedef {import('../../types/annotator').Integration} Integration * @typedef {import('../../types/annotator').Integration} Integration
*/ */
...@@ -16,6 +17,7 @@ import { HTMLMetadata } from './html-metadata'; ...@@ -16,6 +17,7 @@ import { HTMLMetadata } from './html-metadata';
* by a more specific integration (eg. for PDFs). * by a more specific integration (eg. for PDFs).
* *
* @implements {Integration} * @implements {Integration}
* @implements {Destroyable}
*/ */
export class HTMLIntegration { export class HTMLIntegration {
constructor(container = document.body) { constructor(container = document.body) {
......
...@@ -19,6 +19,7 @@ import { PDFMetadata } from './pdf-metadata'; ...@@ -19,6 +19,7 @@ import { PDFMetadata } from './pdf-metadata';
* @typedef {import('../../types/annotator').Anchor} Anchor * @typedef {import('../../types/annotator').Anchor} Anchor
* @typedef {import('../../types/annotator').AnnotationData} AnnotationData * @typedef {import('../../types/annotator').AnnotationData} AnnotationData
* @typedef {import('../../types/annotator').Annotator} Annotator * @typedef {import('../../types/annotator').Annotator} Annotator
* @typedef {import('../../types/annotator').Destroyable} Destroyable
* @typedef {import('../../types/annotator').HypothesisWindow} HypothesisWindow * @typedef {import('../../types/annotator').HypothesisWindow} HypothesisWindow
* @typedef {import('../../types/annotator').Integration} Integration * @typedef {import('../../types/annotator').Integration} Integration
* @typedef {import('../../types/annotator').SidebarLayout} SidebarLayout * @typedef {import('../../types/annotator').SidebarLayout} SidebarLayout
...@@ -48,8 +49,8 @@ function delay(ms) { ...@@ -48,8 +49,8 @@ function delay(ms) {
/** /**
* Integration that works with PDF.js * Integration that works with PDF.js
*
* @implements {Integration} * @implements {Integration}
* @implements {Destroyable}
*/ */
export class PDFIntegration { export class PDFIntegration {
/** /**
......
...@@ -2,6 +2,9 @@ import { createShadowRoot } from './util/shadow-root'; ...@@ -2,6 +2,9 @@ import { createShadowRoot } from './util/shadow-root';
import { render } from 'preact'; import { render } from 'preact';
import NotebookModal from './components/NotebookModal'; import NotebookModal from './components/NotebookModal';
/** @typedef {import('../types/annotator').Destroyable} Destroyable */
/** @implements Destroyable */
export default class Notebook { export default class Notebook {
/** /**
* @param {HTMLElement} element * @param {HTMLElement} element
......
...@@ -18,6 +18,8 @@ import { ListenerCollection } from './util/listener-collection'; ...@@ -18,6 +18,8 @@ import { ListenerCollection } from './util/listener-collection';
* @prop {boolean} expanded * @prop {boolean} expanded
* @prop {number} width * @prop {number} width
* @prop {number} height * @prop {number} height
*
* @typedef {import('../types/annotator').Destroyable} Destroyable
*/ */
// Minimum width to which the iframeContainer can be resized. // Minimum width to which the iframeContainer can be resized.
...@@ -49,6 +51,8 @@ function createSidebarIframe(config) { ...@@ -49,6 +51,8 @@ function createSidebarIframe(config) {
/** /**
* The `Sidebar` class creates (1) the sidebar application iframe, (2) its container, * The `Sidebar` class creates (1) the sidebar application iframe, (2) its container,
* as well as (3) the adjacent controls. * as well as (3) the adjacent controls.
*
* @implements Destroyable
*/ */
export default class Sidebar { export default class Sidebar {
/** /**
......
import { TinyEmitter } from 'tiny-emitter'; import { TinyEmitter } from 'tiny-emitter';
/** @typedef {import('../../types/annotator').Destroyable} Destroyable */
/** /**
* Emitter is a communication class that implements the publisher/subscriber * Emitter is a communication class that implements the publisher/subscriber
* pattern. It allows sending and listening events through a shared EventBus. * pattern. It allows sending and listening events through a shared EventBus.
* The different elements of the application can communicate with each other * The different elements of the application can communicate with each other
* without being tightly coupled. * without being tightly coupled.
*
* @implements Destroyable
*/ */
class Emitter { class Emitter {
/** /**
......
import { RPC } from './frame-rpc'; import { RPC } from './frame-rpc';
/** @typedef {import('../types/annotator').Destroyable} Destroyable */
/** /**
* The Bridge service sets up a channel between frames and provides an events * The Bridge service sets up a channel between frames and provides an events
* API on top of it. * API on top of it.
*
* @implements Destroyable
*/ */
export default class Bridge { export default class Bridge {
constructor() { constructor() {
......
...@@ -39,12 +39,16 @@ const VERSION = '1.0.0'; ...@@ -39,12 +39,16 @@ const VERSION = '1.0.0';
* @prop {any[]} arguments * @prop {any[]} arguments
* *
* @typedef {RequestMessage|ResponseMessage} Message * @typedef {RequestMessage|ResponseMessage} Message
*
* @typedef {import('../types/annotator').Destroyable} Destroyable
*/ */
/** /**
* Class for making RPC requests between frames. * Class for making RPC requests between frames.
* *
* Code adapted from https://github.com/substack/frame-rpc. * Code adapted from https://github.com/substack/frame-rpc.
*
* @implements Destroyable
*/ */
export class RPC { export class RPC {
/** /**
......
...@@ -151,3 +151,11 @@ ...@@ -151,3 +151,11 @@
// Make TypeScript treat this file as a module. // Make TypeScript treat this file as a module.
export const unused = {}; export const unused = {};
/**
* Destroyable classes implement the `destroy` method to properly remove all
* event handlers and other resources.
*
* @typedef Destroyable
* @prop {VoidFunction} destroy
*/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment