feat: add fuzzy find to list manager
This commit is contained in:
parent
36369fb1f0
commit
c3562afe8e
@ -14,6 +14,7 @@
|
||||
href="./src/lib/bootstrap-icons-1.11.3/font/bootstrap-icons.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script src="./lib/fusejs-7.1.0.min.js"></script>
|
||||
<script src="./lib/popper.min.js"></script>
|
||||
<script src="./lib/bootstrap-5.3.3-dist/js/bootstrap.bundle.min.js"></script>
|
||||
<title>Playback Device Server</title>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createMemo, createSignal, mergeProps } from "solid-js";
|
||||
import { createEffect, createMemo, createSignal, mergeProps } from "solid-js";
|
||||
|
||||
function ListManager(props) {
|
||||
props = mergeProps(
|
||||
@ -15,30 +15,44 @@ function ListManager(props) {
|
||||
props
|
||||
);
|
||||
|
||||
const itemToString = (item) => props.itemToString(item);
|
||||
const [selectedAvailableItemIndex, setSelectedAvailableItemIndex] =
|
||||
createSignal(-1);
|
||||
const [selectedItemIndex, setSelectedItemIndex] = createSignal(-1);
|
||||
const [itemsSearchString, setItemsSearchString] = createSignal("");
|
||||
const [availableItemsSearchString, setAvailableItemsSearchString] =
|
||||
createSignal("");
|
||||
|
||||
const itemsFuse = createMemo(
|
||||
() =>
|
||||
new Fuse(props.items, {
|
||||
keys: [{ name: "label", getFn: (item) => props.itemToString(item) }],
|
||||
})
|
||||
);
|
||||
const availableItemsFuse = createMemo(
|
||||
() =>
|
||||
new Fuse(props.availableItems, {
|
||||
keys: [{ name: "label", getFn: (item) => props.itemToString(item) }],
|
||||
})
|
||||
);
|
||||
|
||||
createEffect(() =>
|
||||
console.log(availableItemsFuse().search(availableItemsSearchString()))
|
||||
);
|
||||
|
||||
const selectableAvailableItems = createMemo(() =>
|
||||
props.availableItems
|
||||
.filter((item) => !props.items.includes(item))
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
.concat(props.availableItems)
|
||||
(availableItemsSearchString()
|
||||
? availableItemsFuse()
|
||||
.search(availableItemsSearchString())
|
||||
.map((item) => item.item)
|
||||
: props.availableItems
|
||||
).filter((item) => !props.items.includes(item))
|
||||
);
|
||||
const selectableItems = createMemo(() =>
|
||||
itemsSearchString()
|
||||
? itemsFuse().search(itemsSearchString()).map((item) => item.item)
|
||||
: props.items
|
||||
);
|
||||
const selectableItems = createMemo(() => props.items);
|
||||
const canSelect = createMemo(
|
||||
() =>
|
||||
selectedAvailableItemIndex() >= 0 &&
|
||||
@ -83,20 +97,56 @@ function ListManager(props) {
|
||||
);
|
||||
}
|
||||
|
||||
function ItemList(props) {
|
||||
props = mergeProps(
|
||||
{
|
||||
items: [],
|
||||
onItemSelected: () => {},
|
||||
selectedItemIndex: -1,
|
||||
onSearchStringChange: () => {},
|
||||
},
|
||||
props
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="input-group input-group-sm">
|
||||
<span
|
||||
class="input-group-text border-bottom-0 rounded-bottom-0"
|
||||
id="basic-addon1"
|
||||
>
|
||||
<i class="bi bi-search"></i>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control form-control-sm border-bottom-0 rounded-bottom-0"
|
||||
onInput={(event) => props.onSearchStringChange(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div class="rounded rounded-top-0 border bg-body flex-fill overflow-y-scroll">
|
||||
{props.items.map((item, index) => (
|
||||
<ListItem
|
||||
onClick={() => props.onItemSelected(index)}
|
||||
selected={index === props.selectedItemIndex}
|
||||
>
|
||||
{itemToString(item)}
|
||||
</ListItem>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={"d-flex"} style={props.style}>
|
||||
<div class="flex-shrink-1 w-50 d-flex flex-column">
|
||||
<div class="px-2">{props.itemsTitle}</div>
|
||||
<div class="rounded border bg-body flex-fill overflow-y-scroll">
|
||||
{selectableItems().map((item, index) => (
|
||||
<ListItem
|
||||
onClick={() => setSelectedItemIndex(index)}
|
||||
selected={index === selectedItemIndex()}
|
||||
>
|
||||
{props.itemToString(item)}
|
||||
</ListItem>
|
||||
))}
|
||||
</div>
|
||||
<ItemList
|
||||
items={selectableItems()}
|
||||
onItemSelected={(index) => setSelectedItemIndex(index)}
|
||||
selectedItemIndex={selectedItemIndex()}
|
||||
onSearchStringChange={(value) => setItemsSearchString(value)}
|
||||
/>
|
||||
</div>
|
||||
<div class="p-2 d-flex flex-column justify-content-center align-items-center">
|
||||
<button
|
||||
@ -116,16 +166,12 @@ function ListManager(props) {
|
||||
</div>
|
||||
<div class="flex-shrink-1 w-50 d-flex flex-column">
|
||||
<div class="px-2">{props.availableItemsTitle}</div>
|
||||
<div class="rounded border bg-body flex-fill overflow-y-scroll">
|
||||
{selectableAvailableItems().map((item, index) => (
|
||||
<ListItem
|
||||
onClick={() => setSelectedAvailableItemIndex(index)}
|
||||
selected={index === selectedAvailableItemIndex()}
|
||||
>
|
||||
{props.itemToString(item)}
|
||||
</ListItem>
|
||||
))}
|
||||
</div>
|
||||
<ItemList
|
||||
items={selectableAvailableItems()}
|
||||
onItemSelected={(index) => setSelectedAvailableItemIndex(index)}
|
||||
selectedItemIndex={selectedAvailableItemIndex()}
|
||||
onSearchStringChange={(value) => setAvailableItemsSearchString(value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
9
www/src/lib/fusejs-7.1.0.min.js
vendored
Normal file
9
www/src/lib/fusejs-7.1.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user