sample-ui-component-library 0.0.0-beta
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/.storybook/main.js +22 -0
- package/.storybook/manager.js +6 -0
- package/.storybook/preview.js +13 -0
- package/LICENSE +201 -0
- package/README.md +37 -0
- package/babel.config.js +6 -0
- package/dist/cjs/index.js +47 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.js +47 -0
- package/dist/esm/index.js.map +1 -0
- package/package.json +72 -0
- package/rollup.config.mjs +37 -0
- package/src/components/FlowDiagram/DagreLayout.js +33 -0
- package/src/components/FlowDiagram/FlowDiagram.jsx +64 -0
- package/src/components/FlowDiagram/FlowDiagram.scss +0 -0
- package/src/components/FlowDiagram/helper.js +59 -0
- package/src/components/FlowDiagram/index.js +1 -0
- package/src/components/StackList/StackList.jsx +92 -0
- package/src/components/StackList/StackList.scss +51 -0
- package/src/components/StackList/index.js +1 -0
- package/src/components/Viewer/MonacoInstance/MonacoInstance.jsx +46 -0
- package/src/components/Viewer/MonacoInstance/MonacoInstance.scss +0 -0
- package/src/components/Viewer/Tabs/Tab/Tab.jsx +51 -0
- package/src/components/Viewer/Tabs/Tab/Tab.scss +21 -0
- package/src/components/Viewer/Tabs/Tabs.jsx +159 -0
- package/src/components/Viewer/Tabs/Tabs.scss +50 -0
- package/src/components/Viewer/Viewer.jsx +84 -0
- package/src/components/Viewer/Viewer.scss +25 -0
- package/src/components/Viewer/index.js +1 -0
- package/src/index.js +3 -0
- package/src/stories/FlowDiagram.scss +7 -0
- package/src/stories/FlowDiagram.stories.js +29 -0
- package/src/stories/StackList.stories.js +89 -0
- package/src/stories/StackListStories.scss +13 -0
- package/src/stories/Viewer.stories.js +40 -0
- package/src/stories/ViewerStories.scss +7 -0
- package/src/stories/data/filetree.json +1 -0
- package/src/stories/data/flow/SampleTree.json +8 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import {ChevronDown} from "react-bootstrap-icons";
|
|
5
|
+
import {Tab} from "./Tab/Tab"
|
|
6
|
+
|
|
7
|
+
import Dropdown from 'react-bootstrap/Dropdown';
|
|
8
|
+
|
|
9
|
+
import 'bootstrap/dist/css/bootstrap.css';
|
|
10
|
+
import "./Tabs.scss";
|
|
11
|
+
|
|
12
|
+
// Dropdown needs access to the DOM node in order to position the Menu
|
|
13
|
+
const SelectFileToggle = React.forwardRef(({ children, onClick }, ref) => (
|
|
14
|
+
<a ref={ref} onClick={(e) => { e.preventDefault(); onClick(e); }}>
|
|
15
|
+
{children}
|
|
16
|
+
</a>
|
|
17
|
+
));
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Renders the tabs component.
|
|
21
|
+
*
|
|
22
|
+
* The tabs component accepts a list of files which have been flattened from
|
|
23
|
+
* its system tree structure with a unique id.
|
|
24
|
+
*
|
|
25
|
+
* It allows you to add a tab, close a tab and select add new tabs using the
|
|
26
|
+
* file drop down.
|
|
27
|
+
*
|
|
28
|
+
* @return {JSX}
|
|
29
|
+
*/
|
|
30
|
+
export const Tabs = ({files, selectFile, systemTree}) => {
|
|
31
|
+
|
|
32
|
+
const [activeTab, setActiveTab] = useState(null);
|
|
33
|
+
const [tabsList, setTabsList] = useState([]);
|
|
34
|
+
|
|
35
|
+
const selectTab = (e, file) => {
|
|
36
|
+
setActiveTab(file.key);
|
|
37
|
+
selectFile(file.key)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const addTab = (key) => {
|
|
41
|
+
const currTabsList = [...tabsList];
|
|
42
|
+
|
|
43
|
+
let hasKey = currTabsList.find((tab) => tab.key === key)
|
|
44
|
+
|
|
45
|
+
if (hasKey) {
|
|
46
|
+
// Set the active tab and select the file.
|
|
47
|
+
setActiveTab(key);
|
|
48
|
+
selectFile(key);
|
|
49
|
+
} else {
|
|
50
|
+
// Add tab to the tabs list.
|
|
51
|
+
files.forEach((file, index) => {
|
|
52
|
+
if (file.key === key) {
|
|
53
|
+
currTabsList.push(file);
|
|
54
|
+
setActiveTab(file.key);
|
|
55
|
+
selectFile(file.key);
|
|
56
|
+
setTabsList(currTabsList);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const closeTab = (e, file) => {
|
|
63
|
+
e.stopPropagation();
|
|
64
|
+
|
|
65
|
+
// Get index of file in tabs list
|
|
66
|
+
const index = tabsList.indexOf(file);
|
|
67
|
+
|
|
68
|
+
// Remove the closed tab
|
|
69
|
+
const currTabsList = [...tabsList];
|
|
70
|
+
currTabsList.splice(index, 1);
|
|
71
|
+
setTabsList(currTabsList);
|
|
72
|
+
|
|
73
|
+
// Select new tab
|
|
74
|
+
if (currTabsList.length === 0) {
|
|
75
|
+
selectFile(null);
|
|
76
|
+
setActiveTab(null);
|
|
77
|
+
} else {
|
|
78
|
+
// If its the last tab, use the last tab in the new tabs list.
|
|
79
|
+
const newIndex = (index >= currTabsList.length)?currTabsList.length - 1:index;
|
|
80
|
+
setActiveTab(currTabsList[newIndex].key);
|
|
81
|
+
selectFile(currTabsList[newIndex].key);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const generateDropdown = () => {
|
|
86
|
+
const items = [];
|
|
87
|
+
|
|
88
|
+
if (!systemTree) {
|
|
89
|
+
return items;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Build files list from system
|
|
93
|
+
for (const program in systemTree) {
|
|
94
|
+
items.push(<Dropdown.Header key={`header-${program}`}>{program}</Dropdown.Header>);
|
|
95
|
+
for (const fileName in systemTree[program]) {
|
|
96
|
+
const key = program + "_" + fileName;
|
|
97
|
+
items.push(
|
|
98
|
+
<Dropdown.Item key={key} onClick={() => addTab(key)}>{fileName}</Dropdown.Item>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
items.push(<Dropdown.Divider key={`divider-${program}`} />);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Remove last divider
|
|
105
|
+
items.pop();
|
|
106
|
+
|
|
107
|
+
return items;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (files && files.length > 0) {
|
|
112
|
+
const numFiles = Math.floor(Math.random() * files.length) + 1;
|
|
113
|
+
const newFiles = files.slice(0, numFiles);
|
|
114
|
+
setTabsList(newFiles);
|
|
115
|
+
const randomFileIndex = Math.floor(Math.random() * newFiles.length);
|
|
116
|
+
setActiveTab(newFiles[randomFileIndex].key);
|
|
117
|
+
selectFile(newFiles[randomFileIndex].key);
|
|
118
|
+
}
|
|
119
|
+
}, [files]);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
|
|
123
|
+
<div className="tabsGutter">
|
|
124
|
+
|
|
125
|
+
<div className="tabsContainer">
|
|
126
|
+
{tabsList.map(function(file) {
|
|
127
|
+
return <Tab
|
|
128
|
+
file={file}
|
|
129
|
+
key={file.key}
|
|
130
|
+
activeTab={activeTab}
|
|
131
|
+
selectTab={selectTab}
|
|
132
|
+
closeTab={closeTab}
|
|
133
|
+
/>
|
|
134
|
+
})}
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div className="tabsDropdown">
|
|
138
|
+
{
|
|
139
|
+
(files && files.length > 0) &&
|
|
140
|
+
<Dropdown data-bs-theme="dark">
|
|
141
|
+
<Dropdown.Toggle as={SelectFileToggle}>
|
|
142
|
+
<ChevronDown className="chevron" />
|
|
143
|
+
</Dropdown.Toggle>
|
|
144
|
+
<Dropdown.Menu>
|
|
145
|
+
{generateDropdown()}
|
|
146
|
+
</Dropdown.Menu>
|
|
147
|
+
</Dropdown>
|
|
148
|
+
}
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
Tabs.propTypes = {
|
|
156
|
+
files: PropTypes.array,
|
|
157
|
+
selectFile: PropTypes.func,
|
|
158
|
+
systemTree: PropTypes.object
|
|
159
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
.tabsGutter {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: row;
|
|
4
|
+
width:100%;
|
|
5
|
+
height:40px;
|
|
6
|
+
background-color: #252526;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.tabsContainer {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: row;
|
|
12
|
+
height: 100%;
|
|
13
|
+
flex-grow: 1;
|
|
14
|
+
overflow-x: auto;
|
|
15
|
+
scrollbar-gutter: stable;
|
|
16
|
+
scrollbar-color: #47474766 #1e1e1e;
|
|
17
|
+
scrollbar-width: thin;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.tabsDropdown {
|
|
21
|
+
float: right;
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
color:rgb(138, 138, 138);
|
|
25
|
+
padding: 0 10px;
|
|
26
|
+
cursor:pointer;
|
|
27
|
+
height:100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.chevron {
|
|
31
|
+
color:rgb(177, 177, 177);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.chevron:hover {
|
|
35
|
+
color: rgb(255, 255, 255);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.dropdown-menu {
|
|
39
|
+
border-radius: 0 !important;
|
|
40
|
+
padding: 0 !important;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.dropdown-item {
|
|
44
|
+
font-size:11px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.dropdown-header {
|
|
48
|
+
font-size:12px !important;
|
|
49
|
+
padding-bottom:5px !important;
|
|
50
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import "./Viewer.scss";
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import { MonacoInstance } from "./MonacoInstance/MonacoInstance";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { Tabs } from "./Tabs/Tabs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Renders the viewer component with support for tabs.
|
|
10
|
+
*
|
|
11
|
+
* @return {JSX}
|
|
12
|
+
*/
|
|
13
|
+
export const Viewer = ({systemTree, onFileSelect}) => {
|
|
14
|
+
|
|
15
|
+
const [files, setFiles] = useState();
|
|
16
|
+
const [fileContent, setFileContent] = useState();
|
|
17
|
+
const [editorContent, setEditorContent] = useState();
|
|
18
|
+
|
|
19
|
+
// Flatten system tree into list with necessary information for tabs component
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
|
|
22
|
+
// Store the files meta and content separately
|
|
23
|
+
const files = [];
|
|
24
|
+
const content = {};
|
|
25
|
+
|
|
26
|
+
// Flatten system tree and assign key to each file in the program.
|
|
27
|
+
for (const program in systemTree) {
|
|
28
|
+
for (const filePath in systemTree[program]) {
|
|
29
|
+
const file = systemTree[program][filePath];
|
|
30
|
+
const key = program + "_" + filePath;
|
|
31
|
+
const info = {
|
|
32
|
+
program: program,
|
|
33
|
+
key: key,
|
|
34
|
+
fileName: filePath.split("/").slice(-1)[0],
|
|
35
|
+
path: filePath
|
|
36
|
+
}
|
|
37
|
+
content[key] = file.source
|
|
38
|
+
files.push(info);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (files.length === 0) {
|
|
43
|
+
// TODO: Replace monaco editor with prompt until a tab is added
|
|
44
|
+
setEditorContent("Select file using file navigator or drop down on top right.");
|
|
45
|
+
setFileContent({});
|
|
46
|
+
setFiles([]);
|
|
47
|
+
} else {
|
|
48
|
+
setFileContent(content);
|
|
49
|
+
setFiles(files);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
}, [systemTree])
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
const selectFile = (fileKey) => {
|
|
56
|
+
if (fileKey) {
|
|
57
|
+
if (files && fileContent) {
|
|
58
|
+
let file = files.find((tab) => tab.key === fileKey)
|
|
59
|
+
setEditorContent(fileContent[fileKey]);
|
|
60
|
+
if (onFileSelect && file) {
|
|
61
|
+
onFileSelect(file);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
setEditorContent("Select file using drop down on top right.");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="viewerContainer">
|
|
71
|
+
<div className="tabContainer">
|
|
72
|
+
<Tabs files={files} selectFile={selectFile} systemTree={systemTree}/>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="monacoContainer">
|
|
75
|
+
<MonacoInstance editorContent={editorContent}/>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Viewer.propTypes = {
|
|
82
|
+
systemTree: PropTypes.object,
|
|
83
|
+
onFileSelect: PropTypes.func
|
|
84
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.viewerContainer {
|
|
2
|
+
position: absolute;
|
|
3
|
+
top: 0;
|
|
4
|
+
left: 0;
|
|
5
|
+
bottom: 0;
|
|
6
|
+
right:0;
|
|
7
|
+
overflow:hidden;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.tabContainer{
|
|
11
|
+
position: absolute;
|
|
12
|
+
top: 0;
|
|
13
|
+
left: 0;
|
|
14
|
+
bottom: 40px;
|
|
15
|
+
right:0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.monacoContainer{
|
|
19
|
+
position: absolute;
|
|
20
|
+
top: 40px;
|
|
21
|
+
left: 0;
|
|
22
|
+
bottom: 0;
|
|
23
|
+
right:0;
|
|
24
|
+
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Viewer.jsx"
|
package/src/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { FlowDiagram } from "../components/FlowDiagram";
|
|
2
|
+
import "./FlowDiagram.scss"
|
|
3
|
+
|
|
4
|
+
import sampleTree from "./data/flow/SampleTree.json";
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
title: 'FlowDiagram',
|
|
8
|
+
component: FlowDiagram,
|
|
9
|
+
argTypes: {
|
|
10
|
+
treeInfo: {
|
|
11
|
+
type: 'array'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const Template = (args) => {
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="rootTraceContainer">
|
|
20
|
+
<FlowDiagram {...args}/>
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const Default = Template.bind({})
|
|
26
|
+
|
|
27
|
+
Default.args = {
|
|
28
|
+
treeInfo: sampleTree
|
|
29
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { StackList } from "../components/StackList";
|
|
3
|
+
import { useArgs } from "@storybook/preview-api";
|
|
4
|
+
import { action } from "@storybook/addon-actions";
|
|
5
|
+
|
|
6
|
+
import "./StackListStories.scss"
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: 'CallStack',
|
|
10
|
+
component: StackList,
|
|
11
|
+
argTypes: {
|
|
12
|
+
traces: {
|
|
13
|
+
type: 'array'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const Template = (args) => {
|
|
19
|
+
const [, updateArgs] = useArgs();
|
|
20
|
+
|
|
21
|
+
const selectTraceItem = (selectedIndex) => {
|
|
22
|
+
action('Selected Stack Position:')(selectedIndex);
|
|
23
|
+
const newTraces = [];
|
|
24
|
+
args.traces.forEach((value, index) => {
|
|
25
|
+
value.selected = (selectedIndex === index);
|
|
26
|
+
newTraces.push(value);
|
|
27
|
+
});
|
|
28
|
+
updateArgs({traces : newTraces});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
updateArgs({selectTraceItem : selectTraceItem});
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="rootContainer">
|
|
37
|
+
<div className="stackContainer">
|
|
38
|
+
<StackList {...args}/>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
export const Default = Template.bind({})
|
|
46
|
+
|
|
47
|
+
Default.args = {
|
|
48
|
+
traces: [
|
|
49
|
+
{functionName:"visit_arg", fileName: "helper.py", lineNumber: 3},
|
|
50
|
+
{functionName:"__init__", fileName: "helper.py", lineNumber: 65},
|
|
51
|
+
{functionName:"injectLogTypesA", fileName: "LogInjector.py", lineNumber: 3, selected: true},
|
|
52
|
+
{functionName:"visit_import", fileName: "LogInjector.py", lineNumber: 3},
|
|
53
|
+
{functionName:"__init__", fileName: "LogInjector.py", lineNumber: 65},
|
|
54
|
+
{functionName:"run", fileName: "ProgramProcessor.py", lineNumber: 3},
|
|
55
|
+
{functionName:"main", fileName: "main.py", lineNumber: 3},
|
|
56
|
+
{functionName:"<module>", fileName: "main.py", lineNumber: 65}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
export const StackTopSelected = Template.bind({})
|
|
62
|
+
|
|
63
|
+
StackTopSelected.args = {
|
|
64
|
+
traces: [
|
|
65
|
+
{functionName:"visit_arg", fileName: "helper.py", lineNumber: 3, selected: true},
|
|
66
|
+
{functionName:"__init__", fileName: "helper.py", lineNumber: 65},
|
|
67
|
+
{functionName:"injectLogTypesA", fileName: "LogInjector.py", lineNumber: 3},
|
|
68
|
+
{functionName:"visit_import", fileName: "LogInjector.py", lineNumber: 3},
|
|
69
|
+
{functionName:"__init__", fileName: "LogInjector.py", lineNumber: 65},
|
|
70
|
+
{functionName:"run", fileName: "ProgramProcessor.py", lineNumber: 3},
|
|
71
|
+
{functionName:"main", fileName: "main.py", lineNumber: 3},
|
|
72
|
+
{functionName:"<module>", fileName: "main.py", lineNumber: 65}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const WithException = Template.bind({})
|
|
77
|
+
|
|
78
|
+
WithException.args = {
|
|
79
|
+
traces: [
|
|
80
|
+
{functionName:"visit_arg", fileName: "helper.py", hasException: true, lineNumber: 3, selected: true},
|
|
81
|
+
{functionName:"__init__", fileName: "helper.py", lineNumber: 65},
|
|
82
|
+
{functionName:"injectLogTypesA", fileName: "LogInjector.py", lineNumber: 3},
|
|
83
|
+
{functionName:"visit_import", fileName: "LogInjector.py", lineNumber: 3},
|
|
84
|
+
{functionName:"__init__", fileName: "LogInjector.py", lineNumber: 65},
|
|
85
|
+
{functionName:"run", fileName: "ProgramProcessor.py", lineNumber: 3},
|
|
86
|
+
{functionName:"main", fileName: "main.py", lineNumber: 3},
|
|
87
|
+
{functionName:"<module>", fileName: "main.py", lineNumber: 65}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { Viewer } from "../components/Viewer";
|
|
3
|
+
import { useArgs } from "@storybook/preview-api";
|
|
4
|
+
import { action } from "@storybook/addon-actions";
|
|
5
|
+
|
|
6
|
+
import fileTrees from "./data/filetree.json";
|
|
7
|
+
|
|
8
|
+
import "./ViewerStories.scss"
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
title: 'Viewer',
|
|
12
|
+
component: Viewer,
|
|
13
|
+
argTypes: {}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const Template = (args) => {
|
|
17
|
+
const [, updateArgs] = useArgs();
|
|
18
|
+
|
|
19
|
+
const onFileSelect = (selectedFile) => {
|
|
20
|
+
action('Selected Stack Position:')(selectedFile);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
updateArgs({onFileSelect : onFileSelect});
|
|
25
|
+
}, []);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="viewerStoryWrapper">
|
|
29
|
+
<Viewer {...args} />
|
|
30
|
+
</div>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const Default = Template.bind({})
|
|
35
|
+
|
|
36
|
+
console.log(fileTrees);
|
|
37
|
+
|
|
38
|
+
Default.args = {
|
|
39
|
+
systemTree: fileTrees.fileTrees
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"fileTrees": {"mergeSortWorker.clp.zst": {"mergeSortWorker.py": {"source": "\nimport websockets\nimport asyncio\nimport json\nfrom mergeSort import mergeSort\nimport uuid\n\nconnection = websockets.connect(uri='ws://localhost:8765', ping_interval=None)\n\nMSG_TYPE = {\n \"REGISTER\": 1,\n \"REQUEST\": 2,\n \"RESPONSE\": 3\n}\n\nIS_REGISTERED = False\n\nasync def send_response(websocket, response):\n '''\n Send response to job handler.\n '''\n await websocket.send(json.dumps(response))\n\nasync def handle_request(message):\n '''\n Handle request from job handler.\n '''\n # Print task info\n print(f\"\\nTask: {message['type']}\")\n print(f\"UID: {message['asp_uid']}\")\n print(f\"Val: {message['data']}\")\n print(f\"User: {message['user']}\")\n\n # Execute Task \n n = len(message[\"data\"])\n sortedList = mergeSort(message[\"data\"], 0, n - 1)\n resp = {\n \"code\": MSG_TYPE[\"RESPONSE\"],\n \"worker\": True,\n \"type\": \"mergeSort\",\n \"value\": sortedList,\n \"asp_uid\": message[\"asp_uid\"],\n \"user\": message[\"user\"]\n }\n return resp\n\nasync def handle_message(websocket, message):\n '''\n Handle the received message.\n '''\n message = json.loads(message)\n asp_uid = message[\"asp_uid\"]\n\n if message[\"code\"] == MSG_TYPE[\"REQUEST\"]:\n response = await handle_request(message=message)\n await send_response(websocket=websocket, response=response)\n\n\nasync def register(websocket):\n '''\n Register the worker with the job handler.\n '''\n asp_uid = str(uuid.uuid4())\n\n response = {\n \"code\": MSG_TYPE[\"REGISTER\"],\n \"worker\": True,\n \"type\": \"mergeSort\",\n \"asp_uid\": asp_uid\n }\n\n # Send message to register the worker.\n await send_response(websocket=websocket, response=response)\n\nasync def receieve_message():\n '''\n Main loop receives jobs, executes them and responds.\n '''\n async with connection as websocket:\n await register(websocket=websocket)\n \n try:\n # Listen for messages\n async for message in websocket:\n await handle_message(websocket=websocket, message=message)\n except KeyboardInterrupt:\n await websocket.close()\n return\n\n await websocket.close()\n\nif __name__ == \"__main__\":\n asyncio.run(receieve_message())", "minLt": 0, "maxLt": 46}, "mergeSort.py": {"source": "def merge(arr, l, m, r):\n '''\n Reference: https://www.geeksforgeeks.org/python-program-for-merge-sort/\n '''\n n1 = m - l + 1\n n2 = r - m\n\n # create temp arrays\n L = [0] * (n1)\n R = [0] * (n2)\n\n # Copy data to temp arrays L[] and R[]\n for i in range(0, n1):\n L[i] = arr[l + i]\n\n for j in range(0, n2):\n R[j] = arr[m + 1 + j]\n\n # Merge the temp arrays back into arr[l..r]\n i = 0 # Initial index of first subarray\n j = 0 # Initial index of second subarray\n k = l # Initial index of merged subarray\n\n while i < n1 and j < n2:\n if L[i] <= R[j]:\n arr[k] = L[i]\n i += 1\n else:\n arr[k] = R[j]\n j += 1\n k += 1\n\n # Copy the remaining elements of L[], if there\n # are any\n while i < n1:\n arr[k] = L[i]\n i += 1\n k += 1\n\n # Copy the remaining elements of R[], if there\n # are any\n while j < n2:\n arr[k] = R[j]\n j += 1\n k += 1\n\n# l is for left index and r is right index of the\n# sub-array of arr to be sorted\n\n\ndef mergeSort(arr, l, r):\n '''\n Reference: https://www.geeksforgeeks.org/python-program-for-merge-sort/\n '''\n if l < r:\n\n # Same as (l+r)//2, but avoids overflow for\n # large l and h\n m = l+(r-l)//2\n\n # Sort first and second halves\n mergeSort(arr, l, m)\n mergeSort(arr, m+1, r)\n merge(arr, l, m, r)\n\n return arr", "minLt": 46, "maxLt": 82}}, "bubbleSortWorker.clp.zst": {"bubbleSortWorker.py": {"source": "\nimport websockets\nimport asyncio\nimport json\nfrom bubbleSort import bubble_sort\nimport uuid\n\nconnection = websockets.connect(uri='ws://localhost:8765', ping_interval=None)\n\nMSG_TYPE = {\n \"REGISTER\": 1,\n \"REQUEST\": 2,\n \"RESPONSE\": 3\n}\n\nIS_REGISTERED = False\n\n\nasync def send_response(websocket, response):\n '''\n Send response to job handler.\n '''\n await websocket.send(json.dumps(response))\n\nasync def handle_request(message):\n '''\n Handle request from job handler.\n '''\n # Print task info\n print(f\"\\nTask: {message['type']}\")\n print(f\"UID: {message['asp_uid']}\")\n print(f\"Val: {message['data']}\")\n print(f\"User: {message['user']}\")\n\n # Execute Task\n sortedList = bubble_sort(message[\"data\"])\n resp = {\n \"code\": MSG_TYPE[\"RESPONSE\"],\n \"worker\": True,\n \"type\": \"bubbleSort\",\n \"value\": sortedList,\n \"asp_uid\": message[\"asp_uid\"],\n \"user\": message[\"user\"]\n }\n return resp\n\nasync def handle_message(websocket, message):\n '''\n Handle the received message.\n '''\n message = json.loads(message)\n asp_uid = message[\"asp_uid\"]\n\n if message[\"code\"] == MSG_TYPE[\"REQUEST\"]:\n response = await handle_request(message=message)\n await send_response(websocket=websocket, response=response)\n\n\nasync def register(websocket):\n '''\n Register the worker with the job handler.\n '''\n asp_uid = str(uuid.uuid4())\n\n response = {\n \"code\": MSG_TYPE[\"REGISTER\"],\n \"worker\": True,\n \"type\": \"bubbleSort\",\n \"asp_uid\": asp_uid\n }\n\n # Send message to register the worker.\n await send_response(websocket=websocket, response=response)\n\nasync def receieve_message():\n '''\n Main loop receives jobs, executes them and responds.\n '''\n async with connection as websocket:\n await register(websocket=websocket)\n \n try:\n # Listen for messages\n async for message in websocket:\n await handle_message(websocket=websocket, message=message)\n except KeyboardInterrupt:\n await websocket.close()\n return\n\n await websocket.close()\n\nif __name__ == \"__main__\":\n asyncio.run(receieve_message())", "minLt": 0, "maxLt": 45}, "bubbleSort.py": {"source": "def bubble_sort(arr):\n # Taken from here: https://www.geeksforgeeks.org/python-program-for-bubble-sort/\n \n # Outer loop to iterate through the list n times\n for n in range(len(arr) - 1, 0, -1):\n swapped = False \n for i in range(n):\n if arr[i] > arr[i + 1]:\n\n # Swap elements if they are in the wrong order\n arr[i], arr[i + 1] = arr[i + 1], arr[i]\n swapped = True\n \n if not swapped:\n break\n\n return arr\n ", "minLt": 45, "maxLt": 55}}, "jobHandler.clp.zst": {"jobHandler.py": {"source": "#!/usr/bin/env python\n\nimport asyncio\nimport argparse\nimport json\nfrom websockets.asyncio.server import serve\n\nworkers = {}\nclient = None\n\nMSG_TYPE = {\n \"REGISTER\": 1,\n \"REQUEST\": 2,\n \"RESPONSE\": 3\n}\n\nasync def send_to_worker(websocket, message):\n '''\n Send message to the given worker.\n '''\n await websocket.send(json.dumps(message))\n\nasync def send_to_client(websocket, message):\n '''\n Send message to the given client.\n '''\n await websocket.send(json.dumps(message))\n\nasync def handle_register(websocket, message):\n '''\n Handle register operations.\n '''\n global client, workers \n if \"client\" in message:\n client = websocket\n await send_to_client(client, message)\n elif \"worker\" in message:\n workers[message[\"type\"]] = websocket\n await send_to_worker(workers[message[\"type\"]], message)\n\nasync def handle_request(websocket, message): \n '''\n Handle incoming requests and pass to relevant worker.\n '''\n if message[\"type\"] in workers:\n await workers[message[\"type\"]].send(json.dumps(message))\n else:\n message[\"response\"] = f\"message['type'] worker isn't initialized\"\n print(f\"{message['type']} worker isn't initialized\")\n await websocket.send(json.dumps(message))\n\nasync def handle_response(message): \n '''\n Handle response from worker and pass to client.\n '''\n global client, workers \n if client:\n await send_to_client(client, message)\n else:\n message[\"response\"] = \"Client isn't initialized\"\n print(f\"client isn't initialized\")\n await send_to_client(client, message)\n\nasync def handle_message(websocket, message):\n '''\n Handle message received by client.\n '''\n message = json.loads(message)\n asp_uid = message[\"asp_uid\"]\n print(f\"\\nReceived message: {message}\")\n\n if message[\"code\"] == MSG_TYPE[\"REGISTER\"]: \n await handle_register(websocket, message)\n elif message[\"code\"] == MSG_TYPE[\"REQUEST\"]: \n await handle_request(websocket, message)\n elif message[\"code\"] == MSG_TYPE[\"RESPONSE\"]: \n await handle_response(message)\n\nasync def receieve_message(websocket):\n '''\n Handles messages from websocket.\n\n 1. Workers register using \n {\n \"code\": 1,\n \"type\": \"bubbleSort\"\n \"worker\": True\n }\n\n 2. Client Registers using\n {\n \"code\": 1,\n \"client\": True\n }\n\n 3. Client runs job using\n {\n \"code\": 2,\n \"client\": True,\n \"type\": \"bubbleSort\",\n \"data\": [13,6,3,12,3]\n }\n '''\n async for message in websocket:\n await handle_message(websocket=websocket, message=message)\n\n\nasync def main():\n '''\n Creates a websocket at the provided port and initializes\n the query handler. \n '''\n parser = argparse.ArgumentParser(description='WebSocket server for sample system queries')\n\n parser.add_argument('--host',\n default='localhost',\n help='Host to bind the server to')\n \n parser.add_argument('--port',\n type=int,\n default=8765,\n help='Port to bind the server to')\n \n args = parser.parse_args()\n\n print(f\"Starting WebSocket server on {args.host}:{args.port}\")\n async with serve(receieve_message, args.host, args.port) as server:\n await server.serve_forever()\n\nif __name__ == \"__main__\":\n asyncio.run(main())", "minLt": 0, "maxLt": 63}}, "radixSortWorker.clp.zst": {"radixSortWorker.py": {"source": "\nimport websockets\nimport asyncio\nimport json\nfrom radixSort import radixSort\nimport uuid\n\nconnection = websockets.connect(uri='ws://localhost:8765', ping_interval=None)\n\nMSG_TYPE = {\n \"REGISTER\": 1,\n \"REQUEST\": 2,\n \"RESPONSE\": 3\n}\n\nIS_REGISTERED = False\n\n\nasync def send_response(websocket, response):\n '''\n Send response to job handler.\n '''\n await websocket.send(json.dumps(response))\n\nasync def handle_request(message):\n '''\n Handle request from job handler.\n '''\n # Print task info\n print(f\"\\nTask: {message['type']}\")\n print(f\"UID: {message['asp_uid']}\")\n print(f\"Val: {message['data']}\")\n print(f\"User: {message['user']}\")\n\n # Execute Task\n n = len(message[\"data\"])\n sortedList = radixSort(message[\"data\"])\n resp = {\n \"code\": MSG_TYPE[\"RESPONSE\"],\n \"worker\": True,\n \"type\": \"radixSort\",\n \"value\": sortedList,\n \"asp_uid\": message[\"asp_uid\"],\n \"user\": message[\"user\"]\n }\n return resp\n\nasync def handle_message(websocket, message):\n '''\n Handle the received message.\n '''\n message = json.loads(message)\n asp_uid = message[\"asp_uid\"]\n\n if message[\"code\"] == MSG_TYPE[\"REQUEST\"]:\n response = await handle_request(message=message)\n await send_response(websocket=websocket, response=response)\n\nasync def register(websocket):\n '''\n Register the worker with the job handler.\n '''\n asp_uid = str(uuid.uuid4())\n\n response = {\n \"code\": MSG_TYPE[\"REGISTER\"],\n \"worker\": True,\n \"type\": \"radixSort\",\n \"asp_uid\": asp_uid\n }\n\n # Send message to register the worker.\n await send_response(websocket=websocket, response=response)\n\n\nasync def receieve_message():\n '''\n Main loop receives jobs, executes them and responds.\n '''\n async with connection as websocket:\n await register(websocket=websocket)\n # Listen for messages\n async for message in websocket:\n await handle_message(websocket=websocket, message=message)\n\n await websocket.close()\n\nif __name__ == \"__main__\":\n asyncio.run(receieve_message())", "minLt": 0, "maxLt": 42}, "radixSort.py": {"source": "\n\ndef countingSort(arr, exp1): \n '''\n Reference: https://www.geeksforgeeks.org/python-program-for-radix-sort/\n '''\n \n n = len(arr) \n \n # The output array elements that will have sorted arr \n output = [0] * (n) \n \n # initialize count array as 0 \n count = [0] * (10) \n \n # Store count of occurrences in count[] \n for i in range(0, n): \n index = (arr[i]/exp1) \n count[int((index)%10)] += 1\n \n # Change count[i] so that count[i] now contains actual \n # position of this digit in output array \n for i in range(1,10): \n count[i] += count[i-1] \n \n # Build the output array \n i = n-1\n while i>=0: \n index = (arr[i]/exp1) \n output[ count[ int((index)%10) ] - 1] = arr[i] \n count[int((index)%10)] -= 1\n i -= 1\n \n # Copying the output array to arr[], \n # so that arr now contains sorted numbers \n i = 0\n for i in range(0,len(arr)): \n arr[i] = output[i] \n\ndef radixSort(arr):\n '''\n Reference: https://www.geeksforgeeks.org/python-program-for-radix-sort/\n '''\n\n # Find the maximum number to know number of digits\n max1 = max(arr)\n\n # Do counting sort for every digit. Note that instead\n # of passing digit number, exp is passed. exp is 10^i\n # where i is current digit number\n exp = 1\n while max1 // exp > 0:\n countingSort(arr,exp)\n exp *= 10\n\n return arr", "minLt": 42, "maxLt": 69}}, "simulatedClient.clp.zst": {"simulatedClient.py": {"source": "\nimport websockets\nimport asyncio\nimport json\nimport random\nimport time\nimport uuid\n\nconnection = websockets.connect(uri='ws://localhost:8765', ping_interval=None)\n\nMSG_TYPE = {\n \"REGISTER\": 1,\n \"REQUEST\": 2,\n \"RESPONSE\": 3\n}\n\nJOB_TYPES = [\n \"bubbleSort\",\n \"mergeSort\",\n \"radixSort\"\n]\n\nUSERS = [\n \"user1\",\n \"user2\",\n \"user3\",\n \"user4\",\n \"user5\",\n]\n\nIS_REGISTERED = False\n\nasync def sendRandomJob(websocket):\n '''\n Send a random job from a random user to the jobHandler.\n '''\n asp_uid = str(uuid.uuid4())\n \n await websocket.send(json.dumps({\n \"code\": MSG_TYPE[\"REQUEST\"],\n \"worker\": True,\n \"type\": JOB_TYPES[random.randint(0, len(JOB_TYPES) - 1)],\n \"user\": USERS[random.randint(0, len(USERS) - 1)],\n \"data\": random.sample(range(6, 101), random.randint(5,15)),\n \"asp_uid\": asp_uid\n }))\n\nasync def registerClient(websocket):\n '''\n Register the client.\n '''\n asp_uid = str(uuid.uuid4())\n await websocket.send(json.dumps({\n \"code\": MSG_TYPE[\"REGISTER\"],\n \"client\": True,\n \"asp_uid\": asp_uid\n }))\n\nasync def main():\n '''\n Main loop receives jobs, executes them and responds.\n '''\n async with connection as websocket:\n await registerClient(websocket=websocket)\n time.sleep(2)\n try:\n async for message in websocket:\n await sendRandomJob(websocket= websocket)\n time.sleep(random.randrange(1, 10))\n except KeyboardInterrupt:\n print('interrupted!')\n except:\n pass\n\n await websocket.close()\n\n\nif __name__ == \"__main__\":\n asyncio.run(main())", "minLt": 0, "maxLt": 35}}}}
|