mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 20:03:12 +03:00
Updated to highlight searched keywords in AdminX settings (#18112)
refs https://github.com/TryGhost/Product/issues/3832 --- <!-- Leave the line below if you'd like GitHub Copilot to generate a summary from your commit --> <!-- copilot:summary --> ### <samp>🤖 Generated by Copilot at 1347a85</samp> Added search functionality to the settings page using a custom hook and a service. The `useSearch` hook uses the `useSearchService` function to create a search service object that provides the filter and highlight logic. The `highlightKeywords` function from the search service is passed to the `SettingsGroupHeader` component to render the settings with the matching keywords.
This commit is contained in:
parent
2ad0e73c42
commit
bb0dff3571
@ -1,5 +1,6 @@
|
||||
import Heading from '../global/Heading';
|
||||
import React from 'react';
|
||||
import {useSearch} from '../../components/providers/ServiceProvider';
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
@ -8,12 +9,14 @@ interface Props {
|
||||
}
|
||||
|
||||
const SettingGroupHeader: React.FC<Props> = ({title, description, children}) => {
|
||||
const {highlightKeywords} = useSearch();
|
||||
|
||||
return (
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
{(title || description) &&
|
||||
<div>
|
||||
<Heading level={5}>{title}</Heading>
|
||||
{description && <p className="mt-0.5 hidden max-w-lg text-sm group-[.is-not-editing]/setting-group:!visible group-[.is-not-editing]/setting-group:!block md:!visible md:!block">{description}</p>}
|
||||
<Heading level={5}>{highlightKeywords(title || '')}</Heading>
|
||||
{description && <p className="mt-0.5 hidden max-w-lg text-sm group-[.is-not-editing]/setting-group:!visible group-[.is-not-editing]/setting-group:!block md:!visible md:!block">{highlightKeywords(description)}</p>}
|
||||
</div>
|
||||
}
|
||||
<div className='-mt-0.5'>
|
||||
@ -23,4 +26,4 @@ const SettingGroupHeader: React.FC<Props> = ({title, description, children}) =>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingGroupHeader;
|
||||
export default SettingGroupHeader;
|
||||
|
@ -44,7 +44,7 @@ const ServicesContext = createContext<ServicesContextProps>({
|
||||
ghostVersion: '',
|
||||
officialThemes: [],
|
||||
zapierTemplates: [],
|
||||
search: {filter: '', setFilter: () => {}, checkVisible: () => true},
|
||||
search: {filter: '', setFilter: () => {}, checkVisible: () => true, highlightKeywords: () => ''},
|
||||
unsplashConfig: {
|
||||
Authorization: '',
|
||||
'Accept-Version': '',
|
||||
|
@ -1,27 +0,0 @@
|
||||
import {useState} from 'react';
|
||||
|
||||
export interface SearchService {
|
||||
filter: string;
|
||||
setFilter: (value: string) => void;
|
||||
checkVisible: (keywords: string[]) => boolean;
|
||||
}
|
||||
|
||||
const useSearchService = () => {
|
||||
const [filter, setFilter] = useState('');
|
||||
|
||||
const checkVisible = (keywords: string[]) => {
|
||||
if (!keywords.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return keywords.some(keyword => keyword.toLowerCase().includes(filter.toLowerCase()));
|
||||
};
|
||||
|
||||
return {
|
||||
filter,
|
||||
setFilter,
|
||||
checkVisible
|
||||
};
|
||||
};
|
||||
|
||||
export default useSearchService;
|
54
apps/admin-x-settings/src/utils/search.tsx
Normal file
54
apps/admin-x-settings/src/utils/search.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import React, {ReactNode, useState} from 'react';
|
||||
|
||||
export interface SearchService {
|
||||
filter: string;
|
||||
setFilter: (value: string) => void;
|
||||
checkVisible: (keywords: string[]) => boolean;
|
||||
highlightKeywords: (text: ReactNode) => ReactNode;
|
||||
}
|
||||
|
||||
const useSearchService = () => {
|
||||
const [filter, setFilter] = useState('');
|
||||
|
||||
const checkVisible = (keywords: string[]) => {
|
||||
if (!keywords.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return keywords.some(keyword => keyword.toLowerCase().includes(filter.toLowerCase()));
|
||||
};
|
||||
|
||||
const highlightKeywords = (text: ReactNode): ReactNode => {
|
||||
if (!filter) {
|
||||
return text;
|
||||
}
|
||||
|
||||
if (typeof text === 'string') {
|
||||
const words = filter.split(/\s+/).map(word => word.toLowerCase());
|
||||
const wordsPattern = words.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
|
||||
const parts = text.split(new RegExp(`(${wordsPattern})`, 'gi'));
|
||||
|
||||
return parts.map(part => (words.includes(part.toLowerCase()) ? <span className='bg-yellow-500/40'>{part}</span> : part));
|
||||
} else if (Array.isArray(text)) {
|
||||
return text.map(part => highlightKeywords(part));
|
||||
} else if (text && typeof text === 'object' && text) {
|
||||
return React.Children.map(text, (child) => {
|
||||
if (child && typeof child === 'object' && 'props' in child) {
|
||||
return highlightKeywords(child.props.children);
|
||||
}
|
||||
return child;
|
||||
});
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
filter,
|
||||
setFilter,
|
||||
checkVisible,
|
||||
highlightKeywords
|
||||
};
|
||||
};
|
||||
|
||||
export default useSearchService;
|
@ -1 +1 @@
|
||||
Subproject commit 276e2c9d0140c902e1c8d3760bc194790722fa71
|
||||
Subproject commit 4d3319d05ce92e7b0244e5608d3fc6cc9c86e735
|
Loading…
Reference in New Issue
Block a user