zack commited on
Commit
3fde1d6
1 Parent(s): 651209c

✨ Enhance navbar responsiveness and UI/UX improvements

Browse files
frontend/src/components/Navagation/navbar.js CHANGED
@@ -5,30 +5,31 @@ import { random_colour, random_emoji } from "../../helper/visual";
5
 
6
  import "../../css/dist/output.css"
7
 
8
- import {BsArrowLeftShort} from 'react-icons/bs';
9
- import {ReactComponent as ReactLogo} from '../../images/logo.svg'
 
10
 
11
- export default class Navbar extends Component{
12
- constructor(props){
13
- super(props)
14
  this.temp_host = 0
15
  this.deleteNode = props.onDelete
16
  this.state = {
17
- open : true,
18
- menu : [],
19
- colour : props.colour || [],
20
- text : "",
21
- name : "",
22
- emoji : props.emoji || [],
23
- mode : false,
24
- modal : false,
25
- error : false
26
- }
27
-
28
  }
29
 
30
- componentDidMount(){
31
- this.fetch_classes()
32
 
33
  }
34
 
@@ -38,17 +39,17 @@ export default class Navbar extends Component{
38
  */
39
  fetch_classes = async () => {
40
  try {
41
- setInterval( async () => {
42
- await fetch("http://localhost:2000/api/open/ports", { method: 'GET', mode : 'cors',})
43
- .then(response => response.json())
44
- .then(data => {
45
- this.handelTabs(this.state.menu,data, data)
46
- this.setState({menu : data})
47
- })
48
- .catch(error => {console.log(error)})
49
-
50
- },1000);
51
- }catch(e){
52
  console.log(e)
53
  }
54
  }
@@ -58,33 +59,34 @@ export default class Navbar extends Component{
58
  */
59
  appendStreamNode = async (type) => {
60
  const pattern = {
61
- local : new RegExp('^https?://(localhost)(:[0-9]+)?(/)?$'),
62
- share : new RegExp('^https?://(?:[a-zA-Z0-9]+\\.gradio\\.live)/?$'),
63
  huggingFace: new RegExp('^https?://([a-zA-Z0-9-]+\\.hf\\.space)/?$'),
64
  proxmoxVNC: new RegExp('^wss?://([a-zA-Z0-9-]+\\.yourdomain\\.com)/?$') // Regex pattern for Proxmox VNC URLs
65
- }
66
 
67
  if (this.state.name.length > 20 ||
68
- this.state.text === ""||
69
- this.state.menu.findIndex(element => {return element.name.toLowerCase() === this.state.name.toLowerCase() || element.host.includes(this.state.text) }) !== -1 ||
70
- this.state.text.includes(" ") ||
71
  (!pattern.local.test(this.state.text) &&
72
- !pattern.share.test(this.state.text) &&
73
- !pattern.huggingFace.test(this.state.text) &&
74
- !pattern.proxmoxVNC.test(this.state.text))){
75
-
76
- this.setState({
77
- 'text': '',
78
- 'name': '',
79
- 'error': true})
80
- return
81
- }
82
- fetch(this.state.text, {method: "GET", mode: 'no-cors'}).then((re) => {
83
- fetch("http://localhost:2000/api/append/port", {method: 'POST', mode : 'cors', headers : { 'Content-Type' : 'application/json' }, body: JSON.stringify({file : "", kwargs : { type : type }, name : this.state.name === "" ?`temp_class_${this.temp_host++}` : `${this.state.name}`, port: 0 , host : this.state.text}) }).then(resp => {
84
- this.setState({'text': "",'name' : "",'error' : false,'modal' : false })
85
-
86
- }).catch(() => this.setState({'text': '', 'name' : '', 'error' : true, }))
87
- }).catch((err)=> this.setState({'text': '','name' : '', 'error' : true,}))
 
88
  }
89
 
90
  /**
@@ -96,11 +98,11 @@ export default class Navbar extends Component{
96
  if (!proxmoxVncInfo) return null; // If no Proxmox VNC info, don't render anything
97
 
98
  return (
99
- <li onDragStart={(event) => this.onDragStart(event, 'proxmoxVNC', proxmoxVncInfo, -1)}
100
  className="text-white text-md flex flex-col text-center items-center cursor-grab shadow-lg p-5 px-2 mt-4 rounded-md hover:animate-pulse"
101
  draggable>
102
  <div className="absolute -mt-2 text-4xl opacity-60 z-10">{random_emoji()}</div>
103
- <h4 className="max-w-full font-sans text-blue-50 leading-tight font-bold text-xl flex-1 z-20" style={{"textShadow": "0px 1px 2px rgba(0, 0, 0, 0.25)"}}>
104
  Proxmox VNC
105
  </h4>
106
  </li>
@@ -112,8 +114,10 @@ export default class Navbar extends Component{
112
  * @param {*} bool boolean of the current state of the modal
113
  */
114
  handelModal = (bool) => {
115
- this.setState({'error' : !bool ? false : this.state.error ,
116
- 'modal' : bool})
 
 
117
  }
118
 
119
  /**
@@ -125,10 +129,10 @@ export default class Navbar extends Component{
125
  */
126
  onDragStart = (event, nodeType, item, index) => {
127
  event.dataTransfer.setData('application/reactflow', nodeType);
128
- event.dataTransfer.setData('application/style', JSON.stringify({colour : this.state.colour[index], emoji : this.state.emoji[index] }))
129
- event.dataTransfer.setData('application/item', JSON.stringify(item))
130
  event.dataTransfer.effectAllowed = 'move';
131
- };
132
 
133
  /**
134
  * droped event that occurs when the user drops the Tab within the tash div.
@@ -138,11 +142,11 @@ export default class Navbar extends Component{
138
  */
139
  onDragDrop = (e) => {
140
  e.preventDefault();
141
- var item = JSON.parse(e.dataTransfer.getData('application/item'));
142
- fetch("http://localhost:2000/api/remove/port", {method : "POST", mode: 'cors', headers : { 'Content-Type' : 'application/json' }, body: JSON.stringify(item) }).then((re)=>{
143
  this.deleteNode(item.name)
144
  })
145
-
146
  }
147
 
148
  /**
@@ -157,37 +161,37 @@ export default class Navbar extends Component{
157
  var c = []
158
  var j = []
159
  if (d.length - e.length === 0) return
160
- else if(d.length - e.length < 0){
161
  var a = this.state.menu.filter(item => e.includes(item)) // get the items not in menu anymore
162
  c = this.state.colour
163
  j = this.state.emoji
164
- for(var k=0; k < d.length; k++){
165
  c.splice(this.state.menu.indexOf(a[k]), 1)
166
  j.splice(this.state.menu.indexOf(a[k]), 1)
167
  }
168
- this.setState({'colour' : c, 'emoji' : j})
169
- }else{
170
  //append new colours
171
- for(var i =0; i < d.length; i++){
172
- c.push(random_colour(i === 0 ? null : c[i-1]));
173
- j.push(random_emoji(i === 0 ? null : c[i-1]));
174
-
175
  }
176
  const colour = [...this.state.colour]
177
- const emoji = [...this.state.emoji]
178
- this.setState({'colour' : [...colour, ...c], 'emoji' : [...emoji, ...j],})
179
  }
180
  }
181
 
182
  handelError = (boolean) => {
183
- this.setState({'error' : boolean})
184
  }
185
 
186
  /**
187
  * handel navagation open and close function
188
  */
189
  handelNavbar = () => {
190
- this.setState({'open' : !this.state.open})
191
  }
192
 
193
  /**
@@ -196,7 +200,7 @@ export default class Navbar extends Component{
196
  * @param {*} type : text | name string that set the changed value of the input to the current value
197
  */
198
  updateText = (e, type) => {
199
- this.setState({[`${type}`] : e.target.value })
200
  }
201
 
202
  /**
@@ -205,59 +209,62 @@ export default class Navbar extends Component{
205
  * @param {*} index : current index with in the list
206
  * @returns div component that contians infomation of gradio
207
  */
208
- subComponents(item, index){
209
-
210
- return(<>
211
- <li key={`${index}-li`} onDragStart={(event) => this.onDragStart(event, 'custom', item, index)}
212
- className={` text-white text-md flex flex-col text-center items-center cursor-grab shadow-lg
213
- p-5 px-2 mt-4 rounded-md ${ this.state.open ? `hover:animate-pulse ${this.state.colour[index] === null ? "" : this.state.colour[index]} ` : `hidden`} break-all -z-20`} draggable>
214
 
215
- <div key={`${index}-div`} className=" absolute -mt-2 text-4xl opacity-60 z-10 ">{`${this.state.emoji[index] === null ? "" : this.state.emoji[index]}`}</div>
216
- <h4 key={`${index}-h4`} className={` max-w-full font-sans text-blue-50 leading-tight font-bold text-xl flex-1 z-20 ${this.state.open ? "" : "hidden"}`} style={{"textShadow" : "0px 1px 2px rgba(0, 0, 0, 0.25)"}} >{`${item.name}`} </h4>
217
 
218
- </li >
219
 
220
  </>)
221
  }
222
 
223
 
224
- render(){
225
-
226
  return (<div>
227
-
228
- <div className={`z-10 flex-1 float-left bg-white dark:bg-stone-900 h-screen p-5 pt-8 ${this.state.open ? "lg:w-72 md:64 sm:w-60" : "w-10"} duration-300 absolute shadow-2xl border-black border-r-[1px] dark:border-white dark:text-white`} >
229
 
230
- <BsArrowLeftShort onClick={this.handelNavbar} className={` bg-white text-Retro-darl-blue text-3xl rounded-full absolute -right-3 top-9 border border-black cursor-pointer ${!this.state.open && 'rotate-180'} dark:border-white duration-300 dark:text-white dark:bg-stone-900 `}/>
231
 
 
232
  <div className="inline-flex w-full">
233
- <h1 className={`font-sans font-bold text-lg ${this.state.open ? "" : "hidden"} duration-500 ml-auto mr-auto`}> <ReactLogo className="w-9 h-9 ml-auto mr-auto"/> Gradio Flow </h1>
 
 
 
 
234
  </div>
235
 
236
- <div className={`rounded-md text-center ${this.state.open ? "" : "px-0"} py-3`} onClick={() => {this.handelModal(true)}}>
237
  <div className={` text-center bg-transparent w-full h-10 border border-slate-300 hover:border-Retro-purple hover:animate-pulse border-dashed rounded-md py-2 pl-5 ${this.state.open ? "pr-3" : "hidden"} shadow-sm sm:text-sm`}>
238
- <Icon className=" block mr-auto ml-auto" name="plus"/>
239
  </div>
240
  </div>
241
- <Import open={this.state.modal}
242
- quitHandeler={this.handelModal}
243
- textHandler={this.updateText}
244
- appendHandler={this.appendStreamNode}
245
- handelError={this.handelError}
246
- catch={this.state.error}/>
247
-
248
  <div className=" relative z-10 h-auto overflow-auto pt-4">
249
  <ul className="pt-2">
250
- {this.state.menu.map((menu, index) => {return this.subComponents(menu, index)})}
251
  </ul>
252
  </div>
253
 
254
- <div className={`${this.state.open ? "" : "hidden"} absolute bottom-0 left-0 w-full text-center p-5`} onDragOver={(e)=> {e.preventDefault()}} onDrop={(e)=>{this.onDragDrop(e)}}>
255
  <div className={` text-center bg-transparent w-full h-10 border border-red-600 border-dashed rounded-md py-2 pl-5 p-4 ${this.state.open ? "pr-3" : "hidden"} shadow-sm sm:text-sm`}>
256
- <Icon name='trash alternate' />
257
  </div>
258
  </div>
259
  </div>
260
-
261
  </div>)
262
  }
263
  }
 
5
 
6
  import "../../css/dist/output.css"
7
 
8
+ import { BsArrowLeftShort } from 'react-icons/bs';
9
+ import { ReactComponent as ReactLogo } from '../../images/logo.svg'
10
+ import chattyImage from '../../images/chatty.png';
11
 
12
+ export default class Navbar extends Component {
13
+ constructor(props) {
14
+ super(props)
15
  this.temp_host = 0
16
  this.deleteNode = props.onDelete
17
  this.state = {
18
+ open: true,
19
+ menu: [],
20
+ colour: props.colour || [],
21
+ text: "",
22
+ name: "",
23
+ emoji: props.emoji || [],
24
+ mode: false,
25
+ modal: false,
26
+ error: false
27
+ }
28
+
29
  }
30
 
31
+ componentDidMount() {
32
+ this.fetch_classes()
33
 
34
  }
35
 
 
39
  */
40
  fetch_classes = async () => {
41
  try {
42
+ setInterval(async () => {
43
+ await fetch("http://localhost:2000/api/open/ports", { method: 'GET', mode: 'cors', })
44
+ .then(response => response.json())
45
+ .then(data => {
46
+ this.handelTabs(this.state.menu, data, data)
47
+ this.setState({ menu: data })
48
+ })
49
+ .catch(error => { console.log(error) })
50
+
51
+ }, 1000);
52
+ } catch (e) {
53
  console.log(e)
54
  }
55
  }
 
59
  */
60
  appendStreamNode = async (type) => {
61
  const pattern = {
62
+ local: new RegExp('^https?://(localhost)(:[0-9]+)?(/)?$'),
63
+ share: new RegExp('^https?://(?:[a-zA-Z0-9]+\\.gradio\\.live)/?$'),
64
  huggingFace: new RegExp('^https?://([a-zA-Z0-9-]+\\.hf\\.space)/?$'),
65
  proxmoxVNC: new RegExp('^wss?://([a-zA-Z0-9-]+\\.yourdomain\\.com)/?$') // Regex pattern for Proxmox VNC URLs
66
+ }
67
 
68
  if (this.state.name.length > 20 ||
69
+ this.state.text === "" ||
70
+ this.state.menu.findIndex(element => { return element.name.toLowerCase() === this.state.name.toLowerCase() || element.host.includes(this.state.text) }) !== -1 ||
71
+ this.state.text.includes(" ") ||
72
  (!pattern.local.test(this.state.text) &&
73
+ !pattern.share.test(this.state.text) &&
74
+ !pattern.huggingFace.test(this.state.text) &&
75
+ !pattern.proxmoxVNC.test(this.state.text))) {
76
+
77
+ this.setState({
78
+ 'text': '',
79
+ 'name': '',
80
+ 'error': true
81
+ })
82
+ return
83
+ }
84
+ fetch(this.state.text, { method: "GET", mode: 'no-cors' }).then((re) => {
85
+ fetch("http://localhost:2000/api/append/port", { method: 'POST', mode: 'cors', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ file: "", kwargs: { type: type }, name: this.state.name === "" ? `temp_class_${this.temp_host++}` : `${this.state.name}`, port: 0, host: this.state.text }) }).then(resp => {
86
+ this.setState({ 'text': "", 'name': "", 'error': false, 'modal': false })
87
+
88
+ }).catch(() => this.setState({ 'text': '', 'name': '', 'error': true, }))
89
+ }).catch((err) => this.setState({ 'text': '', 'name': '', 'error': true, }))
90
  }
91
 
92
  /**
 
98
  if (!proxmoxVncInfo) return null; // If no Proxmox VNC info, don't render anything
99
 
100
  return (
101
+ <li onDragStart={(event) => this.onDragStart(event, 'proxmoxVNC', proxmoxVncInfo, -1)}
102
  className="text-white text-md flex flex-col text-center items-center cursor-grab shadow-lg p-5 px-2 mt-4 rounded-md hover:animate-pulse"
103
  draggable>
104
  <div className="absolute -mt-2 text-4xl opacity-60 z-10">{random_emoji()}</div>
105
+ <h4 className="max-w-full font-sans text-blue-50 leading-tight font-bold text-xl flex-1 z-20" style={{ "textShadow": "0px 1px 2px rgba(0, 0, 0, 0.25)" }}>
106
  Proxmox VNC
107
  </h4>
108
  </li>
 
114
  * @param {*} bool boolean of the current state of the modal
115
  */
116
  handelModal = (bool) => {
117
+ this.setState({
118
+ 'error': !bool ? false : this.state.error,
119
+ 'modal': bool
120
+ })
121
  }
122
 
123
  /**
 
129
  */
130
  onDragStart = (event, nodeType, item, index) => {
131
  event.dataTransfer.setData('application/reactflow', nodeType);
132
+ event.dataTransfer.setData('application/style', JSON.stringify({ colour: this.state.colour[index], emoji: this.state.emoji[index] }))
133
+ event.dataTransfer.setData('application/item', JSON.stringify(item))
134
  event.dataTransfer.effectAllowed = 'move';
135
+ };
136
 
137
  /**
138
  * droped event that occurs when the user drops the Tab within the tash div.
 
142
  */
143
  onDragDrop = (e) => {
144
  e.preventDefault();
145
+ var item = JSON.parse(e.dataTransfer.getData('application/item'));
146
+ fetch("http://localhost:2000/api/remove/port", { method: "POST", mode: 'cors', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(item) }).then((re) => {
147
  this.deleteNode(item.name)
148
  })
149
+
150
  }
151
 
152
  /**
 
161
  var c = []
162
  var j = []
163
  if (d.length - e.length === 0) return
164
+ else if (d.length - e.length < 0) {
165
  var a = this.state.menu.filter(item => e.includes(item)) // get the items not in menu anymore
166
  c = this.state.colour
167
  j = this.state.emoji
168
+ for (var k = 0; k < d.length; k++) {
169
  c.splice(this.state.menu.indexOf(a[k]), 1)
170
  j.splice(this.state.menu.indexOf(a[k]), 1)
171
  }
172
+ this.setState({ 'colour': c, 'emoji': j })
173
+ } else {
174
  //append new colours
175
+ for (var i = 0; i < d.length; i++) {
176
+ c.push(random_colour(i === 0 ? null : c[i - 1]));
177
+ j.push(random_emoji(i === 0 ? null : c[i - 1]));
178
+
179
  }
180
  const colour = [...this.state.colour]
181
+ const emoji = [...this.state.emoji]
182
+ this.setState({ 'colour': [...colour, ...c], 'emoji': [...emoji, ...j], })
183
  }
184
  }
185
 
186
  handelError = (boolean) => {
187
+ this.setState({ 'error': boolean })
188
  }
189
 
190
  /**
191
  * handel navagation open and close function
192
  */
193
  handelNavbar = () => {
194
+ this.setState({ 'open': !this.state.open })
195
  }
196
 
197
  /**
 
200
  * @param {*} type : text | name string that set the changed value of the input to the current value
201
  */
202
  updateText = (e, type) => {
203
+ this.setState({ [`${type}`]: e.target.value })
204
  }
205
 
206
  /**
 
209
  * @param {*} index : current index with in the list
210
  * @returns div component that contians infomation of gradio
211
  */
212
+ subComponents(item, index) {
213
+
214
+ return (<>
215
+ <li key={`${index}-li`} onDragStart={(event) => this.onDragStart(event, 'custom', item, index)}
216
+ className={` text-white text-md flex flex-col text-center items-center cursor-grab shadow-lg
217
+ p-5 px-2 mt-4 rounded-md ${this.state.open ? `hover:animate-pulse ${this.state.colour[index] === null ? "" : this.state.colour[index]} ` : `hidden`} break-all -z-20`} draggable>
218
 
219
+ <div key={`${index}-div`} className=" absolute -mt-2 text-4xl opacity-60 z-10 ">{`${this.state.emoji[index] === null ? "" : this.state.emoji[index]}`}</div>
220
+ <h4 key={`${index}-h4`} className={` max-w-full font-sans text-blue-50 leading-tight font-bold text-xl flex-1 z-20 ${this.state.open ? "" : "hidden"}`} style={{ "textShadow": "0px 1px 2px rgba(0, 0, 0, 0.25)" }} >{`${item.name}`} </h4>
221
 
222
+ </li >
223
 
224
  </>)
225
  }
226
 
227
 
228
+ render() {
229
+
230
  return (<div>
 
 
231
 
232
+ <div className={`z-10 flex-1 float-left bg-white dark:bg-stone-900 h-screen p-5 pt-8 ${this.state.open ? "lg:w-72 md:64 sm:w-60" : "w-10"} duration-300 absolute shadow-2xl border-black border-r-[1px] dark:border-white dark:text-white`} >
233
 
234
+ <BsArrowLeftShort onClick={this.handelNavbar} className={` bg-white text-Retro-darl-blue text-3xl rounded-full absolute -right-3 top-9 border border-black cursor-pointer ${!this.state.open && 'rotate-180'} dark:border-white duration-300 dark:text-white dark:bg-stone-900 `} />
235
  <div className="inline-flex w-full">
236
+ <img src={chattyImage} alt="Chat'nFace Logo" className={`h-12 w-auto ${this.state.open ? "visible" : "hidden"}`} /> {/* Display the image */}
237
+ <h1 className={`font-sans font-bold text-lg ${this.state.open ? "" : "hidden"} duration-500 ml-auto mr-auto`}>Chat-nFace</h1>
238
+ </div>
239
+ <div className="inline-flex w-full">
240
+ <h1 className={`font-sans font-bold text-lg ${this.state.open ? "" : "hidden"} duration-500 ml-auto mr-auto`}>Chat-nFace</h1>
241
  </div>
242
 
243
+ <div className={`rounded-md text-center ${this.state.open ? "" : "px-0"} py-3`} onClick={() => { this.handelModal(true) }}>
244
  <div className={` text-center bg-transparent w-full h-10 border border-slate-300 hover:border-Retro-purple hover:animate-pulse border-dashed rounded-md py-2 pl-5 ${this.state.open ? "pr-3" : "hidden"} shadow-sm sm:text-sm`}>
245
+ <Icon className=" block mr-auto ml-auto" name="plus" />
246
  </div>
247
  </div>
248
+ <Import open={this.state.modal}
249
+ quitHandeler={this.handelModal}
250
+ textHandler={this.updateText}
251
+ appendHandler={this.appendStreamNode}
252
+ handelError={this.handelError}
253
+ catch={this.state.error} />
254
+
255
  <div className=" relative z-10 h-auto overflow-auto pt-4">
256
  <ul className="pt-2">
257
+ {this.state.menu.map((menu, index) => { return this.subComponents(menu, index) })}
258
  </ul>
259
  </div>
260
 
261
+ <div className={`${this.state.open ? "" : "hidden"} absolute bottom-0 left-0 w-full text-center p-5`} onDragOver={(e) => { e.preventDefault() }} onDrop={(e) => { this.onDragDrop(e) }}>
262
  <div className={` text-center bg-transparent w-full h-10 border border-red-600 border-dashed rounded-md py-2 pl-5 p-4 ${this.state.open ? "pr-3" : "hidden"} shadow-sm sm:text-sm`}>
263
+ <Icon name='trash alternate' />
264
  </div>
265
  </div>
266
  </div>
267
+
268
  </div>)
269
  }
270
  }