Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions app/assets/tailwind/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -652,3 +652,20 @@ a.pill-focus-reset:focus-visible {
--tw-ring-color: transparent !important;
--tw-ring-offset-width: 0px !important;
}

/* Tabs Component - Progressive Enhancement
Show only first panel when JavaScript not yet loaded or disabled */
[data-controller="pathogen--tabs"]:not(.tabs-initialized)
[data-pathogen--tabs-target="panel"]:not(:first-child) {
display: none;
}

/* Tabs Component - Dynamic styling based on aria-selected
Override the server-rendered classes when JavaScript updates aria-selected */
[role="tab"][aria-selected="true"] {
@apply border-primary-800 dark:border-white text-slate-900 dark:text-white bg-transparent;
}

[role="tab"][aria-selected="false"] {
@apply border-transparent text-slate-700 dark:text-slate-200 hover:text-slate-900 dark:hover:text-white hover:border-slate-700 dark:hover:border-white;
}
8 changes: 8 additions & 0 deletions app/javascript/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ function isElementInViewport(el) {
}

document.addEventListener("turbo:morph", () => {
// Load LocalTime translations from script tag (set by _local_time.html.erb)
const i18nScript = document.getElementById('local-time-i18n');
if (i18nScript) {
const locale = i18nScript.dataset.locale;
const translations = JSON.parse(i18nScript.textContent);
LocalTime.config.i18n[locale] = translations;
}

LocalTime.config.locale = document.documentElement.lang;
LocalTime.run();
// ensure focused element is scrolled into view if out of view
Expand Down
63 changes: 59 additions & 4 deletions app/javascript/controllers/pathogen/datepicker/input_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ export default class extends Controller {
}

idempotentConnect() {
// Clean up any disconnected calendar reference
if (this.#calendar && !this.#calendar.isConnected) {
this.#calendar = null;
}

// Clean up any existing calendar from a previous connection (e.g., after morph)
const existingCalendar = document.getElementById(this.calendarIdValue);
if (existingCalendar && existingCalendar !== this.#calendar) {
existingCalendar.remove();
}

// the currently selected date will be displayed on the initial calendar
this.#setSelectedDate();

Expand All @@ -60,6 +71,11 @@ export default class extends Controller {
// Position the calendar
this.#initializeDropdown();

// Remove event listener first to avoid duplicates
this.datepickerInputTarget.removeEventListener(
"focus",
this.boundHandleDatepickerInputFocus,
);
this.datepickerInputTarget.addEventListener(
"focus",
this.boundHandleDatepickerInputFocus,
Expand All @@ -74,8 +90,15 @@ export default class extends Controller {
this.boundHandleDatepickerInputFocus,
);

this.#calendar.remove();
this.#calendar = null;
if (this.#calendar) {
this.#calendar.remove();
this.#calendar = null;
}

// Clean up the dropdown instance to allow proper reinitialization
if (this.#dropdown) {
this.#dropdown = null;
}
}

#initializeDropdown() {
Expand All @@ -85,6 +108,12 @@ export default class extends Controller {
"Flowbite Dropdown class not found. Make sure Flowbite JS is loaded.",
);
}

// Reinitialize dropdown if it already exists (e.g., after a Turbo morph)
if (this.#dropdown) {
this.#dropdown = null;
}

this.#dropdown = new Dropdown(
this.#calendar,
this.datepickerInputTarget,
Expand Down Expand Up @@ -128,8 +157,22 @@ export default class extends Controller {

#addCalendarTemplate() {
try {
// Don't add calendar if already exists
if (this.#calendar) return;
// Check if we have a calendar reference and if it's still in the DOM
if (this.#calendar && this.#calendar.isConnected) {
return;
}

// Check if calendar exists in DOM (not just if we have a reference)
const existingCalendar = document.getElementById(this.calendarIdValue);
if (existingCalendar && existingCalendar.isConnected) {
this.#calendar = existingCalendar;
return;
}

if (!this.hasCalendarTemplateTarget) {
console.error('[datepicker] calendarTemplateTarget is missing!');
return;
}

// Add the calendar template to the DOM
const calendar = this.calendarTemplateTarget.content.cloneNode(true);
Expand Down Expand Up @@ -194,6 +237,18 @@ export default class extends Controller {
}

handleDatepickerInputFocus() {
// Check if calendar was removed from DOM and recreate if needed
if (!this.#calendar || !this.#calendar.isConnected) {
this.#calendar = null;
this.#dropdown = null;
this.#addCalendarTemplate();
this.#initializeDropdown();
}

if (!this.#dropdown) {
console.error('[datepicker] Dropdown instance is null, reinitializing...');
this.#initializeDropdown();
}
if (!this.#dropdown.isVisible()) {
this.#dropdown.show();
}
Expand Down
Loading
Loading