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 {
room,
toolAreaOpen,
amActiveSpeaker,
onRoomLinkCopy
} = this.props;
@ -155,16 +154,8 @@ class Room extends React.Component
delayHide={100}
/>
</div>
<div
className={classnames('toolarea-wrapper', { open: toolAreaOpen })}
>
{toolAreaOpen ?
<ToolArea
advancedMode={room.advancedMode}
/>
:null
}
</div>
<ToolArea />
</div>
</Appear>
</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 PropTypes from 'prop-types';
import classNames from 'classnames';
import * as toolTabActions from '../../redux/stateActions';
import ParticipantList from '../ParticipantList/ParticipantList';
import Chat from '../Chat/Chat';
import Settings from '../Settings';
import FileSharing from '../FileSharing';
import TabHeader from './TabHeader';
class ToolArea extends React.Component
{
@ -18,88 +20,61 @@ class ToolArea extends React.Component
{
const {
currentToolTab,
toolAreaOpen,
unreadMessages,
unreadFiles,
setToolTab
unreadFiles
} = this.props;
const VisibleTab = {
chat : Chat,
files : FileSharing,
users : ParticipantList,
settings : Settings
}[currentToolTab];
return (
<div data-component='ToolArea'>
<div className='tabs'>
<input
type='radio'
name='tabs'
id='tab-chat'
onChange={() =>
{
setToolTab('chat');
}}
checked={currentToolTab === 'chat'}
<Fragment>
<div
className={classNames('toolarea-shade', {
open : toolAreaOpen
})}
/>
<label htmlFor='tab-chat'>
Chat
{unreadMessages > 0 && (
<span className='badge'>{unreadMessages}</span>
)}
</label>
<div
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 className='tab'>
<Chat />
</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'>
<FileSharing />
</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>
<VisibleTab />
</div>
</div>
</Fragment>
);
}
}
@ -110,13 +85,15 @@ ToolArea.propTypes =
currentToolTab : PropTypes.string.isRequired,
setToolTab : PropTypes.func.isRequired,
unreadMessages : PropTypes.number.isRequired,
unreadFiles : PropTypes.number.isRequired
unreadFiles : PropTypes.number.isRequired,
toolAreaOpen : PropTypes.bool
};
const mapStateToProps = (state) => ({
currentToolTab : state.toolarea.currentToolTab,
unreadMessages : state.toolarea.unreadMessages,
unreadFiles : state.toolarea.unreadFiles
unreadFiles : state.toolarea.unreadFiles,
toolAreaOpen : state.toolarea.toolAreaOpen
});
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'] {
height: 100%;
display: flex;
flex-grow: 1;
flex-direction: column;
}
[data-component='MessageList'] {
background-color: rgba(#000, 0.1);
height: 91vmin;
overflow-y: scroll;
padding-top: 5px;
border-radius: 5px 5px 0px 0px;
flex-grow: 1;
> .message {
margin: 5px;
display: flex;
word-wrap: break-word;
word-break: break-all;
&:not(:first-child) {
margin-top: 0.5rem;
}
> .client {
margin-left: auto;
@ -79,10 +25,10 @@
> .client, > .response {
background-color: rgba(#000, 0.1);
border-radius: 5px;
max-width: 215px;
max-width: 85%;
display: flex;
align-items: center;
padding: 6px;
padding: 0.5rem;
> .message-avatar {
height: 2rem;
@ -90,14 +36,14 @@
}
> .message-content {
padding-left: 6px;
padding-left: 0.5rem;
> .message-text {
font-size: 1.3vmin;
font-size: 1rem;
}
> .message-time {
font-size: 1vmin;
font-size: 0.8rem;
opacity: 0.8;
}
}
@ -106,22 +52,19 @@
}
[data-component='Sender'] {
align-items: center;
display: flex;
background-color: rgba(#000, 0.1);
height: 6vmin;
padding: 0.5vmin;
border-radius: 0 0 5px 5px;
background-color: rgba(0, 0, 0, 0.1);
padding: 1rem;
flex-shrink: 0;
border-radius: 5px;
margin-top: 0.5rem;
height: 3rem;
> .new-message {
width: 100%;
border: 0;
border-radius: 5px;
background-color: rgba(#000, 0.1);
color: #fff;
height: 30px;
padding-left: 10px;
font-size: 1.4vmin;
color: #FFF;
font-size: 1rem;
&.focus {
outline: none;

View File

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

View File

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

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'] {
position: absolute;
z-index: 1000;
z-index: 1020;
right: 0;
height: 36px;
width: 36px;
@ -11,10 +88,6 @@
align-items: center;
transition: right 0.3s;
&.on {
right: 25%;
}
> .button {
flex: 0 0 auto;
margin: 4px 0;
@ -79,58 +152,51 @@
width: 100%;
height: 100%;
color: #fff;
> .tabs {
display: flex;
flex-wrap: wrap;
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;
> label {
order: 1;
display: block;
padding: 1vmin 0 0.8vmin 0;
> .tab-headers {
display: flex;
background: rgba(0, 0, 0, 0.1);
flex-shrink: 0;
> .tab-header {
flex-grow: 1;
cursor: pointer;
background: rgba(0,0,0,0.3);
font-weight: bold;
transition: background ease 0.2s;
padding: 1rem;
font-size: 1.2rem;
text-align: center;
width: 25%;
font-size: 1.3vmin;
height: 3vmin;
&.checked {
background: rgba(0, 0, 0, 0.3);
}
> .badge {
padding: 0.1vmin 1vmin;
padding: 0.2rem 0.6rem;
text-align: center;
font-weight: 300;
font-size: 1.2vmin;
font-size: 1rem;
color: #fff;
background-color: #b12525;
border-radius: 2px;
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);
}
padding: 0.5rem;
display: flex;
flex-direction: column;
}
}