-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reworked API, rewrite unit tests and readme
- Loading branch information
1 parent
cb8d4f2
commit f129ffd
Showing
3 changed files
with
159 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,60 @@ | ||
# StackID | ||
|
||
A lightweight JavaScript package for managing stacked views like modals. Easily create, push, and pop while ensuring secure closure using unique IDs. Ideal for handling modal interactions, such as closing with specific triggers like 'esc' key, while maintaining a clean and efficient stack structure | ||
A lightweight JavaScript package for managing stacked views like modals. Easily create, push, and pop while ensuring secure closure using unique IDs. Ideal for handling modal interactions, such as closing with specific triggers like 'esc' key, while maintaining a clean and efficient stack structure. | ||
|
||
## Installation | ||
|
||
You can install the StackID using npm: | ||
|
||
```bash | ||
npm install --save stackid | ||
``` | ||
|
||
## Usage | ||
|
||
### Creating and use a stack | ||
|
||
To create a new stack, use the `createStack` function: | ||
|
||
```ts | ||
import { createStack } from 'stackid'; | ||
|
||
// Create a stack | ||
const stack = createStack(); | ||
|
||
const id1 = stack.pushStack(); // Pushes a new identifier (e.g., 0) | ||
const id2 = stack.pushStack(); // Pushes another identifier (e.g., 1) | ||
|
||
console.log(stack.onTopStack(id2)); // true | ||
|
||
stack.popStack(id2); // Pops the identifier from the stack | ||
|
||
console.log(stack.getState()); // [0] | ||
``` | ||
|
||
### Managing the stack | ||
|
||
StackID provides several methods for manipulating the stack: | ||
|
||
- `pushStack`: Pushes a new identifier onto the stack and returns the assigned stack id. | ||
- `popStack`: Pops the identifier from the stack if it is on top. Returns the popped stack id or undefined if the stack is empty or the provided identifier is not on top. | ||
- `resetStack`: Resets the stack, removing all identifiers. | ||
- `onTopStack`: Checks if the provided identifier is on top of the stack. | ||
- `getState`: Returns an immutable state of the current stack. | ||
|
||
### Customizing Identifier Type | ||
|
||
You can customize the type of identifiers used in the stack by providing a type parameter to the `createStack` function. By default, the stack uses `number`: | ||
|
||
```ts | ||
const stack = createStack(() => crypto.randomUUID()); | ||
pushStack(); // '44f70f2c-2ba6-4c5f-b21b-36a268360e7d | ||
``` | ||
|
||
## Contributing | ||
|
||
Contributions are welcome! If you have any suggestions, improvements, or bug fixes, please open an issue or submit a pull request. | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,73 +1,88 @@ | ||
import { beforeEach, expect, test } from 'vitest'; | ||
import { pushStack, popStack, stack, createStack, resetStack } from '.'; | ||
import { expect, test } from 'vitest'; | ||
import { createStack } from '.'; | ||
|
||
beforeEach(async () => { | ||
resetStack(); | ||
}); | ||
|
||
test('push to global stack', () => { | ||
const depth1 = pushStack(); | ||
expect(stack).toStrictEqual([depth1]); | ||
}); | ||
|
||
test('pop from the global stack', () => { | ||
const depth1 = pushStack(); | ||
expect(stack).toStrictEqual([depth1]); | ||
popStack(depth1); | ||
expect(stack.length).toBe(0); | ||
}); | ||
|
||
test('reset the global stack', () => { | ||
test('should use a different stack id creation algorith', () => { | ||
const { getState, pushStack } = createStack(() => crypto.randomUUID()); | ||
pushStack(); | ||
expect(stack.length).toBe(1); | ||
resetStack(); | ||
expect(stack.length).toBe(0); | ||
pushStack(); | ||
pushStack(); | ||
expect(getState().length).toBe(3); | ||
expect(getState()[0]).toBeTypeOf('string'); | ||
}); | ||
|
||
test('global stack meganism', () => { | ||
expect(stack.length).toBe(0); | ||
test('should do stack operations push and pop', () => { | ||
const { getState, pushStack, popStack } = createStack(); | ||
|
||
expect(getState().length).toBe(0); | ||
|
||
const depth1 = pushStack(); | ||
expect(stack).toStrictEqual([depth1]); | ||
expect(getState()).toStrictEqual([depth1]); | ||
|
||
const depth2 = pushStack(); | ||
expect(stack).toStrictEqual([depth1, depth2]); | ||
expect(getState()).toStrictEqual([depth1, depth2]); | ||
|
||
popStack(depth1); // depth1 is not on top of the stack, so nothing will change. | ||
expect(stack).toStrictEqual([depth1, depth2]); | ||
expect(getState()).toStrictEqual([depth1, depth2]); | ||
|
||
const depth3 = pushStack(); | ||
expect(stack).toStrictEqual([depth1, depth2, depth3]); | ||
expect(getState()).toStrictEqual([depth1, depth2, depth3]); | ||
|
||
const popDepth2 = popStack(depth1); | ||
expect(popDepth2).toBeUndefined(); | ||
|
||
const popDepth3 = popStack(depth3); | ||
expect(popDepth3).toBeDefined(); | ||
|
||
expect(stack).toStrictEqual([depth1, depth2]); | ||
expect(getState()).toStrictEqual([depth1, depth2]); | ||
|
||
const depth4 = pushStack(); | ||
expect(stack).toStrictEqual([depth1, depth2, depth4]); | ||
expect(getState()).toStrictEqual([depth1, depth2, depth4]); | ||
|
||
// Pop all of the stack | ||
popStack(depth4); | ||
popStack(depth2); | ||
popStack(depth1); | ||
|
||
expect(stack.length).toBe(0); | ||
expect(getState().length).toBe(0); | ||
}); | ||
|
||
test('local stack', () => { | ||
test('should allow to use multiple stacks', () => { | ||
const stack1 = createStack(); | ||
const { stack: stack2, pushStack: pushStack2 } = createStack(); | ||
const stack2 = createStack(); | ||
|
||
const s1d1 = stack1.pushStack(); | ||
expect(stack1.stack).toStrictEqual([s1d1]); | ||
expect(stack1.getState()).toStrictEqual([s1d1]); | ||
|
||
const s2d1 = pushStack2(); | ||
expect(stack2).toStrictEqual([s2d1]); | ||
const s2d1 = stack2.pushStack(); | ||
expect(stack2.getState()).toStrictEqual([s2d1]); | ||
|
||
stack1.popStack(s1d1); | ||
expect(stack1.stack.length).toBe(0); | ||
expect(stack1.getState().length).toBe(0); | ||
}); | ||
|
||
test('should reset the stack', () => { | ||
const { getState, pushStack, resetStack } = createStack(); | ||
|
||
pushStack(); | ||
pushStack(); | ||
expect(getState().length).toBe(2); | ||
|
||
resetStack(); | ||
expect(getState().length).toBe(0); | ||
}); | ||
|
||
test('should detemine if a id is on top of the stack', () => { | ||
const { onTopStack, pushStack } = createStack(); | ||
const first = pushStack(); | ||
const second = pushStack(); | ||
expect(onTopStack(first)).toBe(false); | ||
expect(onTopStack(second)).toBe(true);; | ||
}); | ||
|
||
test('should able to force pop from the stack', () => { | ||
const { getState, popStack, pushStack } = createStack(); | ||
const first = pushStack(); | ||
const second = pushStack(); | ||
popStack(first, true); | ||
expect(getState().length).toBe(1); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,58 @@ | ||
export type StackId = number; | ||
export type Stack = StackId[]; | ||
|
||
let stackId: StackId = 0; | ||
|
||
export const stack: Stack = []; | ||
function genNextId<T>(prev?: T): T { | ||
return (typeof prev === 'number' ? prev + 1 : 0) as T; | ||
} | ||
|
||
const _reset = (_stack: Stack) => { | ||
_stack.length = 0; | ||
}; | ||
export function createStack<T = number>(nextId?: (prev?: T) => T) { | ||
let curId: T | undefined; | ||
const stack: T[] = []; | ||
|
||
const _push = (_stack: Stack) => { | ||
const id = ++stackId; | ||
_stack.push(id); | ||
return id; | ||
}; | ||
/** | ||
* Checks if the provided identifier is on top of the stack. | ||
* @param id | ||
*/ | ||
function onTopStack(id: T) { | ||
return stack.length > 0 && stack[stack.length - 1] === id; | ||
} | ||
|
||
const _pop = (_stack: Stack, id: StackId) => { | ||
if (_stack.length > 0 && _stack[_stack.length - 1] === id) { | ||
return _stack.pop(); | ||
/** | ||
* Resets the stack, removing all identifiers. | ||
*/ | ||
function resetStack() { | ||
curId = undefined; | ||
stack.length = 0; | ||
} | ||
}; | ||
|
||
/** | ||
* Push something to the stack. | ||
* @returns The stack id which needs to be stored in order to popStack. | ||
*/ | ||
export const pushStack = () => _push(stack); | ||
/** | ||
* Pushes a new identifier onto the stack and returns the assigned stack id. | ||
*/ | ||
function pushStack() { | ||
curId = nextId ? nextId(curId) : genNextId(curId); | ||
stack.push(curId); | ||
return curId; | ||
} | ||
|
||
/** | ||
* Pop from the stack. | ||
* @returns The stack id that has been popped of. | ||
*/ | ||
export const popStack = (id: StackId) => _pop(stack, id); | ||
/** | ||
* Pops the identifier from the stack if it is on top. | ||
* Returns the popped stack id or undefined if the stack is empty or the provided identifier is not on top. | ||
* @param id | ||
* @param force | ||
*/ | ||
function popStack(id: T, force?: boolean) { | ||
if (onTopStack(id) || force) return stack.pop(); | ||
} | ||
|
||
export const resetStack = () => _reset(stack); | ||
/** | ||
* Get a immutable state of the current stack. | ||
*/ | ||
function getState(): T[] { | ||
return [...stack]; | ||
} | ||
|
||
export function createStack() { | ||
const localStack: Stack = []; | ||
const pushStack = () => _push(localStack); | ||
const popStack = (id: StackId) => _pop(localStack, id); | ||
const resetStack = () => _reset(localStack); | ||
return { stack: localStack, pushStack, popStack, resetStack }; | ||
return { | ||
getState, | ||
pushStack, | ||
popStack, | ||
resetStack, | ||
onTopStack, | ||
}; | ||
} |