Attempt to slightly improve the sidebar styling

master
Torjus 2018-08-08 13:11:52 +02:00
parent c8d716d43b
commit eab1568544
9 changed files with 240 additions and 365 deletions

View File

@ -1,131 +0,0 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import * as stateActions from '../redux/stateActions';
import * as requestActions from '../redux/requestActions';
import MessageList from './Chat/MessageList';
class ChatWidget extends Component
{
componentWillReceiveProps(nextProps)
{
if (nextProps.chatmessages.length !== this.props.chatmessages.length)
if (!this.props.showChat)
this.props.increaseBadge();
}
render()
{
const {
senderPlaceHolder,
onSendMessage,
onToggleChat,
showChat,
disabledInput,
badge,
autofocus,
displayName
} = this.props;
return (
<div data-component='ChatWidget'>
{
showChat &&
<div data-component='Conversation'>
<MessageList />
<form
data-component='Sender'
onSubmit={(e) => { onSendMessage(e, displayName); }}
>
<input
type='text'
className='new-message'
name='message'
placeholder={senderPlaceHolder}
disabled={disabledInput}
autoFocus={autofocus}
autoComplete='off'
/>
</form>
</div>
}
{
<div
className='launcher'
data-type='dark'
data-tip='Show room chat'
onClick={onToggleChat}
>
{
badge > 0 && <span className='badge'>{badge}</span>
}
</div>
}
</div>
);
}
}
ChatWidget.propTypes =
{
onToggleChat : PropTypes.func,
showChat : PropTypes.bool,
senderPlaceHolder : PropTypes.string,
onSendMessage : PropTypes.func,
disabledInput : PropTypes.bool,
badge : PropTypes.number,
autofocus : PropTypes.bool,
displayName : PropTypes.string,
chatmessages : PropTypes.arrayOf(PropTypes.object),
increaseBadge : PropTypes.func
};
ChatWidget.defaultProps =
{
senderPlaceHolder : 'Type a message...',
autofocus : true
};
const mapStateToProps = (state) =>
{
return {
showChat : state.chatbehavior.showChat,
disabledInput : state.chatbehavior.disabledInput,
displayName : state.me.displayName,
badge : state.chatbehavior.badge,
chatmessages : state.chatmessages
};
};
const mapDispatchToProps = (dispatch) =>
{
return {
onToggleChat : () =>
{
dispatch(stateActions.toggleChat());
},
onSendMessage : (event, displayName) =>
{
event.preventDefault();
const userInput = event.target.message.value;
if (userInput)
{
dispatch(stateActions.addUserMessage(userInput));
dispatch(requestActions.sendChatMessage(userInput, displayName));
}
event.target.message.value = '';
},
increaseBadge : () =>
{
dispatch(stateActions.increaseBadge());
}
};
};
const ChatWidgetContainer = connect(
mapStateToProps,
mapDispatchToProps
)(ChatWidget);
export default ChatWidgetContainer;

View File

