Merge pull request 'UI: Added Machine List Search Bar' (#209) from Qubasa-Qubasa-main into main
This commit is contained in:
commit
a6c6310115
17
pkgs/ui/src/components/hooks/useDebounce.tsx
Normal file
17
pkgs/ui/src/components/hooks/useDebounce.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export function useDebounce(value: any, delay: number) {
|
||||||
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedValue(value);
|
||||||
|
}, delay);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler);
|
||||||
|
};
|
||||||
|
}, [value, delay]);
|
||||||
|
|
||||||
|
return debouncedValue;
|
||||||
|
}
|
@ -67,15 +67,15 @@ export function EnhancedTableToolbar(
|
|||||||
</Grid2>
|
</Grid2>
|
||||||
|
|
||||||
{/*Toolbar Grid */}
|
{/*Toolbar Grid */}
|
||||||
<Grid2 key="Toolbar" xs={12}>
|
<Grid2
|
||||||
<Toolbar
|
key="Toolbar"
|
||||||
sx={{
|
xs={12}
|
||||||
pl: { sm: 2 },
|
container
|
||||||
pr: { xs: 1, sm: 1 },
|
justifyContent="center"
|
||||||
}}
|
alignItems="center"
|
||||||
>
|
sx={{ pl: { sm: 2 }, pr: { xs: 1, sm: 1 }, pt: { xs: 1, sm: 3 } }}
|
||||||
{props.children}
|
>
|
||||||
</Toolbar>
|
{props.children}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
);
|
);
|
||||||
|
@ -59,7 +59,7 @@ export function NodeRow(props: {
|
|||||||
//const labelId = `enhanced-table-checkbox-${index}`;
|
//const labelId = `enhanced-table-checkbox-${index}`;
|
||||||
|
|
||||||
// Speed optimization. We compare string pointers here instead of the string content.
|
// Speed optimization. We compare string pointers here instead of the string content.
|
||||||
const isSelected = selected == row.name;
|
const isSelected = selected == row.id;
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<unknown>, name: string) => {
|
const handleClick = (event: React.MouseEvent<unknown>, name: string) => {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
@ -93,7 +93,7 @@ export function NodeRow(props: {
|
|||||||
<TableCell
|
<TableCell
|
||||||
component="th"
|
component="th"
|
||||||
scope="row"
|
scope="row"
|
||||||
onClick={(event) => handleClick(event, row.name)}
|
onClick={(event) => handleClick(event, row.id)}
|
||||||
>
|
>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Typography component="div" align="left" variant="body1">
|
<Typography component="div" align="left" variant="body1">
|
||||||
|
@ -15,6 +15,7 @@ import { EnhancedTableToolbar } from "./enhancedTableToolbar";
|
|||||||
import { StickySpeedDial } from "./stickySpeedDial";
|
import { StickySpeedDial } from "./stickySpeedDial";
|
||||||
import { NodeTableContainer } from "./nodeTableContainer";
|
import { NodeTableContainer } from "./nodeTableContainer";
|
||||||
import { SearchBar } from "./searchBar";
|
import { SearchBar } from "./searchBar";
|
||||||
|
import Grid2 from "@mui/material/Unstable_Grid2/Grid2";
|
||||||
|
|
||||||
export interface NodeTableProps {
|
export interface NodeTableProps {
|
||||||
tableData: TableData[];
|
tableData: TableData[];
|
||||||
@ -29,7 +30,7 @@ export function NodeTable(props: NodeTableProps) {
|
|||||||
const [selected, setSelected] = useState<string | undefined>(undefined);
|
const [selected, setSelected] = useState<string | undefined>(undefined);
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
const [rowsPerPage, setRowsPerPage] = useState(5);
|
||||||
const [search, setSearch] = useState<string>("");
|
const [filteredList, setFilteredList] = useState<TableData[]>(tableData);
|
||||||
|
|
||||||
const handleChangePage = (event: unknown, newPage: number) => {
|
const handleChangePage = (event: unknown, newPage: number) => {
|
||||||
setPage(newPage);
|
setPage(newPage);
|
||||||
@ -45,10 +46,15 @@ export function NodeTable(props: NodeTableProps) {
|
|||||||
<Paper sx={{ width: "100%", mb: 2 }}>
|
<Paper sx={{ width: "100%", mb: 2 }}>
|
||||||
<StickySpeedDial selected={selected} />
|
<StickySpeedDial selected={selected} />
|
||||||
<EnhancedTableToolbar tableData={tableData}>
|
<EnhancedTableToolbar tableData={tableData}>
|
||||||
<SearchBar search={search} setSearch={setSearch} />
|
<Grid2 xs={12}>
|
||||||
|
<SearchBar
|
||||||
|
tableData={tableData}
|
||||||
|
setFilteredList={setFilteredList}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
</EnhancedTableToolbar>
|
</EnhancedTableToolbar>
|
||||||
<NodeTableContainer
|
<NodeTableContainer
|
||||||
tableData={tableData}
|
tableData={filteredList}
|
||||||
page={page}
|
page={page}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
dense={false}
|
dense={false}
|
||||||
@ -60,7 +66,7 @@ export function NodeTable(props: NodeTableProps) {
|
|||||||
rowsPerPageOptions={[5, 10, 25]}
|
rowsPerPageOptions={[5, 10, 25]}
|
||||||
labelRowsPerPage={is_xs ? "Rows" : "Rows per page:"}
|
labelRowsPerPage={is_xs ? "Rows" : "Rows per page:"}
|
||||||
component="div"
|
component="div"
|
||||||
count={tableData.length}
|
count={filteredList.length}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
page={page}
|
page={page}
|
||||||
onPageChange={handleChangePage}
|
onPageChange={handleChangePage}
|
||||||
|
@ -1,29 +1,107 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ChangeEvent, SetStateAction, Dispatch } from "react";
|
import {
|
||||||
|
SetStateAction,
|
||||||
|
Dispatch,
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useMemo,
|
||||||
|
} from "react";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
import SearchIcon from "@mui/icons-material/Search";
|
||||||
|
import { useDebounce } from "../hooks/useDebounce";
|
||||||
|
import { TableData } from "@/data/nodeData";
|
||||||
|
import {
|
||||||
|
Autocomplete,
|
||||||
|
Box,
|
||||||
|
Container,
|
||||||
|
InputAdornment,
|
||||||
|
Stack,
|
||||||
|
TextField,
|
||||||
|
} from "@mui/material";
|
||||||
|
|
||||||
export interface SearchBarProps {
|
export interface SearchBarProps {
|
||||||
search: string;
|
tableData: TableData[];
|
||||||
setSearch: Dispatch<SetStateAction<string>>;
|
setFilteredList: Dispatch<SetStateAction<TableData[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SearchBar(props: SearchBarProps) {
|
export function SearchBar(props: SearchBarProps) {
|
||||||
const { search, setSearch } = props;
|
let { tableData, setFilteredList } = props;
|
||||||
const handleSearch = (event: ChangeEvent<HTMLInputElement>) => {
|
const [search, setSearch] = useState<string>("");
|
||||||
setSearch(event.target.value);
|
const debouncedSearch = useDebounce(search, 250);
|
||||||
|
|
||||||
|
// Define a function to handle the Esc key press
|
||||||
|
const handleEsc = (event: any) => {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
setSearch("");
|
||||||
|
setFilteredList(tableData);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (debouncedSearch) {
|
||||||
|
const filtered: TableData[] = tableData.filter((row) => {
|
||||||
|
return row.name.toLowerCase().includes(debouncedSearch.toLowerCase());
|
||||||
|
});
|
||||||
|
setFilteredList(filtered);
|
||||||
|
}
|
||||||
|
}, [debouncedSearch]);
|
||||||
|
|
||||||
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (e.target.value === "") {
|
||||||
|
setFilteredList(tableData);
|
||||||
|
}
|
||||||
|
setSearch(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const suggestions = useMemo(
|
||||||
|
() => tableData.map((row) => row.name),
|
||||||
|
[tableData],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label htmlFor="search">
|
<Autocomplete
|
||||||
<Tooltip title="Filter list">
|
freeSolo
|
||||||
<IconButton>
|
options={suggestions}
|
||||||
<SearchIcon />
|
onChange={(event, value) => {
|
||||||
</IconButton>
|
// do something with the selected value
|
||||||
</Tooltip>
|
if (value === null) {
|
||||||
<input id="search" type="text" value={search} onChange={handleSearch} />
|
setSearch("");
|
||||||
</label>
|
setFilteredList(tableData);
|
||||||
|
} else {
|
||||||
|
setSearch(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
fullWidth
|
||||||
|
label="Search"
|
||||||
|
variant="outlined"
|
||||||
|
value={search}
|
||||||
|
onKeyDown={handleEsc}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
autoComplete="nickname"
|
||||||
|
InputProps={{
|
||||||
|
...params.InputProps,
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
<IconButton>
|
||||||
|
<SearchIcon />
|
||||||
|
</IconButton>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* {suggestions.map((item, index) => (
|
||||||
|
<option key={index} onClick={() => handleSelect(item)}>
|
||||||
|
{item}
|
||||||
|
</option>
|
||||||
|
))} */}
|
||||||
|
</TextField>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ function createData(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var nameNumber = 0;
|
||||||
|
|
||||||
// A function to generate random names
|
// A function to generate random names
|
||||||
function getRandomName(): string {
|
function getRandomName(): string {
|
||||||
let names = [
|
let names = [
|
||||||
@ -55,7 +57,7 @@ function getRandomName(): string {
|
|||||||
"Zoe",
|
"Zoe",
|
||||||
];
|
];
|
||||||
let index = Math.floor(Math.random() * names.length);
|
let index = Math.floor(Math.random() * names.length);
|
||||||
return names[index];
|
return names[index] + nameNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A function to generate random IPv6 addresses
|
// A function to generate random IPv6 addresses
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user