@ -72,13 +72,11 @@ export const MultipleChoicesForm = (props: Props) => {
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
<div class="flex">
|
||||
{selectedIndices().length > 0 && (
|
||||
<SendButton disableIcon>
|
||||
{props.options?.buttonLabel ?? 'Send'}
|
||||
</SendButton>
|
||||
)}
|
||||
</div>
|
||||
{selectedIndices().length > 0 && (
|
||||
<SendButton disableIcon>
|
||||
{props.options?.buttonLabel ?? 'Send'}
|
||||
</SendButton>
|
||||
)}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
import { Button } from '@/components/Button'
|
||||
import { SearchInput } from '@/components/inputs/SearchInput'
|
||||
import { InputSubmitContent } from '@/types'
|
||||
import { isMobile } from '@/utils/isMobileSignal'
|
||||
import type { ChoiceInputBlock } from '@typebot.io/schemas'
|
||||
import { For, createSignal, onMount } from 'solid-js'
|
||||
|
||||
type Props = {
|
||||
inputIndex: number
|
||||
defaultItems: ChoiceInputBlock['items']
|
||||
onSubmit: (value: InputSubmitContent) => void
|
||||
}
|
||||
|
||||
export const SearchableButtons = (props: Props) => {
|
||||
let inputRef: HTMLInputElement | undefined
|
||||
const [filteredItems, setFilteredItems] = createSignal(props.defaultItems)
|
||||
|
||||
onMount(() => {
|
||||
if (!isMobile() && inputRef) inputRef.focus()
|
||||
})
|
||||
|
||||
// eslint-disable-next-line solid/reactivity
|
||||
const handleClick = (itemIndex: number) => () =>
|
||||
props.onSubmit({ value: filteredItems()[itemIndex].content ?? '' })
|
||||
|
||||
const filterItems = (inputValue: string) => {
|
||||
setFilteredItems(
|
||||
props.defaultItems.filter((item) =>
|
||||
item.content?.toLowerCase().includes((inputValue ?? '').toLowerCase())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
<div class="flex items-end typebot-input w-full">
|
||||
<SearchInput
|
||||
ref={inputRef}
|
||||
onInput={filterItems}
|
||||
placeholder="Filter the options..."
|
||||
onClear={() => setFilteredItems(props.defaultItems)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap justify-end gap-2 overflow-y-scroll max-h-80 rounded-md hide-scrollbar">
|
||||
<For each={filteredItems()}>
|
||||
{(item, index) => (
|
||||
<span class={'relative' + (isMobile() ? ' w-full' : '')}>
|
||||
<Button
|
||||
on:click={handleClick(index())}
|
||||
data-itemid={item.id}
|
||||
class="w-full"
|
||||
>
|
||||
{item.content}
|
||||
</Button>
|
||||
{props.inputIndex === 0 && props.defaultItems.length === 1 && (
|
||||
<span class="flex h-3 w-3 absolute top-0 right-0 -mt-1 -mr-1 ping">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full brightness-200 opacity-75" />
|
||||
<span class="relative inline-flex rounded-full h-3 w-3 brightness-150" />
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
import { SendButton } from '@/components/SendButton'
|
||||
import { InputSubmitContent } from '@/types'
|
||||
import { isMobile } from '@/utils/isMobileSignal'
|
||||
import type { ChoiceInputBlock } from '@typebot.io/schemas'
|
||||
import { createSignal, For } from 'solid-js'
|
||||
import { Checkbox } from './Checkbox'
|
||||
import { SearchInput } from '@/components/inputs/SearchInput'
|
||||
|
||||
type Props = {
|
||||
inputIndex: number
|
||||
defaultItems: ChoiceInputBlock['items']
|
||||
options: ChoiceInputBlock['options']
|
||||
onSubmit: (value: InputSubmitContent) => void
|
||||
}
|
||||
|
||||
export const SearchableMultipleChoicesForm = (props: Props) => {
|
||||
let inputRef: HTMLInputElement | undefined
|
||||
const [filteredItems, setFilteredItems] = createSignal(props.defaultItems)
|
||||
const [selectedItemIds, setSelectedItemIds] = createSignal<string[]>([])
|
||||
|
||||
const handleClick = (itemId: string) => {
|
||||
toggleSelectedItemId(itemId)
|
||||
}
|
||||
|
||||
const toggleSelectedItemId = (itemId: string) => {
|
||||
const existingIndex = selectedItemIds().indexOf(itemId)
|
||||
if (existingIndex !== -1) {
|
||||
setSelectedItemIds((selectedItemIds) =>
|
||||
selectedItemIds.filter((selectedItemId) => selectedItemId !== itemId)
|
||||
)
|
||||
} else {
|
||||
setSelectedItemIds((selectedIndices) => [...selectedIndices, itemId])
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = () =>
|
||||
props.onSubmit({
|
||||
value: props.defaultItems
|
||||
.filter((item) => selectedItemIds().includes(item.id))
|
||||
.join(', '),
|
||||
})
|
||||
|
||||
const filterItems = (inputValue: string) => {
|
||||
setFilteredItems(
|
||||
props.defaultItems.filter((item) =>
|
||||
item.content?.toLowerCase().includes((inputValue ?? '').toLowerCase())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<form class="flex flex-col items-end gap-2 w-full" onSubmit={handleSubmit}>
|
||||
<div class="flex items-end typebot-input w-full">
|
||||
<SearchInput
|
||||
ref={inputRef}
|
||||
onInput={filterItems}
|
||||
placeholder="Filter the options..."
|
||||
onClear={() => setFilteredItems(props.defaultItems)}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-end gap-2 overflow-y-scroll max-h-80 rounded-md hide-scrollbar">
|
||||
<For each={filteredItems()}>
|
||||
{(item) => (
|
||||
<span class={'relative' + (isMobile() ? ' w-full' : '')}>
|
||||
<div
|
||||
role="checkbox"
|
||||
aria-checked={selectedItemIds().some(
|
||||
(selectedItemId) => selectedItemId === item.id
|
||||
)}
|
||||
on:click={() => handleClick(item.id)}
|
||||
class={
|
||||
'w-full py-2 px-4 font-semibold focus:outline-none cursor-pointer select-none typebot-selectable' +
|
||||
(selectedItemIds().some(
|
||||
(selectedItemId) => selectedItemId === item.id
|
||||
)
|
||||
? ' selected'
|
||||
: '')
|
||||
}
|
||||
data-itemid={item.id}
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox
|
||||
isChecked={selectedItemIds().some(
|
||||
(selectedItemId) => selectedItemId === item.id
|
||||
)}
|
||||
/>
|
||||
<span>{item.content}</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
<For
|
||||
each={selectedItemIds().filter((selectedItemId) =>
|
||||
filteredItems().every((item) => item.id !== selectedItemId)
|
||||
)}
|
||||
>
|
||||
{(selectedItemId) => (
|
||||
<span class={'relative' + (isMobile() ? ' w-full' : '')}>
|
||||
<div
|
||||
role="checkbox"
|
||||
aria-checked
|
||||
on:click={() => handleClick(selectedItemId)}
|
||||
class={
|
||||
'w-full py-2 px-4 font-semibold focus:outline-none cursor-pointer select-none typebot-selectable selected'
|
||||
}
|
||||
data-itemid={selectedItemId}
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox isChecked />
|
||||
<span>
|
||||
{
|
||||
props.defaultItems.find(
|
||||
(item) => item.id === selectedItemId
|
||||
)?.content
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
{selectedItemIds().length > 0 && (
|
||||
<SendButton disableIcon>
|
||||
{props.options?.buttonLabel ?? 'Send'}
|
||||
</SendButton>
|
||||
)}
|
||||
</form>
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user