@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
17
packages/embeds/js/src/components/icons/CloseIcon.tsx
Normal file
17
packages/embeds/js/src/components/icons/CloseIcon.tsx
Normal 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>
|
||||
)
|
||||
43
packages/embeds/js/src/components/inputs/SearchInput.tsx
Normal file
43
packages/embeds/js/src/components/inputs/SearchInput.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user