sample-ui-component-library 0.0.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/.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 +20 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.js +20 -0
- package/dist/esm/index.js.map +1 -0
- package/package.json +66 -0
- package/rollup.config.mjs +35 -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 +43 -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 +156 -0
- package/src/components/Viewer/Tabs/Tabs.scss +50 -0
- package/src/components/Viewer/Viewer.jsx +84 -0
- package/src/components/Viewer/Viewer.scss +5 -0
- package/src/components/Viewer/index.js +1 -0
- package/src/index.js +2 -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/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sample-ui-component-library",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "A library which contains sample UI elements that can be used for populating layouts.",
|
|
5
|
+
"main": "dist/cjs/index.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
|
+
"storybook": "storybook dev -p 6006",
|
|
10
|
+
"build-storybook": "storybook build",
|
|
11
|
+
"build": "rollup -c",
|
|
12
|
+
"predeploy": "npm run build-storybook",
|
|
13
|
+
"deploy-storybook": "gh-pages -d storybook-static"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/vishalpalaniappan/sample-component-library.git"
|
|
18
|
+
},
|
|
19
|
+
"author": "Vishal Palaniappan",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/vishalpalaniappan/sample-component-library/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/vishalpalaniappan/sample-component-library.git",
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@babel/core": "^7.26.10",
|
|
27
|
+
"@babel/preset-env": "^7.26.9",
|
|
28
|
+
"@babel/preset-react": "^7.26.3",
|
|
29
|
+
"@chromatic-com/storybook": "^3.2.6",
|
|
30
|
+
"@rollup/plugin-babel": "^6.0.4",
|
|
31
|
+
"@rollup/plugin-commonjs": "^28.0.3",
|
|
32
|
+
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
33
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
34
|
+
"@storybook/addon-actions": "^8.6.11",
|
|
35
|
+
"@storybook/addon-essentials": "^8.6.8",
|
|
36
|
+
"@storybook/addon-interactions": "^8.6.8",
|
|
37
|
+
"@storybook/addon-onboarding": "^8.6.8",
|
|
38
|
+
"@storybook/addon-webpack5-compiler-swc": "^3.0.0",
|
|
39
|
+
"@storybook/blocks": "^8.6.8",
|
|
40
|
+
"@storybook/manager-api": "^8.6.11",
|
|
41
|
+
"@storybook/preset-scss": "^1.0.3",
|
|
42
|
+
"@storybook/react": "^8.6.8",
|
|
43
|
+
"@storybook/react-webpack5": "^8.6.8",
|
|
44
|
+
"@storybook/test": "^8.6.11",
|
|
45
|
+
"@storybook/theming": "^8.6.11",
|
|
46
|
+
"css-loader": "^7.1.2",
|
|
47
|
+
"gh-pages": "^6.3.0",
|
|
48
|
+
"prop-types": "^15.8.1",
|
|
49
|
+
"react": "^18.2.0",
|
|
50
|
+
"react-dom": "^18.2.0",
|
|
51
|
+
"rollup": "^4.37.0",
|
|
52
|
+
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
53
|
+
"rollup-plugin-postcss": "^4.0.2",
|
|
54
|
+
"sass": "^1.86.0",
|
|
55
|
+
"sass-loader": "^16.0.5",
|
|
56
|
+
"storybook": "^8.6.8",
|
|
57
|
+
"style-loader": "^4.0.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"react": "^18.2.0",
|
|
61
|
+
"react-dom": "^18.2.0"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"asp-react-component-library": "^0.0.9"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import resolve from '@rollup/plugin-node-resolve';
|
|
2
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
3
|
+
import terser from '@rollup/plugin-terser';
|
|
4
|
+
import external from 'rollup-plugin-peer-deps-external';
|
|
5
|
+
import postcss from 'rollup-plugin-postcss';
|
|
6
|
+
import { babel } from '@rollup/plugin-babel';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
input: 'src/index.js',
|
|
10
|
+
output: [
|
|
11
|
+
{
|
|
12
|
+
file: 'dist/cjs/index.js',
|
|
13
|
+
format: 'cjs',
|
|
14
|
+
sourcemap: true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
file: 'dist/esm/index.js',
|
|
18
|
+
format: 'esm',
|
|
19
|
+
sourcemap: true,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
plugins: [
|
|
23
|
+
external(['react', 'react-dom']),
|
|
24
|
+
resolve({
|
|
25
|
+
extensions: ['.js', '.jsx'],
|
|
26
|
+
}),
|
|
27
|
+
postcss(),
|
|
28
|
+
terser(),
|
|
29
|
+
babel({
|
|
30
|
+
babelHelpers: 'bundled',
|
|
31
|
+
exclude: 'node_modules/**',
|
|
32
|
+
}),
|
|
33
|
+
commonjs(),
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import "./StackList.scss";
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
const ROW_STYLE = {
|
|
5
|
+
SELECTED: "#184b2d",
|
|
6
|
+
EXCEPTION: "#420b0e",
|
|
7
|
+
SELECTED_TOP: "#4b4b18"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Renders a row in the stack list component.
|
|
12
|
+
*
|
|
13
|
+
* @param {Number} index
|
|
14
|
+
* @param {String} functionName
|
|
15
|
+
* @param {String} fileName
|
|
16
|
+
* @param {Number} lineNumber
|
|
17
|
+
* @param {Boolean} selected
|
|
18
|
+
* @param {Function} selectTraceItem
|
|
19
|
+
* @param {Boolean} hasException
|
|
20
|
+
* @return {JSX}
|
|
21
|
+
*/
|
|
22
|
+
const StackRow = ({index, functionName, fileName, lineNumber, selected, selectTraceItem, hasException}) => {
|
|
23
|
+
|
|
24
|
+
let style = {};
|
|
25
|
+
if (selected && index === 0) {
|
|
26
|
+
// Style for top of stack
|
|
27
|
+
style = {backgroundColor: hasException ? ROW_STYLE.EXCEPTION : ROW_STYLE.SELECTED_TOP};
|
|
28
|
+
} else if (selected) {
|
|
29
|
+
// Style for rest of stack
|
|
30
|
+
style = {backgroundColor: hasException ? ROW_STYLE.EXCEPTION : ROW_STYLE.SELECTED};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="stackRow" style={style} onClick={(e) => selectTraceItem(index)}>
|
|
35
|
+
<div className="left">
|
|
36
|
+
<span className="functionName">{functionName}</span>
|
|
37
|
+
</div>
|
|
38
|
+
<div className="right">
|
|
39
|
+
<span className="fileName">{fileName}</span>
|
|
40
|
+
<span className="lineNumber">{lineNumber}:1</span>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
StackRow.propTypes = {
|
|
47
|
+
index: PropTypes.number,
|
|
48
|
+
functionName: PropTypes.string,
|
|
49
|
+
fileName: PropTypes.string,
|
|
50
|
+
lineNumber: PropTypes.number,
|
|
51
|
+
selected: PropTypes.bool,
|
|
52
|
+
selectTraceItem: PropTypes.func,
|
|
53
|
+
hasException: PropTypes.bool,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Renders the stack list component.
|
|
58
|
+
*
|
|
59
|
+
* @param {Array} traces
|
|
60
|
+
* @param {Function} selectTraceItem
|
|
61
|
+
* @return {JSX}
|
|
62
|
+
*/
|
|
63
|
+
export const StackList = ({traces, selectTraceItem}) => {
|
|
64
|
+
|
|
65
|
+
const generateStackList = () => {
|
|
66
|
+
const traceList = traces.map((trace, index) => {
|
|
67
|
+
return <StackRow
|
|
68
|
+
key={`${trace.fileName}-${trace.lineNumber}-${trace.functionName}-${index}`}
|
|
69
|
+
functionName={trace.functionName}
|
|
70
|
+
fileName={trace.fileName}
|
|
71
|
+
lineNumber={trace.lineNumber}
|
|
72
|
+
selected={trace.selected}
|
|
73
|
+
hasException={trace.hasException}
|
|
74
|
+
index={index}
|
|
75
|
+
selectTraceItem={selectTraceItem}
|
|
76
|
+
/>
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
return traceList;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div className="stackContainer">
|
|
84
|
+
{generateStackList()}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
StackList.propTypes = {
|
|
90
|
+
traces: PropTypes.array,
|
|
91
|
+
selectTraceItem: PropTypes.func,
|
|
92
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
.stackContainer {
|
|
4
|
+
width: 100%;
|
|
5
|
+
height: 100%;
|
|
6
|
+
overflow-y: auto;
|
|
7
|
+
scrollbar-gutter: stable;
|
|
8
|
+
scrollbar-color: rgba(71, 71, 71, .4) #252526;
|
|
9
|
+
scrollbar-width: thin;
|
|
10
|
+
font-size: 13px;
|
|
11
|
+
font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.stackRow {
|
|
15
|
+
display: flex;
|
|
16
|
+
justify-content: space-between;
|
|
17
|
+
height: 30px;
|
|
18
|
+
align-items: center;
|
|
19
|
+
color:white;
|
|
20
|
+
padding: 0 20px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.stackRow:hover {
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
background-color: #2a2d2e;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.stackRow > .left {
|
|
29
|
+
display: flex;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.stackRow > .right {
|
|
33
|
+
display: flex;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.functionName {
|
|
37
|
+
color: #fff;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.fileName {
|
|
41
|
+
color: #ccc;
|
|
42
|
+
font-size:11px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.lineNumber {
|
|
46
|
+
background:#4d4d4d;
|
|
47
|
+
color:#b0ccc3;
|
|
48
|
+
border-radius:5px;
|
|
49
|
+
margin-left:10px;
|
|
50
|
+
padding:0px 5px;
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./StackList.jsx"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import Editor from '@monaco-editor/react';
|
|
5
|
+
|
|
6
|
+
import "./MonacoInstance.scss"
|
|
7
|
+
|
|
8
|
+
export const MonacoInstance = ({editorContent}) => {
|
|
9
|
+
const editorRef = useRef(null);
|
|
10
|
+
|
|
11
|
+
const content = useRef();
|
|
12
|
+
|
|
13
|
+
const handleEditorDidMount = (editor, monaco) => {
|
|
14
|
+
editorRef.current = editor;
|
|
15
|
+
if(content?.current) {
|
|
16
|
+
editorRef.current.setValue(content.current);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
content.current = editorContent;
|
|
22
|
+
if (editorRef?.current && content.current) {
|
|
23
|
+
editorRef.current.setValue(content.current);
|
|
24
|
+
}
|
|
25
|
+
}, [editorContent]);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Editor
|
|
29
|
+
defaultLanguage="python"
|
|
30
|
+
defaultValue=""
|
|
31
|
+
onMount={handleEditorDidMount}
|
|
32
|
+
theme="vs-dark"
|
|
33
|
+
options={{
|
|
34
|
+
scrollBeyondLastLine:false,
|
|
35
|
+
fontSize:"12px"
|
|
36
|
+
}}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
MonacoInstance.propTypes = {
|
|
42
|
+
editorContent: PropTypes.string,
|
|
43
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import "./Tab.scss";
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import { X } from "react-bootstrap-icons";
|
|
5
|
+
|
|
6
|
+
const tabColors = {
|
|
7
|
+
active: "#1e1e1e",
|
|
8
|
+
disabled: "#2d2d2d"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Renders the tab component.
|
|
13
|
+
*
|
|
14
|
+
* The tab component displays the file name and an option
|
|
15
|
+
* to close a tab. It also sets the style for the active tab.
|
|
16
|
+
*
|
|
17
|
+
* @return {JSX}
|
|
18
|
+
*/
|
|
19
|
+
export const Tab = ({file, activeTab, selectTab, closeTab}) => {
|
|
20
|
+
|
|
21
|
+
const getTabStyle = () => {
|
|
22
|
+
return {
|
|
23
|
+
backgroundColor: (activeTab === file.key)?tabColors.active:tabColors.disabled
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div onClick={(e) => selectTab(e, file)} className="tab" style={getTabStyle()}>
|
|
29
|
+
<div className="fileName">
|
|
30
|
+
{file.fileName}
|
|
31
|
+
</div>
|
|
32
|
+
<div className="close" onClick={(e) => {
|
|
33
|
+
e.stopPropagation();
|
|
34
|
+
closeTab(e, file);
|
|
35
|
+
}}>
|
|
36
|
+
<X style={{color:"white"}}/>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
Tab.propTypes = {
|
|
43
|
+
file: PropTypes.shape({
|
|
44
|
+
key: PropTypes.string.isRequired,
|
|
45
|
+
fileName: PropTypes.oneOfType([PropTypes.string, PropTypes.array]).isRequired,
|
|
46
|
+
path: PropTypes.string
|
|
47
|
+
}).isRequired,
|
|
48
|
+
activeTab: PropTypes.string.isRequired,
|
|
49
|
+
selectTab: PropTypes.func.isRequired,
|
|
50
|
+
closeTab: PropTypes.func.isRequired
|
|
51
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.tab {
|
|
2
|
+
height: 100%;
|
|
3
|
+
display:flex;
|
|
4
|
+
justify-content: center;
|
|
5
|
+
align-items: center;
|
|
6
|
+
padding: 0 5px 0 10px;
|
|
7
|
+
margin-right:2px;
|
|
8
|
+
cursor: default;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.fileName {
|
|
12
|
+
font-family: sans-serif;
|
|
13
|
+
font-size: 12px;
|
|
14
|
+
padding-right: 5px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.close {
|
|
18
|
+
display:flex;
|
|
19
|
+
float:right;
|
|
20
|
+
cursor:pointer;
|
|
21
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
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
|
+
setTabsList(files);
|
|
113
|
+
setActiveTab(files[0].key);
|
|
114
|
+
selectFile(files[0].key);
|
|
115
|
+
}
|
|
116
|
+
}, [files]);
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
|
|
120
|
+
<div className="tabsGutter">
|
|
121
|
+
|
|
122
|
+
<div className="tabsContainer">
|
|
123
|
+
{tabsList.map(function(file) {
|
|
124
|
+
return <Tab
|
|
125
|
+
file={file}
|
|
126
|
+
key={file.key}
|
|
127
|
+
activeTab={activeTab}
|
|
128
|
+
selectTab={selectTab}
|
|
129
|
+
closeTab={closeTab}
|
|
130
|
+
/>
|
|
131
|
+
})}
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div className="tabsDropdown">
|
|
135
|
+
{
|
|
136
|
+
(files && files.length > 0) &&
|
|
137
|
+
<Dropdown data-bs-theme="dark">
|
|
138
|
+
<Dropdown.Toggle as={SelectFileToggle}>
|
|
139
|
+
<ChevronDown className="chevron" />
|
|
140
|
+
</Dropdown.Toggle>
|
|
141
|
+
<Dropdown.Menu>
|
|
142
|
+
{generateDropdown()}
|
|
143
|
+
</Dropdown.Menu>
|
|
144
|
+
</Dropdown>
|
|
145
|
+
}
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
Tabs.propTypes = {
|
|
153
|
+
files: PropTypes.array,
|
|
154
|
+
selectFile: PropTypes.func,
|
|
155
|
+
systemTree: PropTypes.object
|
|
156
|
+
}
|
|
@@ -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 d-flex flex-column">
|
|
71
|
+
<div>
|
|
72
|
+
<Tabs files={files} selectFile={selectFile} systemTree={systemTree}/>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="d-flex flex-grow-1">
|
|
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 @@
|
|
|
1
|
+
export * from "./Viewer.jsx"
|
package/src/index.js
ADDED