From 73b458e998090e177bba42445f800df7de982b72 Mon Sep 17 00:00:00 2001 From: Alex Lion Date: Mon, 3 Aug 2026 09:48:38 +0700 Subject: [PATCH] Use native datetime-local instead of AirDatepicker --- assets/css/app.css | 15 --- assets/js/app.js | 97 ++++++++----------- assets/package-lock.json | 11 --- assets/package.json | 1 - .../event_live/event_form_component.html.heex | 4 +- .../views/components/input_component.ex | 12 ++- test/claper_web/live/event_live_test.exs | 7 ++ 7 files changed, 58 insertions(+), 89 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index 8f28801..8d40ea6 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -1,4 +1,3 @@ -@import 'air-datepicker/air-datepicker.css'; @import 'animate.css/animate.min.css'; @import 'tailwindcss'; @@ -545,20 +544,6 @@ transform:rotate(-20deg); } -/* Air datepicker */ -.air-datepicker-body--day-name { - color: var(--color-primary-600); -} - -.air-datepicker-cell.-selected-, .air-datepicker-cell.-selected-.-current- { - background-color: var(--color-primary-500); - color: var(--color-white); -} - -.air-datepicker-cell.-current- { - color: var(--color-secondary-500); -} - .animate__slow_slow { --animate-duration: 5s; } diff --git a/assets/js/app.js b/assets/js/app.js index c6dcad4..779eee8 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -6,14 +6,6 @@ import { LiveSocket } from "phoenix_live_view"; import topbar from "../vendor/topbar"; import Alpine from "alpinejs"; import moment from "moment-timezone"; -import AirDatepicker from "air-datepicker"; -import airdatepickerLocaleEn from "air-datepicker/locale/en"; -import airdatepickerLocaleFr from "air-datepicker/locale/fr"; -import airdatepickerLocaleDe from "air-datepicker/locale/de"; -import airdatepickerLocaleEs from "air-datepicker/locale/es"; -import airdatepickerLocaleNl from "air-datepicker/locale/nl"; -import airdatepickerLocaleIt from "air-datepicker/locale/it"; -import airdatepickerLocaleHu from "air-datepicker/locale/hu"; import "moment/locale/de"; import "moment/locale/fr"; import "moment/locale/es"; @@ -42,43 +34,19 @@ const supportedLocales = window.claperConfig?.supportedLocales || [ "lv", ]; -const airdatePickrSupportedLocales = window.claperConfig?.supportedLocales || [ - "en", - "fr", - "de", - "es", - "nl", - "it", - "hu", -]; - var locale = document.querySelector("html").getAttribute("lang") || navigator.language.split("-")[0]; -var airdatepickrLocale = locale; - if (!supportedLocales.includes(locale)) { locale = "en"; } -if (!airdatePickrSupportedLocales.includes(airdatepickrLocale)) { - airdatepickrLocale = "en"; -} window.moment.locale("en"); window.moment.locale(locale); window.Alpine = Alpine; Alpine.start(); -let airdatePickrLocales = { - en: airdatepickerLocaleEn, - fr: airdatepickerLocaleFr, - de: airdatepickerLocaleDe, - es: airdatepickerLocaleEs, - nl: airdatepickerLocaleNl, - it: airdatepickerLocaleIt, - hu: airdatepickerLocaleHu, -}; let csrfToken = document .querySelector("meta[name='csrf-token']") .getAttribute("content"); @@ -450,31 +418,52 @@ Hooks.CalendarLocalDate = { this.el.innerHTML = moment.utc(this.el.dataset.date).local().calendar(); }, }; -Hooks.Pickr = { +Hooks.DateTimeLocal = { mounted() { - const localTime = this.el.querySelector("input[type=text]"); - const utcTime = this.el.querySelector("input[type=hidden]"); - localTime.value = moment - .utc(utcTime.value) - .local() - .format("DD-MM-YYYY HH:mm"); - this.pickr = new AirDatepicker(localTime, { - dateFormat: "dd-MM-yyyy", - timepicker: true, - minutesStep: 5, - minDate: moment(), - timeFormat: "HH:mm", - selectedDates: [moment(localTime.value, "DD-MM-YYYY HH:mm").toDate()], - onSelect: ({ date }) => { - const utc = moment(date).utc().format("YYYY-MM-DDTHH:mm:ss"); - utcTime.value = utc; - }, - locale: airdatePickrLocales[airdatepickrLocale] || airdatePickrLocales["en"], - }); + this.localTime = this.el.querySelector("input[type=datetime-local]"); + this.utcTime = this.el.querySelector("input[type=hidden]"); + this.syncLocalTime(); + + this.handleInput = ({ target }) => { + if (target === this.localTime) this.syncUtcTime(); + }; + this.el.addEventListener("input", this.handleInput); + }, + updated() { + this.localTime = this.el.querySelector("input[type=datetime-local]"); + this.utcTime = this.el.querySelector("input[type=hidden]"); + this.syncLocalTime(); + }, + syncLocalTime() { + if (!this.utcTime.value) { + this.localTime.value = ""; + return; + } + + const value = this.utcTime.value.replace(" ", "T").replace(/Z$/, ""); + const date = new Date(`${value}Z`); + + if (!Number.isNaN(date.getTime())) { + const pad = (part) => String(part).padStart(2, "0"); + this.localTime.value = + `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` + + `T${pad(date.getHours())}:${pad(date.getMinutes())}`; + } + }, + syncUtcTime() { + if (!this.localTime.value) { + this.utcTime.value = ""; + return; + } + + const date = new Date(this.localTime.value); + + if (!Number.isNaN(date.getTime())) { + this.utcTime.value = date.toISOString().slice(0, 19); + } }, - updated() {}, destroyed() { - if (this.pickr) this.pickr.destroy(); + this.el.removeEventListener("input", this.handleInput); }, }; Hooks.UpdateAttendees = { diff --git a/assets/package-lock.json b/assets/package-lock.json index fd979bd..4f9aa13 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -5,7 +5,6 @@ "packages": { "": { "dependencies": { - "air-datepicker": "^3.5.0", "animate.css": "^4.1.1", "chart.js": "^4.4.0", "chartjs-adapter-moment": "^1.0.1", @@ -896,11 +895,6 @@ "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", "dev": true }, - "node_modules/air-datepicker": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/air-datepicker/-/air-datepicker-3.5.0.tgz", - "integrity": "sha512-WOpn1MaSl5drcXSwkXg5Gh/jXX/VFfamNnIb8E43AY4UKuW/bNEW06e3GGsiWLDBQLabD22L6b6cP7KHnAy54w==" - }, "node_modules/alpinejs": { "version": "3.13.8", "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.8.tgz", @@ -1930,11 +1924,6 @@ "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", "dev": true }, - "air-datepicker": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/air-datepicker/-/air-datepicker-3.5.0.tgz", - "integrity": "sha512-WOpn1MaSl5drcXSwkXg5Gh/jXX/VFfamNnIb8E43AY4UKuW/bNEW06e3GGsiWLDBQLabD22L6b6cP7KHnAy54w==" - }, "alpinejs": { "version": "3.13.8", "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.8.tgz", diff --git a/assets/package.json b/assets/package.json index 756ad6f..6e6976a 100644 --- a/assets/package.json +++ b/assets/package.json @@ -11,7 +11,6 @@ "tailwindcss": "^4.1.11" }, "dependencies": { - "air-datepicker": "^3.5.0", "animate.css": "^4.1.1", "chart.js": "^4.4.0", "chartjs-adapter-moment": "^1.0.1", diff --git a/lib/claper_web/live/event_live/event_form_component.html.heex b/lib/claper_web/live/event_live/event_form_component.html.heex index 1b61fdd..b6bc61a 100644 --- a/lib/claper_web/live/event_live/event_form_component.html.heex +++ b/lib/claper_web/live/event_live/event_form_component.html.heex @@ -259,14 +259,12 @@

-
+

{gettext("Shown in your local time.")}

diff --git a/lib/claper_web/views/components/input_component.ex b/lib/claper_web/views/components/input_component.ex index b27b809..96d296e 100644 --- a/lib/claper_web/views/components/input_component.ex +++ b/lib/claper_web/views/components/input_component.ex @@ -277,14 +277,16 @@ defmodule ClaperWeb.Component.Input do ~H"""
-
- {label(@form, @key, @name, class: "block text-sm font-bold text-base-content")} +
+ {label(@form, :local_date, @name, class: "block text-sm font-bold text-base-content")}
{hidden_input(@form, @key)} - {text_input(@form, :local_date, + {datetime_local_input(@form, :local_date, + required: @required, + readonly: @readonly, autofocus: @autofocus, - placeholder: @placeholder, - autocomplete: false, + autocomplete: "off", + step: 300, class: "input h-12 w-full bg-white px-4 read-only:opacity-50 #{if Keyword.has_key?(@form.errors, @key), do: "input-error", else: ""}" )} diff --git a/test/claper_web/live/event_live_test.exs b/test/claper_web/live/event_live_test.exs index 31a91ba..fa82ccc 100644 --- a/test/claper_web/live/event_live_test.exs +++ b/test/claper_web/live/event_live_test.exs @@ -44,6 +44,13 @@ defmodule ClaperWeb.EventLiveTest do assert has_element?(edit_live, "#event-editor-title", "Edit event") assert has_element?(edit_live, "#presentation-heading", "Presentation") assert has_element?(edit_live, "#event-details-heading", "Event details") + assert has_element?(edit_live, ~s(#date-picker input[type="datetime-local"])) + + assert has_element?( + edit_live, + ~s(#date-picker input[name="event[started_at]"][type="hidden"]) + ) + assert has_element?(edit_live, "#facilitators-section") assert has_element?(edit_live, "#event-danger-zone", "Delete event") assert has_element?(edit_live, ~s(button[form="event-form"]), "Save changes")