Skip to content

Commit

Permalink
Merge pull request #75 from oramasearch/feature/orm-2153
Browse files Browse the repository at this point in the history
feat: support custom icons per result
  • Loading branch information
rjborba authored Jan 22, 2025
2 parents 0e0ba90 + 58c1f18 commit 386c994
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 10 deletions.
12 changes: 12 additions & 0 deletions apps/storybook/stories/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,16 @@ const demoIndexes: DemoIndexConfig = {
},
}

demoIndexes.oramaWithCustomIcons = {
...demoIndexes.orama,
resultMap: {
...demoIndexes.orama,
icon: (result) => {
return result.category === 'Open Source'
? 'https://cdn.jsdelivr.net/gh/twitter/twemoji@latest/assets/svg/1f680.svg'
: null
},
},
}

export default demoIndexes
1 change: 1 addition & 0 deletions packages/ui-stencil-vue/lib/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export const OramaSearchBox = /*@__PURE__*/ defineContainer<JSX.OramaSearchBox>(
'open',
'facetProperty',
'resultMap',
'resultItemRender',
'sourceBaseUrl',
'linksTarget',
'linksRel',
Expand Down
12 changes: 10 additions & 2 deletions packages/ui-stencil/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { ButtonProps } from "./components/internal/orama-button/orama-button";
import { ChatMarkdownLinkHref, ChatMarkdownLinkTarget, ChatMarkdownLinkTitle, CloudIndexConfig, ColorScheme, Facet, OnAnswerGeneratedCallbackProps, OnAnswerSourceClickCallbackProps, OnChatMarkdownLinkClickedCallbackProps, OnSearchCompletedCallbackProps, OnSearchResultClickCallbackProps, ResultMap, SearchResultBySection, SourcesMap } from "./types/index";
import { ChatMarkdownLinkHref, ChatMarkdownLinkTarget, ChatMarkdownLinkTitle, CloudIndexConfig, ColorScheme, Facet, OnAnswerGeneratedCallbackProps, OnAnswerSourceClickCallbackProps, OnChatMarkdownLinkClickedCallbackProps, OnSearchCompletedCallbackProps, OnSearchResultClickCallbackProps, ResultItemRenderFunction, ResultMap, SearchResultBySection, SourcesMap } from "./types/index";
import { TChatInteraction } from "./context/chatContext";
import { OramaClient } from "@oramacloud/client";
import { AnyOrama, Orama, SearchParams } from "@orama/orama";
Expand All @@ -18,7 +18,7 @@ import { TThemeOverrides as TThemeOverrides1 } from "./components.d";
import { SearchResultsProps } from "./components/internal/orama-search-results/orama-search-results";
import { TextProps } from "./components/internal/orama-text/orama-text";
export { ButtonProps } from "./components/internal/orama-button/orama-button";
export { ChatMarkdownLinkHref, ChatMarkdownLinkTarget, ChatMarkdownLinkTitle, CloudIndexConfig, ColorScheme, Facet, OnAnswerGeneratedCallbackProps, OnAnswerSourceClickCallbackProps, OnChatMarkdownLinkClickedCallbackProps, OnSearchCompletedCallbackProps, OnSearchResultClickCallbackProps, ResultMap, SearchResultBySection, SourcesMap } from "./types/index";
export { ChatMarkdownLinkHref, ChatMarkdownLinkTarget, ChatMarkdownLinkTitle, CloudIndexConfig, ColorScheme, Facet, OnAnswerGeneratedCallbackProps, OnAnswerSourceClickCallbackProps, OnChatMarkdownLinkClickedCallbackProps, OnSearchCompletedCallbackProps, OnSearchResultClickCallbackProps, ResultItemRenderFunction, ResultMap, SearchResultBySection, SourcesMap } from "./types/index";
export { TChatInteraction } from "./context/chatContext";
export { OramaClient } from "@oramacloud/client";
export { AnyOrama, Orama, SearchParams } from "@orama/orama";
Expand Down Expand Up @@ -206,6 +206,10 @@ export namespace Components {
* @deprecated it will be removed on next releases Placeholder for chat input
*/
"placeholder"?: string;
/**
* Used to render a custom icom per result. It should return a local asset path.
*/
"resultItemRender"?: ResultItemRenderFunction;
/**
* Used to map dataset result properties to the expected SearchBox properties
*/
Expand Down Expand Up @@ -868,6 +872,10 @@ declare namespace LocalJSX {
* @deprecated it will be removed on next releases Placeholder for chat input
*/
"placeholder"?: string;
/**
* Used to render a custom icom per result. It should return a local asset path.
*/
"resultItemRender"?: ResultItemRenderFunction;
/**
* Used to map dataset result properties to the expected SearchBox properties
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,18 @@
ph-files {
color: var(--icon-color-primary, icon-color('primary'));
}

.result-item-icon-wrapper {
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
width: 20px;
height: 20px;
}

.custom-result-item-icon {
max-height: 20px;
max-width: 20px;
object-fit: contain;
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,12 @@ export class SearchResults {
id={`search-result-${result.id}`}
onClick={(onClickEvent) => this.handleItemClick(onClickEvent, result)}
>
<div style={{ flexShrink: '0' }}>
<ph-files size="20px" />
<div class="result-item-icon-wrapper">
{result.icon ? (
<img alt="" class="custom-result-item-icon" src={result.icon} />
) : (
<ph-files size="20px" />
)}
</div>
<div class="textWrapper">
<orama-text as="h3" styledAs="p" class="result-title collapsed">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type {
OnChatMarkdownLinkClickedCallbackProps,
OnSearchCompletedCallbackProps,
OnSearchResultClickCallbackProps,
ResultItemRenderFunction,
ResultMap,
SourcesMap,
} from '@/types'
Expand Down Expand Up @@ -63,6 +64,10 @@ export class SearchBox {
* Used to map dataset result properties to the expected SearchBox properties
*/
@Prop() resultMap?: Partial<ResultMap> = {}
/**
* Used to render a custom icom per result. It should return a local asset path.
*/
@Prop() resultItemRender?: ResultItemRenderFunction
/**
* Used to provide source base URL for the Search Results
*/
Expand Down

Large diffs are not rendered by default.

24 changes: 21 additions & 3 deletions packages/ui-stencil/src/services/SearchService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { searchState } from '@/context/searchContext'
import { Switch, type OramaSwitchClient } from '@orama/switch'
import type {
OnSearchCompletedCallbackProps,
ResultItemRenderFunction,
ResultMap,
ResultMapKeys,
ResultMapRenderFunction,
SearchResultBySection,
SearchResultWithScore,
SearchResultWithIcon,
} from '@/types'

const LIMIT_RESULTS = 10
Expand Down Expand Up @@ -143,7 +144,7 @@ export class SearchService {
return perSectionResults
}

private hitToSearchResultParser = (hit: OramaHit, resultMap: ResultMap): SearchResultWithScore => {
private hitToSearchResultParser = (hit: OramaHit, resultMap: ResultMap): SearchResultWithIcon => {
function getResultMapValue(resultMapKey: ResultMapKeys): string {
const resultMapFunctionOrString = resultMap[resultMapKey]

Expand All @@ -160,12 +161,29 @@ export class SearchService {
return hit.document[resultMapString]
}

function getIcon(): string | null {
const iconStringOrFunction = resultMap.icon

if (!iconStringOrFunction) {
return null
}

if (typeof iconStringOrFunction === 'function') {
const iconFunctionRender = iconStringOrFunction as ResultItemRenderFunction
const iconFunctionRenderResult = iconFunctionRender(hit.document)

return iconFunctionRenderResult ?? null
}

return resultMap.icon as string
}

return {
id: hit.id,
score: hit.score,
title: getResultMapValue('title'),
description: getResultMapValue('description'),
path: getResultMapValue('path'),
icon: getIcon(),
}
}

Expand Down
8 changes: 6 additions & 2 deletions packages/ui-stencil/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,23 @@ export type SearchResult = {
export type ColorScheme = 'dark' | 'light' | 'system'

export type SearchResultWithScore = SearchResult & { score: number }
export type SearchResultWithIcon = SearchResult & { icon: string }

export type SearchResultBySection = {
section: string | undefined
items: SearchResultWithScore[]
items: SearchResultWithIcon[]
}

export type Facet = { name: string; count: number }

export type ResultMapKeys = keyof Omit<SearchResult, 'id'> | 'section'
export type ResultMapRenderFunction = (any) => string
export type ResultItemRenderFunction = (any) => string | null | undefined

// TODO: callback function should have the type of the schema
export type ResultMap = { [K in ResultMapKeys]?: string | ResultMapRenderFunction }
export type ResultMap = { [K in ResultMapKeys]?: string | ResultMapRenderFunction } & {
icon?: string | ResultItemRenderFunction
}

export type SourcesMap = { [K in keyof Omit<SearchResult, 'id'>]?: string }

Expand Down

0 comments on commit 386c994

Please sign in to comment.