From 86e89d500c1a4c63b1fa120de8d17a9a11479830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Jos=C3=A9=20Borba=20Fernandes?= Date: Tue, 16 Jul 2024 22:59:14 -0300 Subject: [PATCH] feat: suggestions component --- .../stories/orama-chat-suggestion.stories.tsx | 19 +++++++++++ .../stories/orama-facets.stories.tsx | 2 +- .../src/components/stencil-generated/index.ts | 1 + packages/ui-stencil-vue/lib/components.ts | 10 ++++-- packages/ui-stencil/src/components.d.ts | 25 +++++++++++--- .../orama-chat-assistent-message/readme.md | 2 +- .../orama-chat-user-message/readme.md | 2 +- .../orama-chat-suggestions.scss | 34 +++++++++++++++++++ .../orama-chat-suggestions.tsx | 34 +++++++++++++++++++ .../internal/orama-chat-suggestions/readme.md | 31 +++++++++++++++++ .../internal/orama-chat/orama-chat.scss | 1 + .../internal/orama-chat/orama-chat.tsx | 12 +++++++ .../components/internal/orama-chat/readme.md | 2 ++ .../internal/orama-facets/orama-facets.tsx | 4 +-- .../internal/orama-facets/readme.md | 2 +- .../orama-search-results.tsx | 4 +-- .../internal/orama-search-results/readme.md | 6 ++-- .../internal/orama-search/orama-search.tsx | 2 +- .../src/components/search-box/readme.md | 1 + 19 files changed, 176 insertions(+), 18 deletions(-) create mode 100644 apps/storybook/stories/orama-chat-suggestion.stories.tsx create mode 100644 packages/ui-stencil/src/components/internal/orama-chat-suggestions/orama-chat-suggestions.scss create mode 100644 packages/ui-stencil/src/components/internal/orama-chat-suggestions/orama-chat-suggestions.tsx create mode 100644 packages/ui-stencil/src/components/internal/orama-chat-suggestions/readme.md diff --git a/apps/storybook/stories/orama-chat-suggestion.stories.tsx b/apps/storybook/stories/orama-chat-suggestion.stories.tsx new file mode 100644 index 00000000..345ea3f8 --- /dev/null +++ b/apps/storybook/stories/orama-chat-suggestion.stories.tsx @@ -0,0 +1,19 @@ +import type { StoryObj, Meta } from '@storybook/web-components' +import type { Components } from 'ui-stencil' +import { fn } from '@storybook/test' + +const meta: Meta = { + title: 'Internal/OramaChat', + component: 'orama-chat-suggestions', +} + +export default meta + +type Story = StoryObj + +export const OramaSuggestions: Story = { + args: { + suggestions: ['How to get started?', 'What are the prices?', 'What is Orama?'], + suggestionClicked: fn(), + }, +} diff --git a/apps/storybook/stories/orama-facets.stories.tsx b/apps/storybook/stories/orama-facets.stories.tsx index 1022460f..deba1d4c 100644 --- a/apps/storybook/stories/orama-facets.stories.tsx +++ b/apps/storybook/stories/orama-facets.stories.tsx @@ -21,6 +21,6 @@ export const OramaFacets: Story = { { name: 'FacetThree', count: 74 }, ], selectedFacet: 'FacetOne', - onFacetClick: fn(), + facetClicked: fn(), }, } diff --git a/packages/ui-stencil-react/src/components/stencil-generated/index.ts b/packages/ui-stencil-react/src/components/stencil-generated/index.ts index e532eaa5..50f8bd86 100644 --- a/packages/ui-stencil-react/src/components/stencil-generated/index.ts +++ b/packages/ui-stencil-react/src/components/stencil-generated/index.ts @@ -11,6 +11,7 @@ export const OramaButton = /*@__PURE__*/createReactComponent('orama-chat'); export const OramaChatAssistentMessage = /*@__PURE__*/createReactComponent('orama-chat-assistent-message'); export const OramaChatMessagesContainer = /*@__PURE__*/createReactComponent('orama-chat-messages-container'); +export const OramaChatSuggestions = /*@__PURE__*/createReactComponent('orama-chat-suggestions'); export const OramaChatUserMessage = /*@__PURE__*/createReactComponent('orama-chat-user-message'); export const OramaFacets = /*@__PURE__*/createReactComponent('orama-facets'); export const OramaInput = /*@__PURE__*/createReactComponent('orama-input'); diff --git a/packages/ui-stencil-vue/lib/components.ts b/packages/ui-stencil-vue/lib/components.ts index abf9024f..7a7100b2 100644 --- a/packages/ui-stencil-vue/lib/components.ts +++ b/packages/ui-stencil-vue/lib/components.ts @@ -28,6 +28,12 @@ export const OramaChatAssistentMessage = /*@__PURE__*/ defineContainer('orama-chat-messages-container', undefined); +export const OramaChatSuggestions = /*@__PURE__*/ defineContainer('orama-chat-suggestions', undefined, [ + 'suggestions', + 'suggestionClicked' +]); + + export const OramaChatUserMessage = /*@__PURE__*/ defineContainer('orama-chat-user-message', undefined, [ 'message' ]); @@ -36,7 +42,7 @@ export const OramaChatUserMessage = /*@__PURE__*/ defineContainer('orama-facets', undefined, [ 'facets', 'selectedFacet', - 'onFacetClick' + 'facetClicked' ]); @@ -68,7 +74,7 @@ export const OramaSearchResults = /*@__PURE__*/ defineContainer void; + "suggestions": string[]; + } interface OramaChatUserMessage { "message": TChatMessage; } interface OramaFacets { + "facetClicked": (facetName: string) => void; "facets": Facet[]; - "onFacetClick": (facetName: string) => void; "selectedFacet": string; } interface OramaInput { @@ -137,6 +141,12 @@ declare global { prototype: HTMLOramaChatMessagesContainerElement; new (): HTMLOramaChatMessagesContainerElement; }; + interface HTMLOramaChatSuggestionsElement extends Components.OramaChatSuggestions, HTMLStencilElement { + } + var HTMLOramaChatSuggestionsElement: { + prototype: HTMLOramaChatSuggestionsElement; + new (): HTMLOramaChatSuggestionsElement; + }; interface HTMLOramaChatUserMessageElement extends Components.OramaChatUserMessage, HTMLStencilElement { } var HTMLOramaChatUserMessageElement: { @@ -185,7 +195,7 @@ declare global { new (): HTMLOramaSearchElement; }; interface HTMLOramaSearchResultsElementEventMap { - "onOramaItemClick": SearchResult; + "oramaItemClick": SearchResult; } interface HTMLOramaSearchResultsElement extends Components.OramaSearchResults, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLOramaSearchResultsElement, ev: OramaSearchResultsCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; @@ -236,6 +246,7 @@ declare global { "orama-chat": HTMLOramaChatElement; "orama-chat-assistent-message": HTMLOramaChatAssistentMessageElement; "orama-chat-messages-container": HTMLOramaChatMessagesContainerElement; + "orama-chat-suggestions": HTMLOramaChatSuggestionsElement; "orama-chat-user-message": HTMLOramaChatUserMessageElement; "orama-facets": HTMLOramaFacetsElement; "orama-input": HTMLOramaInputElement; @@ -265,12 +276,16 @@ declare namespace LocalJSX { } interface OramaChatMessagesContainer { } + interface OramaChatSuggestions { + "suggestionClicked"?: (suggestion: string) => void; + "suggestions"?: string[]; + } interface OramaChatUserMessage { "message"?: TChatMessage; } interface OramaFacets { + "facetClicked"?: (facetName: string) => void; "facets"?: Facet[]; - "onFacetClick"?: (facetName: string) => void; "selectedFacet"?: string; } interface OramaInput { @@ -293,7 +308,7 @@ declare namespace LocalJSX { interface OramaSearchResults { "error"?: boolean; "loading"?: boolean; - "onOnOramaItemClick"?: (event: OramaSearchResultsCustomEvent) => void; + "onOramaItemClick"?: (event: OramaSearchResultsCustomEvent) => void; "searchTerm"?: SearchResultsProps['searchTerm']; "sections"?: SearchResultBySection[]; } @@ -339,6 +354,7 @@ declare namespace LocalJSX { "orama-chat": OramaChat; "orama-chat-assistent-message": OramaChatAssistentMessage; "orama-chat-messages-container": OramaChatMessagesContainer; + "orama-chat-suggestions": OramaChatSuggestions; "orama-chat-user-message": OramaChatUserMessage; "orama-facets": OramaFacets; "orama-input": OramaInput; @@ -361,6 +377,7 @@ declare module "@stencil/core" { "orama-chat": LocalJSX.OramaChat & JSXBase.HTMLAttributes; "orama-chat-assistent-message": LocalJSX.OramaChatAssistentMessage & JSXBase.HTMLAttributes; "orama-chat-messages-container": LocalJSX.OramaChatMessagesContainer & JSXBase.HTMLAttributes; + "orama-chat-suggestions": LocalJSX.OramaChatSuggestions & JSXBase.HTMLAttributes; "orama-chat-user-message": LocalJSX.OramaChatUserMessage & JSXBase.HTMLAttributes; "orama-facets": LocalJSX.OramaFacets & JSXBase.HTMLAttributes; "orama-input": LocalJSX.OramaInput & JSXBase.HTMLAttributes; diff --git a/packages/ui-stencil/src/components/internal/orama-chat-messages-container/orama-chat-assistent-message/readme.md b/packages/ui-stencil/src/components/internal/orama-chat-messages-container/orama-chat-assistent-message/readme.md index 3dd55062..8a478d28 100644 --- a/packages/ui-stencil/src/components/internal/orama-chat-messages-container/orama-chat-assistent-message/readme.md +++ b/packages/ui-stencil/src/components/internal/orama-chat-messages-container/orama-chat-assistent-message/readme.md @@ -9,7 +9,7 @@ | Property | Attribute | Description | Type | Default | | --------- | --------- | ----------- | --------------------------------------------------- | ----------- | -| `message` | -- | | `{ role: "user" \| "assistant"; content: string; }` | `undefined` | +| `message` | -- | | `{ role: "assistant" \| "user"; content: string; }` | `undefined` | ## Dependencies diff --git a/packages/ui-stencil/src/components/internal/orama-chat-messages-container/orama-chat-user-message/readme.md b/packages/ui-stencil/src/components/internal/orama-chat-messages-container/orama-chat-user-message/readme.md index 2d09cc5e..d481cd72 100644 --- a/packages/ui-stencil/src/components/internal/orama-chat-messages-container/orama-chat-user-message/readme.md +++ b/packages/ui-stencil/src/components/internal/orama-chat-messages-container/orama-chat-user-message/readme.md @@ -9,7 +9,7 @@ | Property | Attribute | Description | Type | Default | | --------- | --------- | ----------- | --------------------------------------------------- | ----------- | -| `message` | -- | | `{ role: "user" \| "assistant"; content: string; }` | `undefined` | +| `message` | -- | | `{ role: "assistant" \| "user"; content: string; }` | `undefined` | ## Dependencies diff --git a/packages/ui-stencil/src/components/internal/orama-chat-suggestions/orama-chat-suggestions.scss b/packages/ui-stencil/src/components/internal/orama-chat-suggestions/orama-chat-suggestions.scss new file mode 100644 index 00000000..801ed7b4 --- /dev/null +++ b/packages/ui-stencil/src/components/internal/orama-chat-suggestions/orama-chat-suggestions.scss @@ -0,0 +1,34 @@ +.suggestions-list { + display: flex; + align-items: center; + column-gap: var(--spacing-s, $spacing-s); + padding: var(--radius-s, $spacing-s) var(--radius-l, $spacing-l); + + list-style: none; + margin: 0; + overflow-x: auto; + + // TODO: check scroll styles + // Maybe move somewhere else to reuse in some somponents, but not globally + &::-webkit-scrollbar { + height: 0em; + background-color: transparent; + } +} + +.suggestion-button { + @include span(); + cursor: pointer; + + border-radius: var(--radius-l, $radius-l); + border: 1px solid var(--border-color-primary, border-color('primary')); + padding: var(--spacing-m, $spacing-m) var(--spacing-m, $spacing-m); + display: flex; + background-color: var(--background-color-primary, background-color('primary')); + width: max-content; + color: var(--text-color-secondary, text-color('secondary')); + + @media (--md-min) { + padding: var(--spacing-xs, $spacing-xs) var(--spacing-m, $spacing-m); + } +} diff --git a/packages/ui-stencil/src/components/internal/orama-chat-suggestions/orama-chat-suggestions.tsx b/packages/ui-stencil/src/components/internal/orama-chat-suggestions/orama-chat-suggestions.tsx new file mode 100644 index 00000000..e599fcfb --- /dev/null +++ b/packages/ui-stencil/src/components/internal/orama-chat-suggestions/orama-chat-suggestions.tsx @@ -0,0 +1,34 @@ +import { Component, h, Prop } from '@stencil/core' + +@Component({ + tag: 'orama-chat-suggestions', + styleUrl: 'orama-chat-suggestions.scss', +}) +export class OramaChatSuggestions { + @Prop() suggestions: string[] + @Prop() suggestionClicked: (suggestion: string) => void + + handleClick(suggestion: string) { + this.suggestionClicked(suggestion) + } + + render() { + if (!this.suggestions?.length) { + return null + } + + return ( +
    + {this.suggestions.map((suggestion) => { + return ( +
  • + +
  • + ) + })} +
+ ) + } +} diff --git a/packages/ui-stencil/src/components/internal/orama-chat-suggestions/readme.md b/packages/ui-stencil/src/components/internal/orama-chat-suggestions/readme.md new file mode 100644 index 00000000..4ef23b38 --- /dev/null +++ b/packages/ui-stencil/src/components/internal/orama-chat-suggestions/readme.md @@ -0,0 +1,31 @@ +# orama-button + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| ------------------- | --------- | ----------- | ------------------------------ | ----------- | +| `suggestionClicked` | -- | | `(suggestion: string) => void` | `undefined` | +| `suggestions` | -- | | `string[]` | `undefined` | + + +## Dependencies + +### Used by + + - [orama-chat](../orama-chat) + +### Graph +```mermaid +graph TD; + orama-chat --> orama-chat-suggestions + style orama-chat-suggestions fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/ui-stencil/src/components/internal/orama-chat/orama-chat.scss b/packages/ui-stencil/src/components/internal/orama-chat/orama-chat.scss index c8144de2..b26715c9 100644 --- a/packages/ui-stencil/src/components/internal/orama-chat/orama-chat.scss +++ b/packages/ui-stencil/src/components/internal/orama-chat/orama-chat.scss @@ -17,6 +17,7 @@ orama-chat { .chat-form-wrapper { padding: var(--spacing-m, $spacing-m) var(--spacing-l, $spacing-l); + background-color: var(--background-color-tertiary, background-color('tertiary')); } .chat-input { diff --git a/packages/ui-stencil/src/components/internal/orama-chat/orama-chat.tsx b/packages/ui-stencil/src/components/internal/orama-chat/orama-chat.tsx index 27356ad4..b860ebb7 100644 --- a/packages/ui-stencil/src/components/internal/orama-chat/orama-chat.tsx +++ b/packages/ui-stencil/src/components/internal/orama-chat/orama-chat.tsx @@ -3,6 +3,9 @@ import { chatContext } from '@/context/chatContext' import '@phosphor-icons/webcomponents/PhPaperPlaneTilt' import '@phosphor-icons/webcomponents/PhStop' +// TODO: Hardcoding suggestions for now +const SUGGESTIONS = ['How to get started?', 'What are the prices?', 'What is Orama?'] + @Component({ tag: 'orama-chat', styleUrl: 'orama-chat.scss', @@ -25,12 +28,21 @@ export class OramaChat { chatContext.chatService.abortAnswer() } + handleSuggestionClick = (suggestion: string) => { + chatContext.chatService.sendQuestion(suggestion) + this.inputValue = '' + } + render() { return ( {/* CHAT MESSAGES */}
+ {/* TODO: Provide a better animation */} + {!chatContext.messages.length && !chatContext.isLoading ? ( + + ) : null}
diff --git a/packages/ui-stencil/src/components/internal/orama-chat/readme.md b/packages/ui-stencil/src/components/internal/orama-chat/readme.md index 6cc0189b..7f445057 100644 --- a/packages/ui-stencil/src/components/internal/orama-chat/readme.md +++ b/packages/ui-stencil/src/components/internal/orama-chat/readme.md @@ -14,6 +14,7 @@ ### Depends on - [orama-chat-messages-container](../orama-chat-messages-container) +- [orama-chat-suggestions](../orama-chat-suggestions) - [orama-logo-icon](../orama-logo-icon) - [orama-textarea](../orama-textarea) - [orama-button](../orama-button) @@ -23,6 +24,7 @@ ```mermaid graph TD; orama-chat --> orama-chat-messages-container + orama-chat --> orama-chat-suggestions orama-chat --> orama-logo-icon orama-chat --> orama-textarea orama-chat --> orama-button diff --git a/packages/ui-stencil/src/components/internal/orama-facets/orama-facets.tsx b/packages/ui-stencil/src/components/internal/orama-facets/orama-facets.tsx index f86bd926..00984db1 100644 --- a/packages/ui-stencil/src/components/internal/orama-facets/orama-facets.tsx +++ b/packages/ui-stencil/src/components/internal/orama-facets/orama-facets.tsx @@ -14,10 +14,10 @@ export type Facet = { name: string; count: number } export class OramaFacets { @Prop() facets: Facet[] @Prop() selectedFacet: string - @Prop() onFacetClick: (facetName: string) => void + @Prop() facetClicked: (facetName: string) => void handleClick(facet: Facet) { - this.onFacetClick(facet.name) + this.facetClicked(facet.name) } render() { diff --git a/packages/ui-stencil/src/components/internal/orama-facets/readme.md b/packages/ui-stencil/src/components/internal/orama-facets/readme.md index 2e72bbd6..2270773c 100644 --- a/packages/ui-stencil/src/components/internal/orama-facets/readme.md +++ b/packages/ui-stencil/src/components/internal/orama-facets/readme.md @@ -9,8 +9,8 @@ | Property | Attribute | Description | Type | Default | | --------------- | ---------------- | ----------- | ----------------------------- | ----------- | +| `facetClicked` | -- | | `(facetName: string) => void` | `undefined` | | `facets` | -- | | `Facet[]` | `undefined` | -| `onFacetClick` | -- | | `(facetName: string) => void` | `undefined` | | `selectedFacet` | `selected-facet` | | `string` | `undefined` | diff --git a/packages/ui-stencil/src/components/internal/orama-search-results/orama-search-results.tsx b/packages/ui-stencil/src/components/internal/orama-search-results/orama-search-results.tsx index 667d6ece..c6a54f21 100644 --- a/packages/ui-stencil/src/components/internal/orama-search-results/orama-search-results.tsx +++ b/packages/ui-stencil/src/components/internal/orama-search-results/orama-search-results.tsx @@ -13,7 +13,7 @@ export type SearchResultsProps = { }) export class SearchResults { @Element() el: HTMLUListElement - @Event() onOramaItemClick: EventEmitter + @Event() oramaItemClick: EventEmitter @Prop() sections: SearchResultBySection[] = [] @Prop() searchTerm: SearchResultsProps['searchTerm'] @Prop() loading = false @@ -21,7 +21,7 @@ export class SearchResults { handleItemClick = (item: SearchResult) => { if (item?.path) { - this.onOramaItemClick.emit(item) + this.oramaItemClick.emit(item) window.location.href = item.path } else { throw new Error('No path found') diff --git a/packages/ui-stencil/src/components/internal/orama-search-results/readme.md b/packages/ui-stencil/src/components/internal/orama-search-results/readme.md index 8ca40b52..c0a8e4b9 100644 --- a/packages/ui-stencil/src/components/internal/orama-search-results/readme.md +++ b/packages/ui-stencil/src/components/internal/orama-search-results/readme.md @@ -15,9 +15,9 @@ ## Events -| Event | Description | Type | -| ------------------ | ----------- | -------------------------------------------------------------------------------- | -| `onOramaItemClick` | | `CustomEvent<{ id: string; title: string; description: string; path: string; }>` | +| Event | Description | Type | +| ---------------- | ----------- | -------------------------------------------------------------------------------- | +| `oramaItemClick` | | `CustomEvent<{ id: string; title: string; description: string; path: string; }>` | ## Dependencies diff --git a/packages/ui-stencil/src/components/internal/orama-search/orama-search.tsx b/packages/ui-stencil/src/components/internal/orama-search/orama-search.tsx index 56c84ab9..ccf08feb 100644 --- a/packages/ui-stencil/src/components/internal/orama-search/orama-search.tsx +++ b/packages/ui-stencil/src/components/internal/orama-search/orama-search.tsx @@ -44,7 +44,7 @@ export class OramaSearch { orama-search-results orama-search-results --> orama-text orama-chat --> orama-chat-messages-container + orama-chat --> orama-chat-suggestions orama-chat --> orama-logo-icon orama-chat --> orama-textarea orama-chat --> orama-button