mirror of
https://github.com/robonen/tools.git
synced 2026-03-20 19:04:46 +00:00
feat(packages/vue): add useCounter composable
This commit is contained in:
8
packages/vue/src/composables/useCounter/demo.vue
Normal file
8
packages/vue/src/composables/useCounter/demo.vue
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
76
packages/vue/src/composables/useCounter/index.test.ts
Normal file
76
packages/vue/src/composables/useCounter/index.test.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { it, expect, describe } from 'vitest';
|
||||||
|
import { useCounter } from '.';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
describe('useCounter', () => {
|
||||||
|
it('initialize count with the provided initial value', () => {
|
||||||
|
const { count } = useCounter(5);
|
||||||
|
expect(count.value).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('initialize count with the provided initial value from a ref', () => {
|
||||||
|
const { count } = useCounter(ref(5));
|
||||||
|
expect(count.value).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('increment count by 1 by default', () => {
|
||||||
|
const { count, increment } = useCounter(0);
|
||||||
|
increment();
|
||||||
|
expect(count.value).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('increment count by the specified delta', () => {
|
||||||
|
const { count, increment } = useCounter(0);
|
||||||
|
increment(5);
|
||||||
|
expect(count.value).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('decrement count by 1 by default', () => {
|
||||||
|
const { count, decrement } = useCounter(5);
|
||||||
|
decrement();
|
||||||
|
expect(count.value).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('decrement count by the specified delta', () => {
|
||||||
|
const { count, decrement } = useCounter(10);
|
||||||
|
decrement(5);
|
||||||
|
expect(count.value).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set count to the specified value', () => {
|
||||||
|
const { count, set } = useCounter(0);
|
||||||
|
set(10);
|
||||||
|
expect(count.value).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get the current count value', () => {
|
||||||
|
const { get } = useCounter(5);
|
||||||
|
expect(get()).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reset count to the initial value', () => {
|
||||||
|
const { count, reset } = useCounter(10);
|
||||||
|
count.value = 5;
|
||||||
|
reset();
|
||||||
|
expect(count.value).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reset count to the specified value', () => {
|
||||||
|
const { count, reset } = useCounter(10);
|
||||||
|
count.value = 5;
|
||||||
|
reset(20);
|
||||||
|
expect(count.value).toBe(20);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clamp count to the minimum value', () => {
|
||||||
|
const { count, decrement } = useCounter(Number.MIN_SAFE_INTEGER);
|
||||||
|
decrement();
|
||||||
|
expect(count.value).toBe(Number.MIN_SAFE_INTEGER);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clamp count to the maximum value', () => {
|
||||||
|
const { count, increment } = useCounter(Number.MAX_SAFE_INTEGER);
|
||||||
|
increment();
|
||||||
|
expect(count.value).toBe(Number.MAX_SAFE_INTEGER);
|
||||||
|
});
|
||||||
|
});
|
||||||
58
packages/vue/src/composables/useCounter/index.ts
Normal file
58
packages/vue/src/composables/useCounter/index.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { ref, unref, type MaybeRef } from 'vue';
|
||||||
|
import { clamp } from '@robonen/stdlib';
|
||||||
|
|
||||||
|
export interface UseCounterOptions {
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name useCounter
|
||||||
|
* @category Utilities
|
||||||
|
* @description A composable that provides a counter with increment, decrement, set, get, and reset functions
|
||||||
|
*
|
||||||
|
* @param {MaybeRef<number>} [initialValue=0] The initial value of the counter
|
||||||
|
* @param {UseCounterOptions} [options={}] The options for the counter
|
||||||
|
* @param {number} [options.min=Number.MIN_SAFE_INTEGER] The minimum value of the counter
|
||||||
|
* @param {number} [options.max=Number.MAX_SAFE_INTEGER] The maximum value of the counter
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { count, increment } = useCounter(0);
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { count, increment, decrement, set, get, reset } = useCounter(0, { min: 0, max: 10 });
|
||||||
|
*/
|
||||||
|
export function useCounter(initialValue: MaybeRef<number> = 0, options: UseCounterOptions = {}) {
|
||||||
|
let _initialValue = unref(initialValue);
|
||||||
|
const count = ref(initialValue);
|
||||||
|
|
||||||
|
const {
|
||||||
|
min = Number.MIN_SAFE_INTEGER,
|
||||||
|
max = Number.MAX_SAFE_INTEGER,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const increment = (delta = 1) =>
|
||||||
|
count.value = clamp(count.value + delta, min, max);
|
||||||
|
|
||||||
|
const decrement = (delta = 1) =>
|
||||||
|
count.value = clamp(count.value - delta, min, max);
|
||||||
|
|
||||||
|
const set = (value: number) =>
|
||||||
|
count.value = clamp(value, min, max);
|
||||||
|
|
||||||
|
const get = () => count.value;
|
||||||
|
|
||||||
|
const reset = (value = _initialValue) => {
|
||||||
|
_initialValue = value;
|
||||||
|
return set(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
count,
|
||||||
|
increment,
|
||||||
|
decrement,
|
||||||
|
set,
|
||||||
|
get,
|
||||||
|
reset,
|
||||||
|
};
|
||||||
|
};
|
||||||
1
packages/vue/src/index.ts
Normal file
1
packages/vue/src/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './composables';
|
||||||
Reference in New Issue
Block a user