poprawki widget vikunja:

- poprawka kalendarza do wybierania daty (dalej nie dziala wszytko tak
  jakbym chcial)
pull/878/head
Bartosz Nikitiuk 2025-11-21 22:19:45 +07:00
parent 761265a4dc
commit 3885941397
No known key found for this signature in database
GPG Key ID: 725EADAB8CDD0DB1
3 changed files with 257 additions and 64 deletions

@ -337,31 +337,125 @@
color: white; color: white;
} }
@media (max-width: 768px) { /* Flatpickr Customization for Glance Theme */
.vikunja-table th, .vikunja-table td { .flatpickr-calendar {
padding: 0.5rem 0.2rem; background: var(--color-widget-background) !important;
font-size: 0.9rem; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3) !important;
} border: 1px solid var(--color-separator) !important;
color: var(--color-text-base) !important;
.vikunja-table .label { font-family: 'JetBrains Mono', monospace !important;
font-size: 0.8rem; }
padding: 0.1rem 0.3rem;
} .flatpickr-calendar.arrowTop:before, .flatpickr-calendar.arrowTop:after {
border-bottom-color: var(--color-widget-background) !important;
.vikunja-modal-content { }
width: 98%;
margin: 1rem; .flatpickr-calendar.arrowBottom:before, .flatpickr-calendar.arrowBottom:after {
} border-top-color: var(--color-widget-background) !important;
}
.vikunja-modal-body {
padding: 1rem; .flatpickr-month {
} background: var(--color-widget-background) !important;
color: var(--color-text-base) !important;
.vikunja-modal-header { fill: var(--color-text-base) !important;
padding: 1rem; }
}
.flatpickr-current-month .flatpickr-monthDropdown-months {
.vikunja-modal-footer { background: var(--color-widget-background) !important;
padding: 1rem; color: var(--color-text-base) !important;
} }
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
background: var(--color-widget-background-highlight) !important;
}
.flatpickr-current-month input.cur-year {
color: var(--color-text-base) !important;
}
.flatpickr-weekdays {
background: var(--color-widget-background) !important;
}
span.flatpickr-weekday {
color: var(--color-text-subdue) !important;
}
.flatpickr-day {
color: var(--color-text-base) !important;
}
.flatpickr-day.flatpickr-disabled, .flatpickr-day.flatpickr-disabled:hover {
color: var(--color-text-subdue) !important;
}
.flatpickr-day:hover, .flatpickr-day.prevMonthDay:hover, .flatpickr-day.nextMonthDay:hover, .flatpickr-day:focus, .flatpickr-day.prevMonthDay:focus, .flatpickr-day.nextMonthDay:focus {
background: var(--color-widget-background-highlight) !important;
border-color: var(--color-widget-background-highlight) !important;
}
.flatpickr-day.today {
border-color: var(--color-primary) !important;
}
.flatpickr-day.today:hover, .flatpickr-day.today:focus {
background: var(--color-primary) !important;
color: white !important;
}
.flatpickr-day.selected, .flatpickr-day.startRange, .flatpickr-day.endRange, .flatpickr-day.selected.inRange, .flatpickr-day.startRange.inRange, .flatpickr-day.endRange.inRange, .flatpickr-day.selected:focus, .flatpickr-day.startRange:focus, .flatpickr-day.endRange:focus, .flatpickr-day.selected:hover, .flatpickr-day.startRange:hover, .flatpickr-day.endRange:hover, .flatpickr-day.selected.prevMonthDay, .flatpickr-day.startRange.prevMonthDay, .flatpickr-day.endRange.prevMonthDay, .flatpickr-day.selected.nextMonthDay, .flatpickr-day.startRange.nextMonthDay, .flatpickr-day.endRange.nextMonthDay {
background: var(--color-primary) !important;
border-color: var(--color-primary) !important;
color: white !important;
}
.flatpickr-time {
border-top: 1px solid var(--color-separator) !important;
}
.flatpickr-time input {
color: var(--color-text-base) !important;
}
.flatpickr-time input:hover, .flatpickr-time .flatpickr-am-pm:hover, .flatpickr-time input:focus, .flatpickr-time .flatpickr-am-pm:focus {
background: var(--color-widget-background-highlight) !important;
}
.flatpickr-time .flatpickr-time-separator, .flatpickr-time .flatpickr-am-pm {
color: var(--color-text-base) !important;
}
/* Input Group Styling */
.vikunja-input-group {
position: relative;
width: 100%;
}
.vikunja-input-group .vikunja-input {
width: 100%;
padding-right: 2.5rem;
border-radius: 0.3rem;
border: 1px solid var(--color-separator);
}
.vikunja-input-icon {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 2.5rem;
background: transparent;
border: none;
color: var(--color-text-base);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
transition: color 0.2s;
}
.vikunja-input-icon:hover {
background: transparent;
color: var(--color-primary);
} }

