dsfr_typesense-2.1.x-dev/js/search.js
js/search.js
((Drupal, TypesenseInstantSearchAdapter, instantsearch) => {
/**
* Handles the "Read more" / "Read less" functionality.
*/
Drupal.behaviors.readMore = {
attach(context) {
const hitItems = once('read-more', '.hit-item', context);
hitItems.forEach((hitItem) => {
const readMoreLink = hitItem.querySelector('[data-action="read-more"]');
const readLessLink = hitItem.querySelector('[data-action="read-less"]');
if (readMoreLink) {
readMoreLink.addEventListener('click', (e) => {
e.preventDefault();
const container = e.target.closest('.hit-body');
container.querySelector('.text-container').classList.remove('truncated');
e.target.style.display = 'none';
if (readLessLink) readLessLink.style.display = 'inline';
});
}
if (readLessLink) {
readLessLink.addEventListener('click', (e) => {
e.preventDefault();
const container = e.target.closest('.hit-body');
container.querySelector('.text-container').classList.add('truncated');
e.target.style.display = 'none';
if (readMoreLink) readMoreLink.style.display = 'inline';
});
}
});
},
};
/**
* Initializes the Typesense InstantSearch functionality.
*/
Drupal.behaviors.search = {
attach(context, settings) {
const searchbox = once('searchbox', '#searchbox', context).shift();
if (!searchbox) {
return;
}
const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
server: {
apiKey: settings.search_api_typesense.api_key,
nodes: [
{
host: settings.search_api_typesense.host,
port: settings.search_api_typesense.port,
protocol: settings.search_api_typesense.protocol,
},
],
},
additionalSearchParameters: {
query_by: settings.search_api_typesense.query_by_fields,
query_by_weights: settings.search_api_typesense.query_by_weights,
sort_by: settings.search_api_typesense.sort_by_fields,
exclude_fields: 'embedding',
exhaustive_search: true,
},
});
const { searchClient } = typesenseInstantsearchAdapter;
const search = instantsearch({
searchClient,
indexName: settings.search_api_typesense.index,
routing: true,
});
// Custom render function for hits.
const renderHits = (renderOptions, isFirstRender) => {
const { hits, widgetParams } = renderOptions;
const container = document.querySelector(widgetParams.container);
// Build the HTML for all hits.
container.innerHTML = `
<ul class="ais-Hits-list">
${hits
.map(item => {
const url = item.url || '#';
const renderedItem = item.rendered_item || '';
const isLongText = renderedItem.length > 280;
// Use InstantSearch's highlighting function.
const highlightedTitle = instantsearch.highlight({ attribute: 'title', hit: item });
const highlightedBody = instantsearch.highlight({ attribute: 'rendered_item', hit: item });
return `
<li class="ais-Hits-item">
<article class="hit-item" data-hit-id="${item.objectID}">
<h3 class="hit-title">
<a href="${url}">${highlightedTitle}</a>
</h3>
<div class="hit-body">
<div class="text-container ${isLongText ? 'truncated' : ''}">
${highlightedBody}
</div>
${isLongText ? `
<a href="#" class="read-more" data-action="read-more">Lire la suite</a>
<a href="#" class="read-less" style="display: none;" data-action="read-less">Réduire</a>
` : ''}
</div>
</article>
</li>
`;
})
.join('')}
</ul>
`;
// IMPORTANT: Attach Drupal behaviors to the newly created content.
Drupal.attachBehaviors(container);
};
// Create the custom hits widget.
const customHits = instantsearch.connectors.connectHits(renderHits);
search.addWidgets([
instantsearch.widgets.configure({
hitsPerPage: 12,
}),
instantsearch.widgets.searchBox({
container: '#searchbox',
placeholder: 'Search...',
}),
customHits({
container: '#hits',
}),
instantsearch.widgets.pagination({
container: '#pagination',
}),
instantsearch.widgets.stats({
container: '#stats',
}),
]);
settings.search_api_typesense.facet_string_fields.forEach((facet) => {
search.addWidgets([
instantsearch.widgets.refinementList({
container: `#${facet}`,
attribute: facet,
searchable: true,
}),
]);
});
settings.search_api_typesense.facet_number_fields.forEach((facet) => {
search.addWidgets([
instantsearch.widgets.refinementList({
container: `#${facet}`,
attribute: facet,
searchable: true,
}),
]);
});
search.start();
},
};
})(Drupal, TypesenseInstantSearchAdapter, instantsearch); // eslint-disable-line
