Skip to content

Commit 4bfbebd

Browse files
feat(input): add input with native html datalist
1 parent d204794 commit 4bfbebd

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed

lib/input/input/InputDatalist.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { splitProps, type ComponentProps } from "solid-js"
2+
import { Input } from "~ui/input/input/Input"
3+
import type { HasId } from "~ui/utils/HasId"
4+
import type { MayHaveClassAndChildren } from "~ui/utils/MayHaveClassAndChildren"
5+
import { generateId12 } from "~utils/ran/generateId12"
6+
7+
export interface ResourceListSearchProps extends ComponentProps<"input"> {
8+
getOptions: () => string[]
9+
optionDisplayText?: (option: string) => string
10+
}
11+
12+
export function InputDatalist(p: ResourceListSearchProps) {
13+
const [s, rest] = splitProps(p, ["id", "class", "type", "list"])
14+
const id = p.id ?? generateId12()
15+
const listId = id + "-options"
16+
17+
return (
18+
<>
19+
<Input id={id} type={s.type ?? "text"} class={p.class} list={listId} {...rest} />
20+
<DataList id={listId} getOptions={p.getOptions} optionDisplayText={p.optionDisplayText} />
21+
</>
22+
)
23+
}
24+
25+
import { Key } from "@solid-primitives/keyed"
26+
27+
interface DataListProps extends HasId, MayHaveClassAndChildren {
28+
getOptions: () => string[]
29+
optionDisplayText?: (option: string) => string
30+
}
31+
32+
function DataList(p: DataListProps) {
33+
return (
34+
<datalist id={p.id}>
35+
<Key each={p.getOptions()} by={(i) => i}>
36+
{(item) => <option value={item()}>{p.optionDisplayText ? p.optionDisplayText(item()) : item()}</option>}
37+
</Key>
38+
</datalist>
39+
)
40+
}

src/demos/demoList.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ const DemoCheckbox = lazy(async () => {
6161
return { default: c.DemoCheckbox }
6262
})
6363

64+
const DemoInputDatalist = lazy(async () => {
65+
const c = await import("@/demos/input/DemoInputDatalist")
66+
return { default: c.DemoInputDatalist }
67+
})
68+
6469
const DemoSelectMultiple = lazy(async () => {
6570
const c = await import("@/demos/input/DemoSelectMultiple")
6671
return { default: c.DemoSelectMultiple }
@@ -196,6 +201,7 @@ export const demoList = {
196201
DemoSelectSingleNative: DemoSelectSingleNative,
197202
DemoCheckMultiple: DemoCheckMultiple,
198203
DemoCheckbox: DemoCheckbox,
204+
DemoInputDatalist: DemoInputDatalist,
199205
DemoSelectMultiple: DemoSelectMultiple,
200206
},
201207
interactive: {
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { createSignal } from "solid-js"
2+
import { InputDatalist } from "~ui/input/input/InputDatalist"
3+
import { PageWrapper } from "~ui/static/page/PageWrapper"
4+
5+
const fruits = [
6+
"apple",
7+
"banana",
8+
"orange",
9+
"grape",
10+
"strawberry",
11+
"blueberry",
12+
"watermelon",
13+
"pineapple",
14+
"mango",
15+
"kiwi",
16+
]
17+
18+
const fruitEmojis: Record<string, string> = {
19+
apple: "🍎",
20+
banana: "🍌",
21+
orange: "🍊",
22+
grape: "🍇",
23+
strawberry: "🍓",
24+
blueberry: "🫐",
25+
watermelon: "🍉",
26+
pineapple: "🍍",
27+
mango: "🥭",
28+
kiwi: "🥝",
29+
}
30+
31+
export function DemoInputDatalist() {
32+
return (
33+
<PageWrapper>
34+
<div class="space-y-8">
35+
<BasicFruitsDatalist />
36+
<CustomFruitEmojiDatalist />
37+
</div>
38+
</PageWrapper>
39+
)
40+
}
41+
42+
function BasicFruitsDatalist() {
43+
const [basicFruitValue, setBasicFruitValue] = createSignal("")
44+
45+
return (
46+
<div class="space-y-4">
47+
<h3 class="text-lg font-semibold">Basic Fruits Datalist</h3>
48+
<InputDatalist
49+
getOptions={getFruitOptions}
50+
placeholder="Select a fruit..."
51+
value={basicFruitValue()}
52+
onInput={(e) => setBasicFruitValue(e.currentTarget.value)}
53+
/>
54+
<p class="text-sm text-muted-foreground">Selected: {basicFruitValue() || "None"}</p>
55+
</div>
56+
)
57+
}
58+
59+
function CustomFruitEmojiDatalist() {
60+
const [customFruitValue, setCustomFruitValue] = createSignal("")
61+
62+
return (
63+
<div class="space-y-4">
64+
<h3 class="text-lg font-semibold">Custom Display with Emojis</h3>
65+
<InputDatalist
66+
getOptions={getFruitOptions}
67+
optionDisplayText={getFruitWithEmoji}
68+
placeholder="Select a fruit with emoji..."
69+
value={customFruitValue()}
70+
onInput={(e) => setCustomFruitValue(e.currentTarget.value)}
71+
/>
72+
<p class="text-sm text-muted-foreground">Selected: {customFruitValue() || "None"}</p>
73+
</div>
74+
)
75+
}
76+
77+
function getFruitOptions(): string[] {
78+
return fruits
79+
}
80+
81+
function getFruitWithEmoji(fruit: string): string {
82+
return `${fruitEmojis[fruit] || "🍓"} ${fruit}`
83+
}

0 commit comments

Comments
 (0)