Cleave.js представляет собой библиотеку для форматирования пользовательского ввода в реальном времени. В контексте дат она не выполняет полноценную валидацию календарных правил, а работает как слой маскирования и структурирования ввода.
При работе с диапазонами дат ключевая особенность заключается в том, что Cleave.js:
По этой причине ограничения диапазонов реализуются на уровне комбинации Cleave.js и внешней логики.
Наиболее распространённые форматы диапазонов:
YYYY-MM-DD - YYYY-MM-DDDD.MM.YYYY — DD.MM.YYYYMM/DD/YYYY to MM/DD/YYYYCleave.js может быть настроен для форматирования каждого сегмента даты отдельно, но разделитель диапазона рассматривается как обычный символ.
Пример базовой конфигурации для одной даты:
const dateField = new Cleave(input, {
date: true,
datePattern: ['Y', 'm', 'd'],
delimiter: '-'
});
Для диапазона обычно применяется один из двух подходов:
Наиболее устойчивый подход — разделение диапазона на две даты:
const startDate = new Cleave('#start', {
date: true,
datePattern: ['Y', 'm', 'd']
});
const endDate = new Cleave('#end', {
date: true,
datePattern: ['Y', 'm', 'd']
});
Далее ввод ограничивается логикой приложения:
Проверка выполняется отдельно:
function validateRange(start, end) {
const s = new Date(start);
const e = new Date(end);
return s <= e;
}
Такой подход позволяет избежать ограничений самой библиотеки и переносит контроль в доменную логику.
Cleave.js не предоставляет встроенных параметров minDate
и maxDate. Поэтому ограничения реализуются через:
onChange.Пример внешнего ограничения:
const MIN = new Date('2024-01-01');
const MAX = new Date('2026-12-31');
function clampDate(value) {
const d = new Date(value);
if (d < MIN) return MIN;
if (d > MAX) return MAX;
return d;
}
При использовании одного input диапазон приходится парсить вручную. Типичная структура:
2025-01-10 - 2025-02-15
Разделение выполняется через delimiter:
function parseRange(value) {
const [start, end] = value.split(' - ');
return { start, end };
}
После этого применяется логика валидации:
При вводе диапазона пользователь может находиться в состоянии:
Cleave.js в таких сценариях продолжает форматировать каждую часть независимо, что создаёт необходимость учитывать промежуточные состояния.
Пример проверки состояния:
function isRangeComplete(value) {
const parts = value.split(' - ');
return parts.length === 2 && parts[0].length === 10 && parts[1].length === 10;
}
Одной из ключевых проблем является ситуация, когда конечная дата меньше начальной.
Варианты обработки:
Автоматическая коррекция:
function normalizeRange(start, end) {
const s = new Date(start);
const e = new Date(end);
if (s > e) {
return { start: end, end: start };
}
return { start, end };
}
Даже при корректном формате Cleave.js не учитывает:
Например, ввод 2025-02-30 может быть пропущен как
строка, но не является реальной датой.
Поэтому используется дополнительная валидация:
function isValidDate(d) {
const date = new Date(d);
return !isNaN(date.getTime());
}
При работе с двумя input часто требуется динамическое ограничение второго поля на основе первого.
Пример логики:
function syncRange(start, end) {
const startDate = new Date(start);
end.min = startDate;
}
В реальной реализации DOM-значения синхронизируются через события
input или change.
Дополнительные ограничения часто накладываются через:
Cleave.js уже выполняет часть этих задач, но для диапазонов важно учитывать:
Особое внимание требуется для случаев:
"""2025-""2025-01-""2025-01-10 - "Такие состояния должны трактоваться как промежуточные, а не ошибочные.
Типичная стратегия:
draft и committed
состояние.Выбор разделителя влияет на устойчивость парсинга:
" - " — наиболее читаемый вариант;" to " — семантически выраженный;".." — компактный, но менее очевидный.Важно учитывать конфликт с форматами даты:
- используется внутри ISO формата;" - " с
пробелами.Cleave.js следует рассматривать как слой представления. Для строгих ограничений используются библиотеки:
Пример проверки через date-fns:
import { isBefore } from 'date-fns';
function validate(start, end) {
return isBefore(new Date(start), new Date(end));
}
Типичная схема работы:
Такая архитектура обеспечивает: