Pikaday построен вокруг событийной модели и набора точек расширения, которые позволяют внедрять дополнительную логику в жизненный цикл календаря без изменения его исходного кода. В экосистеме компонентов выбора даты понятия hooks и middleware не всегда оформлены как отдельный API-слой, однако поведение легко интерпретируется через существующие callback-и и обёртки над экземпляром календаря.
Основой расширяемости выступают колбэки, вызываемые на ключевых этапах жизненного цикла календаря:
Каждое из этих событий формирует естественные точки внедрения логики, которые в архитектуре хуков рассматриваются как side-effect hooks.
С точки зрения внутренней реализации календаря, состояние делится на три уровня:
Хуки позволяют перехватывать изменения на границах этих уровней.
В стандартной модели календаря выделяются следующие ключевые точки:
Событие выбора даты является центральной точкой расширения. Оно срабатывает после обновления внутреннего состояния и перед синхронизацией с внешним input.
Фактически это post-state-change hook, в который можно внедрять:
Отрисовка календаря представляет собой реактивную точку, в которую можно внедрить кастомизацию DOM:
Это pre-render и post-render зона, где логика часто делится на два слоя: подготовка данных и модификация DOM.
Эти события выступают как lifecycle hooks:
Хотя Pikaday не предоставляет формализованный middleware API, поведение легко моделируется через композицию callback-ов.
Хук в данном контексте — это функция, привязанная к событию, которая получает текущее состояние календаря и может повлиять на дальнейшее выполнение цепочки.
Типовая структура хука:
onSelect: function(date) {
// обработка выбранной даты
}
В расширенной модели хук рассматривается как функция следующего вида:
(state, next) => newState | void
где:
state — текущее состояние календаряnext — функция продолжения цепочкиMiddleware-подход добавляется поверх базовой событийной системы через обёртку экземпляра календаря. Суть заключается в создании цепочки функций, каждая из которых получает контроль над состоянием до передачи его дальше.
Базовая модель middleware:
const middleware = (ctx, next) => {
// логика до
next();
// логика после
};
Контекст календаря обычно включает:
Middleware слой позволяет стандартизировать обработку логики, которая иначе расползалась бы по callback-ам.
Цепочка middleware строится через последовательное оборачивание функций:
function compose(middlewares) {
return function(context) {
let index = -1;
function dispatch(i) {
if (i <= index) return;
index = i;
const fn = middlewares[i];
if (!fn) return;
return fn(context, () => dispatch(i + 1));
}
return dispatch(0);
};
}
Такой подход позволяет:
В терминах календаря это особенно важно при обработке даты, где одна middleware может запрещать выбор, другая — трансформировать значение, третья — синхронизировать с внешним API.
Одной из ключевых задач middleware-слоя является контроль над мутациями состояния.
Типовые сценарии:
Middleware может блокировать переход к следующему состоянию:
const validateDate = (ctx, next) => {
if (ctx.date < ctx.minDate) return;
next();
};
Передача даты в стандартизированном формате:
const normalize = (ctx, next) => {
ctx.date = new Date(ctx.date).toISOString();
next();
};
Логирование, аналитика, синхронизация:
const logger = (ctx, next) => {
console.log('date selected:', ctx.date);
next();
};
Middleware-архитектура в контексте календаря решает проблему неконтролируемых мутаций состояния. В классической callback-модели изменения могут происходить в любом порядке, что приводит к:
Middleware вводит линейную модель исполнения, где каждое изменение состояния проходит через предсказуемую цепочку.
Ключевые принципы:
Поверх экземпляра календаря строится слой обёртки, который перенаправляет события через middleware:
function enhanceCalendar(pikadayInstance, middlewares) {
const pipeline = compose(middlewares);
pikadayInstance._onSelect = pikadayInstance.onSelect;
pikadayInstance.onSel ect = function(date) {
pipeline({
date,
instance: pikadayInstance
});
pikadayInstance._onSelect && pikadayInstance._onSelect(date);
};
return pikadayInstance;
}
Такой подход позволяет:
Одной из важных возможностей middleware является управление продолжением цепочки.
Если middleware не вызывает next(), выполнение
останавливается. Это используется для:
Дополнительно может вводиться механизм ошибок:
const safeMiddleware = async (ctx, next) => {
try {
await next();
} catch (e) {
ctx.error = e;
}
};
Хуки и middleware в календаре выполняют разные роли:
Комбинация этих подходов позволяет разделить ответственность:
В результате формируется гибридная модель расширения, в которой календарь становится реактивной системой с контролируемыми побочными эффектами и предсказуемым потоком состояния.