Skip to content

Commit

Permalink
Add grouping
Browse files Browse the repository at this point in the history
  • Loading branch information
ArthurClemens committed Mar 6, 2023
1 parent cb15bff commit 96c7b9e
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 32 deletions.
39 changes: 36 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Simple undo manager to provide undo and redo actions in JavaScript applications.
- [Installation](#installation)
- [Example](#example)
- [Methods](#methods)
- [add](#add)
- [undo](#undo)
- [redo](#redo)
- [clear](#clear)
Expand Down Expand Up @@ -49,8 +50,7 @@ undoManager.add({
});
```

To make an action undoable, you'd add an undo/redo pair to the undo manager:

To make an action undoable, you'd add an undo/redo command pair to the undo manager:

```js
const undoManager = new UndoManager();
Expand Down Expand Up @@ -93,6 +93,34 @@ console.log(people); // logs: {101: "John"}

## Methods

### add

Adds an undo/redo command pair to the stack.

```js
function createPerson(id, name) {
// first creation
addPerson(id, name);

// make undoable
undoManager.add({
undo: () => removePerson(id),
redo: () => addPerson(id, name)
});
}
```

Optionally add a `groupId` to identify related command pairs. Undo and redo actions will then be performed on all adjacent command pairs with that group id.

```js
undoManager.add({
groupId: 'auth',
undo: () => removePerson(id),
redo: () => addPerson(id, name)
});
```


### undo

Performs the undo action.
Expand All @@ -101,6 +129,8 @@ Performs the undo action.
undoManager.undo();
```

If a `groupId` was set, the undo action will be performed on all adjacent command pairs with that group id.

### redo

Performs the redo action.
Expand All @@ -109,6 +139,8 @@ Performs the redo action.
undoManager.redo();
```

If a `groupId` was set, the redo action will be performed on all adjacent command pairs with that group id.

### clear

Clears all stored states.
Expand Down Expand Up @@ -159,10 +191,11 @@ const index = undoManager.getIndex();

### getCommands

Returns the list of queued commands.
Returns the list of queued commands, optionally filtered by group id.

```js
const commands = undoManager.getCommands();
const commands = undoManager.getCommands(groupId);
```

## Use with CommonJS
Expand Down
7 changes: 7 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@
placeholder="Limit: 0"
/>
<div class="spacer"></div>
<button
type="button"
class="btn btn-default"
id="btnGroup"
>
Group
</button>
<button
type="button"
class="btn btn-default"
Expand Down
11 changes: 11 additions & 0 deletions demo/js/circledrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const CircleDrawer = function (canvasId, undoManager) {
const circles = [];
const circleId = 0;
const drawingCanvas = window.document.getElementById(canvasId);
let groupId;

if (drawingCanvas.getContext === undefined) {
return;
Expand Down Expand Up @@ -61,6 +62,7 @@ const CircleDrawer = function (canvasId, undoManager) {
circles.push(attrs);
draw();
undoManager.add({
groupId,
undo: function () {
removeCircle(attrs.id);
},
Expand All @@ -70,6 +72,13 @@ const CircleDrawer = function (canvasId, undoManager) {
});
}

function setGroupId(id) {
groupId = id;
}
function clearGroupId() {
groupId = undefined;
}

drawingCanvas.addEventListener(
'click',
function (e) {
Expand Down Expand Up @@ -121,5 +130,7 @@ const CircleDrawer = function (canvasId, undoManager) {

return {
clearAll,
setGroupId,
clearGroupId,
};
};
12 changes: 11 additions & 1 deletion demo/js/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ window.onload = function () {

const undoManager = new window.UndoManager();
const circleDrawer = new CircleDrawer('view', undoManager);

const ctrlLimit = document.getElementById('ctrlLimit');
const btnUndo = document.getElementById('btnUndo');
const btnRedo = document.getElementById('btnRedo');
const btnClearMemory = document.getElementById('btnClearMemory');
const btnClearScreen = document.getElementById('btnClearScreen');
const btnGroup = document.getElementById('btnGroup');

function updateUI() {
btnUndo.disabled = !undoManager.hasUndo();
Expand Down Expand Up @@ -37,6 +37,16 @@ window.onload = function () {
updateUI();
});

btnGroup.addEventListener('click', function () {
const c = btnGroup.classList;
c.toggle('active');
if (c.contains('active')) {
circleDrawer.setGroupId(new Date().getTime());
} else {
circleDrawer.clearGroupId();
}
});

function handleLimit(rawLimit) {
const limit = parseInt(rawLimit, 10);
if (!isNaN(limit)) {
Expand Down
45 changes: 29 additions & 16 deletions lib/undomanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ https://github.com/ArthurClemens/JavaScript-Undo-Manager
index = -1,
limit = 0,
isExecuting = false,
callback,
// functions
execute;
callback;

/**
* Executes a single command.
Expand All @@ -34,7 +32,7 @@ https://github.com/ArthurClemens/JavaScript-Undo-Manager
* @property {function} command.redo - Redo function
* @property {string} action - "undo" or "redo"
*/
execute = function (command, action) {
function execute(command, action) {
if (!command || typeof command[action] !== 'function') {
return this;
}
Expand All @@ -44,14 +42,15 @@ https://github.com/ArthurClemens/JavaScript-Undo-Manager

isExecuting = false;
return this;
};
}

return {
/**
* Adds a command to the queue.
* @property {object} command - Command
* @property {function} command.undo - Undo function
* @property {function} command.redo - Redo function
* @property {object} command - Command
* @property {function} command.undo - Undo function
* @property {function} command.redo - Redo function
* @property {string} [command.groupId] - Optional group id
*/
add: function (command) {
if (isExecuting) {
Expand All @@ -60,7 +59,6 @@ https://github.com/ArthurClemens/JavaScript-Undo-Manager
// if we are here after having called undo,
// invalidate items higher on the stack
commands.splice(index + 1, commands.length - index);

commands.push(command);

// if limit is set, remove items from the start
Expand All @@ -76,7 +74,7 @@ https://github.com/ArthurClemens/JavaScript-Undo-Manager
return this;
},

/**
/**
* Pass a function to be called on undo and redo actions.
* @property {function} callbackFunc - Callback function
*/
Expand All @@ -92,8 +90,15 @@ https://github.com/ArthurClemens/JavaScript-Undo-Manager
if (!command) {
return this;
}
execute(command, 'undo');
index -= 1;

const groupId = command.groupId;
while (command.groupId === groupId) {
execute(command, 'undo');
index -= 1;
command = commands[index];
if (!command || !command.groupId) break;
}

if (callback) {
callback();
}
Expand All @@ -108,8 +113,15 @@ https://github.com/ArthurClemens/JavaScript-Undo-Manager
if (!command) {
return this;
}
execute(command, 'redo');
index += 1;

const groupId = command.groupId;
while (command.groupId === groupId) {
execute(command, 'redo');
index += 1;
command = commands[index + 1];
if (!command || !command.groupId) break;
}

if (callback) {
callback();
}
Expand Down Expand Up @@ -148,10 +160,11 @@ https://github.com/ArthurClemens/JavaScript-Undo-Manager

/**
* Returns the list of queued commands.
* @param {string} [groupId] - Optionally filter commands by group ID
* @returns {array}
*/
getCommands: function () {
return commands;
getCommands: function (groupId) {
return groupId ? commands.filter(c => c.groupId === groupId) : commands;
},

/**
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "undo-manager",
"version": "1.0.6",
"version": "1.1.0",
"description": "Simple undo manager to provide undo and redo actions in JavaScript applications.",
"main": "lib/undomanager.js",
"scripts": {
Expand Down
54 changes: 54 additions & 0 deletions spec/SpecRunner.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!-- Use this file to manually iterate on tests. -->
<!DOCTYPE html>
<html>
<head>
<title>Jasmine Spec Runner for Undo Manager</title>

<link
rel="stylesheet"
type="text/css"
href="http://cdnjs.cloudflare.com/ajax/libs/jasmine/1.3.1/jasmine.css"
/>
<script
type="text/javascript"
src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/1.3.1/jasmine.js"
></script>
<script
type="text/javascript"
src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/1.3.1/jasmine-html.js"
></script>

<script type="text/javascript" src="../lib/undomanager.js"></script>
<script type="text/javascript" src="undomanager.spec.js"></script>

<script type="text/javascript">
(function () {
const jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;

const htmlReporter = new jasmine.HtmlReporter();

jasmineEnv.addReporter(htmlReporter);

jasmineEnv.specFilter = function (spec) {
return htmlReporter.specFilter(spec);
};

const currentWindowOnload = window.onload;

window.onload = function () {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};

function execJasmine() {
jasmineEnv.execute();
}
})();
</script>
</head>

<body></body>
</html>
Loading

0 comments on commit 96c7b9e

Please sign in to comment.