@webeach/collection
v1.2.0
Published
Managed collection of items with hooks, events, and strict type safety
Maintainers
Readme
💎 Возможности
- Строго типизированная коллекция с настраиваемым первичным ключом
- Хуки жизненного цикла (
insert,patch,remove,clear) со стадиямиbefore/after - Событийная модель обновлений через коллбэк
onUpdateиaddEventListener - Поддержка
string,numberиbigintв качестве типов первичного ключа - Ноль зависимостей в runtime
📦 Установка
npm install @webeach/collectionpnpm add @webeach/collectionyarn add @webeach/collectionБраузер через CDN
Без сборки — загружай напрямую через unpkg:
<script type="module">
import { Collection } from 'https://unpkg.com/@webeach/collection';
const users = new Collection({ primaryKey: 'id' });
users.appendItem({ id: 1, name: 'Alice' });
</script>🚀 Быстрый старт
Добавление элементов
import { Collection } from '@webeach/collection';
const users = new Collection({
primaryKey: 'id',
});
users.appendItem({ id: 1, firstName: 'Ivan', lastName: 'Petrov' });
users.appendItem({ id: 2, firstName: 'Jason', lastName: 'Statham' });
console.log(users.numItems); // 2
console.log(users.getItem(2)?.firstName); // 'Jason'Замена элемента
import { Collection } from '@webeach/collection';
const products = new Collection({ primaryKey: 'sku' });
products.appendItem({ sku: 'A001', name: 'Laptop' });
products.replaceItem('A001', { sku: 'A001', name: 'Laptop Pro' });
console.log(products.getItem('A001')?.name); // 'Laptop Pro'Массовая замена через setItems
import { Collection } from '@webeach/collection';
const tasks = new Collection({
primaryKey: 'id',
initialItems: [
{ id: 1, title: 'Задача 1' },
{ id: 2, title: 'Задача 2' },
],
});
tasks.setItems([
{ id: 3, title: 'Новая задача 3' },
{ id: 4, title: 'Новая задача 4' },
]);
console.log(tasks.numItems); // 2
console.log(tasks.getItem(3)?.title); // 'Новая задача 3'Подписка на обновления
import { Collection } from '@webeach/collection';
const list = new Collection({ primaryKey: 'id' });
list.onUpdate = (event) => {
console.log('Коллекция обновлена:', event.detail);
};
// Или через addEventListener
list.addEventListener('update', (event) => {
console.log('Коллекция обновлена:', event.detail);
});
list.appendItem({ id: 1, name: 'Alice' });Использование хуков жизненного цикла
import {
Collection,
$CollectionHookDispatcherSymbol,
} from '@webeach/collection';
const users = new Collection({ primaryKey: 'id' });
// Блокируем добавление элементов с чётными id
const { unregister } = users[$CollectionHookDispatcherSymbol].register(
'insert:before',
({ item }) => {
if (item.id % 2 === 0) {
return false; // отменяем вставку
}
},
);
users.appendItem({ id: 1, name: 'Alice' }); // успешно
users.appendItem({ id: 2, name: 'Bob' }); // заблокировано
console.log(users.numItems); // 1
unregister();🛠️ API
Collection
- constructor
- Методы
- Свойства
- Хуки
CollectionUpdateEvent
- constructor
- Наследует API CustomEvent
🧩 TypeScript
Коллекция полностью обобщена и автоматически выводит типы на основе первичного ключа и формы данных.
import { Collection } from '@webeach/collection';
interface User {
id: number;
name: string;
role: 'admin' | 'user';
}
const users = new Collection<'id', number, User>({
primaryKey: 'id',
});
users.appendItem({ id: 1, name: 'Alice', role: 'admin' });
const user = users.getItem(1);
// user: CollectionItem<'id', number, User> | null📖 Примеры из реальных проектов
Список задач в React
import { FC, useEffect, useRef, useState } from 'react';
import { Collection } from '@webeach/collection';
interface Task {
id: number;
title: string;
done: boolean;
}
export const TaskList: FC = () => {
const collectionRef = useRef(
new Collection<'id', number, Task>({ primaryKey: 'id' }),
);
const [tasks, setTasks] = useState<Task[]>([]);
useEffect(() => {
const collection = collectionRef.current;
collection.onUpdate = (event) => {
setTasks([...event.detail] as Task[]);
};
collection.appendItem({ id: 1, title: 'Купить продукты', done: false });
collection.appendItem({ id: 2, title: 'Написать тесты', done: false });
}, []);
const toggle = (id: number) => {
const item = collectionRef.current.getItem(id);
if (item) {
collectionRef.current.patchItem(id, { done: !item.done });
}
};
return (
<ul>
{tasks.map((task) => (
<li key={task.id} onClick={() => toggle(task.id)}>
{task.done ? '✓' : '○'} {task.title}
</li>
))}
</ul>
);
};Ограничение размера коллекции через хук
import {
Collection,
$CollectionHookDispatcherSymbol,
} from '@webeach/collection';
function createBoundedCollection<T extends { id: number }>(maxSize: number) {
const collection = new Collection<'id', number, T>({ primaryKey: 'id' });
collection[$CollectionHookDispatcherSymbol].register('insert:before', () => {
if (collection.numItems >= maxSize) {
return false;
}
});
return collection;
}
const limited = createBoundedCollection(3);
limited.appendItem({ id: 1 }); // ok
limited.appendItem({ id: 2 }); // ok
limited.appendItem({ id: 3 }); // ok
limited.appendItem({ id: 4 }); // заблокировано — лимит достигнут
console.log(limited.numItems); // 3👨💻 Автор
Разработка и поддержка: Руслан Мартынов
Если у тебя есть предложения или найден баг — открывай issue или отправляй pull request.
📄 Лицензия
Этот пакет распространяется под лицензией MIT.