@ -65,7 +65,6 @@ class Room extends React.Component
{ {
const { const {
room, room,
toolAreaOpen,
amActiveSpeaker, amActiveSpeaker,
onRoomLinkCopy onRoomLinkCopy
} = this.props; } = this.props;
@ -155,16 +154,8 @@ class Room extends React.Component
delayHide={100} delayHide={100}
/> />
</div> </div>
<div
className={classnames('toolarea-wrapper', { open: toolAreaOpen })} <ToolArea />
>
{toolAreaOpen ?
<ToolArea
advancedMode={room.advancedMode}
/>
:null
}
</div>
</div> </div>
</Appear> </Appear>
</Fragment> </Fragment>

View File

@ -0,0 +1,41 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import classNames from 'classnames';
import * as stateActions from '../../redux/stateActions';
const TabHeader = ({ currentToolTab, setToolTab, id, name, badge }) => (
<div
className={classNames('tab-header', {
checked : currentToolTab === id
})}
onClick={() => setToolTab(id)}
>
{name}
{badge > 0 && (
<span className='badge'>{badge}</span>
)}
</div>
);
TabHeader.propTypes = {
currentToolTab : PropTypes.string.isRequired,
setToolTab : PropTypes.func.isRequired,
id : PropTypes.string.isRequired,
name : PropTypes.string.isRequired,
badge : PropTypes.number
};
const mapStateToProps = (state) => ({
currentToolTab : state.toolarea.currentToolTab
});
const mapDispatchToProps = {
setToolTab : stateActions.setToolTab
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(TabHeader);

View File

@ -1,11 +1,13 @@
import React from 'react'; import React, { Fragment } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames';
import * as toolTabActions from '../../redux/stateActions'; import * as toolTabActions from '../../redux/stateActions';
import ParticipantList from '../ParticipantList/ParticipantList'; import ParticipantList from '../ParticipantList/ParticipantList';
import Chat from '../Chat/Chat'; import Chat from '../Chat/Chat';
import Settings from '../Settings'; import Settings from '../Settings';
import FileSharing from '../FileSharing'; import FileSharing from '../FileSharing';
import TabHeader from './TabHeader';
class ToolArea extends React.Component class ToolArea extends React.Component
{ {
@ -18,88 +20,61 @@ class ToolArea extends React.Component
{ {
const { const {
currentToolTab, currentToolTab,
toolAreaOpen,
unreadMessages, unreadMessages,
unreadFiles, unreadFiles
setToolTab
} = this.props; } = this.props;
const VisibleTab = {
chat : Chat,
files : FileSharing,
users : ParticipantList,
settings : Settings
}[currentToolTab];
return ( return (
<div data-component='ToolArea'> <Fragment>
<div className='tabs'> <div
<input className={classNames('toolarea-shade', {
type='radio' open : toolAreaOpen
name='tabs' })}
id='tab-chat' />
onChange={() =>
{
setToolTab('chat');
}}
checked={currentToolTab === 'chat'}
/>
<label htmlFor='tab-chat'>
Chat
{unreadMessages > 0 && (
<span className='badge'>{unreadMessages}</span>
)}
</label>
<div className='tab'> <div
<Chat /> data-component='ToolArea'
className={classNames({
open : toolAreaOpen
})}
>
<div className='tab-headers'>
<TabHeader
id='chat'
name='Chat'
badge={unreadMessages}
/>
<TabHeader
id='files'
name='Files'
badge={unreadFiles}
/>
<TabHeader
id='users'
name='Users'
/>
<TabHeader
id='settings'
name='Settings'
/>
</div> </div>
<input
type='radio'
name='tabs'
id='tab-files'
onChange={() => setToolTab('files')}
checked={currentToolTab === 'files'}
/>
<label htmlFor='tab-files'>
Files
{unreadFiles > 0 && (
<span className='badge'>{unreadFiles}</span>
)}
</label>
<div className='tab'> <div className='tab'>
<FileSharing /> <VisibleTab />
</div>
<input
type='radio'
name='tabs'
id='tab-users'
onChange={() =>
{
setToolTab('users');
}}
checked={currentToolTab === 'users'}
/>
<label htmlFor='tab-users'>Users</label>
<div className='tab'>
<ParticipantList />
</div>
<input
type='radio'
name='tabs'
id='tab-settings'
onChange={() =>
{
setToolTab('settings');
}}
checked={currentToolTab === 'settings'}
/>
<label htmlFor='tab-settings'>Settings</label>
<div className='tab'>
<Settings />
</div> </div>
</div> </div>
</div> </Fragment>
); );
} }
} }
@ -110,13 +85,15 @@ ToolArea.propTypes =
currentToolTab : PropTypes.string.isRequired, currentToolTab : PropTypes.string.isRequired,
setToolTab : PropTypes.func.isRequired, setToolTab : PropTypes.func.isRequired,
unreadMessages : PropTypes.number.isRequired, unreadMessages : PropTypes.number.isRequired,
unreadFiles : PropTypes.number.isRequired unreadFiles : PropTypes.number.isRequired,
toolAreaOpen : PropTypes.bool
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
currentToolTab : state.toolarea.currentToolTab, currentToolTab : state.toolarea.currentToolTab,
unreadMessages : state.toolarea.unreadMessages, unreadMessages : state.toolarea.unreadMessages,
unreadFiles : state.toolarea.unreadFiles unreadFiles : state.toolarea.unreadFiles,
toolAreaOpen : state.toolarea.toolAreaOpen
}); });
const mapDispatchToProps = { const mapDispatchToProps = {

View File

@ -1,76 +1,22 @@
[data-component='ChatWidget'] {
position: absolute;
bottom: 0;
display: flex;
flex-direction: column;
margin: 0 10px 10px 0;
max-width: 300px;
right: 0;
width: 90vw;
z-index: 100;
> .launcher {
align-self: flex-end;
margin-top: 10px;
background-position: center;
background-size: 70%;
background-repeat: no-repeat;
background-color: rgba(#fff, 0.3);
background-image: url('/resources/images/chat-icon.svg');
cursor: pointer;
transition-property: opacity, background-color;
transition-duration: 0.15s;
border-radius: 100%;
height: 45px;
width: 45px;
position: relative;
&.focus {
outline: none;
}
&.on {
background-color: rgba(#fff, 0.7);
}
&.disabled {
pointer-events: none;
opacity: 0.5;
}
> .badge{
border-radius: 50%;
padding: 0.7vmin;
top: -1vmin;
font-size: 1.5vmin;
left: -1vmin;
background: rgba(255,0,0,0.9);
color: #fff;
font-weight: bold;
position: absolute;
}
}
}
[data-component='Conversation'] {
border-radius: 5px;
box-shadow: 0px 2px 10px 1px #000;
}
[data-component='Chat'] { [data-component='Chat'] {
height: 100%; height: 100%;
display: flex;
flex-grow: 1;
flex-direction: column;
} }
[data-component='MessageList'] { [data-component='MessageList'] {
background-color: rgba(#000, 0.1);
height: 91vmin;
overflow-y: scroll; overflow-y: scroll;
padding-top: 5px; flex-grow: 1;
border-radius: 5px 5px 0px 0px;
> .message { > .message {
margin: 5px;
display: flex; display: flex;
word-wrap: break-word; word-wrap: break-word;
word-break: break-all;
&:not(:first-child) {
margin-top: 0.5rem;
}
> .client { > .client {
margin-left: auto; margin-left: auto;
@ -79,10 +25,10 @@
> .client, > .response { > .client, > .response {
background-color: rgba(#000, 0.1); background-color: rgba(#000, 0.1);
border-radius: 5px; border-radius: 5px;
max-width: 215px; max-width: 85%;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 6px; padding: 0.5rem;
> .message-avatar { > .message-avatar {
height: 2rem; height: 2rem;
@ -90,14 +36,14 @@
} }
> .message-content { > .message-content {
padding-left: 6px; padding-left: 0.5rem;
> .message-text { > .message-text {
font-size: 1.3vmin; font-size: 1rem;
} }
> .message-time { > .message-time {
font-size: 1vmin; font-size: 0.8rem;
opacity: 0.8; opacity: 0.8;
} }
} }
@ -106,25 +52,22 @@
} }
[data-component='Sender'] { [data-component='Sender'] {
align-items: center;
display: flex; display: flex;
background-color: rgba(#000, 0.1); background-color: rgba(0, 0, 0, 0.1);
height: 6vmin; padding: 1rem;
padding: 0.5vmin; flex-shrink: 0;
border-radius: 0 0 5px 5px; border-radius: 5px;
margin-top: 0.5rem;
height: 3rem;
> .new-message { > .new-message {
width: 100%; width: 100%;
border: 0; border: 0;
border-radius: 5px; color: #FFF;
background-color: rgba(#000, 0.1); font-size: 1rem;
color: #fff;
height: 30px;
padding-left: 10px;
font-size: 1.4vmin;
&.focus { &.focus {
outline: none; outline: none;
} }
} }
} }

View File

@ -1,7 +1,7 @@
[data-component='FileSharing'] { [data-component='FileSharing'] {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; flex-grow: 1;
> .sharing-toolbar { > .sharing-toolbar {
> .share-file { > .share-file {
@ -22,6 +22,7 @@
> .shared-files { > .shared-files {
flex-grow: 1; flex-grow: 1;
overflow-y: scroll; overflow-y: scroll;
margin-top: 0.5rem;
> .file-entry { > .file-entry {
background-color: rgba(0,0,0,0.1); background-color: rgba(0,0,0,0.1);
@ -29,7 +30,10 @@
width: 100%; width: 100%;
padding: 0.5rem; padding: 0.5rem;
display: flex; display: flex;
margin-top: 0.5rem;
&:not(:first-child) {
margin-top: 0.5rem;
}
&:last-child { &:last-child {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;

View File

@ -6,7 +6,7 @@
0 4px 20px 0 rgba(0,0,0,0.19); 0 4px 20px 0 rgba(0,0,0,0.19);
> .list-item { > .list-item {
padding: 0.5vmin; padding: 0.5rem;
border-bottom: 1px solid #CBCBCB; border-bottom: 1px solid #CBCBCB;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
@ -31,15 +31,14 @@
left: 0; left: 0;
top: 0; top: 0;
display: flex; display: flex;
flex-direction:; row; flex-direction: row;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
padding: 0.4vmin;
transition: opacity 0.3s; transition: opacity 0.3s;
> .icon { > .icon {
flex: 0 0 auto; flex: 0 0 auto;
margin: 0.2vmin; margin: 0.3rem;
border-radius: 2px; border-radius: 2px;
background-position: center; background-position: center;
background-size: 75%; background-size: 75%;
@ -85,7 +84,7 @@
> .button { > .button {
flex: 0 0 auto; flex: 0 0 auto;
margin: 0.2vmin; margin: 0.3rem;
border-radius: 2px; border-radius: 2px;
background-position: center; background-position: center;
background-size: 75%; background-size: 75%;
@ -176,10 +175,10 @@
} }
> .peer-info { > .peer-info {
font-size: 1.4vmin; font-size: 1rem;
border: none; border: none;
display: flex; display: flex;
padding: 1vmin; padding-left: 0.5rem;
flex-grow: 1; flex-grow: 1;
align-items: center; align-items: center;
} }

View File

@ -164,21 +164,6 @@
} }
} }
} }
> .toolarea-wrapper {
position: fixed;
width: 0;
top: 0;
right: 0;
height: 100%;
background-color: rgba(50, 50, 50, 0.9);
transition: width 0.3s;
z-index: 1000;
&.open {
width: 25%;
}
}
} }
.room-controls { .room-controls {

View File

@ -1,6 +1,83 @@
.toolarea-shade {
position: fixed;
z-index: 1000;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
display: none;
&.open {
display: block;
}
}
[data-component='ToolAreaButton'] {
&.on {
right: 80%;
}
}
[data-component='ToolArea'] {
&.open {
width: 80%;
}
.toolarea-shade.open {
display: block;
}
}
@media (min-width: 600px) {
[data-component='ToolAreaButton'] {
&.on {
right: 60%;
}
}
[data-component='ToolArea'] {
&.open {
width: 60%;
}
}
.toolarea-shade.open {
display: none;
}
}
@media (min-width: 900px) {
[data-component='ToolAreaButton'] {
&.on {
right: 40%;
}
}
[data-component='ToolArea'] {
&.open {
width: 40%;
}
}
}
@media (min-width: 1500px) {
[data-component='ToolAreaButton'] {
&.on {
right: 25%;
}
}
[data-component='ToolArea'] {
&.open {
width: 25%;
}
}
}
[data-component='ToolAreaButton'] { [data-component='ToolAreaButton'] {
position: absolute; position: absolute;
z-index: 1000; z-index: 1020;
right: 0; right: 0;
height: 36px; height: 36px;
width: 36px; width: 36px;
@ -11,10 +88,6 @@
align-items: center; align-items: center;
transition: right 0.3s; transition: right 0.3s;
&.on {
right: 25%;
}
> .button { > .button {
flex: 0 0 auto; flex: 0 0 auto;
margin: 4px 0; margin: 4px 0;
@ -79,58 +152,51 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
color: #fff; color: #fff;
position: fixed;
width: 0;
top: 0;
right: 0;
height: 100%;
background-color: rgba(50, 50, 50, 0.9);
transition: width 0.3s;
z-index: 1010;
display: flex;
flex-direction: column;
border-left: 1px solid #222;
> .tabs { > .tab-headers {
display: flex; display: flex;
flex-wrap: wrap; background: rgba(0, 0, 0, 0.1);
height: 100%; flex-shrink: 0;
> label { > .tab-header {
order: 1; flex-grow: 1;
display: block;
padding: 1vmin 0 0.8vmin 0;
cursor: pointer; cursor: pointer;
background: rgba(0,0,0,0.3); padding: 1rem;
font-weight: bold; font-size: 1.2rem;
transition: background ease 0.2s;
text-align: center; text-align: center;
width: 25%;
font-size: 1.3vmin; &.checked {
height: 3vmin; background: rgba(0, 0, 0, 0.3);
}
> .badge { > .badge {
padding: 0.1vmin 1vmin; padding: 0.2rem 0.6rem;
text-align: center; text-align: center;
font-weight: 300; font-weight: 300;
font-size: 1.2vmin; font-size: 1rem;
color: #fff; color: #fff;
background-color: #b12525; background-color: #b12525;
border-radius: 2px; border-radius: 2px;
margin-left: 1vmin; margin-left: 1vmin;
} }
} }
> .tab {
order: 99;
flex-grow: 1;
width: 100%;
height: 100%;
display: none;
padding: 1vmin;
background: rgba(0,0,0,0.1);
}
> input[type="radio"] {
display: none;
}
> input[type="radio"]:checked + label {
background: rgba(0,0,0,0.1);
}
> input[type="radio"]:checked + label + .tab {
display: block;
background: rgba(0,0,0,0.1);
}
} }
}
> .tab {
flex-grow: 1;
padding: 0.5rem;
display: flex;
flex-direction: column;
}
}