add: frontend update
This commit is contained in:
@@ -1,55 +1,119 @@
|
||||
import { AutocompleteItem, Select, SelectProps } from "@mantine/core";
|
||||
import React, { useState } from "react";
|
||||
import { Combobox, TextInput, useCombobox } from "@mantine/core";
|
||||
import { useState } from "react";
|
||||
import { ipInterfacesQuery } from "../js/utils";
|
||||
|
||||
|
||||
const AutoCompleteItem = React.forwardRef<HTMLDivElement, ItemProps>(
|
||||
({ netint, value, ...props }: ItemProps, ref) => <div ref={ref} {...props}>
|
||||
( <b>{netint}</b> ) -{">"} <b>{value}</b>
|
||||
</div>
|
||||
);
|
||||
|
||||
interface ItemProps extends AutocompleteItem {
|
||||
interface ItemProps{
|
||||
value: string
|
||||
netint: string;
|
||||
}
|
||||
|
||||
interface InterfaceInputProps extends Omit<SelectProps, "data">{
|
||||
initialCustomInterfaces?:AutocompleteItem[],
|
||||
includeInterfaceNames?:boolean
|
||||
interface InterfaceInputProps{
|
||||
initialCustomInterfaces?:ItemProps[],
|
||||
includeInterfaceNames?:boolean,
|
||||
onChange?: (value:string) => void,
|
||||
value?:string,
|
||||
defaultValue?:string
|
||||
}
|
||||
|
||||
export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, ...props }:InterfaceInputProps) => {
|
||||
export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, onChange, value, defaultValue }:InterfaceInputProps) => {
|
||||
|
||||
const [customIpInterfaces, setCustomIpInterfaces] = useState<AutocompleteItem[]>(initialCustomInterfaces??[]);
|
||||
const [customIpInterfaces, setCustomIpInterfaces] = useState<ItemProps[]>(initialCustomInterfaces??[]);
|
||||
const interfacesQuery = ipInterfacesQuery()
|
||||
|
||||
const getInterfaces = () => {
|
||||
if (interfacesQuery.isLoading || !interfacesQuery.data) return []
|
||||
if(includeInterfaceNames){
|
||||
const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr, label:item.addr})) as AutocompleteItem[]
|
||||
interfacesQuery.data.map(item => item.name).filter((item, index, arr) => arr.indexOf(item) === index).forEach(item => result.push({netint:"INT", value:item, label:item}))
|
||||
const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr})) as ItemProps[]
|
||||
interfacesQuery.data.map(item => item.name).filter((item, index, arr) => arr.indexOf(item) === index).forEach(item => result.push({netint:"INT", value:item}))
|
||||
return result
|
||||
}
|
||||
return (interfacesQuery.data.map(item => ({netint:item.name, value:item.addr, label:item.addr})) as AutocompleteItem[])
|
||||
return (interfacesQuery.data.map(item => ({netint:item.name, value:item.addr})) as ItemProps[])
|
||||
}
|
||||
|
||||
const interfaces = getInterfaces()
|
||||
|
||||
return <Select
|
||||
placeholder="10.1.1.1"
|
||||
itemComponent={AutoCompleteItem}
|
||||
data={[...customIpInterfaces, ...interfaces]}
|
||||
searchable
|
||||
dropdownPosition="bottom"
|
||||
maxDropdownHeight={200}
|
||||
creatable
|
||||
getCreateLabel={(query) => `+ Use this: ${query}`}
|
||||
onCreate={(query) => {
|
||||
const item = { value: query, netint: "CUSTOM", label: query };
|
||||
setCustomIpInterfaces((current) => [...current, item]);
|
||||
return item;
|
||||
const combobox = useCombobox({
|
||||
onDropdownClose: () => {
|
||||
combobox.resetSelectedOption()
|
||||
},
|
||||
});
|
||||
|
||||
const data = [...customIpInterfaces, ...interfaces]
|
||||
const [selectedValue, setSelectedValue] = useState<string | null>(null);
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
const exactOptionMatch: ItemProps|undefined = data.find((item) => item.value === search);
|
||||
|
||||
const filteredOptions = data.filter((item) => item.value.toLowerCase().includes(search.toLowerCase().trim())).sort((a, b) => {
|
||||
if (exactOptionMatch != null) {
|
||||
if (a.value == exactOptionMatch.value) return -1
|
||||
if (b.value == exactOptionMatch.value) return 1
|
||||
}
|
||||
return a.value.localeCompare(b.value)
|
||||
});
|
||||
|
||||
const options = filteredOptions.map((item) => (
|
||||
<Combobox.Option value={item.value} key={item.value}>
|
||||
( <b>{item.netint}</b> ) -{">"} <b>{item.value}</b>
|
||||
</Combobox.Option>
|
||||
));
|
||||
|
||||
return <>
|
||||
<Combobox
|
||||
store={combobox}
|
||||
withinPortal={false}
|
||||
position="bottom-end"
|
||||
onOptionSubmit={(value) => {
|
||||
if (value === '$create') {
|
||||
const item = { value: search, netint: "CUSTOM" };
|
||||
setCustomIpInterfaces((current) => [...current, item]);
|
||||
setSelectedValue(search);
|
||||
onChange?.(search)
|
||||
} else {
|
||||
setSelectedValue(value);
|
||||
setSearch(value);
|
||||
onChange?.(value)
|
||||
}
|
||||
combobox.closeDropdown();
|
||||
}}
|
||||
style={props.style?{width:"100%", ...props.style}:{width:"100%"}}
|
||||
{...props}
|
||||
/>
|
||||
>
|
||||
|
||||
<Combobox.Target>
|
||||
<TextInput
|
||||
style={{width:"100%"}}
|
||||
defaultValue={defaultValue}
|
||||
rightSection={<Combobox.Chevron />}
|
||||
value={value??(defaultValue?undefined:search)}
|
||||
placeholder="10.1.1.1"
|
||||
rightSectionPointerEvents="none"
|
||||
onChange={(event) => {
|
||||
combobox.openDropdown();
|
||||
combobox.updateSelectedOptionIndex();
|
||||
setSearch(event.currentTarget.value)
|
||||
onChange?.(event.currentTarget.value)
|
||||
|
||||
}}
|
||||
onClick={(e) => {
|
||||
combobox.openDropdown()
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
combobox.openDropdown()
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
combobox.closeDropdown();
|
||||
setSearch(selectedValue??'');
|
||||
}}
|
||||
/>
|
||||
</Combobox.Target>
|
||||
|
||||
<Combobox.Dropdown>
|
||||
<Combobox.Options mah={100} style={{ overflowY: 'auto' }}>
|
||||
{options}
|
||||
{(exactOptionMatch==null) && search.trim().length > 0 && (
|
||||
<Combobox.Option value="$create">+ Use this: {search}</Combobox.Option>
|
||||
)}
|
||||
</Combobox.Options>
|
||||
</Combobox.Dropdown>
|
||||
</Combobox>
|
||||
</>
|
||||
}
|
||||
Reference in New Issue
Block a user