Scroll List View on Doctypes

frappe.listview_settings[“Contact”] = {
hide_name_column: true,

onload: function(listview) {
    // Check if we are in the Contact doctype
    if (frappe.get_route()[1] === "Contact") {
        // Hide specific input fields in the standard filter section
        $('.standard-filter-section input[data-fieldname="name"]').closest('.form-group').hide();
        $('.standard-filter-section input[data-fieldname="full_name"]').closest('.form-group').hide();
    }
},

on_page_load: function (listview) {
    if (frappe.get_route()[1] === "Contact") {
        console.log('Contact List View: Page Load Started');
        injectInitialStyles();
        applyScrollStyles();
        setupResizeListener();
        observeDomChanges();
    }
},

refresh: function (listview) {
    if (frappe.get_route()[1] === "Contact") {
        console.log('Contact List View: Refresh Triggered');
        injectInitialStyles();
        applyScrollStyles();
        setupResizeListener();
        observeDomChanges();
    }
},

on_list_view_render: function (listview) {
    if (frappe.get_route()[1] === "Contact") {
        console.log('Contact List View: Render Triggered');
        injectInitialStyles();
        applyScrollStyles();
        setupResizeListener();
        observeDomChanges();
    }
},

};

function injectInitialStyles() {
// Check if we are in the Contact doctype
if (frappe.get_route()[1] !== “Contact”) {
console.log(‘Not in Contact doctype - skipping style injection’);
return;
}

console.log('Injecting Initial Styles');
const styleElement = document.createElement('style');
styleElement.id = 'contact-list-view-styles';
styleElement.innerHTML = `
    .contact-doctype .frappe-list {
        overflow-x: auto !important;
        overflow-y: auto !important;
        width: 100% !important;
        max-width: 100% !important;
        display: block !important;
    }
    .contact-doctype .list-view-container {
        overflow-x: auto !important;
        overflow-y: hidden !important;
        width: 100% !important;
        max-width: 100% !important;
    }
    .contact-doctype .list-row-container {
        width: 100% !important;
        min-width: max-content !important;
        display: flex !important;
    }
    .contact-doctype .list-row {
        min-width: max-content !important;
        width: 100% !important;
    }
    .contact-doctype header.level.list-row-head.text-muted {
        width: max-content !important;
    }
    .contact-doctype .list-row .level-left, 
    .contact-doctype .list-row-head .level-left {
        flex: 4;
        min-width: 100%;
    }
    .contact-doctype .list-row, 
    .contact-doctype .list-row-head {
        padding: 15px 80px 15px 0;
        height: 40px;
        cursor: pointer;
        transition: color .2s;
        -webkit-transition: color .2s;
        font-size: var(--text-base);
        font-weight: var(--weight-regular);
        letter-spacing: .02em;
        border-radius: var(--border-radius-md);
    }
`;

// Remove any existing style element
const existingStyle = document.getElementById('contact-list-view-styles');
if (existingStyle) {
    existingStyle.remove();
}

document.head.appendChild(styleElement);

}

function applyScrollStyles() {
// Check if we are in the Contact doctype
if (frappe.get_route()[1] !== “Contact”) {
console.log(‘Not in Contact doctype - skipping custom styles’);
return;
}

console.log('Applying Scroll Styles - Screen Width:', window.innerWidth);

// Early return if not desktop
if (window.innerWidth <= 768) {
    console.log('Mobile view - skipping custom styles');
    return;
}

try {
    // Select key containers
    const mainContainer = document.querySelector('.frappe-list');
    const listViewContainer = document.querySelector('.list-view-container');

    // Inline styles to prevent initial rendering issues
    if (mainContainer) {
        mainContainer.style.cssText = `
            overflow-x: auto;
            overflow-y: auto;
            width: 100%;
            max-width: 100%;
        `;
    }

    if (listViewContainer) {
        listViewContainer.style.cssText = `
            overflow-x: auto;
            overflow-y: hidden;
            width: 100%;
            max-width: 100%;
        `;
    }

    // Mutation observer to react to DOM changes immediately
    const applyStyles = () => {
        const columns = document.querySelectorAll('.list-row-col');
        const subjectColumns = document.querySelectorAll('.list-subject');
        const rowContainers = document.querySelectorAll('.list-row-container');
        const rows = document.querySelectorAll('.list-row');

        columns.forEach((col) => {
            col.style.cssText = `
                min-width: 150px;
                max-width: 150px;
            `;
        });

        subjectColumns.forEach((col) => {
            col.style.cssText = `
                min-width: 200px;
                max-width: 200px;
            `;
        });

        rowContainers.forEach((container) => {
            container.style.cssText = `
                min-width: max-content;
                width: 100%;
            `;
        });

        rows.forEach((row) => {
            row.style.cssText = `
                min-width: max-content;
                width: 100%;
            `;
        });

        console.log('Scroll styles applied successfully');
    };

    // Apply styles immediately to existing elements
    applyStyles();

    // Observe changes to ensure newly added elements get styled
    const observer = new MutationObserver(() => {
        if (frappe.get_route()[1] === "Contact") {
            applyStyles();
        }
    });
    
    observer.observe(document.body, { childList: true, subtree: true });

} catch (error) {
    console.error('Error in applyScrollStyles:', error);
}

}

// Run on DOMContentLoaded for zero delay
document.addEventListener(‘DOMContentLoaded’, () => {
if (frappe.get_route()[1] === “Contact”) {
applyScrollStyles();
}
});

function setupResizeListener() {
console.log(‘Setting up Resize Listener’);
window.addEventListener(‘resize’, () => {
if (frappe.get_route()[1] === “Contact”) {
console.log(‘Window Resized - Contact doctype’);
applyScrollStyles();
}
});
}

function observeDomChanges() {
console.log(‘Setting up DOM Change Observer’);
const targetNode = document.querySelector(‘.frappe-list’);

if (!targetNode) {
console.warn(‘No list view container found’);
return;
}

const config = {
childList: true,
subtree: true
};

const callback = function (mutationsList) {
console.log(‘DOM Mutation Detected’);
for (let mutation of mutationsList) {
if (mutation.type === ‘childList’) {
console.log(‘Child List Mutation - Applying Styles’);
applyScrollStyles();
}
}
};

const observer = new MutationObserver(callback);
observer.observe(targetNode, config);
}

// Ensure immediate and delayed application only for Contact doctype
setTimeout(() => {
if (frappe.get_route()[1] === “Contact”) {
console.log(‘Delayed Style Application for Contact’);
injectInitialStyles();
applyScrollStyles();
}
}, 100);

You can easily set using the short code, so please check this video.

1 Like