sample-ui-component-library 0.0.6-beta → 0.0.6-dev

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sample-ui-component-library",
3
- "version": "0.0.6-beta",
3
+ "version": "0.0.6-dev",
4
4
  "description": "A library which contains sample UI elements that can be used for populating layouts.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -58,6 +58,7 @@
58
58
  "style-loader": "^4.0.0"
59
59
  },
60
60
  "peerDependencies": {
61
+ "@dnd-kit/core": "^6.3.1",
61
62
  "react": "^18.2.0",
62
63
  "react-dom": "^18.2.0"
63
64
  },
package/rollup.config.mjs CHANGED
@@ -21,7 +21,7 @@ export default {
21
21
  },
22
22
  ],
23
23
  plugins: [
24
- external(['react', 'react-dom']),
24
+ external(['react', 'react-dom', '@dnd-kit/core']),
25
25
  resolve({
26
26
  extensions: ['.js', '.jsx'],
27
27
  }),
@@ -0,0 +1,59 @@
1
+ import "./Editor.scss";
2
+ import PropTypes from 'prop-types';
3
+
4
+ import { MonacoInstance } from "./MonacoInstance/MonacoInstance";
5
+ import { EditorTabs } from "./EditorTabs/EditorTabs";
6
+ import { useEffect, useState } from "react";
7
+
8
+ /**
9
+ * Renders the editor component with support for tabs.
10
+ *
11
+ * @return {JSX}
12
+ */
13
+ export const Editor = ({systemTree, onFileSelect}) => {
14
+
15
+ const [editorContent, setEditorContent] = useState("asdf");
16
+
17
+ const [activeTab, setActiveTab] = useState();
18
+ const [tabs, setTabs] = useState([
19
+ { id: "tab1", label: "Tab 1" },
20
+ { id: "tab2", label: "Tab 2" },
21
+ { id: "tab3", label: "Tab 3" },
22
+ ]);
23
+
24
+ useEffect(() => {
25
+ if (activeTab) {
26
+ setEditorContent(tabs[activeTab - 1].label);
27
+ }
28
+ }, [activeTab]);
29
+
30
+
31
+ useEffect(() => {
32
+ if (tabs) {
33
+ if ((activeTab && activeTab > tabs.length) || activeTab == null) {
34
+ setActiveTab(tabs.length);
35
+ }
36
+ }
37
+ }, [tabs]);
38
+
39
+ const onTabClick = (event) => {
40
+ const tabIndex = tabs.findIndex(obj => obj.id === event.target.id);
41
+ setActiveTab(tabIndex + 1);
42
+ }
43
+
44
+ return (
45
+ <div className="editorContainer">
46
+ <div className="tabContainer">
47
+ <EditorTabs activeTab={1} tabs={tabs} selectTab={onTabClick} />
48
+ </div>
49
+ <div className="monacoContainer">
50
+ <MonacoInstance editorContent={editorContent}/>
51
+ </div>
52
+ </div>
53
+ );
54
+ }
55
+
56
+ Editor.propTypes = {
57
+ systemTree: PropTypes.object,
58
+ onFileSelect: PropTypes.func
59
+ }
@@ -0,0 +1,16 @@
1
+ .editorContainer {
2
+ width:100%;
3
+ height:100%;
4
+ display:flex;
5
+ flex-direction:column;
6
+ overflow:hidden;
7
+ }
8
+
9
+ .tabContainer{
10
+ width:100%;
11
+ height: 40px;
12
+ }
13
+
14
+ .monacoContainer{
15
+ flex-grow: 1;
16
+ }
@@ -0,0 +1,82 @@
1
+ import "./EditorTabs.scss";
2
+ import PropTypes from "prop-types";
3
+
4
+ import { useEffect, useState } from "react";
5
+ import {
6
+ DndContext,
7
+ DragOverlay,
8
+ useDraggable,
9
+ useDroppable,
10
+ } from "@dnd-kit/core";
11
+
12
+ /**
13
+ * Tab Component
14
+ * @param {String} id
15
+ * @param {String} label
16
+ * @returns
17
+ */
18
+ function Tab({id, label, onSelectTab}) {
19
+ const { attributes, listeners, setNodeRef, transform } = useDraggable({id});
20
+
21
+ const style = {
22
+ transform: transform
23
+ ? `translate3d(${transform.x}px, ${transform.y}px, 0)`
24
+ : undefined
25
+ };
26
+
27
+ return (
28
+ <div ref={setNodeRef} style={style} id={id} onClick={onSelectTab} className="tab" {...listeners} {...attributes}>
29
+ {label}
30
+ </div>
31
+ );
32
+ }
33
+
34
+ /**
35
+ * Tab Gutter Component
36
+ * @param {String} id
37
+ * @returns
38
+ */
39
+ function Gutter({id}) {
40
+ const { setNodeRef, isOver } = useDroppable({id});
41
+ return (
42
+ <div
43
+ className="gutter"
44
+ ref={setNodeRef}
45
+ style={{background: isOver ? "white" : "#4da3ff33"}}
46
+ ></div>
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Tabs component.
52
+ * @param {Object} activeTab
53
+ * @param {Array} tabs
54
+ * @returns
55
+ */
56
+ export const EditorTabs = ({activeTab, tabs, selectTab}) => {
57
+
58
+ const [tabsList, setTabsList] = useState();
59
+
60
+ useEffect(() => {
61
+ if (tabs) {
62
+ const list = [];
63
+ tabs.forEach((tab, index) => {
64
+ list.push(
65
+ <>
66
+ <Gutter id={tab.id} />
67
+ <Tab onSelectTab={selectTab} key={tab.id} {...tab} />
68
+ </>
69
+ );
70
+ })
71
+ setTabsList(list);
72
+ }
73
+ }, [tabs]);
74
+
75
+ return (
76
+ <div style={{ display: "flex", background: "#222425" }}>
77
+ {tabsList}
78
+ </div>
79
+ );
80
+ };
81
+
82
+ EditorTabs.propTypes = {};
@@ -0,0 +1,22 @@
1
+ .tabs {
2
+ display: flex;
3
+ flex-direction: row;
4
+ background-color: #1e1e1e;
5
+ height: 100%;
6
+ width: 100%;
7
+ }
8
+
9
+ .tab {
10
+ padding: 0px 20px;
11
+ background-color: #2d2d2d;
12
+ color: #fff;
13
+ cursor: pointer;
14
+ height: 40px;
15
+ display:flex;
16
+ align-items: center;
17
+ }
18
+
19
+ .gutter {
20
+ height: 40px;
21
+ width: 1px;
22
+ }
@@ -0,0 +1,66 @@
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
+ function Gutter({ id }) {
9
+ const { setNodeRef, isOver } = useDroppable({
10
+ id,
11
+ });
12
+
13
+ return (
14
+ <div
15
+ ref={setNodeRef}
16
+ style={{
17
+ height: 40,
18
+ width: 1,
19
+ background: isOver ? "white" : "#4da3ff33",
20
+ display: "flex",
21
+ alignItems: "center",
22
+ justifyContent: "center"
23
+ }}
24
+ ></div>
25
+ );
26
+ }
27
+
28
+ export const MonacoInstance = ({editorContent}) => {
29
+ const editorRef = useRef(null);
30
+
31
+ const content = useRef();
32
+
33
+ const handleEditorDidMount = (editor, monaco) => {
34
+ editorRef.current = editor;
35
+ if(content?.current) {
36
+ editorRef.current.setValue(content.current);
37
+ }
38
+ }
39
+
40
+ useEffect(() => {
41
+ content.current = editorContent;
42
+ if (editorRef?.current && content.current) {
43
+ editorRef.current.setValue(content.current);
44
+ }
45
+ }, [editorContent]);
46
+
47
+ return (
48
+ <Editor
49
+ defaultLanguage="python"
50
+ defaultValue=""
51
+ onMount={handleEditorDidMount}
52
+ theme="vs-dark"
53
+ options={{
54
+ scrollBeyondLastLine:false,
55
+ fontSize:"12px",
56
+ minimap: {
57
+ enabled: false
58
+ }
59
+ }}
60
+ />
61
+ );
62
+ }
63
+
64
+ MonacoInstance.propTypes = {
65
+ editorContent: PropTypes.string,
66
+ }
@@ -0,0 +1 @@
1
+ export * from "./Editor.jsx"
@@ -5,6 +5,12 @@ import { setDefaultCollapsed, collapseTree, selectNode, flattenTree } from "./he
5
5
 
6
6
  import { FileCode, ChevronRight, ChevronDown, Braces, FiletypeScss, FiletypeJs, FiletypePy} from "react-bootstrap-icons";
7
7
  import PropTypes from 'prop-types';
8
+ import {
9
+ DndContext,
10
+ DragOverlay,
11
+ useDraggable,
12
+ useDroppable,
13
+ } from "@dnd-kit/core";
8
14
 
9
15
  const INDENT_WIDTH = 20;
10
16
  const SELECTED_FILE_COLOR = "#00426b";
@@ -27,7 +33,9 @@ function useEvent(fn) {
27
33
  /**
28
34
  * Renders a single node in the file tree.
29
35
  */
30
- const TreeNode = ({node, onRowClick}) => {
36
+ const TreeNode = ({id, node, onRowClick}) => {
37
+ const { attributes, listeners, setNodeRef, transform } = useDraggable({id});
38
+
31
39
  /**
32
40
  * Gets the appropriate icon for the node based on its type and collapsed state.
33
41
  * @returns <JSX>
@@ -44,9 +52,15 @@ const TreeNode = ({node, onRowClick}) => {
44
52
  * Sets the background color of the row if the node is selected.
45
53
  */
46
54
  const getRowStyle = () => {
55
+ const style = {
56
+ transform: transform
57
+ ? `translate3d(${transform.x}px, ${transform.y}px, 0)`
58
+ : undefined
59
+ };
47
60
  if (node.selected) {
48
- return {"backgroundColor": SELECTED_FILE_COLOR};
61
+ style["backgroundColor"] = SELECTED_FILE_COLOR;
49
62
  }
63
+ return style;
50
64
  }
51
65
 
52
66
 
@@ -72,7 +86,7 @@ const TreeNode = ({node, onRowClick}) => {
72
86
  }
73
87
 
74
88
  return (
75
- <div className="file-node-row" style={getRowStyle()} onClick={() => onRowClick(node)}>
89
+ <div className="file-node-row" ref={setNodeRef} {...listeners} {...attributes} style={getRowStyle()} onClick={() => onRowClick(node)}>
76
90
  <div className="indent" style={{ width: node.level * INDENT_WIDTH + "px"}} />
77
91
  {
78
92
  node.type === "folder" ?
@@ -115,7 +129,7 @@ export const FileBrowser = ({tree, onNodeSelect}) => {
115
129
  const nodes = collapseTree(treeRef.current);
116
130
  const rows = [];
117
131
  nodes.forEach((node) => {
118
- rows.push(<TreeNode key={node.id} node={node} onRowClick={handleFileClick}/>);
132
+ rows.push(<TreeNode key={node.id} node={node} id={node.name} onRowClick={handleFileClick}/>);
119
133
  });
120
134
  setNodes(rows);
121
135
  }
@@ -1,5 +1,4 @@
1
1
  .file-browser{
2
- background-color: #333;
3
2
  width: 100%;
4
3
  height:100%;
5
4
  padding:5px;
package/src/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./components/StackList";
2
2
  export * from "./components/Viewer";
3
3
  export * from "./components/FlowDiagram";
4
- export * from "./components/FileBrowser";
4
+ export * from "./components/FileBrowser";
5
+ export * from "./components/Editor";
@@ -0,0 +1,106 @@
1
+ import { useEffect } from "react";
2
+ import { Editor } from "../components/Editor";
3
+ import { useArgs } from "@storybook/preview-api";
4
+ import { action } from "@storybook/addon-actions";
5
+ import {
6
+ DndContext,
7
+ DragOverlay,
8
+ useDraggable,
9
+ useDroppable,
10
+ } from "@dnd-kit/core";
11
+
12
+ import fileTrees from "./data/filetree.json";
13
+
14
+ import "./EditorStories.scss"
15
+
16
+ export default {
17
+ title: 'Editor',
18
+ component: Editor,
19
+ argTypes: {}
20
+ };
21
+
22
+ /**
23
+ * Preview for the div being dragged.
24
+ * @returns
25
+ */
26
+ function DragPreview({ label }) {
27
+ return (
28
+ <div
29
+ style={{
30
+ padding: "6px 12px",
31
+ background: "#2d2d2d",
32
+ color: "white",
33
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
34
+ opacity:0.3
35
+ }}
36
+ >
37
+ {label}
38
+ </div>
39
+ );
40
+ }
41
+
42
+ /**
43
+ * Offset for the drag overlay.
44
+ * @returns
45
+ */
46
+ const offsetOverlay = ({ transform }) => {
47
+ return {
48
+ ...transform,
49
+ x: transform.x + 20,
50
+ y: transform.y + 20
51
+ };
52
+ };
53
+
54
+ const Template = (args) => {
55
+ const [, updateArgs] = useArgs();
56
+
57
+ const onFileSelect = (selectedFile) => {
58
+ action('Selected Stack Position:')(selectedFile);
59
+ }
60
+
61
+ useEffect(() => {
62
+ updateArgs({onFileSelect : onFileSelect});
63
+ }, []);
64
+
65
+ /**
66
+ * Callback for when drag ends.
67
+ */
68
+ const handleDragEnd = (event) =>{
69
+ const { active, over } = event;
70
+ console.log("Drag Ended");
71
+ const rect = event.activatorEvent;
72
+
73
+ console.log(over );
74
+
75
+ if (over) {
76
+ console.log("Dragged item:", active.id);
77
+ console.log("Dropped on:", over.id);
78
+ } else {
79
+ console.log("Dropped outside any droppable");
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Callback for when drag is started.
85
+ */
86
+ const onDragStart = (event) => {
87
+ console.log("Drag Started");
88
+ }
89
+
90
+ return (
91
+ <DndContext onDragStart={onDragStart} onDragEnd={handleDragEnd}>
92
+ <div className="editorStoryWrapper">
93
+ <Editor {...args} />
94
+ </div>
95
+ <DragOverlay modifiers={[offsetOverlay]} dropAnimation={null}>
96
+ <DragPreview label={"preview"}/>
97
+ </DragOverlay>
98
+ </DndContext>
99
+ )
100
+ }
101
+
102
+ export const Default = Template.bind({});
103
+
104
+ Default.args = {
105
+ systemTree: fileTrees.fileTrees
106
+ }
@@ -0,0 +1,7 @@
1
+ .editorStoryWrapper{
2
+ position: absolute;
3
+ top:0;
4
+ bottom:0;
5
+ left:0;
6
+ right:0;
7
+ }
@@ -2,6 +2,15 @@ import { useEffect } from "react";
2
2
  import { FileBrowser } from "../components/FileBrowser";
3
3
  import { useArgs } from "@storybook/preview-api";
4
4
  import { action } from "@storybook/addon-actions";
5
+ import {
6
+ DndContext,
7
+ DragOverlay,
8
+ useDraggable,
9
+ useDroppable,
10
+ PointerSensor,
11
+ useSensor,
12
+ useSensors
13
+ } from "@dnd-kit/core";
5
14
 
6
15
  import FileTree1 from "./data/FileBrowser/Tree1.json"
7
16
  import FileTree2 from "./data/FileBrowser/Tree2.json"
@@ -14,6 +23,38 @@ export default {
14
23
  argTypes: {}
15
24
  };
16
25
 
26
+ /**
27
+ * Preview for the div being dragged.
28
+ * @returns
29
+ */
30
+ function DragPreview({ label }) {
31
+ return (
32
+ <div
33
+ style={{
34
+ padding: "6px 12px",
35
+ background: "#2d2d2d",
36
+ color: "white",
37
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
38
+ opacity:0.3
39
+ }}
40
+ >
41
+ {label}
42
+ </div>
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Offset for the drag overlay.
48
+ * @returns
49
+ */
50
+ const offsetOverlay = ({ transform }) => {
51
+ return {
52
+ ...transform,
53
+ x: transform.x + 20,
54
+ y: transform.y + 20
55
+ };
56
+ };
57
+
17
58
 
18
59
  const Template = (args) => {
19
60
  const [, updateArgs] = useArgs();
@@ -26,12 +67,50 @@ const Template = (args) => {
26
67
  updateArgs({onNodeSelect : onNodeSelect});
27
68
  }, []);
28
69
 
70
+ /**
71
+ * Callback for when drag ends.
72
+ */
73
+ const handleDragEnd = (event) =>{
74
+ const { active, over } = event;
75
+ console.log("Drag Ended");
76
+ const rect = event.activatorEvent;
77
+
78
+ console.log(over );
79
+
80
+ if (over) {
81
+ console.log("Dragged item:", active.id);
82
+ console.log("Dropped on:", over.id);
83
+ } else {
84
+ console.log("Dropped outside any droppable");
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Callback for when drag is started.
90
+ */
91
+ const onDragStart = (event) => {
92
+ console.log("Drag Started");
93
+ }
94
+
95
+ const sensors = useSensors(
96
+ useSensor(PointerSensor, {
97
+ activationConstraint: {
98
+ distance: 8
99
+ }
100
+ })
101
+ );
102
+
29
103
  return (
30
- <div className="viewerStoryWrapper">
31
- <div className="file-browser">
32
- <FileBrowser {...args} />
104
+ <DndContext sensors={sensors} onDragStart={onDragStart} onDragEnd={handleDragEnd}>
105
+ <div className="viewerStoryWrapper">
106
+ <div className="file-browser">
107
+ <FileBrowser {...args} />
108
+ </div>
33
109
  </div>
34
- </div>
110
+ <DragOverlay modifiers={[offsetOverlay]} dropAnimation={null}>
111
+ <DragPreview label={"preview"}/>
112
+ </DragOverlay>
113
+ </DndContext>
35
114
  )
36
115
  }
37
116
 
@@ -11,6 +11,7 @@
11
11
 
12
12
  .viewerStoryWrapper > .file-browser{
13
13
  height: 300px;
14
+ background-color: #333;
14
15
  overflow: auto;
15
16
  scrollbar-gutter: stable;
16
17
  scrollbar-color: #47474766 #252526;