mirror of
https://github.com/fluencelabs/fluent-pad
synced 2025-06-09 13:51:19 +00:00
better css
This commit is contained in:
parent
6d7b03c34e
commit
f7e57bf9b3
@ -3,6 +3,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tonsky/FiraCode@4/distr/fira_code.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta
|
||||||
|
@ -1,22 +1,223 @@
|
|||||||
.App {
|
$bg-color1: white;
|
||||||
background-color: #282c34;
|
$bg-color2: #f4f4f4;
|
||||||
min-height: 100vh;
|
$font-color1: black;
|
||||||
|
$font-color1-disabled: lightgray;
|
||||||
|
|
||||||
|
$color1: black;
|
||||||
|
$color2: rgb(214, 214, 214);
|
||||||
|
|
||||||
|
$accent-color: rgb(225, 30, 90);
|
||||||
|
|
||||||
|
$header-height: 60px;
|
||||||
|
$border-radius: 10px;
|
||||||
|
|
||||||
|
@mixin font($size) {
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-size: $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
$shadow: 0px 5px 20px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: $bg-color2;
|
||||||
|
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-wrapper {
|
||||||
|
padding: 0 20px;
|
||||||
|
height: $header-height;
|
||||||
|
|
||||||
|
background-color: $bg-color1;
|
||||||
|
color: $font-color1;
|
||||||
|
|
||||||
|
box-shadow: $shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
|
||||||
color: white;
|
width: 1200px;
|
||||||
|
height: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
@media (max-width: 1280px) {
|
||||||
|
width: calc(100vw - 80px);
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-right: 45px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.App div {
|
.content {
|
||||||
margin: 20px;
|
width: 1200px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
@media (max-width: 1280px) {
|
||||||
|
width: calc(100vw - 80px);
|
||||||
|
margin-left: 35px;
|
||||||
|
margin-right: 45px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
.welcome-form {
|
||||||
width: 500px;
|
display: block;
|
||||||
height: 200px;
|
|
||||||
|
width: 600px;
|
||||||
|
height: auto;
|
||||||
|
min-height: 300px;
|
||||||
|
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-top: 120px;
|
||||||
|
|
||||||
|
padding: 50px 80px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
box-shadow: $shadow;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
background-color: $bg-color1;
|
||||||
|
color: $font-color1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.fluent-pad {
|
||||||
display: flex;
|
display: inline-block;
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 40px;
|
||||||
|
height: 60px;
|
||||||
|
color: $accent-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userlist {
|
||||||
|
margin-top: 10px;
|
||||||
|
height: 30px;
|
||||||
|
|
||||||
|
& li {
|
||||||
|
display: inline;
|
||||||
|
margin-right: 10px;
|
||||||
|
@include font(14px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-editor {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 500px;
|
||||||
|
|
||||||
|
resize: none;
|
||||||
|
padding: 1em 0.5em 0.5em 0.5em;
|
||||||
|
|
||||||
|
box-shadow: $shadow;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
|
||||||
|
background-color: $bg-color1;
|
||||||
|
color: $font-color1;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-top: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
margin-top: 5px;
|
||||||
|
border-top: none;
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 2px solid $color2;
|
||||||
|
|
||||||
|
font-style: normal;
|
||||||
|
@include font(15px);
|
||||||
|
color: $color1;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: $color2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
outline: 1px solid white;
|
||||||
|
border-bottom: 2px solid $accent-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-caption {
|
||||||
|
color: $accent-color;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
@include font(20px);
|
||||||
|
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-button {
|
||||||
|
background-color: white;
|
||||||
|
color: $accent-color;
|
||||||
|
border: 2px solid $accent-color;
|
||||||
|
|
||||||
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 15px;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 46px;
|
||||||
|
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background-color: $accent-color;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-item {
|
||||||
|
@include font(14px);
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
& button {
|
||||||
|
@include font(14px);
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: $accent-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent {
|
||||||
|
color: $accent-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
@ -42,39 +42,57 @@ const App = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FluenceClientContext.Provider value={client}>
|
<FluenceClientContext.Provider value={client}>
|
||||||
<div className="App">
|
<div className="header-wrapper">
|
||||||
<div>
|
<div className="header">
|
||||||
<div>Connection status: {client ? 'connected' : 'disconnected'}</div>
|
<div className="header-item">
|
||||||
<div>
|
{isInRoom && (
|
||||||
<label>Nickname: </label>
|
<button className="button" disabled={!isInRoom} onClick={leaveRoom}>
|
||||||
<input
|
Leave
|
||||||
type="text"
|
</button>
|
||||||
value={nickName}
|
)}
|
||||||
disabled={isInRoom}
|
|
||||||
onChange={(e) => {
|
|
||||||
const name = e.target.value;
|
|
||||||
setNickName(name);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<button disabled={isInRoom || !client} onClick={joinRoom}>
|
<div className="header-item">
|
||||||
Join Room
|
<button className="button" onClick={() => calls.clean(client!)}>
|
||||||
|
Clean
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<button disabled={!isInRoom} onClick={leaveRoom}>
|
<div className="header-item">
|
||||||
Leave Room
|
Connection status: {client ? <span className="accent">connected</span> : 'disconnected'}
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button onClick={() => calls.clean(client!)}>Clean</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="content">
|
||||||
|
{!isInRoom && (
|
||||||
|
<div className="welcome-form">
|
||||||
|
<h1 className="form-caption">Welcome to FluentPad</h1>
|
||||||
|
<input
|
||||||
|
className="text-input"
|
||||||
|
placeholder="Your name"
|
||||||
|
type="text"
|
||||||
|
value={nickName}
|
||||||
|
disabled={isInRoom}
|
||||||
|
onChange={(e) => {
|
||||||
|
const name = e.target.value;
|
||||||
|
setNickName(name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="wrapper">
|
<button className="join-button" disabled={isInRoom || !client} onClick={joinRoom}>
|
||||||
<div>{isInRoom && client && <CollaborativeEditor />}</div>
|
Join
|
||||||
<div>{isInRoom && client && <UserList selfName={nickName} />}</div>
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isInRoom && (
|
||||||
|
<div className="room-wrapper">
|
||||||
|
<h1 className="fluent-pad">FluentPad</h1>
|
||||||
|
<UserList selfName={nickName} />
|
||||||
|
<CollaborativeEditor />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</FluenceClientContext.Provider>
|
</FluenceClientContext.Provider>
|
||||||
|
@ -64,9 +64,5 @@ export const CollaborativeEditor = () => {
|
|||||||
broadcastUpdates(newText, syncClient);
|
broadcastUpdates(newText, syncClient);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <textarea spellCheck={false} className="code-editor" value={text} onChange={handleTextUpdate} />;
|
||||||
<div>
|
|
||||||
<textarea value={text} onChange={handleTextUpdate} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -9,8 +9,10 @@ import { useFluenceClient } from '../app/FluenceClientContext';
|
|||||||
import * as calls from 'src/app/api';
|
import * as calls from 'src/app/api';
|
||||||
import { subscribeToEvent } from '@fluencelabs/fluence';
|
import { subscribeToEvent } from '@fluencelabs/fluence';
|
||||||
|
|
||||||
|
type PeerId = string;
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: string;
|
id: PeerId;
|
||||||
name: string;
|
name: string;
|
||||||
isOnline: boolean;
|
isOnline: boolean;
|
||||||
shouldBecomeOnline: boolean;
|
shouldBecomeOnline: boolean;
|
||||||
@ -24,8 +26,6 @@ const turnUserAsOfflineCandidate = (u: User): User => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type PeerId = string;
|
|
||||||
|
|
||||||
const refreshTimeoutMs = 2000;
|
const refreshTimeoutMs = 2000;
|
||||||
|
|
||||||
export const UserList = (props: { selfName: string }) => {
|
export const UserList = (props: { selfName: string }) => {
|
||||||
@ -112,14 +112,12 @@ export const UserList = (props: { selfName: string }) => {
|
|||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="userlist">
|
||||||
<ul>
|
<ul>
|
||||||
{usersArray.map((x) => (
|
{usersArray.map((x) => (
|
||||||
<li key={x.id}>
|
<li key={x.id}>
|
||||||
{x.name}{' '}
|
<span className={x.id === client.selfPeerId.toB58String() ? 'bold' : ''}>{x.name}</span>
|
||||||
<span style={{ color: x.isOnline ? 'green' : 'red' }}>
|
<span className={x.isOnline ? 'green' : 'red'}> ({x.isOnline ? 'online' : 'offline'})</span>
|
||||||
({x.isOnline ? 'online' : 'offline'})
|
|
||||||
</span>
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user