Hi... We have created a custom menu widget using i...
# built-with-typesense
s
Hi... We have created a custom menu widget using instantsearch.js. Instead of expanding the "Show More button", we are opening a modal with live search functionality of menu items. Further menu items are grouped by their first letter of the alphabet. I hope it helps others.
Copy code
let categoryMenuListLength = '';
    let isRefineCalled = false;
    const customMenu = instantsearch.connectors.connectMenu(
        (renderOptions, isFirstRender) => {
        const {
            items,
            refine,
            sendEvent,
            createURL,
            isShowingMore,
            canToggleShowMore,
            toggleShowMore,
            widgetParams,
        } = renderOptions;

        widgetParams.refine = refine;

        if (isFirstRender) {
            const ul = document.createElement('ul');
            const button = document.createElement('button');
            button.textContent = categoryMenuListLength;
            button.classList.add('mt-1', 'mb-2', 'text-sm', 'font-normal', 'text-red-600', 'ps-0', 'hover:underline', 'hover:text-blue-600');


            button.addEventListener('click', () => {
                $wire.dispatch('open-modal',{name:'lead-business-category-search'});
            });

            widgetParams.container.appendChild(ul);
            widgetParams.container.appendChild(button);
        }

        widgetParams.container.querySelector('ul').innerHTML = items
            .map(
            item => `
                <li class="mt-1 mb-2 text-sm font-normal text-gray-700">
                <a
                    href="${createURL(item.value)}"
                    data-value="${item.value}"
                    class="${item.isRefined ? 'text-red-600 font-semi-bold' : ''}"
                >
                    <span class="hover:underline hover:text-primary-600">${item.label}</span> <span class="ml-1 text-xs text-primary-600 font-semi-bold">${item.count}</span>
                </a>
                </li>
            `
            )
            .join('');



        [...widgetParams.container.querySelectorAll('a')].forEach(element => {
            element.addEventListener('click', event => {
                event.preventDefault();
                refine(event.currentTarget.dataset.value);
            });
        });

        const button = widgetParams.container.querySelector('button');

        button.disabled = !canToggleShowMore;
        button.textContent = isShowingMore ? 'Show less' : categoryMenuListLength;

        // we are sending livewire event to open
        $wire.on('lead-business-category-selected-in-child', (event) => {

                if (!isRefineCalled){
                    widgetParams.refine(event.categoryName);
                    isRefineCalled = true;
                    // Reset the flag after a short delay to allow future calls
                    setTimeout(() => {
                        isRefineCalled = false;
                    }, 500);
                }
            });
    }
    );
then use below code inside. search.addWidgets[].
Copy code
customMenu({
            container: document.querySelector('#menu'),
            attribute: 'category.level0',
            limit: 10,
            showMore: true,
            showMoreLimit: 500,
            transformItems( items, { results }) {
                categoryMenuListLength = `+${results.hierarchicalFacets[0].data.length - 10} more`;
                menuData = results.hierarchicalFacets[0].data;
                $wire.dispatch('updateFullCategoryMenuList', { data: menuData });  //you can use your own javascript send event to pass full menu data to modal. 

                return items.map(item => ({
                    ...item,
                    label: item.label,
                    count: item.count,
                }));
            },
        }),
👍 2