*
*/
(function () {
'use strict';
if (!window.NCDM_DELIVERY) {
console.error('[delivery-datepicker] Сначала подключите delivery-date.js');
return;
}
var CORE = window.NCDM_DELIVERY;
// ==================== НАСТРОЙКИ ====================
var CONFIG = {
// Залейте оба файла в «Файловый менеджер» Тильды и подставьте ссылки
// на static.tildacdn.com — надёжнее, чем внешний CDN.
lib: {
css: 'https://cdn.jsdelivr.net/npm/air-datepicker@3.5.3/air-datepicker.css',
js: 'https://cdn.jsdelivr.net/npm/air-datepicker@3.5.3/air-datepicker.js'
},
dateInput: 'input[type="date"], input[data-delivery-date]',
form: '.t706__form, .t-form',
maxDaysAhead: 60, // насколько далеко вперёд можно выбрать дату
autoSelectFirst: false, // сразу подставлять ближайшую доступную дату
mobileBreakpoint: 640, // ниже — календарь открывается модалкой
placeholderEmpty: 'Сначала выберите город доставки',
placeholderReady: 'Выберите дату доставки',
theme: {
accent: '#111111', // цвет выбранного дня
radius: '8px',
zIndex: 100000000 // выше попапа корзины Тильды
}
};
var LOCALE_RU = {
days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
daysShort: ['Вос', 'Пон', 'Вто', 'Сре', 'Чет', 'Пят', 'Суб'],
daysMin: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь',
'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
monthsShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн',
'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
today: 'Сегодня',
clear: 'Очистить',
dateFormat: 'dd.MM.yyyy',
timeFormat: 'HH:mm',
firstDay: 1
};
var DAY = 86400000;
// ==================== ЗАГРУЗКА БИБЛИОТЕКИ ====================
var libPromise = null;
function loadLib() {
if (libPromise) return libPromise;
libPromise = new Promise(function (resolve, reject) {
if (window.AirDatepicker) return resolve();
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = CONFIG.lib.css;
document.head.appendChild(link);
var style = document.createElement('style');
style.textContent =
'.air-datepicker{' +
'--adp-z-index:' + CONFIG.theme.zIndex + ';' +
'--adp-accent-color:' + CONFIG.theme.accent + ';' +
'--adp-color-current-date:' + CONFIG.theme.accent + ';' +
'--adp-border-radius:' + CONFIG.theme.radius + ';' +
'--adp-font-family:inherit;font-family:inherit;}' +
'.air-datepicker-cell.-disabled-{opacity:.28;text-decoration:line-through;cursor:not-allowed;}' +
'.ncdm-dp-hint{font-size:12px;line-height:1.4;margin-top:6px;opacity:.65;}' +
'.ncdm-dp-input{cursor:pointer;}' +
'.ncdm-dp-input[disabled]{cursor:not-allowed;opacity:.6;}';
document.head.appendChild(style);
var script = document.createElement('script');
script.src = CONFIG.lib.js;
script.onload = resolve;
script.onerror = function () { reject(new Error('air-datepicker не загрузился')); };
document.head.appendChild(script);
});
return libPromise;
}
// ==================== ХЕЛПЕРЫ ====================
/** UTC-полночь -> локальный Date (air-datepicker работает с локальными датами). */
function tsToDate(ts) {
var d = new Date(ts);
return new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
}
/** Локальный Date -> UTC-полночь для функций ядра. */
function dateToTs(date) {
return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());
}
function formatRu(ts) {
return new Intl.DateTimeFormat('ru-RU', {
weekday: 'short', day: 'numeric', month: 'long', timeZone: 'UTC'
}).format(new Date(ts));
}
function pad(n) { return n < 10 ? '0' + n : '' + n; }
function formatValue(ts) {
var d = new Date(ts);
return pad(d.getUTCDate()) + '.' + pad(d.getUTCMonth() + 1) + '.' + d.getUTCFullYear();
}
function getRegion(input) {
var form = input.closest(CONFIG.form) || document;
return CORE.getSelectedRegion(form);
}
function getHint(input) {
var hint = input.parentNode.querySelector('.ncdm-dp-hint');
if (!hint) {
hint = document.createElement('div');
hint.className = 'ncdm-dp-hint';
input.parentNode.appendChild(hint);
}
return hint;
}
/** Значение должно попасть в заказ Тильды — дублируем нативные события. */
function commit(input, value) {
input.value = value;
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
}
// ==================== ИНИЦИАЛИЗАЦИЯ ПОЛЯ ====================
function attach(input) {
if (input._ncdmDp) return;
// нативный date-пикер нам мешает: переводим поле в текст и запрещаем ручной ввод
if (input.type === 'date') input.type = 'text';
input.readOnly = true;
input.autocomplete = 'off';
input.classList.add('ncdm-dp-input');
var dp = new AirDatepicker(input, {
locale: LOCALE_RU,
autoClose: true,
isMobile: window.innerWidth < CONFIG.mobileBreakpoint,
position: 'bottom left',
minDate: new Date(),
maxDate: tsToDate(CORE.getMinDeliveryDate('msk') + CONFIG.maxDaysAhead * DAY),
onRenderCell: function (params) {
if (params.cellType !== 'day') return;
var region = getRegion(input);
if (!region) return { disabled: true };
if (!CORE.isDateAllowed(region, dateToTs(params.date))) return { disabled: true };
},
onSelect: function (params) {
if (!params.date) return;
commit(input, formatValue(dateToTs(params.date)));
}
});
input._ncdmDp = dp;
sync(input);
}
/** Пересчёт границ под текущий регион и время. Вызывается при любой смене формы. */
function sync(input) {
var dp = input._ncdmDp;
if (!dp) return;
var region = getRegion(input);
var hint = getHint(input);
if (!region) {
dp.clear();
input.value = '';
input.disabled = true;
input.placeholder = CONFIG.placeholderEmpty;
hint.textContent = '';
return;
}
input.disabled = false;
input.placeholder = CONFIG.placeholderReady;
var min = CORE.getMinDeliveryDate(region);
dp.update({
minDate: tsToDate(min),
maxDate: tsToDate(min + CONFIG.maxDaysAhead * DAY)
});
// выбранная ранее дата могла стать недоступной (сменили город / прошло 11:00)
var selected = dp.selectedDates[0];
if (selected && !CORE.isDateAllowed(region, dateToTs(selected))) {
dp.clear();
input.value = '';
}
if (!dp.selectedDates.length && CONFIG.autoSelectFirst) {
dp.selectDate(tsToDate(min));
}
hint.textContent = 'Ближайшая доставка — ' + formatRu(min) +
(region === 'mo' ? ' (по МО без воскресений)' : '');
}
// ==================== НАБЛЮДЕНИЕ ЗА КОРЗИНОЙ ====================
var timer = null;
function scan() {
clearTimeout(timer);
timer = setTimeout(function () {
var inputs = document.querySelectorAll(CONFIG.dateInput);
if (!inputs.length) return;
loadLib().then(function () {
Array.prototype.forEach.call(document.querySelectorAll(CONFIG.dateInput), function (input) {
if (input._ncdmDp) sync(input); else attach(input);
});
}).catch(function (e) {
console.error('[delivery-datepicker]', e);
});
}, 120);
}
// корзина Тильды перерисовывается динамически
new MutationObserver(scan).observe(document.documentElement, { childList: true, subtree: true });
document.addEventListener('change', scan, true);
document.addEventListener('click', scan, true);
window.addEventListener('load', scan);
setInterval(scan, 60000); // подхватить наступление 11:00 на открытой странице
scan();
window.NCDM_DELIVERY_PICKER = { CONFIG: CONFIG, scan: scan, attach: attach, sync: sync };
})();