Skip to content

Commit

Permalink
Add typescript, vite and vitest
Browse files Browse the repository at this point in the history
  • Loading branch information
NansPellicari committed Feb 14, 2025
1 parent 5700af7 commit 5b6bad5
Show file tree
Hide file tree
Showing 23 changed files with 1,980 additions and 3,470 deletions.
10 changes: 0 additions & 10 deletions babel.config.json

This file was deleted.

2,256 changes: 317 additions & 1,939 deletions dist/bundle.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { default as Undo } from './undo';
export { type UndoConfig, type UndoConstructor, type UndoSettings } from './undo';
export * from './observer';
export * from './types';
export default Undo;
37 changes: 37 additions & 0 deletions dist/observer.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* @typedef {Object} Observer
* @description Custom MutationObserver to detect changes in the editor.
* @property {String} holder — Editor.js holder id.
* @property {Object} observer - MutationObserver object that detects changes in the editor.
* @property {Number} debounceTimer - Delay time for the debouncer.
* @property {Function} mutationDebouncer - Debouncer to delay the changes registration.
*/
export default class Observer {
private debounceTimer;
private holder;
private mutationDebouncer;
private observer;
/**
* Creates a new instance of the Observer object.
* @param {Function} registerChange - Function that register a change in the history stack.
* @param {String} holder - Editor.js holder id.
* @param {Number} debounceTimer Delay time for the debouncer.
*/
constructor(registerChange: () => void, holder: Element, debounceTimer: number);
/**
* Sets a mutation observer to catch every change in the editor.
*/
setMutationObserver(): void;
/**
* Handles the mutations and checks if a new mutation has been produced.
* @param {Object} mutationList The registered mutations
*/
mutationHandler(mutationList: MutationRecord[]): void;
/**
* Delays invoking a function until after wait millis have elapsed.
* @param {Function} callback The function to be delayed.
* @param {Number} wait The deplay time in millis.
*/
debounce(callback: (...args: unknown[]) => void, wait: number): (...args: unknown[]) => void;
onDestroy(): void;
}
4 changes: 4 additions & 0 deletions dist/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { default as EditorJS, EditorConfig } from '@editorjs/editorjs';
export interface EditorJsReady extends EditorJS {
configuration: EditorConfig;
}
165 changes: 165 additions & 0 deletions dist/undo.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { default as EditorJS, OutputBlockData, OutputData } from '@editorjs/editorjs';
import { EditorJsReady } from './types';
import { Blocks, Caret } from '@editorjs/editorjs/types/api';
export type UndoConfig = {
debounceTimer: number;
shortcuts: {
redo: string[];
undo: string[];
};
};
export type UndoSettings = {
debounceTimer?: number;
shortcuts?: {
redo?: string[] | string;
undo?: string[] | string;
};
};
interface StackStated {
caretIndex?: null | number;
index: number;
state: OutputBlockData[];
}
export type UndoConstructor = {
config?: UndoSettings;
maxLength?: number;
onUpdate?: () => void;
editor: EditorJS;
};
/**
* Undo/Redo feature for Editor.js.
*
* @typedef {Object} Undo
* @description Feature's initialization class.
* @property {Object} editor — Editor.js instance object.
* @property {Number} maxLength - Max amount of changes recorded by the history stack.
* @property {Function} onUpdate - Callback called when the user performs an undo or redo action.
* @property {Boolean} shouldSaveHistory - Defines if the plugin should save the change in the stack
* @property {Object} initialItem - Initial data object.
*/
export default class Undo {
blocks: Blocks;
caret: Caret;
config: UndoConfig;
defaultBlock: string | undefined;
editor: EditorJsReady;
holder: HTMLElement | null | undefined;
initialItem: null | StackStated;
maxLength: number;
onUpdate: () => void;
position: number;
readOnly: boolean | undefined;
shouldSaveHistory: boolean;
stack: StackStated[];
/**
* @param options — Plugin custom options.
*/
constructor({ editor, config, onUpdate, maxLength }: UndoConstructor);
/**
* Notify core that read-only mode is suppoorted
*
* @returns {boolean}
*/
static get isReadOnlySupported(): boolean;
/**
* Truncates the history stack when it excedes the limit of changes.
*
* @param {Object} stack Changes history stack.
* @param {Number} stack Limit of changes recorded by the history stack.
*/
truncate(stack: StackStated[], limit: number): void;
/**
* Initializes the stack when the user provides initial data.
*
* @param {Object} initialItem Initial data provided by the user.
*/
initialize(initialItem: OutputData | OutputBlockData[]): void;
/**
* Clears the history stack.
*/
clear(): void;
/**
* Returns true if readOnly was toggled to true
* @returns {Node} Indirectly shows if readOnly was set to true or false
*/
setReadOnly(): void;
/**
* Registers the data returned by API's save method into the history stack.
*/
registerChange(): void;
/**
* Checks if the saved data has to be added to the history stack.
*
* @param {Object} newData New data to be saved in the history stack.
* @returns {Boolean}
*/
editorDidUpdate(newData: OutputBlockData[]): boolean;
/**
* Adds the saved data in the history stack and updates current position.
*/
save(state: OutputBlockData[]): void;
/**
* Gets the caret position.
* @param {Number} index is the block index
* @returns The caret position
*/
getCaretIndex(index: number): number | null;
/**
* Decreases the current position and update the respective block in the editor.
*/
undo(): Promise<void>;
/**
* Sets the caret position.
* @param {Number} index is the block index
* @param {Number} caretIndex is the caret position
* @param {Array} state is the current state according to this.position.
*/
setCaretIndex(index: number, caretIndex: number): void;
/**
* Inserts new block
* @param {Array} state is the current state according to this.position.
* @param {Number} index is the block index
*/
insertBlock(state: OutputBlockData[], index: number): void;
/**
* Updates the passed block or render the state when the content was copied.
* @param {Array} state is the current state according to this.position.
* @param {Number} index is the block index.
*/
updateModifiedBlock(state: OutputBlockData[], index: number): Promise<void | import('@editorjs/editorjs').BlockAPI>;
/**
* Increases the current position and update the respective block in the editor.
*/
redo(): Promise<void>;
switchState(stateToApply: OutputBlockData[], stateToCompare: OutputBlockData[]): Promise<void>;
/**
* Checks if the history stack can perform an undo action.
*
* @returns {Boolean}
*/
canUndo(): boolean;
/**
* Checks if the history stack can perform a redo action.
*
* @returns {Boolean}
*/
canRedo(): boolean;
/**
* Returns the number of changes recorded in the history stack.
*
* @returns {Number}
*/
count(): number;
/**
* Parses the keys passed in the shortcut property to accept CMD,ALT and SHIFT
*
* @param {Array} keys are the keys passed in shortcuts in config
* @returns {Array}
*/
parseKeys(keys: string[]): string[];
/**
* Sets events listeners to allow keyboard actions support
*/
setEventListeners(): void;
}
export {};
34 changes: 18 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@
],
"description": "Undo tool for Editor.js",
"main": "./dist/bundle.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "webpack --mode production",
"build:dev": "webpack --mode development --watch",
"test": "jest"
"build": "vite build --mode production",
"build:dev": "vite",
"test": "vitest run",
"test:watch": "vitest"
},
"repository": {
"type": "git",
"url": "git+https://github.com/kommitters/editorjs-undo"
},
"type": "module",
"author": {
"name": "kommitters Open Source",
"email": "[email protected]"
Expand All @@ -28,28 +31,27 @@
},
"homepage": "https://github.com/kommitters/editorjs-undo#readme",
"devDependencies": {
"@babel/core": "^7.10.2",
"@babel/plugin-transform-runtime": "^7.23.2",
"@babel/preset-env": "^7.10.2",
"babel-jest": "^29.0.0",
"babel-loader": "^9.0.0",
"@editorjs/editorjs": "^2.30.8",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@types/deep-equal": "^1.0.4",
"eslint": "^8.3.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jest": "^27.0.0",
"jest": "^29.0.0",
"jest-environment-jsdom": "^29.3.1",
"webpack": "^5.52.0",
"webpack-cli": "^5.0.0"
},
"jest": {
"transform": {
"^.+\\.jsx?$": "babel-jest",
"^.+\\.(css|svg)$": "<rootDir>/test/config/assetsTransform.js"
}
"rollup-plugin-node-externals": "^8.0.0",
"typescript": "^5.7.3",
"vite": "^6.1.0",
"vite-plugin-dts": "^4.5.0",
"vitest": "^3.0.5"
},
"dependencies": {
"deep-equal": "^2.2.3",
"vanilla-caret-js": "^1.0.1"
},
"peerDependencies": {
"@editorjs/editorjs": ">=2"
}
}
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Undo from "./undo";
export { type UndoConfig, type UndoConstructor, type UndoSettings } from "./undo";
export * from "./observer";
export * from "./types";

