import React, { useState } from 'react' import { alpha, useTheme } from '@mui/material/styles' import Autocomplete from '@mui/material/Autocomplete' import TextField from '@mui/material/TextField' import Magnify from 'mdi-material-ui/Magnify' import Paper from '@mui/material/Paper' import { useDebouncedCallback } from 'use-debounce' import InputAdornment from '@mui/material/InputAdornment' import useSWR from 'swr' import { CONFIG } from '@CONFIG' import { PageComponent } from '../../typings/generated/schema' import LmIcon from '../icon/LmIcon' import MuiNextLink from '../link/MuiNextLink' import { getLinkAttrs } from '../../utils/linkHandler' import { LmListSearchAutocompleteProps } from './listWidgetTypes' import { ListSearchAutocompleteContainer } from './ListSearchAutocompleteContainer' import { match, parse } from './autosuggest' import { useAppContext } from '@context/AppContext' import { ListItem } from '@mui/material' import { LmStoryblokService } from '../../utils/initial-props/StoryblokService' import { ISbStoriesParams, ISbStoryData } from 'storyblok-js-client' const fetcher = async ([_path, searchterm, locale, locales]: string[]): Promise< ISbStoryData[] > => { if (!searchterm) { return [] } let excluding_slugs = 'demo-content*' const params: ISbStoriesParams = { per_page: 25, sort_by: 'content.preview_title:desc', filter_query: { component: { in: 'page' } }, excluding_fields: 'body,right_body,meta_robots,property,seo_body', search_term: searchterm } if (locale) { params.starts_with = `${locale}/` } else if (locales) { params.excluding_slugs = `${excluding_slugs},${locales .split(',') .map((lang) => `${lang}/*`) .join(',')}` } const { data } = await LmStoryblokService.getStories(params) return data.stories || [] } type ListOptions = { uuid: string full_slug: string label: string }[] export default function LmListSearchAutocomplete({ content }: LmListSearchAutocompleteProps): JSX.Element { const [searchTerm, setSearchTerm] = useState() const theme = useTheme() const { defaultLocale, locale, locales } = useAppContext() const [open, setOpen] = useState() let prefixLocale = defaultLocale !== locale ? locale : undefined if (CONFIG.rootDirectory) { prefixLocale = CONFIG.rootDirectory } if (CONFIG.enableLocaleSuffix) { prefixLocale = locale } const callback = useDebouncedCallback((value: string) => { if (value.length < 2) { return } setSearchTerm(value) setOpen(true) }, 400) const additionalLocales = locales ?.filter((i) => i !== defaultLocale) .join(',') const { data } = useSWR( searchTerm ? [`cdn/stories`, searchTerm, prefixLocale, additionalLocales] : null, fetcher ) const allStories = data || [] const options: ListOptions = allStories .map((option) => ({ uuid: option.uuid, full_slug: option.full_slug, label: option.content?.preview_title || option.content?.meta_title || option.name || '' })) .sort((a, b) => (a.label > b.label ? 1 : b.label > a.label ? -1 : 0)) return ( setOpen(true)} onClose={() => setOpen(false)} options={options} freeSolo sx={{ display: 'inline-flex', verticalAlign: 'middle', color: 'inherit', '& .MuiOutlinedInput-notchedOutline': { backgroundColor: alpha('rgba(0,0,0,.05)', 0.15), borderRadius: content.shape === 'square' ? '0px' : content.shape === 'rounded' ? '20px' : undefined }, '& .MuiInputBase-root': { color: 'inherit' }, '& .MuiTextField-root': { width: 180, transition: theme.transitions.create('width') }, '&.Mui-focused .MuiTextField-root': { width: 220, '& .MuiOutlinedInput-notchedOutline': { backgroundColor: alpha('rgba(0,0,0,.05)', 0.2), borderColor: theme.palette.action.focus } } }} renderInput={(params) => { return ( { setOpen(true) }, onBlur: () => { setOpen(false) }, onChange: (event: React.ChangeEvent) => callback(event.currentTarget.value), autoComplete: 'new-password', startAdornment: ( {' '} {content.icon?.name ? ( ) : ( )} ) }} /> ) }} noOptionsText={content.not_found_label} getOptionLabel={(option) => typeof option === 'string' ? option : option.label } PaperComponent={(props) => ( )} renderOption={(_props, item, { inputValue }) => { const { href } = getLinkAttrs( { cached_url: item.full_slug, linktype: 'story' }, {} ) const matchValue = match(item.label, inputValue) const parts = parse(item.label, matchValue) return ( {parts.map((part, index) => ( {part.text} ))} ) }} /> ) }