ui/search: add loading state
This commit is contained in:
@@ -117,6 +117,27 @@ export const Default: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Loading: Story = {
|
||||
args: {
|
||||
// Test with lots of modules
|
||||
loading: true,
|
||||
options: [],
|
||||
renderItem: () => <span></span>,
|
||||
},
|
||||
render: (args: SearchProps<Module>) => {
|
||||
return (
|
||||
<div class="absolute bottom-1/3 w-3/4 px-3">
|
||||
<Search<Module>
|
||||
{...args}
|
||||
onChange={(module) => {
|
||||
// Go to the module configuration
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
type MachineOrTag =
|
||||
| {
|
||||
value: string;
|
||||
|
||||
@@ -2,9 +2,10 @@ import Icon from "../Icon/Icon";
|
||||
import { Button } from "../Button/Button";
|
||||
import styles from "./Search.module.css";
|
||||
import { Combobox } from "@kobalte/core/combobox";
|
||||
import { createMemo, createSignal, For, JSX } from "solid-js";
|
||||
import { createMemo, createSignal, For, JSX, Match, Switch } from "solid-js";
|
||||
import { createVirtualizer } from "@tanstack/solid-virtual";
|
||||
import { CollectionNode } from "@kobalte/core/*";
|
||||
import { Loader } from "../Loader/Loader";
|
||||
|
||||
export interface Option {
|
||||
value: string;
|
||||
@@ -15,6 +16,8 @@ export interface SearchProps<T> {
|
||||
onChange: (value: T | null) => void;
|
||||
options: T[];
|
||||
renderItem: (item: T) => JSX.Element;
|
||||
loading?: boolean;
|
||||
loadingComponent?: JSX.Element;
|
||||
}
|
||||
export function Search<T extends Option>(props: SearchProps<T>) {
|
||||
// Controlled input value, to allow resetting the input itself
|
||||
@@ -136,41 +139,55 @@ export function Search<T extends Option>(props: SearchProps<T>) {
|
||||
setComboboxItems(arr);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: `${virtualizer().getTotalSize()}px`,
|
||||
width: "100%",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<For each={virtualizer().getVirtualItems()}>
|
||||
{(virtualRow) => {
|
||||
const item: CollectionNode<T> | undefined =
|
||||
items().getItem(virtualRow.key as string);
|
||||
<Switch>
|
||||
<Match when={props.loading}>
|
||||
{props.loadingComponent ?? (
|
||||
<div class="flex w-full justify-center py-2">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
</Match>
|
||||
<Match when={!props.loading}>
|
||||
<div
|
||||
style={{
|
||||
height: `${virtualizer().getTotalSize()}px`,
|
||||
width: "100%",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<For each={virtualizer().getVirtualItems()}>
|
||||
{(virtualRow) => {
|
||||
const item: CollectionNode<T> | undefined =
|
||||
items().getItem(virtualRow.key as string);
|
||||
|
||||
if (!item) {
|
||||
console.warn("Item not found for key:", virtualRow.key);
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Combobox.Item
|
||||
item={item}
|
||||
class={styles.searchItem}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: `${virtualRow.size}px`,
|
||||
transform: `translateY(${virtualRow.start}px)`,
|
||||
}}
|
||||
>
|
||||
{props.renderItem(item.rawValue)}
|
||||
</Combobox.Item>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
if (!item) {
|
||||
console.warn(
|
||||
"Item not found for key:",
|
||||
virtualRow.key,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Combobox.Item
|
||||
item={item}
|
||||
class={styles.searchItem}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: `${virtualRow.size}px`,
|
||||
transform: `translateY(${virtualRow.start}px)`,
|
||||
}}
|
||||
>
|
||||
{props.renderItem(item.rawValue)}
|
||||
</Combobox.Item>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
</Match>
|
||||
</Switch>
|
||||
);
|
||||
}}
|
||||
</Combobox.Listbox>
|
||||
|
||||
Reference in New Issue
Block a user