UNPKG

@progress/sitefinity-nextjs-sdk

Version:

Provides OOB widgets developed using the Next.js framework, which includes an abstraction layer for Sitefinity communication. Additionally, it offers an expanded API, typings, and tools for further development and integration.

180 lines (179 loc) 8.11 kB
'use client'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import React from 'react'; import { classNames } from '../../editor/utils/classNames'; import { getCustomAttributes } from '../../editor/widget-framework/attributes'; import { getSearchBoxParams, getSearchUrl } from './utils'; import { useRouter, useSearchParams } from 'next/navigation'; const dataSfItemAttribute = 'data-sfitem'; const activeAttribute = 'data-sf-active'; export function SearchBoxClient(props) { const [searchItems, setSearchItems] = React.useState([]); const [dropDownWidth, setDropDownWidth] = React.useState(undefined); const [dropDownShow, setDropDownShow] = React.useState(false); const [suggestions, setSuggestions] = React.useState([]); const searchParams = useSearchParams(); const router = useRouter(); const dropdownRef = React.useRef(null); const inputRef = React.useRef(null); const searchBoxCustomAttributes = getCustomAttributes(props.widgetContext.model.Properties.Attributes, 'SearchBox'); const disabled = props.isEdit; const activeClass = props.activeClass; const handleOnSearch = (suggestions) => { const items = Array.isArray(suggestions) ? suggestions : []; setSearchItems(items); }; const handleShowDropdown = () => { const inputWidth = inputRef.current?.clientWidth; setDropDownWidth(inputWidth); setDropDownShow(true); }; const handleHideDropdown = (clear = true) => { if (clear) { handleOnSearch([]); } setDropDownWidth(undefined); setDropDownShow(false); }; const getSuggestions = (input) => { const data = getSearchBoxParams(props, searchParams.get('orderby') || ''); let requestUrl = data.servicePath + '/Default.GetSuggestions()' + '?indexName=' + data.catalogue + '&sf_culture=' + data.culture + '&siteId=' + data.siteId + '&scoringInfo=' + data.scoringSetting + '&suggestionFields=' + data.suggestionFields + '&searchQuery=' + encodeURIComponent(input.value)?.toLowerCase(); if (data.resultsForAllSites === 1) { requestUrl += '&resultsForAllSites=True'; } else if (data.resultsForAllSites === 2) { requestUrl += '&resultsForAllSites=False'; } fetch(requestUrl).then(function (res) { res.json().then((suggestions) => { handleOnSearch(suggestions.value); setSuggestions(suggestions.value); handleShowDropdown(); }); }).catch(function () { handleHideDropdown(); }); }; const navigateToResults = () => { const input = inputRef.current; if (window.DataIntelligenceSubmitScript) { window.DataIntelligenceSubmitScript._client.fetchClient.sendInteraction({ P: 'Search for', O: input.value.trim(), OM: { PageUrl: location.href } }); } const url = getSearchUrl(input.value.trim(), props, searchParams.get('orderby') || ''); handleHideDropdown(); router.push(url); }; const inputKeyupHandler = (e) => { const keyCode = e.keyCode || e.charCode; if (e.code !== 'ArrowUp' && e.code !== 'ArrowDown' && e.code !== 'Escape' && keyCode !== 13) { const searchText = e.target.value.trim(); const config = getSearchBoxParams(props, searchParams.get('orderby') || ''); if (config.minSuggestionLength && searchText.length >= config.minSuggestionLength) { getSuggestions(e.target); } else { handleHideDropdown(); } } if (e.code === 'ArrowDown' && suggestions.length) { handleShowDropdown(); firstItemFocus(); } if (e.code === 'Escape') { handleHideDropdown(); } }; const inputKeydownHandler = (e) => { const keyCode = e.keyCode || e.charCode; if (keyCode === 13) { navigateToResults(); } }; const handleDropDownClick = (e) => { const target = e.target; const content = target.innerText; inputRef.current.value = content; handleHideDropdown(); navigateToResults(); }; const handleDropDownBlur = (e) => { if (dropdownRef.current != null && !dropdownRef.current.contains(e.relatedTarget)) { handleHideDropdown(false); } }; const handleDropDownKeyUp = (e) => { const dropdown = dropdownRef.current; const key = e.keyCode || e.charCode; const activeLinkSelector = `[${dataSfItemAttribute}][${activeAttribute}]`; const activeLink = dropdown.querySelector(activeLinkSelector); if (!activeLink) { return; } const previousParent = activeLink.parentElement.previousElementSibling; const nextParent = activeLink.parentElement.nextElementSibling; if (key === 38 && previousParent) { e.preventDefault(); focusItem(previousParent); } else if (key === 40 && nextParent) { e.preventDefault(); focusItem(nextParent); } else if (key === 13) { inputRef.current.value = activeLink.innerText; navigateToResults(); handleHideDropdown(); inputRef.current.focus(); } else if (key === 27) { resetActiveClass(); handleHideDropdown(false); inputRef.current.focus(); } }; const firstItemFocus = () => { const dropdown = dropdownRef.current; if (dropdown && dropdown.children.length) { const item = dropdown.children[0].querySelector(`[${dataSfItemAttribute}]`); focusItem(item?.parentElement); } }; const focusItem = (item) => { resetActiveClass(); const link = item.querySelector(`[${dataSfItemAttribute}]`); if (link && activeClass) { link.classList.add(...activeClass); } //set data attribute, to be used in queries instead of class link.setAttribute(activeAttribute, ''); link.focus(); }; const resetActiveClass = () => { const dropdown = dropdownRef.current; const activeLink = dropdown.querySelector(`[${activeAttribute}]`); if (activeLink && activeClass) { activeLink.classList.remove(...activeClass); activeLink.removeAttribute(activeAttribute); } }; return (_jsxs("div", { ...props.attributes, children: [_jsxs("div", { className: "d-flex", children: [_jsx("input", { type: "text", className: "form-control", disabled: disabled, placeholder: props.searchBoxPlaceholder || undefined, defaultValue: searchParams.get('searchQuery') || '', ref: inputRef, onKeyUp: inputKeyupHandler, onKeyDown: inputKeydownHandler, onBlur: handleDropDownBlur, ...searchBoxCustomAttributes }), _jsx("button", { "data-sf-role": "search-button", className: "btn btn-primary ms-2 flex-shrink-0", disabled: disabled, onClick: navigateToResults, children: props.searchButtonLabel })] }), props.suggestionsTriggerCharCount != null && props.suggestionsTriggerCharCount >= 2 && (_jsx("ul", { role: "listbox", onClick: handleDropDownClick, onKeyUp: handleDropDownKeyUp, onBlur: handleDropDownBlur, style: { width: dropDownWidth }, ref: dropdownRef, className: classNames('border bg-body list-unstyled position-absolute', { [props.visibilityClassHidden]: !dropDownShow }), children: searchItems.map((item, idx) => { return (item && _jsx("li", { role: 'option', "aria-selected": false, children: _jsx("button", { role: "presentation", className: "dropdown-item text-truncate", "data-sfitem": "", title: item, tabIndex: -1, children: item }) }, idx)); }) }))] })); }