export default Undo;
29 changes: 19 additions & 10 deletions src/observer.js → src/observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@
* @property {Function} mutationDebouncer - Debouncer to delay the changes registration.
*/
export default class Observer {
private debounceTimer: number;
private holder: Element;
private mutationDebouncer: (...args: any[]) => void;
private observer: MutationObserver | null;

/**
* Creates a new instance of the Observer object.
* @param {Function} registerChange - Function that register a change in the history stack.
* @param {String} holder - Editor.js holder id.
* @param {Number} debounceTimer Delay time for the debouncer.
*/
constructor(registerChange, holder, debounceTimer) {
constructor(registerChange: () => void, holder: Element, debounceTimer: number) {
this.holder = holder;
this.observer = null;
this.debounceTimer = debounceTimer;
Expand All @@ -39,14 +44,14 @@ export default class Observer {
this.observer = new MutationObserver((mutationList) => {
this.mutationHandler(mutationList);
});
this.observer.observe(target, observerOptions);
this.observer.observe(target as Node, observerOptions);
}

/**
* Handles the mutations and checks if a new mutation has been produced.
* @param {Object} mutationList The registered mutations
*/
mutationHandler(mutationList) {
mutationHandler(mutationList: MutationRecord[]) {
let contentMutated = false;

mutationList.forEach((mutation) => {
Expand All @@ -62,7 +67,11 @@ export default class Observer {
contentMutated = true;
break;
case 'attributes':
if (!mutation.target.classList.contains('ce-block') && !mutation.target.classList.contains('tc-toolbox')) {
if (
mutation.target instanceof Element &&
!mutation.target.classList.contains('ce-block') &&
!mutation.target.classList.contains('tc-toolbox')
) {
contentMutated = true;
}
break;
Expand All @@ -79,18 +88,18 @@ export default class Observer {
* @param {Function} callback The function to be delayed.
* @param {Number} wait The deplay time in millis.
*/
debounce(callback, wait) {
let timeout;
return (...args) => {
debounce(callback: (...args: unknown[]) => void, wait: number) {
let timeout: number;
return (...args: unknown[]) => {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => callback.apply(context, args), wait);
window.clearTimeout(timeout);
timeout = window.setTimeout(() => callback.apply(context, args), wait);
};
}

onDestroy() {
const destroyEvent = new CustomEvent('destroy');
document.dispatchEvent(destroyEvent);
this.observer.disconnect();
this.observer != null && this.observer.disconnect();
}
}
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import EditorJS from '@editorjs/editorjs';
import { EditorConfig } from '@editorjs/editorjs';

export interface EditorJsReady extends EditorJS {
configuration: EditorConfig;
}
Loading

0 comments on commit 5b6bad5

Please sign in to comment.