lhcb-ntuple-wizard 2.0.4 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.js +2 -1
- package/dist/components/DecayCard.d.ts +1 -0
- package/dist/components/{DttNameInput.js → DecayCard.js} +4 -4
- package/dist/components/DecayLatex.js +1 -1
- package/dist/components/DecayTreeCard.js +7 -10
- package/dist/components/DecayTreeGraph.d.ts +1 -2
- package/dist/components/DecayTreeGraph.js +132 -46
- package/dist/components/DeleteButton.d.ts +2 -1
- package/dist/components/DeleteButton.js +2 -2
- package/dist/components/NtupleWizard.js +6 -1
- package/dist/components/RequestButtonGroup.js +2 -2
- package/dist/components/RequestRow.js +5 -4
- package/dist/components/StrippingLineBadge.js +1 -1
- package/dist/components/{TupleToolDropdown.d.ts → TupleToolClassDropdown.d.ts} +2 -5
- package/dist/components/{TupleToolDropdown.js → TupleToolClassDropdown.js} +10 -4
- package/dist/components/TupleToolGroup.d.ts +1 -2
- package/dist/components/TupleToolGroup.js +5 -5
- package/dist/components/TupleToolList.d.ts +1 -2
- package/dist/components/TupleToolList.js +11 -7
- package/dist/components/VerticalLine.d.ts +7 -0
- package/dist/components/VerticalLine.js +4 -0
- package/dist/components/modals/AddTupleToolModal.d.ts +3 -4
- package/dist/components/modals/AddTupleToolModal.js +12 -12
- package/dist/components/modals/ConfigureTupleToolModal.d.ts +2 -2
- package/dist/components/modals/ConfigureTupleToolModal.js +28 -11
- package/dist/config.d.ts +18 -47
- package/dist/config.js +26 -39
- package/dist/pages/DecayTreeConfigPage.js +13 -13
- package/dist/pages/RequestPage.js +10 -13
- package/dist/providers/DttProvider.d.ts +2 -0
- package/dist/providers/DttProvider.js +4 -1
- package/dist/utils/utils.js +36 -3
- package/package.json +10 -5
- package/dist/components/DttNameInput.d.ts +0 -1
package/dist/App.js
CHANGED
|
@@ -15,6 +15,7 @@ import { Archive, CodeSlash, House } from "react-bootstrap-icons";
|
|
|
15
15
|
import { config } from "./config";
|
|
16
16
|
import { NtupleWizard } from "./components/NtupleWizard";
|
|
17
17
|
import { BrowserRouter } from "react-router-dom";
|
|
18
|
+
import { Slide, ToastContainer } from "react-toastify";
|
|
18
19
|
export function App() {
|
|
19
|
-
return (_jsxs(_Fragment, { children: [_jsx(Navbar, { collapseOnSelect: true, expand: "lg", bg: "dark", variant: "dark", children: _jsxs(Container, { children: [_jsxs(Navbar.Brand, { href: "/", children: [_jsx("img", { src: "/logo.svg", height: "30", className: "d-inline-block align-top", alt: "LHCb logo" }), " LHCb NTuple Wizard"] }), _jsx(Navbar.Toggle, { "aria-controls": "responsive-navbar-nav" }), _jsx(Navbar.Collapse, { className: "justify-content-end", children: _jsxs(Nav, { children: [_jsxs(NavDropdown, { title: "About", id: "about-dropdown", menuVariant: "dark", children: [_jsxs(NavDropdown.Item, { href: "https://lhcb-dpa.web.cern.ch/lhcb-dpa/wp6/ntupling-wizard.html", target: "_blank", children: [_jsx(House, {}), " Project home"] }), _jsxs(NavDropdown.Item, { href: "https://gitlab.cern.ch/lhcb-dpa/wp6-analysis-preservation-and-open-data/lhcb-ntuple-wizard-frontend", target: "_blank", children: [_jsx(CodeSlash, {}), " Source code"] }), _jsxs(NavDropdown.Item, { href: config.metadata_baseurl, target: "_blank", children: [_jsx(Archive, {}), " Metadata files"] })] }), _jsx(Nav.Link, { href: "https://opendata.cern.ch/", target: "_blank", children: _jsx("img", { src: "/open_data_portal.png", height: "30", alt: "CERN Open Data Portal" }) })] }) })] }) }), _jsx(Container, { className: "mt-4", children:
|
|
20
|
+
return (_jsxs(_Fragment, { children: [_jsx(Navbar, { collapseOnSelect: true, expand: "lg", bg: "dark", variant: "dark", children: _jsxs(Container, { children: [_jsxs(Navbar.Brand, { href: "/", children: [_jsx("img", { src: "/logo.svg", height: "30", className: "d-inline-block align-top", alt: "LHCb logo" }), " LHCb NTuple Wizard"] }), _jsx(Navbar.Toggle, { "aria-controls": "responsive-navbar-nav" }), _jsx(Navbar.Collapse, { className: "justify-content-end", children: _jsxs(Nav, { children: [_jsxs(NavDropdown, { title: "About", id: "about-dropdown", menuVariant: "dark", children: [_jsxs(NavDropdown.Item, { href: "https://lhcb-dpa.web.cern.ch/lhcb-dpa/wp6/ntupling-wizard.html", target: "_blank", children: [_jsx(House, {}), " Project home"] }), _jsxs(NavDropdown.Item, { href: "https://gitlab.cern.ch/lhcb-dpa/wp6-analysis-preservation-and-open-data/lhcb-ntuple-wizard-frontend", target: "_blank", children: [_jsx(CodeSlash, {}), " Source code"] }), _jsxs(NavDropdown.Item, { href: config.metadata_baseurl, target: "_blank", children: [_jsx(Archive, {}), " Metadata files"] })] }), _jsx(Nav.Link, { href: "https://opendata.cern.ch/", target: "_blank", children: _jsx("img", { src: "/open_data_portal.png", height: "30", alt: "CERN Open Data Portal" }) })] }) })] }) }), _jsx(Container, { className: "mt-4", children: _jsxs(BrowserRouter, { children: [_jsx(ToastContainer, { transition: Slide, hideProgressBar: true }), _jsx(NtupleWizard, {})] }) })] }));
|
|
20
21
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function DecayCard(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -14,9 +14,9 @@ import { UploadDttConfigModal } from "./modals/UploadDttConfigModal";
|
|
|
14
14
|
import { VARIABLES_PATH } from "../constants";
|
|
15
15
|
import { useRow } from "../providers/RowProvider";
|
|
16
16
|
import { useWizardConfig } from "../providers/WizardConfigProvider";
|
|
17
|
-
export function
|
|
17
|
+
export function DecayCard() {
|
|
18
18
|
const { row } = useRow();
|
|
19
|
-
const { rows, updateRow } = useRows();
|
|
19
|
+
const { rows, setRows, updateRow } = useRows();
|
|
20
20
|
const navigate = useNavigate();
|
|
21
21
|
const metadata = useMetadata();
|
|
22
22
|
const { basePath, variant } = useWizardConfig();
|
|
@@ -50,7 +50,7 @@ export function DttNameInput() {
|
|
|
50
50
|
setAutoFocus(true);
|
|
51
51
|
};
|
|
52
52
|
const handleConfigureDtt = () => {
|
|
53
|
-
|
|
53
|
+
setRows((rows) => rows.map((r) => (r.id === row.id ? { ...r, editTree: true } : { ...r, editTree: false })));
|
|
54
54
|
void navigate(`${basePath}${VARIABLES_PATH}`);
|
|
55
55
|
};
|
|
56
56
|
const handleDeleteDtt = () => {
|
|
@@ -71,5 +71,5 @@ export function DttNameInput() {
|
|
|
71
71
|
? "Name contains invalid characters"
|
|
72
72
|
: "Name is already taken" })] })), _jsx(ButtonGroup, { children: row.dtt ? (_jsxs(_Fragment, { children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Configure this DecayTreeTuple" }), children: _jsx(Button, { type: "submit", variant: "secondary", onClick: handleConfigureDtt, disabled: !dttNameValid, className: "align-items-center d-flex", children: _jsx(GearWideConnected, {}) }) }), variant === "standalone" && (_jsx(_Fragment, { children: _jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Download DecayTreeTuple YAML configuration file" }), children: _jsx(Button, { className: "align-items-center d-flex ms-auto", type: "button", onClick: () => {
|
|
73
73
|
downloadText(YamlFile.fromDtt(row.dtt));
|
|
74
|
-
}, disabled: !dttNameValid, children: _jsx(Download, {}) }) }) })), _jsx(DeleteButton, { action: handleDeleteDtt })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "d-flex align-items-center gap-2", children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Please add a DecayTreeTuple in order to complete the production configuration" }), children: _jsx(ExclamationCircle, { width: 20, height: 20, className: "text-danger me-2" }) }), _jsxs(ButtonGroup, { children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Add a DecayTreeTuple" }), children: _jsx(Button, { type: "submit", variant: "success", onClick: handleCreateDTT, disabled: !metadata, children: _jsx(PlusLg, {}) }) }), variant === "standalone" && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Upload DecayTreeTuple configuration file" }), children: _jsx(Button, { className: "ms-auto", type: "button", onClick: () => setShowUploadModal(true), children: _jsx(Upload, {}) }) }))] })] }), showUploadModal && (_jsx(UploadDttConfigModal, { currentRow: row, onClose: () => setShowUploadModal(false) }))] })) })] }), _jsx(Card.Body, { children: _jsx(DecayLatex, { decay: row.decay }) })] }));
|
|
74
|
+
}, disabled: !dttNameValid, children: _jsx(Download, {}) }) }) })), _jsx(DeleteButton, { action: handleDeleteDtt, popupMessage: "Delete DecayTreeTuple" })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "d-flex align-items-center gap-2", children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Please add a DecayTreeTuple in order to complete the production configuration" }), children: _jsx(ExclamationCircle, { width: 20, height: 20, className: "text-danger me-2" }) }), _jsxs(ButtonGroup, { children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Add a DecayTreeTuple" }), children: _jsx(Button, { type: "submit", variant: "success", onClick: handleCreateDTT, disabled: !metadata, children: _jsx(PlusLg, {}) }) }), variant === "standalone" && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Upload DecayTreeTuple configuration file" }), children: _jsx(Button, { className: "ms-auto", type: "button", onClick: () => setShowUploadModal(true), children: _jsx(Upload, {}) }) }))] })] }), showUploadModal && (_jsx(UploadDttConfigModal, { currentRow: row, onClose: () => setShowUploadModal(false) }))] })) })] }), _jsx(Card.Body, { style: { overflowX: "auto" }, children: _jsx(DecayLatex, { decay: row.decay }) })] }));
|
|
75
75
|
}
|
|
@@ -95,7 +95,7 @@ export function DecayLatex({ decay, selection }) {
|
|
|
95
95
|
let latex = metadata.particleProperties[cleanName].latex;
|
|
96
96
|
// Underline marked particles
|
|
97
97
|
if (isMarked) {
|
|
98
|
-
latex = LATEX_COMMANDS.COLOR("
|
|
98
|
+
latex = LATEX_COMMANDS.COLOR("#4169E1", latex);
|
|
99
99
|
}
|
|
100
100
|
return `${openBrace}${latex}${closeBrace}`;
|
|
101
101
|
};
|
|
@@ -13,30 +13,27 @@ import { DecayTagBadge } from "./DecayTagBadge.js";
|
|
|
13
13
|
import { DecayLatex } from "./DecayLatex";
|
|
14
14
|
import { TupleToolList } from "./TupleToolList";
|
|
15
15
|
import { useMetadata } from "../providers/MetadataProvider.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { useEffect, useState } from "react";
|
|
16
|
+
import { Card, OverlayTrigger, Stack, Tooltip } from "react-bootstrap";
|
|
17
|
+
import { QuestionCircle } from "react-bootstrap-icons";
|
|
18
|
+
import { useEffect } from "react";
|
|
20
19
|
import { DecayTreeGraph } from "./DecayTreeGraph";
|
|
21
20
|
import { DecayTreeCardHeader } from "./DecayTreeCardHeader";
|
|
22
21
|
import { ParticleTagFilters } from "./ParticleTagFilters";
|
|
23
22
|
import { useDtt } from "../providers/DttProvider";
|
|
24
23
|
export function DecayTreeCard({ onConfigSaved, onDirtyUpdated }) {
|
|
25
24
|
const metadata = useMetadata();
|
|
26
|
-
const { dtt, decay, dirty } = useDtt();
|
|
27
|
-
const [selectedBranch, setSelectedBranch] = useState([]);
|
|
28
|
-
const [lockGraphZoom, setLockGraphZoom] = useState(!config.dttGraphOptions.interaction.zoomView);
|
|
25
|
+
const { dtt, decay, dirty, selectedBranch, setSelectedBranch } = useDtt();
|
|
29
26
|
useEffect(() => {
|
|
30
27
|
onDirtyUpdated(dtt.getName(), dirty);
|
|
31
28
|
}, [dtt, dirty]);
|
|
32
29
|
if (!metadata) {
|
|
33
30
|
return null;
|
|
34
31
|
}
|
|
35
|
-
return (_jsxs(Card, { children: [_jsx(DecayTreeCardHeader, { dttName: dtt.getName(), inputs: dtt.config.inputs }), _jsxs(Card.Body, { children: [
|
|
32
|
+
return (_jsxs(Card, { children: [_jsx(DecayTreeCardHeader, { dttName: dtt.getName(), inputs: dtt.config.inputs }), _jsxs(Card.Body, { children: [_jsx(Card.Title, { className: "d-flex justify-content-between align-items-start", children: _jsxs("span", { children: [_jsx(OverlayTrigger, { placement: "bottom", overlay: _jsx(Tooltip, { children: "Click to select particles to configure. Hold ctrl to select multiple. Clear the selection to configure the entire decay." }), children: _jsx(QuestionCircle, {}) }), " ", "Configure ", _jsx(DecayLatex, { decay: decay })] }) }), _jsx(Stack, { direction: "horizontal", style: { flexWrap: "wrap", justifyContent: "center" }, gap: 2, children: decay.tags
|
|
36
33
|
.filter((tag) => metadata.userHints.decayTags[tag].warn)
|
|
37
|
-
.map((tag) => (_jsx(DecayTagBadge, { tag: tag }, tag))) }), _jsx(DecayTreeGraph, { decay: decay,
|
|
34
|
+
.map((tag) => (_jsx(DecayTagBadge, { tag: tag }, tag))) }), _jsx(DecayTreeGraph, { decay: decay, selectedParticlesIds: selectedBranch, onNodeSelectionChanged: (nodeIds) => {
|
|
38
35
|
setSelectedBranch([...nodeIds].sort());
|
|
39
|
-
} }), _jsx(ParticleTagFilters, { branch: selectedBranch, onSelectTag: setSelectedBranch }), _jsxs(Card.Title, { className: "mt-4", children: ["Current selection: ", _jsx(DecayLatex, { decay: decay, selection: selectedBranch })] }), _jsx(TupleToolList, {
|
|
36
|
+
} }), _jsx(ParticleTagFilters, { branch: selectedBranch, onSelectTag: setSelectedBranch }), _jsxs(Card.Title, { className: "mt-4", children: ["Current selection: ", _jsx(DecayLatex, { decay: decay, selection: selectedBranch })] }), _jsx(TupleToolList, { onGroupSelected: (group) => {
|
|
40
37
|
const selection = group.split(",");
|
|
41
38
|
setSelectedBranch(selection);
|
|
42
39
|
}, onSave: () => onConfigSaved(dtt.config) })] })] }));
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { DecayData } from "../models/decayData";
|
|
2
2
|
interface Props {
|
|
3
3
|
decay: DecayData;
|
|
4
|
-
lockGraphZoom: boolean;
|
|
5
4
|
selectedParticlesIds: string[];
|
|
6
5
|
onNodeSelectionChanged: (nodeIds: string[]) => void;
|
|
7
6
|
}
|
|
8
|
-
export declare function DecayTreeGraph({ decay,
|
|
7
|
+
export declare function DecayTreeGraph({ decay, selectedParticlesIds, onNodeSelectionChanged }: Props): import("react/jsx-runtime").JSX.Element;
|
|
9
8
|
export {};
|
|
@@ -1,52 +1,66 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
import
|
|
2
|
+
import CytoscapeComponent from "react-cytoscapejs";
|
|
3
|
+
import cytoscape from "cytoscape";
|
|
4
|
+
import dagre from "cytoscape-dagre";
|
|
4
5
|
import { config } from "../config";
|
|
5
6
|
import { tex2svg } from "../utils/mathjaxUtils";
|
|
6
7
|
import { useMetadata } from "../providers/MetadataProvider";
|
|
7
|
-
import { useEffect,
|
|
8
|
-
|
|
8
|
+
import { useEffect, useRef } from "react";
|
|
9
|
+
cytoscape.use(dagre);
|
|
10
|
+
export function DecayTreeGraph({ decay, selectedParticlesIds, onNodeSelectionChanged }) {
|
|
9
11
|
const metadata = useMetadata();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
const cyRef = useRef(null);
|
|
13
|
+
// Sync external selection state to cytoscape selection
|
|
12
14
|
useEffect(() => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
const cy = cyRef.current;
|
|
16
|
+
if (!cy)
|
|
17
|
+
return;
|
|
18
|
+
cy.nodes().forEach((node) => {
|
|
19
|
+
if (selectedParticlesIds.includes(node.id())) {
|
|
20
|
+
node.select();
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
node.unselect();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
15
26
|
}, [selectedParticlesIds]);
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
// eslint-disable-next-line
|
|
18
|
-
network?.setOptions({ interaction: { zoomView: lockGraphZoom } });
|
|
19
|
-
}, [lockGraphZoom]);
|
|
20
|
-
function htmlTitle(text) {
|
|
21
|
-
const container = document.createElement("span");
|
|
22
|
-
container.innerText = text;
|
|
23
|
-
return container;
|
|
24
|
-
}
|
|
25
27
|
function makeSVG(text, colour = "black") {
|
|
26
|
-
const pipes = "\\phantom{{}^{|-}_{|-}}";
|
|
28
|
+
const pipes = "\\phantom{{}^{|-}_{|-}}";
|
|
27
29
|
const texSVG = tex2svg(`${pipes}${text}${pipes}`).innerHTML.replaceAll("currentColor", colour);
|
|
28
30
|
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(texSVG)}`;
|
|
29
31
|
}
|
|
30
|
-
function
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
function getHtmlTitle(text) {
|
|
33
|
+
const container = document.createElement("span");
|
|
34
|
+
container.innerText = text;
|
|
35
|
+
return container;
|
|
36
|
+
}
|
|
37
|
+
function buildElements() {
|
|
38
|
+
const elements = [];
|
|
39
|
+
if (!metadata)
|
|
40
|
+
return elements;
|
|
35
41
|
const addNodeAndEdge = (item, parentId) => {
|
|
36
42
|
const { branch, particle } = item;
|
|
37
43
|
const particleProperties = metadata.particleProperties[particle];
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
elements.push({
|
|
45
|
+
data: {
|
|
46
|
+
id: branch,
|
|
47
|
+
label: branch,
|
|
48
|
+
// Store both SVG variants so we can swap on select/unselect
|
|
49
|
+
imageSrc: makeSVG(particleProperties.latex, "black"),
|
|
50
|
+
imageSelected: makeSVG(particleProperties.latex, "#0d6efd"),
|
|
51
|
+
// Tooltip text (accessible via tippy or title attr if needed)
|
|
52
|
+
title: getHtmlTitle(particleProperties.html).innerText,
|
|
46
53
|
},
|
|
54
|
+
selected: selectedParticlesIds.includes(branch),
|
|
47
55
|
});
|
|
48
56
|
if (parentId !== null) {
|
|
49
|
-
|
|
57
|
+
elements.push({
|
|
58
|
+
data: {
|
|
59
|
+
id: `${parentId}->${branch}`,
|
|
60
|
+
source: parentId,
|
|
61
|
+
target: branch,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
50
64
|
}
|
|
51
65
|
return branch;
|
|
52
66
|
};
|
|
@@ -68,22 +82,94 @@ export function DecayTreeGraph({ decay, lockGraphZoom, selectedParticlesIds, onN
|
|
|
68
82
|
}
|
|
69
83
|
};
|
|
70
84
|
walk(decay.descriptors.mapped_list, null);
|
|
71
|
-
return
|
|
85
|
+
return elements;
|
|
72
86
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
const stylesheet = [
|
|
88
|
+
// Disable the grey circle on pan/click
|
|
89
|
+
{
|
|
90
|
+
selector: "core",
|
|
91
|
+
style: {
|
|
92
|
+
"active-bg-size": 0,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
selector: "node",
|
|
97
|
+
style: {
|
|
98
|
+
shape: "ellipse",
|
|
99
|
+
// Use the unselected SVG as the background image
|
|
100
|
+
"background-image": "data(imageSrc)",
|
|
101
|
+
"background-fit": "contain",
|
|
102
|
+
"background-opacity": 0,
|
|
103
|
+
width: config.dttGraphOptions.nodes.width,
|
|
104
|
+
height: config.dttGraphOptions.nodes.height,
|
|
105
|
+
label: "",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
selector: "node:active",
|
|
110
|
+
style: {
|
|
111
|
+
"overlay-opacity": 0,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
selector: "node.hover",
|
|
116
|
+
style: {
|
|
117
|
+
"background-opacity": 0.2,
|
|
118
|
+
"background-color": config.dttGraphOptions.nodes.hoverColor,
|
|
78
119
|
},
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
selector: "node:selected",
|
|
123
|
+
style: {
|
|
124
|
+
// Swap to the selected SVG when the node is selected
|
|
125
|
+
"background-image": "data(imageSelected)",
|
|
83
126
|
},
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
selector: "edge",
|
|
130
|
+
style: {
|
|
131
|
+
"curve-style": "bezier",
|
|
132
|
+
"target-arrow-shape": "triangle",
|
|
133
|
+
width: config.dttGraphOptions.edges.width,
|
|
134
|
+
"line-color": config.dttGraphOptions.edges.color,
|
|
135
|
+
"target-arrow-color": config.dttGraphOptions.edges.color,
|
|
136
|
+
events: "no",
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
return (_jsx(CytoscapeComponent, { elements: buildElements(), stylesheet: stylesheet, style: { width: "100%", height: config.dttGraphOptions.height }, autoungrabify: !config.dttGraphOptions.nodes.draggable, autounselectify: false, boxSelectionEnabled: true, cy: (cy) => {
|
|
141
|
+
cyRef.current = cy;
|
|
142
|
+
// Set up zoom
|
|
143
|
+
cy.userZoomingEnabled(false);
|
|
144
|
+
cy.minZoom(config.dttGraphOptions.zoomMin);
|
|
145
|
+
cy.maxZoom(config.dttGraphOptions.zoomMax);
|
|
146
|
+
// Handle zoom event
|
|
147
|
+
cy.container()?.addEventListener("wheel", (e) => {
|
|
148
|
+
if (!(e.ctrlKey || e.metaKey))
|
|
149
|
+
return;
|
|
150
|
+
e.preventDefault();
|
|
151
|
+
const zoom = cy.zoom();
|
|
152
|
+
const factor = 1 + (e.deltaY < 0 ? config.dttGraphOptions.zoomStep : -config.dttGraphOptions.zoomStep);
|
|
153
|
+
cy.zoom({
|
|
154
|
+
level: zoom * factor,
|
|
155
|
+
renderedPosition: { x: e.offsetX, y: e.offsetY },
|
|
156
|
+
});
|
|
157
|
+
}, { passive: false });
|
|
158
|
+
// Handle node selection events
|
|
159
|
+
cy.off("select unselect", "node");
|
|
160
|
+
cy.on("select unselect", "node", () => {
|
|
161
|
+
const selected = cy.nodes(":selected").map((n) => n.id());
|
|
162
|
+
onNodeSelectionChanged(selected);
|
|
163
|
+
});
|
|
164
|
+
// Handle hover events
|
|
165
|
+
cy.on("mouseover", "node", (e) => {
|
|
166
|
+
e.target.addClass("hover");
|
|
167
|
+
});
|
|
168
|
+
cy.on("mouseout", "node", (e) => {
|
|
169
|
+
e.target.removeClass("hover");
|
|
170
|
+
});
|
|
171
|
+
}, layout: {
|
|
172
|
+
...config.dttGraphOptions.layout,
|
|
173
|
+
name: "dagre",
|
|
88
174
|
} }));
|
|
89
175
|
}
|
|
@@ -3,7 +3,8 @@ interface Props {
|
|
|
3
3
|
action: () => void;
|
|
4
4
|
outline?: boolean;
|
|
5
5
|
disabled?: boolean;
|
|
6
|
+
popupMessage?: string;
|
|
6
7
|
children?: ReactNode;
|
|
7
8
|
}
|
|
8
|
-
export declare function DeleteButton({ action, outline, disabled, children }: Props): import("react/jsx-runtime").JSX.Element | null;
|
|
9
|
+
export declare function DeleteButton({ action, outline, disabled, popupMessage, children }: Props): import("react/jsx-runtime").JSX.Element | null;
|
|
9
10
|
export {};
|
|
@@ -11,9 +11,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
11
11
|
\*****************************************************************************/
|
|
12
12
|
import { Button, OverlayTrigger, Popover } from "react-bootstrap";
|
|
13
13
|
import { Trash } from "react-bootstrap-icons";
|
|
14
|
-
export function DeleteButton({ action, outline, disabled, children }) {
|
|
14
|
+
export function DeleteButton({ action, outline, disabled, popupMessage = "Confirm", children }) {
|
|
15
15
|
if (disabled) {
|
|
16
16
|
return null;
|
|
17
17
|
}
|
|
18
|
-
return (_jsx(OverlayTrigger, { trigger: "click", placement: "auto", rootClose: true, overlay: _jsx(Popover, { children: _jsx(Popover.Body, { children: _jsx(Button, { variant: "danger", onClick: action, children:
|
|
18
|
+
return (_jsx(OverlayTrigger, { trigger: "click", placement: "auto", rootClose: true, overlay: _jsx(Popover, { children: _jsx(Popover.Body, { children: _jsx(Button, { variant: "danger", onClick: action, children: popupMessage }) }) }), children: _jsxs(Button, { variant: outline ? "outline-danger" : "danger", disabled: disabled, children: [_jsx(Trash, {}), " ", children] }) }));
|
|
19
19
|
}
|
|
@@ -25,5 +25,10 @@ export function NtupleWizard({ basePath = "", contactEmail, submitLocation, requ
|
|
|
25
25
|
if (contactEmail) {
|
|
26
26
|
localStorage.setItem("email", contactEmail);
|
|
27
27
|
}
|
|
28
|
-
return (_jsx(MetadataProvider, { children: _jsx(WizardConfigProvider, { basePath: basePath, submitLocation: submitLocation, requestReasonMessage: requestReasonMessage, requestSubmittedMessage: requestSubmittedMessage, onRequestSubmitted: onRequestSubmitted, children: _jsx(RowsProvider, { children: _jsx(RequestProvider, { emailIsKnown: !!contactEmail, children: _jsx(MathJaxContext, {
|
|
28
|
+
return (_jsx(MetadataProvider, { children: _jsx(WizardConfigProvider, { basePath: basePath, submitLocation: submitLocation, requestReasonMessage: requestReasonMessage, requestSubmittedMessage: requestSubmittedMessage, onRequestSubmitted: onRequestSubmitted, children: _jsx(RowsProvider, { children: _jsx(RequestProvider, { emailIsKnown: !!contactEmail, children: _jsx(MathJaxContext, { config: {
|
|
29
|
+
loader: { load: ["[tex]/color"] },
|
|
30
|
+
tex: {
|
|
31
|
+
packages: { "[+]": ["color"] },
|
|
32
|
+
},
|
|
33
|
+
}, children: pathname.endsWith(SELECT_DECAYS_PATH) ? (_jsx(DecaySearchPage, {})) : pathname.endsWith(VARIABLES_PATH) ? (_jsx(DecayTreeConfigPage, {})) : (_jsx(RequestPage, {})) }) }) }) }) }));
|
|
29
34
|
}
|
|
@@ -18,7 +18,7 @@ export function RequestButtonGroup() {
|
|
|
18
18
|
const { rows, configuredRows, setRows } = useRows();
|
|
19
19
|
const navigate = useNavigate();
|
|
20
20
|
const [showProdUploadModal, setShowProdUploadModal] = useState(false);
|
|
21
|
-
const
|
|
21
|
+
const configureAllDtts = async () => {
|
|
22
22
|
setRows((prev) => prev.map((r) => ({ ...r, editTree: true })));
|
|
23
23
|
await navigate(`${basePath}${VARIABLES_PATH}`);
|
|
24
24
|
};
|
|
@@ -26,6 +26,6 @@ export function RequestButtonGroup() {
|
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
28
|
return (_jsxs(_Fragment, { children: [showProdUploadModal && _jsx(UploadDttConfigModal, { onClose: () => setShowProdUploadModal(false) }), _jsxs(ButtonGroup, { children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Select decays" }), children: _jsxs(Button, { type: "submit", variant: "success", onClick: () => void navigate(`${basePath}${SELECT_DECAYS_PATH}`), className: "align-items-center d-flex gap-1", children: [_jsx(PlusLg, {}), " Select decays"] }) }), variant === "standalone" && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Upload Production configuration file" }), children: _jsx(Button, { className: "ms-auto align-items-center d-flex", type: "button", onClick: () => setShowProdUploadModal(true), children: _jsx(Upload, {}) }) })), _jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Configure all DecayTreeTuples" }), children: _jsxs(Button, { type: "submit", variant: "secondary", onClick: () => {
|
|
29
|
-
|
|
29
|
+
configureAllDtts().catch(console.error);
|
|
30
30
|
}, disabled: !validation.allRowsHaveDtt, className: "align-items-center d-flex gap-1", children: [_jsx(GearWideConnected, {}), " ", _jsx(Badge, { pill: true, bg: "primary", children: configuredRows.length })] }) }), variant === "standalone" && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Download Analysis Production configuration file (info.yaml)" }), children: _jsx(Button, { variant: "primary", type: "button", onClick: () => downloadText(YamlFile.createInfoYaml(rows, metadata)), disabled: !validation.allRowsHaveDtt || !validation.allRowsHavePaths || !validation.isEmailValid, className: "align-items-center d-flex", children: _jsx(Download, {}) }) }))] })] }));
|
|
31
31
|
}
|
|
@@ -2,13 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Button, Col, OverlayTrigger, Popover, Row } from "react-bootstrap";
|
|
3
3
|
import { InfoCircle } from "react-bootstrap-icons";
|
|
4
4
|
import { StrippingLineInfo } from "./StrippingLineInfo";
|
|
5
|
-
import {
|
|
6
|
-
import { DttNameInput } from "./DttNameInput";
|
|
5
|
+
import { DecayCard } from "./DecayCard";
|
|
7
6
|
import { useMetadata } from "../providers/MetadataProvider";
|
|
8
|
-
import { useRows } from "../providers/RowsProvider";
|
|
9
7
|
import { BookkeepingPathDropdown } from "./BookkeepingPathDropdown";
|
|
10
8
|
import { StrippingLineDropdown } from "./StrippingLineDropdown";
|
|
11
9
|
import { useRow } from "../providers/RowProvider";
|
|
10
|
+
import { VerticalLine } from "./VerticalLine";
|
|
11
|
+
import { DeleteButton } from "./DeleteButton";
|
|
12
|
+
import { useRows } from "../providers/RowsProvider";
|
|
12
13
|
export function RequestRow() {
|
|
13
14
|
const metadata = useMetadata();
|
|
14
15
|
const { row } = useRow();
|
|
@@ -16,5 +17,5 @@ export function RequestRow() {
|
|
|
16
17
|
if (!metadata) {
|
|
17
18
|
return null;
|
|
18
19
|
}
|
|
19
|
-
return (_jsxs(Row, { className: "align-items-
|
|
20
|
+
return (_jsxs(Row, { className: "align-items-stretch mt-1", children: [_jsx(Col, { xs: 5, children: _jsx(DecayCard, {}) }), _jsxs(Col, { xs: 6, className: "d-flex flex-column gap-2", children: [_jsx(Row, { children: _jsxs("div", { className: "d-flex flex-row justify-content-between align-items-center gap-3", children: [_jsx("div", { style: { width: "100%" }, children: _jsx(StrippingLineDropdown, {}) }), _jsx(OverlayTrigger, { trigger: "click", rootClose: true, overlay: _jsx(Popover, { children: _jsx(Popover.Body, { children: row.lines.map((line) => (_jsx(StrippingLineInfo, { line: line.line, stream: line.stream, versions: line.versions, showLink: true }, line.line))) }) }), children: _jsx(Button, { disabled: row.lines.length === 0, children: _jsx(InfoCircle, {}) }) })] }) }), _jsx(Row, { children: _jsx(BookkeepingPathDropdown, {}) })] }), _jsxs(Col, { xs: 1, className: "d-flex align-items-center justify-content-center gap-4", children: [_jsx(VerticalLine, {}), _jsx(DeleteButton, { action: () => removeRow(row.id), popupMessage: "Delete row" })] })] }));
|
|
20
21
|
}
|
|
@@ -24,5 +24,5 @@ export function StrippingLineBadge({ version, line, stream, showLink }) {
|
|
|
24
24
|
target: "_blank",
|
|
25
25
|
}
|
|
26
26
|
: {};
|
|
27
|
-
return (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: metadata ? metadata.strippingHints[version].description : "..." }), children: _jsx(Badge, { pill: true, bg: "info", ...badgeProps, children: "S" + version }) }));
|
|
27
|
+
return (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { style: { zIndex: 9999 }, children: metadata ? metadata.strippingHints[version].description : "..." }), children: _jsx(Badge, { pill: true, bg: "info", ...badgeProps, children: "S" + version }) }));
|
|
28
28
|
}
|
|
@@ -10,12 +10,9 @@
|
|
|
10
10
|
\*****************************************************************************/
|
|
11
11
|
import { GroupBase, Props as SelectProps } from "react-select";
|
|
12
12
|
import { ReactNode } from "react";
|
|
13
|
-
type
|
|
13
|
+
type ToolDropdownOption = {
|
|
14
14
|
value: string;
|
|
15
15
|
label: ReactNode;
|
|
16
16
|
};
|
|
17
|
-
|
|
18
|
-
filterToolTags: string[];
|
|
19
|
-
}
|
|
20
|
-
export declare function TupleToolDropdown({ filterToolTags, ...props }: Props): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function TupleToolClassDropdown(props: SelectProps<ToolDropdownOption, false, GroupBase<ToolDropdownOption>>): import("react/jsx-runtime").JSX.Element;
|
|
21
18
|
export {};
|
|
@@ -13,17 +13,23 @@ import Select from "react-select";
|
|
|
13
13
|
import { TupleToolLabel } from "./TupleToolLabel";
|
|
14
14
|
import { useMetadata } from "../providers/MetadataProvider.js";
|
|
15
15
|
import { TupleTool } from "../models/tupleTool";
|
|
16
|
-
|
|
16
|
+
import { useDtt } from "../providers/DttProvider";
|
|
17
|
+
export function TupleToolClassDropdown(props) {
|
|
17
18
|
const metadata = useMetadata();
|
|
19
|
+
const { selectedBranch } = useDtt();
|
|
20
|
+
const allowedTags = ["IParticleTupleTool", ...(selectedBranch.length === 0 ? ["IEventTupleTool"] : [])];
|
|
21
|
+
const disallowedTools = ["LoKi__Hybrid__ArrayTupleTool"];
|
|
22
|
+
// Group tools by tag
|
|
18
23
|
const options = metadata
|
|
19
|
-
?
|
|
24
|
+
? allowedTags.map((allowedTag) => ({
|
|
20
25
|
label: allowedTag,
|
|
21
26
|
options: Object.keys(metadata.tupleTools.tupleTools)
|
|
22
27
|
.sort()
|
|
23
28
|
.map(TupleTool.fromString)
|
|
24
29
|
.map((tool) => ({ value: tool.toString(), label: _jsx(TupleToolLabel, { tool: tool }) }))
|
|
25
|
-
.filter((tool) => metadata.tupleTools.tupleTools[tool.value].tags.includes(allowedTag)
|
|
30
|
+
.filter((tool) => metadata.tupleTools.tupleTools[tool.value].tags.includes(allowedTag) &&
|
|
31
|
+
!disallowedTools.includes(tool.value)),
|
|
26
32
|
}))
|
|
27
33
|
: [];
|
|
28
|
-
return (_jsx("div", { className: "react-select form-control p-0", children: _jsx(Select, { options: options, isLoading: !metadata, ...props }) }));
|
|
34
|
+
return (_jsx("div", { className: "react-select form-control p-0", children: _jsx(Select, { placeholder: "TupleTool class", options: options, isLoading: !metadata, ...props }) }));
|
|
29
35
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
interface Props {
|
|
2
|
-
branch: string[];
|
|
3
2
|
onGroupSelected: (group: string) => void;
|
|
4
3
|
}
|
|
5
|
-
export declare function TupleToolGroup({
|
|
4
|
+
export declare function TupleToolGroup({ onGroupSelected }: Props): import("react/jsx-runtime").JSX.Element;
|
|
6
5
|
export {};
|
|
@@ -3,14 +3,14 @@ import { Button, Card, ListGroup, OverlayTrigger, Popover } from "react-bootstra
|
|
|
3
3
|
import { PencilSquare } from "react-bootstrap-icons";
|
|
4
4
|
import { DecayLatex } from "./DecayLatex";
|
|
5
5
|
import { useDtt } from "../providers/DttProvider";
|
|
6
|
-
export function TupleToolGroup({
|
|
7
|
-
const { dtt, decay } = useDtt();
|
|
6
|
+
export function TupleToolGroup({ onGroupSelected }) {
|
|
7
|
+
const { dtt, decay, selectedBranch } = useDtt();
|
|
8
8
|
let groups = {};
|
|
9
|
-
if (
|
|
9
|
+
if (selectedBranch.length === 0) {
|
|
10
10
|
groups = dtt.config.groups;
|
|
11
11
|
}
|
|
12
|
-
else if (
|
|
13
|
-
const filteredGroups = Object.entries(dtt.config.groups).filter(([key]) => key.includes(
|
|
12
|
+
else if (selectedBranch.length === 1) {
|
|
13
|
+
const filteredGroups = Object.entries(dtt.config.groups).filter(([key]) => key.includes(selectedBranch[0]));
|
|
14
14
|
groups = Object.fromEntries(filteredGroups);
|
|
15
15
|
}
|
|
16
16
|
const groupKeys = Object.keys(groups);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
interface Props {
|
|
2
|
-
branch: string[];
|
|
3
2
|
onGroupSelected: (group: string) => void;
|
|
4
3
|
onSave: () => void;
|
|
5
4
|
}
|
|
6
|
-
export declare function TupleToolList({
|
|
5
|
+
export declare function TupleToolList({ onGroupSelected, onSave }: Props): import("react/jsx-runtime").JSX.Element | null;
|
|
7
6
|
export {};
|
|
@@ -19,19 +19,23 @@ import { TupleToolGroup } from "./TupleToolGroup";
|
|
|
19
19
|
import { AddTupleToolModal } from "./modals/AddTupleToolModal";
|
|
20
20
|
import { ConfigureTupleToolModal } from "./modals/ConfigureTupleToolModal";
|
|
21
21
|
import { useDtt } from "../providers/DttProvider";
|
|
22
|
-
export function TupleToolList({
|
|
22
|
+
export function TupleToolList({ onGroupSelected, onSave }) {
|
|
23
23
|
const metadata = useMetadata();
|
|
24
|
-
const { dtt, dirty, save, revert, removeTool } = useDtt();
|
|
24
|
+
const { dtt, dirty, save, revert, removeTool, selectedBranch } = useDtt();
|
|
25
25
|
const [showAddModal, setShowAddModal] = useState(false);
|
|
26
|
-
const [
|
|
26
|
+
const [showConfigModal, setShowConfigModal] = useState(null);
|
|
27
27
|
if (!metadata) {
|
|
28
28
|
return null;
|
|
29
29
|
}
|
|
30
|
-
const tools = dtt.listTools(
|
|
31
|
-
const filterToolTags = branch.length === 0 ? ["IParticleTupleTool", "IEventTupleTool"] : ["IParticleTupleTool"];
|
|
30
|
+
const tools = dtt.listTools(selectedBranch);
|
|
32
31
|
return (_jsxs(_Fragment, { children: [_jsxs(Card, { children: [_jsxs(Card.Header, { className: "d-flex justify-content-between align-items-start", children: [_jsxs("span", { children: [tools.length, " TupleTool", tools.length === 1 ? "" : "s"] }), _jsx(Button, { variant: "success", size: "sm", onClick: () => setShowAddModal(true), children: _jsx(PlusLg, {}) })] }), _jsx(ListGroup, { variant: "flush", children: tools.map((tool) => {
|
|
33
|
-
return (_jsxs(ListGroup.Item, { className: "d-flex justify-content-between align-items-start", variant:
|
|
34
|
-
}) })] }), showAddModal && (_jsx(AddTupleToolModal, {
|
|
32
|
+
return (_jsxs(ListGroup.Item, { className: "d-flex justify-content-between align-items-start", variant: showConfigModal?.tool.equals(tool) ? "secondary" : "", children: [_jsx(TupleToolLabel, { tool: tool }), _jsxs(ButtonGroup, { size: "sm", children: [_jsx(Button, { variant: "outline-secondary", onClick: () => setShowConfigModal({ tool, deleteIfCancelled: false }), children: _jsx(PencilSquare, {}) }), _jsx(DeleteButton, { outline: true, action: () => removeTool(selectedBranch, tool) })] })] }, tool.toString()));
|
|
33
|
+
}) })] }), showAddModal && (_jsx(AddTupleToolModal, { onClose: (newTool) => {
|
|
34
|
+
setShowAddModal(false);
|
|
35
|
+
if (newTool && newTool.class === "LoKi__Hybrid__TupleTool") {
|
|
36
|
+
setShowConfigModal({ tool: newTool, deleteIfCancelled: true });
|
|
37
|
+
}
|
|
38
|
+
} })), showConfigModal && (_jsx(ConfigureTupleToolModal, { tool: showConfigModal.tool, deleteIfCancelled: showConfigModal.deleteIfCancelled, onClose: () => setShowConfigModal(null) })), selectedBranch.length === 0 && Object.keys(dtt.config.groups).length > 0 && (_jsx(TupleToolGroup, { onGroupSelected: onGroupSelected })), _jsxs(ButtonGroup, { className: "mt-2", children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Save changes to this DecayTreeTuple" }), children: _jsx(Button, { onClick: () => {
|
|
35
39
|
save();
|
|
36
40
|
onSave();
|
|
37
41
|
}, disabled: !dirty, children: "Save" }) }), _jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Discard changes to this DecayTreeTuple" }), children: _jsx(Button, { variant: "outline-danger", onClick: revert, disabled: !dirty, children: "Revert" }) })] })] }));
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import { TupleTool } from "../../models/tupleTool";
|
|
1
2
|
interface Props {
|
|
2
|
-
|
|
3
|
-
filterToolTags: string[];
|
|
4
|
-
onClose: () => void;
|
|
3
|
+
onClose: (newTool: TupleTool | null) => void;
|
|
5
4
|
}
|
|
6
|
-
export declare function AddTupleToolModal({
|
|
5
|
+
export declare function AddTupleToolModal({ onClose }: Props): import("react/jsx-runtime").JSX.Element;
|
|
7
6
|
export {};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Button, FormControl, InputGroup, Modal } from "react-bootstrap";
|
|
3
|
-
import {
|
|
2
|
+
import { Button, ButtonGroup, FormControl, InputGroup, Modal } from "react-bootstrap";
|
|
3
|
+
import { TupleToolClassDropdown } from "../TupleToolClassDropdown";
|
|
4
4
|
import { TupleToolDocsAccordion } from "../TupleToolDocsAccordion";
|
|
5
5
|
import { useDtt } from "../../providers/DttProvider";
|
|
6
6
|
import { TupleTool } from "../../models/tupleTool";
|
|
7
7
|
import { useEffect, useRef, useState } from "react";
|
|
8
|
-
export function AddTupleToolModal({
|
|
9
|
-
const { dtt, addTool } = useDtt();
|
|
8
|
+
export function AddTupleToolModal({ onClose }) {
|
|
9
|
+
const { dtt, addTool, selectedBranch } = useDtt();
|
|
10
10
|
const nameInputRef = useRef(null);
|
|
11
11
|
const [selectedTool, setSelectedTool] = useState(null);
|
|
12
12
|
useEffect(() => {
|
|
13
13
|
nameInputRef.current?.focus();
|
|
14
14
|
}, [selectedTool]);
|
|
15
|
-
return (_jsxs(Modal, { show: true, onHide: onClose, children: [_jsx(Modal.Header, { closeButton: true, children: "Add TupleTool" }), _jsxs(Modal.Body, { className: "gap-3 d-flex flex-column", children: [_jsxs(InputGroup, { hasValidation: true, children: [_jsx(
|
|
15
|
+
return (_jsxs(Modal, { show: true, onHide: () => onClose(null), children: [_jsx(Modal.Header, { closeButton: true, children: "Add TupleTool" }), _jsxs(Modal.Body, { className: "gap-3 d-flex flex-column", children: [_jsxs(InputGroup, { hasValidation: true, children: [_jsx(TupleToolClassDropdown, { onChange: (option) => {
|
|
16
16
|
if (option) {
|
|
17
17
|
setSelectedTool((prev) => new TupleTool(option.value, prev?.name || ""));
|
|
18
18
|
}
|
|
@@ -20,13 +20,13 @@ export function AddTupleToolModal({ branch, filterToolTags, onClose }) {
|
|
|
20
20
|
// Remove all non-word characters
|
|
21
21
|
const name = event.target.value.replaceAll(/[^\w]/g, "");
|
|
22
22
|
setSelectedTool((prev) => new TupleTool(prev.class, name));
|
|
23
|
-
}, value: selectedTool?.name || "", disabled: !selectedTool, isInvalid: !!selectedTool && dtt.toolExists(selectedTool,
|
|
23
|
+
}, value: selectedTool?.name || "", disabled: !selectedTool, isInvalid: !!selectedTool && dtt.toolExists(selectedTool, selectedBranch), onKeyDown: (event) => {
|
|
24
24
|
if (event.key === "Enter") {
|
|
25
|
-
addTool(
|
|
26
|
-
onClose();
|
|
25
|
+
addTool(selectedBranch, selectedTool);
|
|
26
|
+
onClose(selectedTool);
|
|
27
27
|
}
|
|
28
|
-
} }), selectedTool && (_jsxs(FormControl.Feedback, { type: "invalid", children: ["A ", selectedTool.class, " with the name \"", selectedTool.name, "\" already exists"] }))] }), selectedTool && _jsx(TupleToolDocsAccordion, { toolClass: selectedTool.class }),
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
} }), selectedTool && (_jsxs(FormControl.Feedback, { type: "invalid", children: ["A ", selectedTool.class, " with the name \"", selectedTool.name, "\" already exists"] }))] }), selectedTool && _jsx(TupleToolDocsAccordion, { toolClass: selectedTool.class }), _jsxs(ButtonGroup, { className: "align-self-end", children: [_jsx(Button, { variant: "outline-dark", onClick: () => onClose(null), children: "Cancel" }), _jsx(Button, { disabled: !selectedTool || dtt.toolExists(selectedTool, selectedBranch), onClick: () => {
|
|
29
|
+
addTool(selectedBranch, selectedTool);
|
|
30
|
+
onClose(selectedTool);
|
|
31
|
+
}, children: "Add tool" })] })] })] }));
|
|
32
32
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TupleTool } from "../../models/tupleTool";
|
|
2
2
|
interface Props {
|
|
3
|
-
branch: string[];
|
|
4
3
|
tool: TupleTool;
|
|
4
|
+
deleteIfCancelled: boolean;
|
|
5
5
|
onClose: () => void;
|
|
6
6
|
}
|
|
7
|
-
export declare function ConfigureTupleToolModal({
|
|
7
|
+
export declare function ConfigureTupleToolModal({ tool, deleteIfCancelled, onClose }: Props): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Button, Col, Form, Modal, OverlayTrigger, Row, Tooltip } from "react-bootstrap";
|
|
2
|
+
import { Button, ButtonGroup, Col, Form, Modal, OverlayTrigger, Row, Tooltip } from "react-bootstrap";
|
|
3
3
|
import { StrParamInput } from "../tupleToolParams/StrParamInput";
|
|
4
4
|
import { BoolParamInput } from "../tupleToolParams/BoolParamInput";
|
|
5
5
|
import { NumParamInput } from "../tupleToolParams/NumParamInput";
|
|
@@ -8,36 +8,53 @@ import { DictParamInput } from "../tupleToolParams/DictParamInput";
|
|
|
8
8
|
import { QuestionCircle } from "react-bootstrap-icons";
|
|
9
9
|
import { TupleToolDocsAccordion } from "../TupleToolDocsAccordion";
|
|
10
10
|
import { useDtt } from "../../providers/DttProvider";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
import { config } from "../../config";
|
|
12
|
+
import { toast } from "react-toastify";
|
|
13
|
+
export function ConfigureTupleToolModal({ tool, deleteIfCancelled, onClose }) {
|
|
14
|
+
const { dtt, updateToolParam, removeTool, selectedBranch } = useDtt();
|
|
15
|
+
const toolConfig = dtt.getToolConfig(selectedBranch, tool);
|
|
16
|
+
return (_jsxs(Modal, { show: true, size: "xl", fullscreen: "lg-down", backdrop: "static", keyboard: false, children: [_jsxs(Modal.Header, { children: ["Configure ", tool.toString()] }), _jsxs(Modal.Body, { className: "gap-3 d-flex flex-column", children: [_jsx(Form, { children: Object.entries(toolConfig).map(([paramName, param]) => {
|
|
14
17
|
const { description, type, default: defaultValue, value } = param;
|
|
15
18
|
let inputComponent;
|
|
16
19
|
switch (type) {
|
|
17
20
|
case "str":
|
|
18
|
-
inputComponent = (_jsx(StrParamInput, { value: value, defaultValue: defaultValue, onChange: (newValue) => updateToolParam(
|
|
21
|
+
inputComponent = (_jsx(StrParamInput, { value: value, defaultValue: defaultValue, onChange: (newValue) => updateToolParam(selectedBranch, tool, paramName, newValue), param: paramName }));
|
|
19
22
|
break;
|
|
20
23
|
case "bool":
|
|
21
|
-
inputComponent = (_jsx(BoolParamInput, { value: value, onChange: (newValue) => updateToolParam(
|
|
24
|
+
inputComponent = (_jsx(BoolParamInput, { value: value, onChange: (newValue) => updateToolParam(selectedBranch, tool, paramName, newValue) }));
|
|
22
25
|
break;
|
|
23
26
|
case "int":
|
|
24
27
|
case "uint":
|
|
25
28
|
case "float":
|
|
26
|
-
inputComponent = (_jsx(NumParamInput, { type: type, value: value, defaultValue: defaultValue, onChange: (newValue) => updateToolParam(
|
|
29
|
+
inputComponent = (_jsx(NumParamInput, { type: type, value: value, defaultValue: defaultValue, onChange: (newValue) => updateToolParam(selectedBranch, tool, paramName, newValue) }));
|
|
27
30
|
break;
|
|
28
31
|
case "text":
|
|
29
|
-
inputComponent = (_jsx(ListParamInput, { initialValues: value, defaultValues: defaultValue, newItemValue: "", onChange: (newValue) => updateToolParam(
|
|
32
|
+
inputComponent = (_jsx(ListParamInput, { initialValues: value, defaultValues: defaultValue, newItemValue: "", onChange: (newValue) => updateToolParam(selectedBranch, tool, paramName, newValue), buildInnerInput: (props) => _jsx(StrParamInput, { ...props, param: paramName }) }));
|
|
30
33
|
break;
|
|
31
34
|
case "[int]":
|
|
32
35
|
case "[uint]":
|
|
33
36
|
case "[float]":
|
|
34
|
-
inputComponent = (_jsx(ListParamInput, { initialValues: value, defaultValues: defaultValue, newItemValue: 0, onChange: (newValue) => updateToolParam(
|
|
37
|
+
inputComponent = (_jsx(ListParamInput, { initialValues: value, defaultValues: defaultValue, newItemValue: 0, onChange: (newValue) => updateToolParam(selectedBranch, tool, paramName, newValue), buildInnerInput: (props) => (_jsx(NumParamInput, { type: type.slice(1, -1), ...props })) }));
|
|
35
38
|
break;
|
|
36
39
|
case "{str:str}":
|
|
37
40
|
case "{str:[str]}":
|
|
38
|
-
inputComponent = (_jsx(DictParamInput, { value: value, onChange: (newValue) => updateToolParam(
|
|
41
|
+
inputComponent = (_jsx(DictParamInput, { value: value, onChange: (newValue) => updateToolParam(selectedBranch, tool, paramName, newValue) }));
|
|
39
42
|
break;
|
|
40
43
|
}
|
|
41
44
|
return (_jsxs(Form.Group, { as: Row, className: "mb-2", children: [_jsxs(Form.Label, { column: true, sm: 3, children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: description }), children: _jsx(QuestionCircle, {}) }), _jsxs("code", { children: [" ", paramName, " "] })] }), _jsx(Form.Label, { column: true, sm: 2, children: _jsxs("code", { children: [" ", type, " "] }) }), _jsx(Col, { sm: 7, children: inputComponent })] }, paramName));
|
|
42
|
-
}) })
|
|
45
|
+
}) }), _jsx(TupleToolDocsAccordion, { toolClass: tool.class }), _jsxs(ButtonGroup, { className: "align-self-end", children: [_jsx(Button, { variant: deleteIfCancelled ? "outline-danger" : "outline-dark", onClick: () => {
|
|
46
|
+
if (deleteIfCancelled) {
|
|
47
|
+
removeTool(selectedBranch, tool);
|
|
48
|
+
}
|
|
49
|
+
onClose();
|
|
50
|
+
}, children: "Cancel" }), _jsx(Button, { onClick: () => {
|
|
51
|
+
if (Object.keys(config.tupleToolValidation).includes(tool.class)) {
|
|
52
|
+
const errorMsg = config.tupleToolValidation[tool.class].validate(toolConfig);
|
|
53
|
+
if (errorMsg) {
|
|
54
|
+
toast(errorMsg, { type: "error" });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
onClose();
|
|
59
|
+
}, children: "Save & close" })] })] })] }));
|
|
43
60
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DagreLayoutOptions } from "cytoscape-dagre";
|
|
2
|
+
import { ToolConfig } from "./models/dtt";
|
|
1
3
|
export declare const config: {
|
|
2
4
|
metadata_baseurl: string;
|
|
3
5
|
metadata_files: {
|
|
@@ -21,58 +23,27 @@ export declare const config: {
|
|
|
21
23
|
lifetime: string;
|
|
22
24
|
};
|
|
23
25
|
dttGraphOptions: {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
height: string;
|
|
27
|
+
zoomMin: number;
|
|
28
|
+
zoomMax: number;
|
|
29
|
+
zoomStep: number;
|
|
30
|
+
layout: DagreLayoutOptions;
|
|
29
31
|
nodes: {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
strokeWidth: number;
|
|
35
|
-
background: string;
|
|
36
|
-
};
|
|
37
|
-
color: {
|
|
38
|
-
background: string;
|
|
39
|
-
highlight: {
|
|
40
|
-
background: string;
|
|
41
|
-
};
|
|
42
|
-
hover: {
|
|
43
|
-
background: string;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
scaling: {
|
|
47
|
-
min: number;
|
|
48
|
-
max: number;
|
|
49
|
-
};
|
|
50
|
-
value: number;
|
|
51
|
-
shape: string;
|
|
52
|
-
shapeProperties: {
|
|
53
|
-
useBorderWithImage: boolean;
|
|
54
|
-
useImageSize: boolean;
|
|
55
|
-
};
|
|
32
|
+
draggable: boolean;
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
hoverColor: string;
|
|
56
36
|
};
|
|
57
37
|
edges: {
|
|
58
38
|
color: string;
|
|
59
39
|
width: number;
|
|
60
|
-
selectionWidth: number;
|
|
61
|
-
hoverWidth: number;
|
|
62
|
-
};
|
|
63
|
-
physics: {
|
|
64
|
-
enabled: boolean;
|
|
65
40
|
};
|
|
66
|
-
interaction: {
|
|
67
|
-
dragNodes: boolean;
|
|
68
|
-
multiselect: boolean;
|
|
69
|
-
selectConnectedEdges: boolean;
|
|
70
|
-
hoverConnectedEdges: boolean;
|
|
71
|
-
hover: boolean;
|
|
72
|
-
zoomView: boolean;
|
|
73
|
-
};
|
|
74
|
-
height: string;
|
|
75
|
-
autoResize: boolean;
|
|
76
|
-
width: string;
|
|
77
41
|
};
|
|
42
|
+
tupleToolValidation: TupleToolValidation;
|
|
78
43
|
};
|
|
44
|
+
interface TupleToolValidation {
|
|
45
|
+
[toolClass: string]: {
|
|
46
|
+
validate: (config: ToolConfig) => string | null;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export {};
|
package/dist/config.js
CHANGED
|
@@ -21,52 +21,39 @@ export const config = {
|
|
|
21
21
|
lifetime: "secondary",
|
|
22
22
|
},
|
|
23
23
|
dttGraphOptions: {
|
|
24
|
+
height: "500px",
|
|
25
|
+
zoomMin: 0.2,
|
|
26
|
+
zoomMax: 3.0,
|
|
27
|
+
zoomStep: 0.018,
|
|
24
28
|
layout: {
|
|
25
|
-
|
|
29
|
+
rankDir: "TB", // top -> bottom
|
|
30
|
+
nodeSep: 50,
|
|
31
|
+
rankSep: 80,
|
|
32
|
+
animate: false,
|
|
26
33
|
},
|
|
27
34
|
nodes: {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
strokeWidth: 20,
|
|
33
|
-
background: "white",
|
|
34
|
-
},
|
|
35
|
-
color: {
|
|
36
|
-
background: "white",
|
|
37
|
-
highlight: { background: "white" },
|
|
38
|
-
hover: { background: "#eee" },
|
|
39
|
-
},
|
|
40
|
-
scaling: {
|
|
41
|
-
min: 32,
|
|
42
|
-
max: 32,
|
|
43
|
-
},
|
|
44
|
-
value: 1,
|
|
45
|
-
shape: "image",
|
|
46
|
-
shapeProperties: {
|
|
47
|
-
useBorderWithImage: true,
|
|
48
|
-
useImageSize: false,
|
|
49
|
-
},
|
|
35
|
+
draggable: false,
|
|
36
|
+
width: 80,
|
|
37
|
+
height: 30,
|
|
38
|
+
hoverColor: "royalblue",
|
|
50
39
|
},
|
|
51
40
|
edges: {
|
|
52
|
-
color: "
|
|
41
|
+
color: "#222",
|
|
53
42
|
width: 1,
|
|
54
|
-
selectionWidth: 0,
|
|
55
|
-
hoverWidth: 0,
|
|
56
43
|
},
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
44
|
+
},
|
|
45
|
+
tupleToolValidation: {
|
|
46
|
+
["LoKi__Hybrid__TupleTool"]: {
|
|
47
|
+
validate: (config) => {
|
|
48
|
+
const hasVariables = Object.entries(config["Variables"].value).length > 0;
|
|
49
|
+
const hasBoolVariables = Object.entries(config["BoolVariables"].value).length > 0;
|
|
50
|
+
const hasFloatVariables = Object.entries(config["FloatVariables"].value).length > 0;
|
|
51
|
+
const hasIntVariables = Object.entries(config["IntVariables"].value).length > 0;
|
|
52
|
+
if (!hasVariables && !hasBoolVariables && !hasFloatVariables && !hasIntVariables) {
|
|
53
|
+
return "Please add at least one variable";
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
},
|
|
67
57
|
},
|
|
68
|
-
height: "500px",
|
|
69
|
-
autoResize: true,
|
|
70
|
-
width: "100%",
|
|
71
58
|
},
|
|
72
59
|
};
|
|
@@ -9,7 +9,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
9
9
|
* granted to it by virtue of its status as an Intergovernmental Organization *
|
|
10
10
|
* or submit itself to any jurisdiction. *
|
|
11
11
|
\*****************************************************************************/
|
|
12
|
-
import { Button,
|
|
12
|
+
import { Button, OverlayTrigger, Stack, Tooltip } from "react-bootstrap";
|
|
13
13
|
import { useNavigate } from "react-router-dom";
|
|
14
14
|
import { useMetadata } from "../providers/MetadataProvider";
|
|
15
15
|
import { DecayTreeCard } from "../components/DecayTreeCard";
|
|
@@ -41,18 +41,18 @@ export function DecayTreeConfigPage() {
|
|
|
41
41
|
return _jsx(LoadingIndicator, { height: "70vh" });
|
|
42
42
|
}
|
|
43
43
|
const rowsToEdit = rows.filter((row) => !!row.editTree);
|
|
44
|
-
return (_jsxs(_Fragment, { children: [_jsx("h3", { children: "DecayTreeTuple configuration" }), _jsx(Stack, { gap: 5, children: rowsToEdit.map((row) => (_jsx(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
return (_jsxs(_Fragment, { children: [_jsx("h3", { className: "mb-4", children: "DecayTreeTuple configuration" }), _jsx(Stack, { gap: 5, children: rowsToEdit.map((row) => (_jsx(DttProvider, { metadata: metadata, decay: row.decay, initialConfig: row.dtt.config, children: _jsx(DecayTreeCard, { onConfigSaved: (newConfig) => {
|
|
45
|
+
updateRow(row.id, (r) => ({ ...r, dtt: new Dtt(newConfig, {}) }));
|
|
46
|
+
}, onDirtyUpdated: (dttName, dirty) => setDirtyDtts((prev) => {
|
|
47
|
+
if (dirty) {
|
|
48
|
+
return new Set(prev.add(dttName));
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const newSet = new Set(prev);
|
|
52
|
+
newSet.delete(dttName);
|
|
53
|
+
return newSet;
|
|
54
|
+
}
|
|
55
|
+
}) }) }, row.id))) }), _jsx(OverlayTrigger, { placement: "right", overlay: _jsx(Tooltip, { children: "Return to Stripping line and dataset selection" }), children: _jsx(Button, { className: "mt-2 mb-4", type: "submit", onClick: () => {
|
|
56
56
|
if (dirtyDtts.size > 0) {
|
|
57
57
|
if (!confirm("You have unsaved changes. Are you sure you want to discard them?")) {
|
|
58
58
|
return;
|
|
@@ -12,7 +12,6 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
12
12
|
import { useEffect, useState } from "react";
|
|
13
13
|
import { Alert, Button, Col, Row, Stack } from "react-bootstrap";
|
|
14
14
|
import { Download, Send } from "react-bootstrap-icons";
|
|
15
|
-
import { useLocation } from "react-router-dom";
|
|
16
15
|
import { downloadZip, processProductionFiles } from "../utils/utils";
|
|
17
16
|
import { DeleteButton } from "../components/DeleteButton";
|
|
18
17
|
import { useMetadata } from "../providers/MetadataProvider";
|
|
@@ -29,7 +28,6 @@ import { RequestEmailInput } from "../components/RequestEmailInput";
|
|
|
29
28
|
import { RequestButtonGroup } from "../components/RequestButtonGroup";
|
|
30
29
|
import { useWizardConfig } from "../providers/WizardConfigProvider";
|
|
31
30
|
export function RequestPage() {
|
|
32
|
-
const location = useLocation();
|
|
33
31
|
const metadata = useMetadata();
|
|
34
32
|
const { rows, setRows, generateAllFiles } = useRows();
|
|
35
33
|
const { emailIsKnown, productionName, setProductionName, validation, showErrors, clearAll, trySubmit } = useRequest();
|
|
@@ -38,17 +36,15 @@ export function RequestPage() {
|
|
|
38
36
|
const [prodUploadLoading, setProdUploadLoading] = useState(false);
|
|
39
37
|
const [requestSubmitted, setRequestSubmitted] = useState(false);
|
|
40
38
|
const [showReasonForRequestModal, setShowReasonForRequestModal] = useState(false);
|
|
39
|
+
const [checkedIfCloneNeeded, setCheckedIfCloneNeeded] = useState(false);
|
|
41
40
|
useEffect(() => {
|
|
42
41
|
if (!metadata) {
|
|
43
42
|
return;
|
|
44
43
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (!dttConfigsString) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
44
|
+
// If the localStorage has been set, we should clone the configuration
|
|
45
|
+
const infoYamlString = localStorage.getItem("infoYamlToClone");
|
|
46
|
+
const dttConfigsString = localStorage.getItem("dttConfigsToClone");
|
|
47
|
+
if (dttConfigsString) {
|
|
52
48
|
setProdUploadLoading(true);
|
|
53
49
|
setRows([]);
|
|
54
50
|
try {
|
|
@@ -69,7 +65,8 @@ export function RequestPage() {
|
|
|
69
65
|
localStorage.removeItem("dttConfigsToClone");
|
|
70
66
|
}
|
|
71
67
|
}
|
|
72
|
-
|
|
68
|
+
setCheckedIfCloneNeeded(true);
|
|
69
|
+
}, [metadata]);
|
|
73
70
|
const handlePrimaryAction = async () => {
|
|
74
71
|
if (trySubmit()) {
|
|
75
72
|
if (variant === "standalone") {
|
|
@@ -81,7 +78,7 @@ export function RequestPage() {
|
|
|
81
78
|
}
|
|
82
79
|
}
|
|
83
80
|
};
|
|
84
|
-
if (!metadata) {
|
|
81
|
+
if (!metadata || !checkedIfCloneNeeded) {
|
|
85
82
|
return _jsx(LoadingIndicator, { height: "70vh" });
|
|
86
83
|
}
|
|
87
84
|
if (requestSubmitted) {
|
|
@@ -93,11 +90,11 @@ export function RequestPage() {
|
|
|
93
90
|
setRequestSubmitted(true);
|
|
94
91
|
onRequestSubmitted?.();
|
|
95
92
|
}
|
|
96
|
-
} })), showProdUploadModal && _jsx(UploadDttConfigModal, { onClose: () => setShowProdUploadModal(false) }), _jsxs("div", { className: "d-flex flex-column gap-
|
|
93
|
+
} })), showProdUploadModal && _jsx(UploadDttConfigModal, { onClose: () => setShowProdUploadModal(false) }), _jsxs("div", { className: "d-flex flex-column gap-4", children: [rows.map((row) => (_jsx(RowProvider, { row: row, children: _jsx(RequestRow, {}, row.id) }, row.id))), prodUploadLoading && _jsx(ConfigFilesUploadingAlert, {}), showErrors &&
|
|
97
94
|
(rows.length === 0 ||
|
|
98
95
|
!validation.allRowsHaveDtt ||
|
|
99
96
|
!validation.allRowsHaveStrippingLine ||
|
|
100
97
|
!validation.allRowsHavePaths) && (_jsx(Alert, { variant: "danger", className: "mb-0", children: rows.length === 0
|
|
101
98
|
? "Please select at least one decay"
|
|
102
|
-
: "Please name all DecayTreeTuples and select at least one stripping line and bookkeeping path for each decay" })), _jsx(Row, { children: _jsx(Col, { xs: "auto", children: _jsx(RequestButtonGroup, {}) }) }), _jsx(Row, { children: _jsxs(Col, { xs:
|
|
99
|
+
: "Please name all DecayTreeTuples and select at least one stripping line and bookkeeping path for each decay" })), _jsx(Row, { children: _jsx(Col, { xs: "auto", children: _jsx(RequestButtonGroup, {}) }) }), _jsx(Row, { children: _jsxs(Col, { xs: "auto", children: [_jsx(ProductionNameInput, {}), !emailIsKnown && _jsx(RequestEmailInput, {}), _jsxs(Stack, { direction: "horizontal", gap: 1, className: "mt-3 mb-3", children: [_jsxs(Button, { className: "align-items-center d-flex gap-1", onClick: () => void handlePrimaryAction(), children: [variant === "standalone" ? _jsx(Download, {}) : _jsx(Send, {}), variant === "standalone" ? "Download" : "Submit"] }), _jsx(DeleteButton, { action: clearAll, disabled: validation.isEmptySession, outline: undefined, children: "Clear all" })] })] }) })] })] }));
|
|
103
100
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useCallback, useContext, useReducer } from "react";
|
|
2
|
+
import { createContext, useCallback, useContext, useReducer, useState } from "react";
|
|
3
3
|
import Dtt from "../models/dtt";
|
|
4
4
|
const DttContext = createContext(null);
|
|
5
5
|
function reducer(state, action) {
|
|
@@ -31,6 +31,7 @@ function reducer(state, action) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
export function DttProvider({ initialConfig, decay, metadata, children }) {
|
|
34
|
+
const [selectedBranch, setSelectedBranch] = useState([]);
|
|
34
35
|
const [state, dispatch] = useReducer(reducer, null, () => ({
|
|
35
36
|
dtt: new Dtt(structuredClone(initialConfig), metadata.tupleTools.tupleTools),
|
|
36
37
|
decay,
|
|
@@ -66,6 +67,8 @@ export function DttProvider({ initialConfig, decay, metadata, children }) {
|
|
|
66
67
|
removeTool,
|
|
67
68
|
save,
|
|
68
69
|
revert,
|
|
70
|
+
selectedBranch,
|
|
71
|
+
setSelectedBranch,
|
|
69
72
|
};
|
|
70
73
|
return _jsx(DttContext.Provider, { value: value, children: children });
|
|
71
74
|
}
|
package/dist/utils/utils.js
CHANGED
|
@@ -23,18 +23,41 @@ const parseInput = (decay, input) => {
|
|
|
23
23
|
versions: decay.lines[`${stream}/${line}`],
|
|
24
24
|
};
|
|
25
25
|
};
|
|
26
|
+
const getDttName = (value) => {
|
|
27
|
+
const basename = value.split("/").pop() ?? value;
|
|
28
|
+
return basename.replace(/\.py$/i, "");
|
|
29
|
+
};
|
|
30
|
+
const getConfigOrderFromInfoYaml = (infoYaml) => {
|
|
31
|
+
const order = new Map();
|
|
32
|
+
if (!infoYaml) {
|
|
33
|
+
return order;
|
|
34
|
+
}
|
|
35
|
+
for (const [key, jobConfig] of Object.entries(infoYaml)) {
|
|
36
|
+
if (key === "defaults") {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const job = jobConfig;
|
|
40
|
+
for (const option of job.options) {
|
|
41
|
+
const dttName = getDttName(option);
|
|
42
|
+
if (!order.has(dttName)) {
|
|
43
|
+
order.set(dttName, order.size);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return order;
|
|
48
|
+
};
|
|
26
49
|
const addBkPathsToRows = (infoYaml, rows) => {
|
|
27
50
|
for (const row of rows) {
|
|
28
51
|
if (!row.dtt?.config.name) {
|
|
29
52
|
continue;
|
|
30
53
|
}
|
|
31
|
-
const dttName = row.dtt.config.name
|
|
54
|
+
const dttName = getDttName(row.dtt.config.name);
|
|
32
55
|
for (const [key, jobConfig] of Object.entries(infoYaml)) {
|
|
33
56
|
if (key === "defaults") {
|
|
34
57
|
continue;
|
|
35
58
|
}
|
|
36
59
|
const job = jobConfig;
|
|
37
|
-
if (job.options.some((option) => option
|
|
60
|
+
if (job.options.some((option) => getDttName(option) === dttName)) {
|
|
38
61
|
row.paths.push(job.input.bk_query);
|
|
39
62
|
}
|
|
40
63
|
}
|
|
@@ -516,7 +539,17 @@ export async function parseProductionFiles(files, metadata) {
|
|
|
516
539
|
export function processProductionFiles(metadata, configs, infoYaml) {
|
|
517
540
|
const rows = [];
|
|
518
541
|
const emailsToInform = [];
|
|
519
|
-
|
|
542
|
+
const configOrder = getConfigOrderFromInfoYaml(infoYaml);
|
|
543
|
+
const orderedConfigs = [...configs].sort((a, b) => {
|
|
544
|
+
const aName = getDttName(a.name ?? "");
|
|
545
|
+
const bName = getDttName(b.name ?? "");
|
|
546
|
+
const aOrder = configOrder.get(aName) ?? Number.MAX_SAFE_INTEGER;
|
|
547
|
+
const bOrder = configOrder.get(bName) ?? Number.MAX_SAFE_INTEGER;
|
|
548
|
+
if (aOrder !== bOrder)
|
|
549
|
+
return aOrder - bOrder;
|
|
550
|
+
return aName.localeCompare(bName);
|
|
551
|
+
});
|
|
552
|
+
for (const config of orderedConfigs) {
|
|
520
553
|
const decay = Object.values(metadata.decays).find((d) => d.descriptors.template === config.descriptorTemplate);
|
|
521
554
|
if (!decay) {
|
|
522
555
|
return `[${config.name}] Unknown decay '${config.descriptorTemplate}'`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lhcb-ntuple-wizard",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "An application to access large-scale open data from LHCb",
|
|
5
5
|
"url": "https://gitlab.cern.ch/lhcb-dpa/wp6-analysis-preservation-and-open-data/lhcb-ntuple-wizard-frontend/issues",
|
|
6
6
|
"private": false,
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
"@mathjax/src": "4.1.0",
|
|
20
20
|
"better-react-mathjax": "2.3.0",
|
|
21
21
|
"bootstrap": "5.3.8",
|
|
22
|
+
"cytoscape": "3.33.1",
|
|
23
|
+
"cytoscape-dagre": "^2.5.0",
|
|
22
24
|
"dompurify": "^3.3.1",
|
|
23
25
|
"email-validator": "2.0.4",
|
|
24
26
|
"js-yaml": "4.1.1",
|
|
@@ -29,9 +31,10 @@
|
|
|
29
31
|
"pako": "2.1.0",
|
|
30
32
|
"react-bootstrap": "2.10.10",
|
|
31
33
|
"react-bootstrap-icons": "1.11.6",
|
|
32
|
-
"react-
|
|
34
|
+
"react-cytoscapejs": "^2.0.0",
|
|
33
35
|
"react-infinite-scroll-component": "^6.1.1",
|
|
34
36
|
"react-select": "5.10.2",
|
|
37
|
+
"react-toastify": "^11.0.5",
|
|
35
38
|
"typia": "^11.0.3"
|
|
36
39
|
},
|
|
37
40
|
"peerDependencies": {
|
|
@@ -69,19 +72,21 @@
|
|
|
69
72
|
"@babel/preset-react": "7.27.1",
|
|
70
73
|
"@eslint/js": "^9.17.0",
|
|
71
74
|
"@kennethwkz/unplugin-typia": "^2.6.7",
|
|
75
|
+
"@types/cytoscape-dagre": "^2.3.4",
|
|
72
76
|
"@types/jest": "^30.0.0",
|
|
73
77
|
"@types/js-yaml": "^4.0.9",
|
|
74
78
|
"@types/lodash": "^4.17.21",
|
|
75
79
|
"@types/lodash.memoize": "^4.1.9",
|
|
76
80
|
"@types/pako": "^2.0.4",
|
|
77
81
|
"@types/react": "^19.2.7",
|
|
82
|
+
"@types/react-cytoscapejs": "^1.2.6",
|
|
78
83
|
"@types/react-dom": "^19.2.3",
|
|
79
84
|
"@types/react-router-dom": "^5.3.3",
|
|
80
85
|
"@types/vis": "^4.21.27",
|
|
81
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
82
|
-
"@typescript-eslint/parser": "^8.
|
|
86
|
+
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
|
87
|
+
"@typescript-eslint/parser": "^8.56.0",
|
|
83
88
|
"@vitejs/plugin-react": "5.1.2",
|
|
84
|
-
"eslint": "^9.
|
|
89
|
+
"eslint": "^9.39.3",
|
|
85
90
|
"eslint-plugin-react": "^7.37.5",
|
|
86
91
|
"globals": "^16.0.0",
|
|
87
92
|
"react": "^19.2.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function DttNameInput(): import("react/jsx-runtime").JSX.Element | null;
|