Skip to content
Snippets Groups Projects
Commit 543cd2e4 authored by matthiasf's avatar matthiasf
Browse files

wip_search_complete

parent 3b38b5c0
No related branches found
No related tags found
2 merge requests!1162Draft: Ui integration,!1128UI: Implement yang model view
......@@ -16,12 +16,6 @@ import { router } from './routes'
import './shared/icons/icons'
import { persistor, store } from './stores'
const installToastify = () => {
return (
<ToastContainer />
)
};
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<Provider store={store}>
......@@ -29,7 +23,7 @@ ReactDOM.createRoot(document.getElementById("root")).render(
<I18nextProvider i18n={i18next}>
<UtilsProvider>
<MenuProvider>
{installToastify()}
<ToastContainer />
<RouterProvider router={router} />
</MenuProvider>
</UtilsProvider>
......
......@@ -34,6 +34,10 @@
padding-top: 0 !important;
padding-right: 5px !important;
}
& > .text-element {
max-width: 100px;
}
}
.list-item-td.object {
......
import { faAlignRight, faCopy, faPenToSquare, faTrashCan } from "@fortawesome/free-solid-svg-icons"
import { faAlignRight, faPenToSquare, faTrashCan } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { useMenu } from "@provider/menu/menu.provider"
import { useUtils } from "@provider/utils.provider"
import React, { Suspense, useEffect, useMemo, useRef } from "react"
import { Table } from "react-bootstrap"
import React, { Suspense, useMemo, useRef } from "react"
import { Form, Table } from "react-bootstrap"
import { useTranslation } from "react-i18next"
import { useJsonViewer } from "../viewmodel/json_viewer.viewmodel"
import './json_viewer.scss'
......@@ -13,38 +11,11 @@ type JsonViewerProbs = {
}
export const JsonViewer = ({ json }: JsonViewerProbs) => {
const { getSubset, breadcrumbs, isCollapsed, collapseable, collapse } = useJsonViewer();
const { subscribe } = useMenu();
const htmlContainer = useRef(null);
const { t } = useTranslation('common');
const { toClipboard } = useUtils();
const htmlContainer = useRef(null);
const search = useRef<HTMLInputElement>(null);
useEffect(() => {
if (htmlContainer.current) {
const subscription = subscribe({
target: htmlContainer.current,
actions: [
{
key: t('json_viewer.copy'),
icon: faCopy,
action: (clickedElement) => {
let parent = clickedElement;
while (parent && parent.tagName !== 'TR') {
parent = parent.parentNode;
}
const copyValue = parent.dataset.copyValue
toClipboard(copyValue)
}
}
]
})
return () => {
subscription.unsubscribe();
}
}
}, [])
const { getSubset, breadcrumbs, isCollapsed, collapseable, collapse, parameterizedJson, searchTerm } = useJsonViewer({ json, search, container: htmlContainer });
const breadcrumbHTML = useMemo(() => {
return (
......@@ -59,8 +30,17 @@ export const JsonViewer = ({ json }: JsonViewerProbs) => {
}, [breadcrumbs])
const renderInner = (innerJson: JSON, nested: number = 0): JSX.Element => {
const renderInner = (innerJson: JSON, nested: number = 0, path: string = "/network-instance/0/"): JSX.Element => {
return Object.entries(innerJson).map(([key, value]): JSX.Element => {
if (searchTerm !== "") {
path += key + "/"
const is = parameterizedJson.current.filter(_path => _path === path)[0]
if (!is) {
return (<></>)
}
}
const isObject = value instanceof Object;
const readableValue = isObject ? '' : value;
......@@ -69,7 +49,6 @@ export const JsonViewer = ({ json }: JsonViewerProbs) => {
const icon = isObject ?
<span className={collapsed ? 'fa-rotate-90' : ''}>&gt;</span> : <FontAwesomeIcon className="icon fa-rotate-180" icon={faAlignRight} size="xs" />
// determine the margin-left: n indent
let tabs = 0.0;
for (let i = 0; i < nested; i++) {
......@@ -78,9 +57,13 @@ export const JsonViewer = ({ json }: JsonViewerProbs) => {
return (
<React.Fragment key={`${nested}-${key}`}>
<tr className={"list-item-td " + key + " " + nested + " " + (isObject ? 'object' : '')} data-copy-value={readableValue} onClick={() => { isObject ? collapse(key, nested, value) : null }} >
<tr
className={"list-item-td " + key + " " + nested + " " + (isObject ? 'object' : '')}
data-copy-value={readableValue}
onClick={() => { isObject ? collapse(key, nested, value) : null }}
>
<td style={{ marginLeft: tabs + 'em' }} className={"d-flex align-items-center "}>{icon}<span>&ensp;{key}</span></td>
<td>{readableValue}</td>
<td className="text-element text-truncate">{readableValue}</td>
<td className="text-end">
<div className="d-flex icons justify-content-end align-items-center">
<FontAwesomeIcon icon={faPenToSquare} size="sm" />
......@@ -88,13 +71,12 @@ export const JsonViewer = ({ json }: JsonViewerProbs) => {
</div>
</td>
</tr >
{isObject && collapsed ? renderInner(value, nested + 1) : ''}
{isObject && collapsed ? renderInner(value, nested + 1, path) : ''}
</React.Fragment >
)
})
}
const renderJson = (json: JSON): JSX.Element => {
return (
<Table className="list-group-tr">
......@@ -117,10 +99,21 @@ export const JsonViewer = ({ json }: JsonViewerProbs) => {
</Suspense>
</>
)
}, [json, collapseable])
}, [json, collapseable, searchTerm])
const searchHTML = () => {
return (
<>
<Form.Group controlId='device.search' className='p-0 mx-1 pt-2'>
<Form.Control type="text" placeholder={t('device.search.placeholder')} ref={search} />
</Form.Group>
</>
)
}
return (
<div ref={htmlContainer}>
{searchHTML()}
{breadcrumbHTML}
{hierarchyHTML}
</div>
......
import { faCopy } from "@fortawesome/free-solid-svg-icons";
import { useAppDispatch, useAppSelector } from "@hooks";
import { useMenu } from "@provider/menu/menu.provider";
import { useUtils } from "@provider/utils.provider";
import React, { MutableRefObject, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { compareIdentifier, toggleCollapse } from "../reducer/json_viewer.reducer";
export enum CollapseValues {
......@@ -7,10 +12,25 @@ export enum CollapseValues {
TRUE
}
interface JsonViewerViewModelType {
json: JSON,
search: React.RefObject<HTMLInputElement>,
container: React.RefObject<HTMLElement>
}
export const MARKED = "?marked"
export const useJsonViewer = () => {
export const useJsonViewer = ({ json, search, container }: JsonViewerViewModelType) => {
const { breadcrumbs, collapseContainer } = useAppSelector(state => state.json_viwer)
const dispatch = useAppDispatch();
const [searchTerm, setSearchTerm] = useState('');
const { toClipboard } = useUtils();
const { t } = useTranslation('common');
const { subscribe } = useMenu();
// Map that contains a filtered key list with all keys that are found by the searchTerm
// The number represents
const parameterizedJsonMap = useRef<Array<string>>([]);
const getSubset = (json: JSON) => {
......@@ -42,14 +62,94 @@ export const useJsonViewer = () => {
if (keys.length === 1) {
collapse(keys[0], nested + 1, json[keys[0]], CollapseValues.TRUE)
}
}
const handleSearchInput = () => {
setSearchTerm(search.current!.value)
}
const registerMenuOptions = () => {
if (container.current) {
const subscription = subscribe({
target: container.current,
actions: [
{
key: t('json_viewer.copy'),
icon: faCopy,
action: (clickedElement) => {
let parent = clickedElement;
while (parent && parent.tagName !== 'TR') {
parent = parent.parentNode;
}
const copyValue = parent.dataset.copyValue
toClipboard(copyValue)
}
}
]
})
return () => {
subscription.unsubscribe();
}
}
}
const innerSearch = (json: Object, searchValue: string, path: string = "/"): boolean => {
let found = false;
path = JSON.parse(JSON.stringify(path))
for (const [key, childJson] of Object.entries(json)) {
path += key + "/"
if (!(childJson instanceof Object)) {
const marked = key.includes(searchValue) || childJson.includes(searchValue);
if (marked) {
parameterizedJsonMap.current.push(path)
found = true
}
continue
}
const marked = innerSearch(childJson, searchValue, JSON.parse(JSON.stringify(path)));
if (marked) {
found = true
parameterizedJsonMap.current.push(path)
}
}
return found
}
const parameterizedJson = useMemo<MutableRefObject<Array<string>>>(() => {
parameterizedJsonMap.current = []
if (searchTerm === "") {
return json
}
innerSearch(json, searchTerm);
return parameterizedJsonMap
}, [searchTerm])
useEffect(() => {
registerMenuOptions();
if (search.current) {
search.current.addEventListener('input', handleSearchInput)
}
return () => {
search.current!.removeEventListener('input', handleSearchInput)
}
}, [])
return {
getSubset,
breadcrumbs,
collapseable: collapseContainer,
isCollapsed,
collapse
collapse,
searchTerm,
parameterizedJson
}
}
\ No newline at end of file
export const stringToHash = (text: string): number => {
let hash = 0;
if (text.length === 0) return hash;
for (const char of text) {
hash ^= char.charCodeAt(0);
}
return hash;
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment