Skip to content
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

Draft: feat: show match context #396 #436

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/mqueryfront/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"react": "^18.3.1",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.3.1",
"react-draggable": "^4.4.6",
"react-router-dom": "^6.26.2",
"react-select": "^5.8.1",
"replace-js-pagination": "^1.0.5",
Expand Down
27 changes: 27 additions & 0 deletions src/mqueryfront/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,30 @@
.cursor-pointer {
cursor: pointer;
}

.modal-container {
position: fixed;
offset-distance: 10px;
z-index: auto;
max-width: 50vw;
right: 5vw;
}

.modal-block {
position: relative;
block-size: "fit-content";
right: 5vw;
}

.modal-dialog {
margin: 0;
}

.modal-header:hover {
cursor: grab;
}

.modal-table {
overflow-y: scroll;
max-height: 50vh;
}
143 changes: 143 additions & 0 deletions src/mqueryfront/src/components/ActionShowMatchContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import React, { useState, useRef, useEffect } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faLightbulb } from "@fortawesome/free-solid-svg-icons";
import Draggable from "react-draggable";

const useClickOutside = (ref, callback) => {
const handleClick = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
// lose focus (higher z-index) only if other modal was clicked
const modals = document.querySelectorAll(".modal");
const wasClicked = (modal) => modal.contains(event.target);
if (Array.from(modals).some(wasClicked)) {
callback();
}
}
};

useEffect(() => {
document.addEventListener("click", handleClick);

return () => {
document.removeEventListener("click", handleClick);
};
});
};

function base64ToHex(str64) {
return atob(str64)
.split("")
.map(function (aChar) {
return ("0" + aChar.charCodeAt(0).toString(16)).slice(-2);
})
.join("")
.toUpperCase(); // Per your example output
}

const ActionShowMatchContext = (props) => {
const ref = useRef(null);
const [showModal, setShowModal] = useState(false);
const [focus, setFocus] = useState(true);
useClickOutside(ref, () => setFocus(false));

const modalHeader = (
<div className="modal-header d-flex justify-content-between">
<h6 className="modal-title">{`Match context for ${props.filename}`}</h6>
<button
type="button"
className="close "
onClick={() => setShowModal(false)}
>
<span aria-hidden="true">&times;</span>
</button>
</div>
);
// Buffer.from(rawData, 'base64');

const tableRows = Object.keys(props.context).map((rulename, index) => {
const rulenameRows = props.context[rulename].map((foundSample) => {
return (
<>
<td scope="row">
{atob(foundSample["before"])}
{<b>{atob(foundSample["matching"])}</b>}
{atob(foundSample["after"])}
</td>
<td scope="row">
{base64ToHex(foundSample["before"])}
{<b>{base64ToHex(foundSample["matching"])}</b>}
{base64ToHex(foundSample["after"])}
</td>
</>
);
});

return (
<>
<tr key={index}>
<td
scope="row fit-content"
rowSpan={props.context[rulename].length}
>
<span className="badge rounded-pill bg-primary ms-1 mt-1">
{rulename}
</span>
</td>
{rulenameRows[0]}
</tr>
{rulenameRows.slice(1).map((row) => (
<tr>{row}</tr>
))}
</>
);
});

const modalBody = (
<div className="modal-body modal-table">
{!Object.keys(props.context).length ? (
"No context available"
) : (
<table className="table">
<tbody>{tableRows}</tbody>
</table>
)}
</div>
);

return (
<div className="d-flex flex-row">
<button
title="Show match context"
className="text-secondary"
style={{ border: 0, background: 0 }}
onClick={() => setShowModal(!showModal)}
>
<FontAwesomeIcon icon={faLightbulb} size="sm" />
</button>
{showModal && (
<Draggable handle=".modal-header">
<div
className="modal-container"
style={{ zIndex: focus ? 100 : 10 }}
ref={ref}
onClick={() => setFocus(true)}
>
<div
className="modal modal-block"
style={{ display: showModal ? "block" : "none" }}
>
<div className="modal-dialog modal-lg">
<div className="modal-content">
{modalHeader}
{modalBody}
</div>
</div>
</div>
</div>
</Draggable>
)}
</div>
);
};

export default ActionShowMatchContext;
38 changes: 37 additions & 1 deletion src/mqueryfront/src/query/QueryMatchesItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,40 @@ import React from "react";
import path from "path-browserify";
import ActionDownload from "../components/ActionDownload";
import ActionCopyToClipboard from "../components/ActionCopyToClipboard";
import ActionShowMatchContext from "../components/ActionShowMatchContext";

const QueryMatchesItem = (props) => {
const { match, download_url } = props;
const { matches, meta, file } = match;
const { matches, meta, file, context } = match; // NOTE: presuming new field in Match schema context which would be dict with 'matches' as keys

const stubContext = context
? context
: {
Multiple_Strings_Match: [
{
before: "",
matching: "ZGVm",
after: "IHRlc3RfcHl0aG9uMygpOgogICAgcHJpbnQoImhlbGw=",
},
],
mquery_exceptions4: [
{
before: "",
matching: "ZGVm",
after: "IHRlc3RfcHl0aG9uMygpOgogICAgcHJpbnQoImhlbGw=",
},
{
before: "",
matching: "dGVzdA==",
after: "X3B5dGhvbjMoKToKICAgIHByaW50KCJoZWxsbyEiKQo=",
},
{
before: "ZWxsbyEiKQogICAgYSA9IDQKICAgIHJldHVybiBhCgo=",
matching: "dGVzdA==",
after: "X3B5dGhvbjMoKQoK",
},
],
};

const fileBasename = path.basename(file);

Expand Down Expand Up @@ -68,6 +98,12 @@ const QueryMatchesItem = (props) => {
tooltipMessage="Copy file name to clipboard"
/>
</small>
<small className="text-secondary ms-2 me-1 mt-1">
<ActionShowMatchContext
filename={fileBasename}
context={stubContext}
/>
</small>
{matchBadges}
{metadataBadges}
</div>
Expand Down
13 changes: 13 additions & 0 deletions src/mqueryfront/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,11 @@ classnames@^2.2.5:
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==

clsx@^1.1.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==

color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
Expand Down Expand Up @@ -2119,6 +2124,14 @@ react-dom@^18.3.1:
loose-envify "^1.1.0"
scheduler "^0.23.2"

react-draggable@^4.4.6:
version "4.4.6"
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.6.tgz#63343ee945770881ca1256a5b6fa5c9f5983fe1e"
integrity sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==
dependencies:
clsx "^1.1.1"
prop-types "^15.8.1"

react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
Expand Down
Loading