(buttons) Add searchable choices

Closes #473
This commit is contained in:
Baptiste Arnaud
2023-04-26 17:54:00 +02:00
parent 124f350aa2
commit 5b4a6c523d
14 changed files with 687 additions and 77 deletions

View File

@@ -47,38 +47,42 @@ export const ChatChunk = (props: Props) => {
return (
<div class="flex flex-col w-full min-w-0 gap-2">
<div class={'flex' + (isMobile() ? ' gap-1' : ' gap-2')}>
<Show
when={
props.theme.chat.hostAvatar?.isEnabled && props.messages.length > 0
}
>
<AvatarSideContainer
hostAvatarSrc={props.theme.chat.hostAvatar?.url}
hideAvatar={props.hideAvatar}
/>
</Show>
<div
class="flex flex-col flex-1 gap-2"
style={{
'margin-right': props.theme.chat.guestAvatar?.isEnabled
? isMobile()
? '32px'
: '48px'
: undefined,
}}
>
<For each={props.messages.slice(0, displayedMessageIndex() + 1)}>
{(message) => (
<HostBubble
message={message}
typingEmulation={props.settings.typingEmulation}
onTransitionEnd={displayNextMessage}
/>
)}
</For>
<Show when={props.messages.length > 0}>
<div class={'flex' + (isMobile() ? ' gap-1' : ' gap-2')}>
<Show
when={
props.theme.chat.hostAvatar?.isEnabled &&
props.messages.length > 0
}
>
<AvatarSideContainer
hostAvatarSrc={props.theme.chat.hostAvatar?.url}
hideAvatar={props.hideAvatar}
/>
</Show>
<div
class="flex flex-col flex-1 gap-2"
style={{
'margin-right': props.theme.chat.guestAvatar?.isEnabled
? isMobile()
? '32px'
: '48px'
: undefined,
}}
>
<For each={props.messages.slice(0, displayedMessageIndex() + 1)}>
{(message) => (
<HostBubble
message={message}
typingEmulation={props.settings.typingEmulation}
onTransitionEnd={displayNextMessage}
/>
)}
</For>
</div>
</div>
</div>
</Show>
{props.input && displayedMessageIndex() === props.messages.length && (
<InputChatBlock
block={props.input}

View File

@@ -30,6 +30,8 @@ import { isMobile } from '@/utils/isMobileSignal'
import { PaymentForm } from '@/features/blocks/inputs/payment'
import { MultipleChoicesForm } from '@/features/blocks/inputs/buttons/components/MultipleChoicesForm'
import { Buttons } from '@/features/blocks/inputs/buttons/components/Buttons'
import { SearchableButtons } from '@/features/blocks/inputs/buttons/components/SearchableButtons'
import { SearchableMultipleChoicesForm } from '@/features/blocks/inputs/buttons/components/SearchableMultipleChoicesForm'
type Props = {
block: NonNullable<ChatReply['input']>
@@ -159,30 +161,49 @@ const Input = (props: {
onSubmit={onSubmit}
/>
</Match>
<Match
when={
props.block.type === InputBlockType.CHOICE &&
props.block.options.isMultipleChoice
}
>
<MultipleChoicesForm
inputIndex={props.inputIndex}
items={(props.block as ChoiceInputBlock).items}
options={props.block.options as ChoiceInputBlock['options']}
onSubmit={onSubmit}
/>
</Match>
<Match
when={
props.block.type === InputBlockType.CHOICE &&
!props.block.options.isMultipleChoice
}
>
<Buttons
inputIndex={props.inputIndex}
items={(props.block as ChoiceInputBlock).items}
onSubmit={onSubmit}
/>
<Match when={isButtonsBlock(props.block)} keyed>
{(block) => (
<Switch>
<Match when={!block.options.isMultipleChoice}>
<Switch>
<Match when={block.options.isSearchable}>
<SearchableButtons
inputIndex={props.inputIndex}
defaultItems={block.items}
onSubmit={onSubmit}
/>
</Match>
<Match when={!block.options.isSearchable}>
<Buttons
inputIndex={props.inputIndex}
items={block.items}
onSubmit={onSubmit}
/>
</Match>
</Switch>
</Match>
<Match when={block.options.isMultipleChoice}>
<Switch>
<Match when={block.options.isSearchable}>
<SearchableMultipleChoicesForm
inputIndex={props.inputIndex}
defaultItems={block.items}
options={block.options}
onSubmit={onSubmit}
/>
</Match>
<Match when={!block.options.isSearchable}>
<MultipleChoicesForm
inputIndex={props.inputIndex}
items={block.items}
options={block.options}
onSubmit={onSubmit}
/>
</Match>
</Switch>
</Match>
</Switch>
)}
</Match>
<Match when={props.block.type === InputBlockType.RATING}>
<RatingForm
@@ -214,3 +235,8 @@ const Input = (props: {
</Switch>
)
}
const isButtonsBlock = (
block: ChatReply['input']
): ChoiceInputBlock | undefined =>
block?.type === InputBlockType.CHOICE ? block : undefined

View File

@@ -0,0 +1,17 @@
import { JSX } from 'solid-js/jsx-runtime'
export const CloseIcon = (props: JSX.SvgSVGAttributes<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2px"
stroke-linecap="round"
stroke-linejoin="round"
{...props}
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
)

View File

@@ -0,0 +1,43 @@
import { Show, createSignal, splitProps } from 'solid-js'
import { JSX } from 'solid-js/jsx-runtime'
import { CloseIcon } from '../icons/CloseIcon'
type Props = {
ref: HTMLInputElement | undefined
onInput: (value: string) => void
onClear: () => void
} & Omit<JSX.InputHTMLAttributes<HTMLInputElement>, 'onInput'>
export const SearchInput = (props: Props) => {
const [value, setValue] = createSignal('')
const [local, others] = splitProps(props, ['onInput', 'ref'])
const changeValue = (value: string) => {
setValue(value)
local.onInput(value)
}
const clearValue = () => {
setValue('')
props.onClear()
}
return (
<div class="flex justify-between items-center gap-2 w-full pr-4">
<input
ref={props.ref}
class="focus:outline-none bg-transparent px-4 py-4 flex-1 w-full text-input"
type="text"
style={{ 'font-size': '16px' }}
value={value()}
onInput={(e) => changeValue(e.currentTarget.value)}
{...others}
/>
<Show when={value().length > 0}>
<button class="w-5 h-5" on:click={clearValue}>
<CloseIcon />
</button>
</Show>
</div>
)
}