@ -1,7 +1,52 @@
// Vikunja widget interactivity // Vikunja widget interactivity
let flatpickrLoadingPromise = null;
function loadFlatpickr() {
if (window.flatpickr) return Promise.resolve();
if (flatpickrLoadingPromise) return flatpickrLoadingPromise;
flatpickrLoadingPromise = new Promise(async (resolve, reject) => {
try {
// Load CSS
const cssLink = document.createElement("link");
cssLink.rel = "stylesheet";
cssLink.href = "https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css";
document.head.appendChild(cssLink);
// Load JS
await new Promise((res, rej) => {
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/flatpickr";
script.onload = res;
script.onerror = rej;
document.head.appendChild(script);
});
// Load Locale
await new Promise((res, rej) => {
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/pl.js";
script.onload = res;
script.onerror = rej;
document.head.appendChild(script);
});
resolve();
} catch (e) {
console.error("Failed to load flatpickr", e);
reject(e);
}
});
return flatpickrLoadingPromise;
}
function initVikunjaWidget(widget) { function initVikunjaWidget(widget) {
if (!widget) return; if (!widget) return;
// Start loading flatpickr immediately
loadFlatpickr();
{ {
const widgetID = widget.querySelector('.vikunja-table')?.dataset.widgetId || const widgetID = widget.querySelector('.vikunja-table')?.dataset.widgetId ||
widget.querySelector('.vikunja-add-btn')?.dataset.widgetId; widget.querySelector('.vikunja-add-btn')?.dataset.widgetId;
@ -107,7 +152,43 @@ function initVikunjaWidget(widget) {
} }
} }
function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLabelIDs, row) { function initFlatpickr(element, defaultDate) {
if (element._flatpickr) {
element._flatpickr.destroy();
}
if (!window.flatpickr) {
console.error("Flatpickr not loaded yet!");
return;
}
// Find the wrapper element (parent of the input)
const wrapper = element.closest('.flatpickr-wrapper');
const fp = flatpickr(wrapper || element, {
wrap: true, // Enable wrap mode to use external toggle
enableTime: true,
dateFormat: "Y-m-d H:i",
time_24hr: true,
locale: "pl",
defaultDate: defaultDate || null,
disableMobile: true,
allowInput: true, // Allow manual input
clickOpens: false // Only open on toggle button click
});
// Ensure the input element has reference to the flatpickr instance
// This is needed because we initialize on the wrapper but access it via the input in other functions
if (wrapper && element !== wrapper) {
element._flatpickr = fp;
}
return fp;
}
async function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLabelIDs, row) {
await loadFlatpickr();
const modal = document.getElementById('vikunja-edit-modal'); const modal = document.getElementById('vikunja-edit-modal');
const titleInput = document.getElementById('vikunja-edit-title'); const titleInput = document.getElementById('vikunja-edit-title');
const dueDateInput = document.getElementById('vikunja-edit-due-date'); const dueDateInput = document.getElementById('vikunja-edit-due-date');
@ -117,18 +198,9 @@ function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLa
// Set current values // Set current values
titleInput.value = title || ''; titleInput.value = title || '';
// Convert date format from "2006-01-02 15:04" to datetime-local format "2006-01-02T15:04" // Initialize flatpickr
if (dueDate) { initFlatpickr(dueDateInput, dueDate);
dueDateInput.value = dueDate.replace(' ', 'T'); initFlatpickr(reminderDateInput, reminderDate);
} else {
dueDateInput.value = '';
}
if (reminderDate) {
reminderDateInput.value = reminderDate.replace(' ', 'T');
} else {
reminderDateInput.value = '';
}
// Fetch and display labels // Fetch and display labels
labelsContainer.innerHTML = '<p>Ładowanie etykiet...</p>'; labelsContainer.innerHTML = '<p>Ładowanie etykiet...</p>';
@ -182,8 +254,8 @@ function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLa
async function saveTask() { async function saveTask() {
const newTitle = titleInput.value.trim(); const newTitle = titleInput.value.trim();
const newDueDate = dueDateInput.value; const dueDateFP = dueDateInput._flatpickr.selectedDates[0];
const newReminderDate = reminderDateInput.value; const reminderDateFP = reminderDateInput._flatpickr.selectedDates[0];
// Get selected label IDs // Get selected label IDs
const selectedLabels = Array.from(labelsContainer.querySelectorAll('input[type="checkbox"]:checked')) const selectedLabels = Array.from(labelsContainer.querySelectorAll('input[type="checkbox"]:checked'))
@ -194,17 +266,15 @@ function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLa
return; return;
} }
// Convert datetime-local format to RFC3339 // Convert to RFC3339
let formattedDueDate = ''; let formattedDueDate = '';
if (newDueDate) { if (dueDateFP) {
const date = new Date(newDueDate); formattedDueDate = dueDateFP.toISOString();
formattedDueDate = date.toISOString();
} }
let formattedReminderDate = ''; let formattedReminderDate = '';
if (newReminderDate) { if (reminderDateFP) {
const date = new Date(newReminderDate); formattedReminderDate = reminderDateFP.toISOString();
formattedReminderDate = date.toISOString();
} }
try { try {
@ -315,7 +385,9 @@ function openEditModal(widgetID, taskID, title, dueDate, reminderDate, currentLa
}); });
} }
function openCreateModal(widgetID) { async function openCreateModal(widgetID) {
await loadFlatpickr();
const modal = document.getElementById('vikunja-create-modal'); const modal = document.getElementById('vikunja-create-modal');
const titleInput = document.getElementById('vikunja-create-title'); const titleInput = document.getElementById('vikunja-create-title');
const dueDateInput = document.getElementById('vikunja-create-due-date'); const dueDateInput = document.getElementById('vikunja-create-due-date');
@ -325,8 +397,9 @@ function openCreateModal(widgetID) {
// Clear the form // Clear the form
titleInput.value = ''; titleInput.value = '';
dueDateInput.value = '';
reminderDateInput.value = ''; initFlatpickr(dueDateInput);
initFlatpickr(reminderDateInput);
// Fetch and populate projects // Fetch and populate projects
projectSelect.innerHTML = '<option value="">Ładowanie...</option>'; projectSelect.innerHTML = '<option value="">Ładowanie...</option>';
@ -400,8 +473,8 @@ function openCreateModal(widgetID) {
async function createTask() { async function createTask() {
const title = titleInput.value.trim(); const title = titleInput.value.trim();
const dueDate = dueDateInput.value; const dueDateFP = dueDateInput._flatpickr.selectedDates[0];
const reminderDate = reminderDateInput.value; const reminderDateFP = reminderDateInput._flatpickr.selectedDates[0];
const projectID = projectSelect.value ? parseInt(projectSelect.value) : 0; const projectID = projectSelect.value ? parseInt(projectSelect.value) : 0;
// Get selected label IDs // Get selected label IDs
@ -415,15 +488,13 @@ function openCreateModal(widgetID) {
// Convert datetime-local format to RFC3339 // Convert datetime-local format to RFC3339
let formattedDueDate = ''; let formattedDueDate = '';
if (dueDate) { if (dueDateFP) {
const date = new Date(dueDate); formattedDueDate = dueDateFP.toISOString();
formattedDueDate = date.toISOString();
} }
let formattedReminderDate = ''; let formattedReminderDate = '';
if (reminderDate) { if (reminderDateFP) {
const date = new Date(reminderDate); formattedReminderDate = reminderDateFP.toISOString();
formattedReminderDate = date.toISOString();
} }
try { try {

@ -77,11 +77,25 @@
</div> </div>
<div class="vikunja-form-group"> <div class="vikunja-form-group">
<label for="vikunja-create-due-date">Termin:</label> <label for="vikunja-create-due-date">Termin:</label>
<input type="datetime-local" id="vikunja-create-due-date" class="vikunja-input" step="60"> <div class="vikunja-input-group flatpickr-wrapper">
<input type="text" id="vikunja-create-due-date" class="vikunja-input" placeholder="RRRR-MM-DD GG:MM" data-input>
<button type="button" class="vikunja-input-icon" title="Wybierz datę" data-toggle>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width: 16px; height: 16px;">
<path fill-rule="evenodd" d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div> </div>
<div class="vikunja-form-group"> <div class="vikunja-form-group">
<label for="vikunja-create-reminder-date">Przypomnienie:</label> <label for="vikunja-create-reminder-date">Przypomnienie:</label>
<input type="datetime-local" id="vikunja-create-reminder-date" class="vikunja-input" step="60"> <div class="vikunja-input-group flatpickr-wrapper">
<input type="text" id="vikunja-create-reminder-date" class="vikunja-input" placeholder="RRRR-MM-DD GG:MM" data-input>
<button type="button" class="vikunja-input-icon" title="Wybierz datę" data-toggle>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width: 16px; height: 16px;">
<path fill-rule="evenodd" d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div> </div>
<div class="vikunja-form-group"> <div class="vikunja-form-group">
<label for="vikunja-create-project">Projekt:</label> <label for="vikunja-create-project">Projekt:</label>
@ -117,11 +131,25 @@
</div> </div>
<div class="vikunja-form-group"> <div class="vikunja-form-group">
<label for="vikunja-edit-due-date">Termin:</label> <label for="vikunja-edit-due-date">Termin:</label>
<input type="datetime-local" id="vikunja-edit-due-date" class="vikunja-input" step="60"> <div class="vikunja-input-group flatpickr-wrapper">
<input type="text" id="vikunja-edit-due-date" class="vikunja-input" placeholder="RRRR-MM-DD GG:MM" data-input>
<button type="button" class="vikunja-input-icon" title="Wybierz datę" data-toggle>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width: 16px; height: 16px;">
<path fill-rule="evenodd" d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div> </div>
<div class="vikunja-form-group"> <div class="vikunja-form-group">
<label for="vikunja-edit-reminder-date">Przypomnienie:</label> <label for="vikunja-edit-reminder-date">Przypomnienie:</label>
<input type="datetime-local" id="vikunja-edit-reminder-date" class="vikunja-input" step="60"> <div class="vikunja-input-group flatpickr-wrapper">
<input type="text" id="vikunja-edit-reminder-date" class="vikunja-input" placeholder="RRRR-MM-DD GG:MM" data-input>
<button type="button" class="vikunja-input-icon" title="Wybierz datę" data-toggle>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width: 16px; height: 16px;">
<path fill-rule="evenodd" d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div> </div>
<div class="vikunja-form-group"> <div class="vikunja-form-group">
<label>Etykiety:</label> <label>Etykiety:</label>