Air Datepicker, легкий та гарний вибір дати
Хочу поділитися досвідом написання компонента вибору дати для текстового поля.

Працюючи над останнім проектом, виникла потреба додати до програми календар з можливістю вибрати конкретний місяць. Всі популярні плагіни таку можливість надають, мій вибір зупинився на Zebra Datepicker – маленький, функціональний, все чудово. Але деяких речей все ж таки не вистачало:
- передача об'єктівDate() у параметри замість рядків
- менш громіздка розмітка
- гнучке позиціонування елемента
- анімація з появою
Що стосується громіздкої розмітки, то до неї додається ще й таблична верстка, до осередків якої без зайвих проблем не додатиposition: relative;.
Ну і насамкінець все ж таки хочеться, щоб була можливість додати невелику анімацію, а через те, що багато популярних календарів використовують метод.show(), який задіює властивістьdisplay, плавні переходи (Transition) додавати трудомістко.
Розробка
Календар я розділив на три частини:
При настанні будь-яких подій у тілі чи навігації, вони повідомляють про це основну частину, і календар оновлює свій стан відповідно до цих подій.
У цьому завдання мені допомогли getter'и та setter'и. Наприклад, при зміні місяця простоприсвоюється нова відображається дата зі зміненим номером місяця, і всередині геттера викликається метод перемальовування тіла та навігації календаря. Незважаючи на те, що можна було б обійтися і без них, мені цей підхід видається красивішим. Наприклад, ось так виглядає метод переходу до наступного місяця, року чи декади, залежно від поточного виду:
У свою чергу всередині геттера відбувається виклик відображення елементів календаря (спрощено):
Точно так само відбувається перехід на інший вигляд, дуже просто:
Формування розмітки
Основа для календаря виглядає так:
Без таблиць та натяку на них. Осередок є простим
, що дає можливість додавати псевдо елементи до них і позиціонувати контент усередині них як заманеться.
Я не бачу особливого сенсу в поділі на ряди осередків, оскільки це додатковий непотрібний елемент. Всі дати йдуть одна за одною, у них задана відносна ширина, яка дозволяє здійснювати перехід на інший рядок у потрібний момент.
Обчислення загальної кількості днів на місяць
Щоб сформувати коректний HTML, потрібно знати, скільки днів на місяці. Для цього використовується невеликий трюк із передачею наступного місяця та нульової дати (у Date() дата місяця починається з одиниці).
Формування назв днів

Коли ініціалізуєш календар, можна задавати день, з якого починається тиждень. Мені здалося цікавим показати як можна сформувати розмітку із назвами днів за допомогою рекурсії:
Використання flexbox
Для позиціонування всередині календаря використовуюflexbox. Він з легкістю дозволяє відцентрувати контент всередині осередків, буде по центру у всіх браузерах (які підтримують цю технологію) і нарізних ОС, на відміну від техніки завдання висоти та такого ж міжрядкового інтервалу.
Плюс він дозволяє розташовувати елементи на рівновіддаленій відстані один від одного лише одним рядком:
Не потрібно турбуватися про різні значення ширини, все буде розраховуватися автоматично.
Можна також згадати про кнопки «Сьогодні» та «Очистити»:

Якщо їх дві, вони займають по 50% від усієї ширини, якщо одна, вона займає всю ширину. Цього також можна досягти одним рядком:
Це означає, що елемент у разі потреби може як збільшуватися в розмірах, так і зменшуватися, але при цьому розміри всіх сусідів будуть однакові. Коли кнопка одна, вона розширюється на всю ширину, коли дві вони пропорційно зменшуються і займають по 50%, і т.д. Можна додавати скільки завгодно елементів, у всіх будуть однакові розміри, і в сумі вони займатимуть всю ширину батька.
У результаті отримуємо легкість позиціонування контенту як із використанні таблиць, але зберігаємо у своїй чистоту і валідність розмітки.
Позиціювання
Позиція елемента визначається двома значеннями:
- сторона, з якою з'являтиметься календар
- становище на цьому боці
Для того, щоб додати анімацію «під'їзду» до текстового поля, я додав допоміжні класи, які говорять, з якого боку потрібно починати анімацію. В даному випадку цей клас виглядав би як.-from-top-. За анімацію відповідаютьcss transition таcss transform. Це дозволяє досягти плавності, а також додавати кастомні переходи.
Що стосується Date()
Як я згадував спочатку, мені не зовсім зрозумілі ситуації,коли замість об'єкта дати потрібно передавати рядок. Можливо це зручно при автоматичній ініціалізації, коли параметри потрібно передавати черезdata атрибути, але для мене все ж таки зручніше просто передати new Date(). Тим більше, що запис виду new Date (2015, 11, 17) не є особливо складним '2015-12-17'. Тому у всіх параметрах, де задається дата, необхідно передавати new Date().
Декілька слів про використання
Мені подобається практика автоматичної ініціалізації плагінів, тому для ініціалізації календаря до текстового поля достатньо додати клас'datepicker-here' і все запрацює.
Опції можна надіслати через data атрибути.
Кастомізований вміст комірки
Air Datepicker має можливість повністю змінювати вміст осередків. Це дозволяє додавати, наприклад, назви подій або якийсь допоміжний контент у комірки. Для цього потрібно використовувати опцію onRenderCell() :
Висновок
Дякую за увагу.
Хардкорна конфа за С++. Ми запрошуємо лише профі.