-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Component search upgrades #7314
base: staging
Are you sure you want to change the base?
Changes from all commits
47944fb
6d391ad
3463881
1e9f0a4
023d0a2
d8fb287
c81dc67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Autocomplete, FormControl } from "@mui/material"; | ||
import { nodeInput } from "../graph/node-modal/NodeDetailsContent/NodeTableStyled"; | ||
import React from "react"; | ||
|
||
export const SearchLabeledAutocomplete = ({ children, name, options, value, setFilterFields }) => { | ||
function handleChange(_, value) { | ||
setFilterFields((prev) => ({ ...prev, [name]: [value] })); | ||
} | ||
|
||
return ( | ||
<FormControl sx={{ display: "flex", flexDirection: "column", m: 0, gap: 1, width: "100%" }}> | ||
{children} | ||
<Autocomplete | ||
freeSolo | ||
options={options} | ||
value={value.join(",")} | ||
onChange={handleChange} | ||
onInputChange={handleChange} | ||
className={nodeInput} | ||
renderInput={(params) => ( | ||
<div ref={params.InputProps.ref}> | ||
<input name={name} {...params.inputProps} className={nodeInput} /> | ||
</div> | ||
)} | ||
/> | ||
</FormControl> | ||
); | ||
}; | ||
|
||
SearchLabeledAutocomplete.displayName = "SearchLabeledAutocomplete"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,18 @@ | ||
import { forwardRef, PropsWithChildren } from "react"; | ||
import React from "react"; | ||
import { FormControl } from "@mui/material"; | ||
import { nodeInput } from "../graph/node-modal/NodeDetailsContent/NodeTableStyled"; | ||
import React from "react"; | ||
|
||
export type SearchLabeledInputProps = PropsWithChildren<{ | ||
name: string; | ||
}>; | ||
export const SearchLabeledInput = ({ children, name, value, setFilterFields }) => { | ||
function handleChange(event) { | ||
setFilterFields((prev) => ({ ...prev, [name]: event.target.value.split(",") })); | ||
} | ||
|
||
export const SearchLabeledInput = forwardRef<HTMLInputElement, SearchLabeledInputProps>(({ children, ...props }, ref) => { | ||
return ( | ||
<FormControl sx={{ display: "flex", flexDirection: "column", m: 0, gap: 1, width: "100%" }}> | ||
{children} | ||
<input ref={ref} {...props} className={nodeInput} /> | ||
<input name={name} value={value} className={nodeInput} onChange={handleChange} /> | ||
</FormControl> | ||
); | ||
}); | ||
}; | ||
|
||
SearchLabeledInput.displayName = "SearchLabeledInput"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,99 +1,58 @@ | ||
import React, { MutableRefObject, useEffect, useMemo, useRef, useState } from "react"; | ||
import React, { useEffect, useMemo } from "react"; | ||
import { Button, Box, Typography } from "@mui/material"; | ||
import { useTranslation } from "react-i18next"; | ||
import { SearchLabeledInput } from "../../sidePanels/SearchLabeledInput"; | ||
import { SearchLabel } from "../../sidePanels/SearchLabel"; | ||
import { resolveSearchQuery, searchQueryToString, useNodeTypes } from "./utils"; | ||
import { SearchQuery } from "./SearchResults"; | ||
import { resolveSearchQuery } from "./utils"; | ||
|
||
const transformInput = (input: string, fieldName: string) => { | ||
return input === "" ? "" : `${fieldName}:(${input})`; | ||
}; | ||
|
||
function extractSimpleSearchQuery(text: string): string { | ||
const regex = /(\w+):\(([^)]*)\)/g; | ||
let match: RegExpExecArray | null; | ||
let lastIndex = 0; | ||
|
||
while ((match = regex.exec(text)) !== null) { | ||
lastIndex = regex.lastIndex; | ||
} | ||
|
||
const rest = text.slice(lastIndex).trim(); | ||
|
||
return rest; | ||
} | ||
import { SearchLabeledAutocomplete } from "../../sidePanels/SearchLabeledAutocomplete"; | ||
|
||
export function AdvancedSearchFilters({ | ||
filterFields, | ||
setFilterFields, | ||
filter, | ||
setFilter, | ||
setCollapsedHandler, | ||
refForm, | ||
}: { | ||
filterFields: SearchQuery; | ||
setFilterFields: React.Dispatch<React.SetStateAction<SearchQuery>>; | ||
filter: string; | ||
setFilter: React.Dispatch<React.SetStateAction<string>>; | ||
setCollapsedHandler: React.Dispatch<React.SetStateAction<boolean>>; | ||
refForm: MutableRefObject<HTMLFormElement>; | ||
}) { | ||
const { t } = useTranslation(); | ||
//const refForm = useRef<HTMLFormElement>(null); | ||
|
||
const displayNames = useMemo( | ||
() => ({ | ||
id: t("panels.search.field.id", "Name"), | ||
name: t("panels.search.field.id", "Name"), | ||
description: t("panels.search.field.description", "Description"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. panels.search.field.id -> panels.search.field.name? |
||
type: t("panels.search.field.type", "Type"), | ||
paramName: t("panels.search.field.paramName", "Label"), | ||
paramValue: t("panels.search.field.paramValue", "Value"), | ||
outputValue: t("panels.search.field.outputValue", "Output"), | ||
edgeExpression: t("panels.search.field.edgeExpression", "Edge"), | ||
label: t("panels.search.field.paramName", "Label"), | ||
value: t("panels.search.field.paramValue", "Value"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. panels.search.field.label |
||
output: t("panels.search.field.outputValue", "Output"), | ||
edge: t("panels.search.field.edgeExpression", "Edge"), | ||
}), | ||
[t], | ||
); | ||
|
||
//Here be dragons: direct DOM manipulation | ||
useEffect(() => { | ||
if (refForm.current) { | ||
const searchQuery = resolveSearchQuery(filter); | ||
const formElements = refForm.current.elements; | ||
|
||
Array.from(formElements).forEach((element: HTMLInputElement) => { | ||
if (element.name in searchQuery) { | ||
element.value = (searchQuery[element.name] || []).join(","); | ||
} else { | ||
element.value = ""; | ||
} | ||
}); | ||
} | ||
const searchQuery = resolveSearchQuery(filter); | ||
setFilterFields(searchQuery); | ||
}, [filter]); | ||
|
||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { | ||
event.preventDefault(); | ||
|
||
const formData = new FormData(event.currentTarget); | ||
|
||
const transformedInputs = Array.from(formData.entries()) | ||
.map(([fieldName, fieldValue]) => { | ||
const input = (fieldValue as string) || ""; | ||
return transformInput(input, fieldName); | ||
}) | ||
.filter((input) => input !== ""); | ||
|
||
const finalText = transformedInputs.join(" ").trim() + " " + extractSimpleSearchQuery(filter); | ||
|
||
setFilter(finalText); | ||
setFilter(searchQueryToString(filterFields)); | ||
setCollapsedHandler(false); | ||
}; | ||
|
||
const handleClear = () => { | ||
setFilter(extractSimpleSearchQuery(filter)); | ||
|
||
refForm.current.reset(); | ||
setFilter(filterFields?.plainQuery); | ||
}; | ||
|
||
return ( | ||
<Box | ||
ref={refForm} | ||
component="form" | ||
onSubmit={handleSubmit} | ||
sx={{ | ||
|
@@ -105,26 +64,31 @@ export function AdvancedSearchFilters({ | |
}} | ||
> | ||
<Typography fontWeight="bold">{t("search.panel.advancedFilters.label", "Advanced Search")}</Typography> | ||
<SearchLabeledInput name="id"> | ||
<SearchLabel label={displayNames["id"]} /> | ||
</SearchLabeledInput> | ||
<SearchLabeledInput name="description"> | ||
<SearchLabel label={displayNames["description"]} /> | ||
<SearchLabeledInput name="name" value={filterFields?.name || []} setFilterFields={setFilterFields}> | ||
<SearchLabel label={displayNames["name"]} /> | ||
</SearchLabeledInput> | ||
<SearchLabeledInput name="paramName"> | ||
<SearchLabel label={displayNames["paramName"]} /> | ||
<SearchLabeledInput name="value" value={filterFields?.value || []} setFilterFields={setFilterFields}> | ||
<SearchLabel label={displayNames["value"]} /> | ||
</SearchLabeledInput> | ||
<SearchLabeledInput name="paramValue"> | ||
<SearchLabel label={displayNames["paramValue"]} /> | ||
<SearchLabeledAutocomplete | ||
name="type" | ||
options={useNodeTypes()} | ||
value={filterFields?.type || []} | ||
setFilterFields={setFilterFields} | ||
> | ||
<SearchLabel label={displayNames["type"]} /> | ||
</SearchLabeledAutocomplete> | ||
<SearchLabeledInput name="label" value={filterFields?.label || []} setFilterFields={setFilterFields}> | ||
<SearchLabel label={displayNames["label"]} /> | ||
</SearchLabeledInput> | ||
<SearchLabeledInput name="outputValue"> | ||
<SearchLabel label={displayNames["outputValue"]} /> | ||
<SearchLabeledInput name="description" value={filterFields?.description || []} setFilterFields={setFilterFields}> | ||
<SearchLabel label={displayNames["description"]} /> | ||
</SearchLabeledInput> | ||
<SearchLabeledInput name="type"> | ||
<SearchLabel label={displayNames["type"]} /> | ||
<SearchLabeledInput name="output" value={filterFields?.output || []} setFilterFields={setFilterFields}> | ||
<SearchLabel label={displayNames["output"]} /> | ||
</SearchLabeledInput> | ||
<SearchLabeledInput name="edgeExpression"> | ||
<SearchLabel label={displayNames["edgeExpression"]} /> | ||
<SearchLabeledInput name="edge" value={filterFields?.edge || []} setFilterFields={setFilterFields}> | ||
<SearchLabel label={displayNames["edge"]} /> | ||
</SearchLabeledInput> | ||
<Box sx={{ display: "flex", flexDirection: "row", justifyContent: "space-between", width: "100%", mt: 2, mb: 1 }}> | ||
<Button sx={{ width: "45%" }} size="small" variant="outlined" onClick={handleClear}> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need separated component? It seems this is just a proxy for styles/?