feat: toast positioned dynamically at click Y-coordinate — slides out right from interaction point
This commit is contained in:
parent
1bb0a94bdd
commit
e09cf22b77
3 changed files with 50 additions and 16 deletions
|
|
@ -2341,6 +2341,7 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
|
|||
let dagboek = {}; // { "2026-07-23": { "Ontbijt": [{item, portie}, ...], ... } }
|
||||
let activeDate = formatDate(new Date());
|
||||
let activeTab = 'zoeken';
|
||||
let lastClickY = null; // tracked for toast positioning
|
||||
let selectedCategory = null; // null = alle
|
||||
let searchQuery = '';
|
||||
let searchIndex = null; // Map<item, expanded search tokens string>
|
||||
|
|
@ -4139,6 +4140,13 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
|
|||
toast.className = 'toast';
|
||||
toast.textContent = message;
|
||||
container.appendChild(toast);
|
||||
// Position at last click Y, or default bottom
|
||||
const y = lastClickY || window.innerHeight - 120;
|
||||
const h = toast.offsetHeight || 50;
|
||||
const maxY = window.innerHeight - h - 16;
|
||||
const top = Math.min(y, maxY);
|
||||
toast.style.top = top + 'px';
|
||||
toast.style.bottom = 'auto';
|
||||
setTimeout(() => {
|
||||
toast.classList.add('hiding');
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
|
|
@ -4667,15 +4675,16 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
|
|||
}
|
||||
|
||||
// Start the app
|
||||
// Haptic feedback: vibrate on every button press (native Vibration API — Android only, iOS silently ignores)
|
||||
// Track last click position for toast positioning + haptic feedback
|
||||
(function setupHaptics() {
|
||||
if (typeof navigator !== 'undefined' && navigator.vibrate) {
|
||||
document.addEventListener('click', (e) => {
|
||||
document.addEventListener('click', (e) => {
|
||||
lastClickY = e.clientY;
|
||||
if (typeof navigator !== 'undefined' && navigator.vibrate) {
|
||||
if (e.target.tagName === 'BUTTON' || e.target.closest('button')) {
|
||||
navigator.vibrate(8);
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
}
|
||||
}, { passive: true });
|
||||
})();
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
|
|
|
|||
19
index.html
19
index.html
|
|
@ -2341,6 +2341,7 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
|
|||
let dagboek = {}; // { "2026-07-23": { "Ontbijt": [{item, portie}, ...], ... } }
|
||||
let activeDate = formatDate(new Date());
|
||||
let activeTab = 'zoeken';
|
||||
let lastClickY = null; // tracked for toast positioning
|
||||
let selectedCategory = null; // null = alle
|
||||
let searchQuery = '';
|
||||
let searchIndex = null; // Map<item, expanded search tokens string>
|
||||
|
|
@ -4139,6 +4140,13 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
|
|||
toast.className = 'toast';
|
||||
toast.textContent = message;
|
||||
container.appendChild(toast);
|
||||
// Position at last click Y, or default bottom
|
||||
const y = lastClickY || window.innerHeight - 120;
|
||||
const h = toast.offsetHeight || 50;
|
||||
const maxY = window.innerHeight - h - 16;
|
||||
const top = Math.min(y, maxY);
|
||||
toast.style.top = top + 'px';
|
||||
toast.style.bottom = 'auto';
|
||||
setTimeout(() => {
|
||||
toast.classList.add('hiding');
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
|
|
@ -4667,15 +4675,16 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
|
|||
}
|
||||
|
||||
// Start the app
|
||||
// Haptic feedback: vibrate on every button press (native Vibration API — Android only, iOS silently ignores)
|
||||
// Track last click position for toast positioning + haptic feedback
|
||||
(function setupHaptics() {
|
||||
if (typeof navigator !== 'undefined' && navigator.vibrate) {
|
||||
document.addEventListener('click', (e) => {
|
||||
document.addEventListener('click', (e) => {
|
||||
lastClickY = e.clientY;
|
||||
if (typeof navigator !== 'undefined' && navigator.vibrate) {
|
||||
if (e.target.tagName === 'BUTTON' || e.target.closest('button')) {
|
||||
navigator.vibrate(8);
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
}
|
||||
}, { passive: true });
|
||||
})();
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
|
|
|
|||
|
|
@ -290,29 +290,45 @@ test.describe('Karby Eetdagboek', () => {
|
|||
|
||||
test('meal-group uitklappen: saved meal toggles expand/collapse in dagboek', async ({ page }) => {
|
||||
await page.goto(BASE);
|
||||
// Stap 1: Zoek voedingsmiddel
|
||||
// Stap 1: Zoek voedingsmiddel en voeg toe aan dagboek
|
||||
await page.locator('.search-input').fill('brood');
|
||||
await page.waitForTimeout(800);
|
||||
await page.locator('.food-item').first().click();
|
||||
await page.waitForTimeout(500);
|
||||
// Stap 2: Voeg toe via detailAddBtn → addMealModal
|
||||
await page.locator('#detailAddBtn').click();
|
||||
await page.waitForTimeout(500);
|
||||
// Stap 3: Klik Toevoegen in addMealModal → item in dagboek
|
||||
await page.locator('#addMealSubmit').click();
|
||||
await page.waitForTimeout(800);
|
||||
// Stap 4: Opslaan als maaltijd
|
||||
// Stap 2: Sla op als maaltijd
|
||||
const saveBtn = page.locator('.save-meal-btn');
|
||||
await expect(saveBtn.first()).toBeVisible({ timeout: 5000 });
|
||||
await saveBtn.first().click();
|
||||
await page.waitForTimeout(500);
|
||||
// Stap 5: Naam invullen en opslaan
|
||||
const nameInput = page.locator('#mealNameInput');
|
||||
await expect(nameInput).toBeVisible({ timeout: 5000 });
|
||||
await nameInput.fill('Test ontbijt');
|
||||
await page.locator('#mealNameSave').click();
|
||||
await page.waitForTimeout(600);
|
||||
// Stap 6: Ga naar dagboek om meal-group te zien
|
||||
// Stap 3: Ga naar maaltijden tab om de maaltijd toe te voegen aan dagboek
|
||||
await page.locator('#navMaaltijden').click();
|
||||
await page.waitForTimeout(600);
|
||||
// Stap 4: Klik "Toevoegen aan dagboek" op de opgeslagen maaltijd
|
||||
const mealCardAdd = page.locator('.meal-card-add').first();
|
||||
await expect(mealCardAdd).toBeVisible({ timeout: 5000 });
|
||||
await mealCardAdd.click();
|
||||
await page.waitForTimeout(500);
|
||||
// Stap 5: Klik op een eetmoment-optie in de modal (bv. Ontbijt)
|
||||
const mealOption = page.locator('.meal-add-option').first();
|
||||
await expect(mealOption).toBeVisible({ timeout: 3000 });
|
||||
await mealOption.click();
|
||||
await page.waitForTimeout(600);
|
||||
// Sluit eventuele resteerde modals door op de overlay te klikken
|
||||
const anyOverlay = page.locator('.modal-overlay:not(.hidden)').first();
|
||||
if (await anyOverlay.isVisible().catch(() => false)) {
|
||||
await anyOverlay.click({ position: { x: 10, y: 10 } });
|
||||
await page.waitForTimeout(300);
|
||||
}
|
||||
// Stap 6: Ga naar dagboek om de meal-group te zien
|
||||
await page.locator('#navDagboek').click();
|
||||
await page.waitForTimeout(800);
|
||||
// Stap 7: Zoek meal-group
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue