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.
Files changed (38) hide show
  1. package/.storybook/main.js +22 -0
  2. package/.storybook/manager.js +6 -0
  3. package/.storybook/preview.js +13 -0
  4. package/LICENSE +201 -0
  5. package/README.md +37 -0
  6. package/babel.config.js +6 -0
  7. package/dist/cjs/index.js +47 -0
  8. package/dist/cjs/index.js.map +1 -0
  9. package/dist/esm/index.js +47 -0
  10. package/dist/esm/index.js.map +1 -0
  11. package/package.json +72 -0
  12. package/rollup.config.mjs +37 -0
  13. package/src/components/FlowDiagram/DagreLayout.js +33 -0
  14. package/src/components/FlowDiagram/FlowDiagram.jsx +64 -0
  15. package/src/components/FlowDiagram/FlowDiagram.scss +0 -0
  16. package/src/components/FlowDiagram/helper.js +59 -0
  17. package/src/components/FlowDiagram/index.js +1 -0
  18. package/src/components/StackList/StackList.jsx +92 -0
  19. package/src/components/StackList/StackList.scss +51 -0
  20. package/src/components/StackList/index.js +1 -0
  21. package/src/components/Viewer/MonacoInstance/MonacoInstance.jsx +46 -0
  22. package/src/components/Viewer/MonacoInstance/MonacoInstance.scss +0 -0
  23. package/src/components/Viewer/Tabs/Tab/Tab.jsx +51 -0
  24. package/src/components/Viewer/Tabs/Tab/Tab.scss +21 -0
  25. package/src/components/Viewer/Tabs/Tabs.jsx +159 -0
  26. package/src/components/Viewer/Tabs/Tabs.scss +50 -0
  27. package/src/components/Viewer/Viewer.jsx +84 -0
  28. package/src/components/Viewer/Viewer.scss +25 -0
  29. package/src/components/Viewer/index.js +1 -0
  30. package/src/index.js +3 -0
  31. package/src/stories/FlowDiagram.scss +7 -0
  32. package/src/stories/FlowDiagram.stories.js +29 -0
  33. package/src/stories/StackList.stories.js +89 -0
  34. package/src/stories/StackListStories.scss +13 -0
  35. package/src/stories/Viewer.stories.js +40 -0
  36. package/src/stories/ViewerStories.scss +7 -0
  37. package/src/stories/data/filetree.json +1 -0
  38. package/src/stories/data/flow/SampleTree.json +8 -0
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "sample-ui-component-library",
3
+ "version": "0.0.0-beta",
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 6007",
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-ui-component-library.git"
18
+ },
19
+ "author": "Vishal Palaniappan",
20
+ "license": "MIT",
21
+ "bugs": {
22
+ "url": "https://github.com/vishalpalaniappan/sample-ui-component-library/issues"
23
+ },
24
+ "homepage": "https://github.com/vishalpalaniappan/sample-ui-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-json": "^6.1.0",
33
+ "@rollup/plugin-node-resolve": "^16.0.1",
34
+ "@rollup/plugin-terser": "^0.4.4",
35
+ "@storybook/addon-actions": "^8.6.11",
36
+ "@storybook/addon-essentials": "^8.6.8",
37
+ "@storybook/addon-interactions": "^8.6.8",
38
+ "@storybook/addon-onboarding": "^8.6.8",
39
+ "@storybook/addon-webpack5-compiler-swc": "^3.0.0",
40
+ "@storybook/blocks": "^8.6.8",
41
+ "@storybook/manager-api": "^8.6.11",
42
+ "@storybook/preset-scss": "^1.0.3",
43
+ "@storybook/react": "^8.6.8",
44
+ "@storybook/react-webpack5": "^8.6.8",
45
+ "@storybook/test": "^8.6.11",
46
+ "@storybook/theming": "^8.6.11",
47
+ "css-loader": "^7.1.2",
48
+ "gh-pages": "^6.3.0",
49
+ "prop-types": "^15.8.1",
50
+ "react": "^18.2.0",
51
+ "react-dom": "^18.2.0",
52
+ "rollup": "^4.37.0",
53
+ "rollup-plugin-peer-deps-external": "^2.2.4",
54
+ "rollup-plugin-postcss": "^4.0.2",
55
+ "sass": "^1.86.0",
56
+ "sass-loader": "^16.0.5",
57
+ "storybook": "^8.6.8",
58
+ "style-loader": "^4.0.0"
59
+ },
60
+ "peerDependencies": {
61
+ "react": "^18.2.0",
62
+ "react-dom": "^18.2.0"
63
+ },
64
+ "dependencies": {
65
+ "@dagrejs/dagre": "^1.1.4",
66
+ "@monaco-editor/react": "^4.7.0",
67
+ "@xyflow/react": "^12.6.0",
68
+ "bootstrap": "^5.3.4",
69
+ "react-bootstrap": "^2.10.9",
70
+ "react-bootstrap-icons": "^1.11.5"
71
+ }
72
+ }
@@ -0,0 +1,37 @@
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 json from '@rollup/plugin-json';
7
+ import { babel } from '@rollup/plugin-babel';
8
+
9
+ export default {
10
+ input: 'src/index.js',
11
+ output: [
12
+ {
13
+ file: 'dist/cjs/index.js',
14
+ format: 'cjs',
15
+ sourcemap: true
16
+ },
17
+ {
18
+ file: 'dist/esm/index.js',
19
+ format: 'esm',
20
+ sourcemap: true,
21
+ },
22
+ ],
23
+ plugins: [
24
+ external(['react', 'react-dom']),
25
+ resolve({
26
+ extensions: ['.js', '.jsx'],
27
+ }),
28
+ postcss(),
29
+ terser(),
30
+ babel({
31
+ babelHelpers: 'bundled',
32
+ exclude: 'node_modules/**',
33
+ }),
34
+ commonjs(),
35
+ json()
36
+ ]
37
+ }
@@ -0,0 +1,33 @@
1
+
2
+ import Dagre from '@dagrejs/dagre';
3
+
4
+ export const getLayoutedElements = (nodes, edges, options) => {
5
+ // Reference: https://reactflow.dev/learn/layouting/layouting
6
+
7
+ const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
8
+ g.setGraph({ rankdir: options.direction, ranksep: 100, nodesep: 170});
9
+
10
+ edges.forEach((edge) => g.setEdge(edge.source, edge.target));
11
+ nodes.forEach((node) =>
12
+ g.setNode(node.id, {
13
+ ...node,
14
+ width: node.measured?.width ?? 0,
15
+ height: node.measured?.height ?? 0,
16
+ }),
17
+ );
18
+
19
+ Dagre.layout(g);
20
+
21
+ return {
22
+ nodes: nodes.map((node) => {
23
+ const position = g.node(node.id);
24
+ // We are shifting the dagre node position (anchor=center center) to the top left
25
+ // so it matches the React Flow node anchor point (top left).
26
+ const x = position.x - (node.measured?.width ?? 0) / 2;
27
+ const y = position.y - (node.measured?.height ?? 0) / 2;
28
+
29
+ return { ...node, position: { x, y } };
30
+ }),
31
+ edges,
32
+ };
33
+ };
@@ -0,0 +1,64 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import {
3
+ ReactFlow,
4
+ ReactFlowProvider,
5
+ useNodesState,
6
+ useEdgesState,
7
+ Controls,
8
+ useReactFlow
9
+ } from "@xyflow/react";
10
+ import "@xyflow/react/dist/style.css";
11
+
12
+ import { getLayoutedElements } from "./DagreLayout.js";
13
+ import { getLayoutInfoFromTree } from "./helper.js"
14
+
15
+ const Flow = ({tree}) => {
16
+ const { fitView } = useReactFlow();
17
+ const [nodes, setNodes, onNodesChange] = useNodesState([]);
18
+ const [edges, setEdges, onEdgesChange] = useEdgesState([]);
19
+
20
+ useEffect(() => {
21
+ if (tree) {
22
+ const flowInfo = getLayoutInfoFromTree(tree.data);
23
+
24
+ //direction: TB, BT, LR, or RL, where T = top, B = bottom, L = left, and R = right.
25
+ const layouted = getLayoutedElements(
26
+ flowInfo.nodes,
27
+ flowInfo.edges,
28
+ {direction:tree.orientation}
29
+ );
30
+
31
+ setNodes([...layouted.nodes]);
32
+ setEdges([...layouted.edges]);
33
+
34
+ fitView();
35
+ }
36
+ }, [tree]);
37
+
38
+ return (
39
+ <ReactFlow
40
+ nodes={nodes}
41
+ edges={edges}
42
+ onNodesChange={onNodesChange}
43
+ onEdgesChange={onEdgesChange}
44
+ colorMode={"dark"}
45
+ fitView
46
+ >
47
+ <Controls />
48
+ </ReactFlow>
49
+ );
50
+ };
51
+
52
+ export const FlowDiagram = ({treeInfo}) => {
53
+ const [tree, setTree] = useState();
54
+
55
+ useEffect(() => {
56
+ setTree(treeInfo)
57
+ }, [treeInfo])
58
+
59
+ return (
60
+ <ReactFlowProvider>
61
+ <Flow tree={tree} />
62
+ </ReactFlowProvider>
63
+ );
64
+ }
File without changes
@@ -0,0 +1,59 @@
1
+ import { MarkerType } from "@xyflow/react";
2
+
3
+ const marker = {
4
+ type: MarkerType.ArrowClosed,
5
+ width: 20,
6
+ height: 20,
7
+ color: '#FF0072',
8
+ }
9
+
10
+ const arrowStyle = {
11
+ strokeWidth: 2,
12
+ stroke: '#FF0072',
13
+ }
14
+
15
+ /**
16
+ * Returns react flow nodes and edges from the given tree.
17
+ * @param {Object} tree
18
+ * @returns {Array} An array co
19
+ */
20
+ export const getLayoutInfoFromTree = (tree) => {
21
+ const edges = [];
22
+ const nodes = [];
23
+ Object.keys(tree).forEach((branchName, index1) => {
24
+ tree[branchName].forEach((node, index) => {
25
+
26
+ // Position doesn't matter, it will be set by layout algorithm
27
+ const flowNode = {
28
+ id: node,
29
+ flowId: node,
30
+ position: { x: 250, y: index * 200 },
31
+ data: { label: String(node) }
32
+ }
33
+
34
+ if (index == 0) {
35
+ flowNode.type = "input"
36
+ }
37
+
38
+ if (index > 0) {
39
+ const prevNode = nodes[nodes.length-1]
40
+ const edge = {
41
+ id: prevNode.flowId + "-" + branchName + "-" + index + "-" + flowNode.flowId,
42
+ source: prevNode.flowId,
43
+ target: flowNode.flowId,
44
+ animated: true,
45
+ markerEnd: marker,
46
+ style: arrowStyle
47
+ }
48
+ edges.push(edge);
49
+ }
50
+
51
+ nodes.push(flowNode);
52
+ });
53
+ });
54
+
55
+ return {
56
+ nodes: nodes,
57
+ edges: edges
58
+ }
59
+ }
@@ -0,0 +1 @@
1
+ export * from "./FlowDiagram.jsx"
@@ -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,46 @@
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
+ minimap: {
37
+ enabled: false
38
+ }
39
+ }}
40
+ />
41
+ );
42
+ }
43
+
44
+ MonacoInstance.propTypes = {
45
+ editorContent: PropTypes.string,
46
+ }
@@ -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
+ }