Reshuffle to put "HTML" (JSX) and CSS together as a theme with logic elsewhere.

This commit is contained in:
David Baker 2015-06-23 14:40:50 +01:00
parent cc4fa6140c
commit a022a4b9a6
37 changed files with 56 additions and 54 deletions

View file

@ -0,0 +1,6 @@
.mx_MessageTimestamp {
color: #777;
width: 75px;
font-size: 80%;
display: inline-block;
}

View file

@ -0,0 +1,3 @@
div.error {
color: red;
}

View file

@ -0,0 +1,3 @@
.mx_MNoticeTile {
color: #666;
}

View file

@ -0,0 +1,10 @@
.mx_MessageComposer {
}
.mx_MessageComposer textarea {
position: absolute;
width: 100%;
margin-left: auto;
margin-right: auto;
padding: 0px;
}

View file

@ -0,0 +1,11 @@
.mx_MessageTile {
border-bottom: 1px solid #eee;
}
.mx_MessageTile.sending {
color: #ddd;
}
.mx_MessageTile.not_sent {
color: #f11;
}

View file

@ -0,0 +1,12 @@
.mx_ProgressBar {
height: 5px;
border-radius: 5px;
border: 1px solid black;
margin-bottom: 10px;
}
.mx_ProgressBar_fill {
height: 100%;
background-color: #666;
transition: width 0.1s ease;
}

View file

@ -0,0 +1,6 @@
.mx_RoomHeader {
background-color: #eee;
font-weight: bold;
height: 1em;
padding: 0px;
}

View file

@ -0,0 +1,36 @@
.mx_RoomTile {
height: 20px;
background-color: #ddd;
margin-top: 1px;
margin-bottom: 1px;
padding: 5px;
cursor: pointer;
border: 2px solid white;
transition: background-color 0.1s ease;
}
.mx_RoomTile.selected {
border: 2px inset #eee;
}
.mx_RoomTile_name {
font-size: 80%;
font-weight: bold;
}
.mx_RoomTile div {
overflow: hidden;
text-overflow: ellipsis;
}
.mx_RoomTile.unread {
background-color: #66e;
}
.mx_RoomTile.invited {
background-color: #6e6;
}
.mx_RoomTile:hover {
background-color: #ff8;
}

View file

@ -0,0 +1,6 @@
.mx_SenderProfile {
display: inline-block;
width: 150px;
color: #444;
font-size: 80%;
}

View file

@ -0,0 +1,11 @@
.mx_RoomList {
}
.mx_RoomList ul {
padding: 0px;
margin: 0px;
}
.mx_RoomList ul li {
list-style-type: none;
}

View file

@ -0,0 +1,59 @@
.mx_RoomView {
word-wrap: break-word;
}
.mx_RoomView .mx_RoomHeader {
position: absolute;
top: 0px;
width: 100%;
height: 30px;
}
.mx_RoomView .mx_RoomView_MessageList {
padding: 0px;
margin: 0px;
position: absolute;
top: 35px;
bottom: 40px;
left: 0px;
right: 150px;
overflow-y: scroll;
}
.mx_RoomView ul li {
list-style-type: none;
}
.mx_RoomView .mx_RoomView_invitePrompt {
text-align: center;
background-color: #ddd;
border: 1px solid black;
width: 400px;
margin-left: auto;
margin-right: auto;
margin-top: 200px;
padding: 50px;
}
.mx_RoomView .mx_MemberList {
position: absolute;
top: 35px;
bottom: 40px;
right: 0px;
width: 150px;
margin: 0px;
padding: 0px;
}
.mx_RoomView .mx_MemberList ul {
margin: 0px;
padding: 0px;
}
.mx_RoomView .mx_MessageComposer {
position: absolute;
bottom: 5px;
right: 0px;
left: 0px;
height: 36px;
}

View file

@ -0,0 +1,32 @@
.mx_MatrixChat {
position: relative;
width: 100%;
height: 100%;
}
.mx_MatrixChat_leftPanel {
position: absolute;
left: 0px;
width: 250px;
height: 100%;
}
.mx_MatrixChat_leftPanel .mx_MatrixToolbar {
position: absolute;
height: 20px;
}
.mx_MatrixChat_leftPanel .mx_RoomList {
position: absolute;
top: 20px;
bottom: 0px;
width: 250px;
overflow-y: scroll;
}
.mx_MatrixChat .mx_RoomView {
position: absolute;
left: 255px;
right: 0px;
height: 100%;
}

View file

@ -0,0 +1,12 @@
.mx_Login {
width: 600px;
height: 350px;
margin-left: auto;
margin-right: auto;
padding: 10px;
text-align: center;
background-color: #eee;
border: 1px solid black;
position: relative;
}

View file

@ -0,0 +1,14 @@
var React = require('react');
var LogoutButtonController = require("../../../../src/controllers/atoms/LogoutButton");
module.exports = React.createClass({
displayName: 'LogoutButton',
mixins: [LogoutButtonController],
render: function() {
return (
<button className="mx_LogoutButton" onClick={this.onClick}>Sign out</button>
);
}
});

View file

@ -0,0 +1,18 @@
var React = require('react');
var MessageTimestampController = require("../../../../src/controllers/atoms/MessageTimestamp");
module.exports = React.createClass({
displayName: 'MessageTimestamp',
mixins: [MessageTimestampController],
render: function() {
var date = new Date(this.props.ts);
return (
<span className="mx_MessageTimestamp">
{date.toLocaleTimeString()}
</span>
);
},
});

View file

@ -0,0 +1,20 @@
var React = require('react');
var MEmoteTileController = require("../../../../src/controllers/molecules/MEmoteTile");
module.exports = React.createClass({
displayName: 'MEmoteTile',
mixins: [MEmoteTileController],
render: function() {
var mxEvent = this.props.mxEvent;
var content = mxEvent.getContent();
var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
return (
<span className="mx_MEmoteTile">
{name} {content.body}
</span>
);
},
});

View file

@ -0,0 +1,18 @@
var React = require('react');
var MNoticeTileController = require("../../../../src/controllers/molecules/MNoticeTile");
module.exports = React.createClass({
displayName: 'MNoticeTile',
mixins: [MNoticeTileController],
render: function() {
var content = this.props.mxEvent.getContent();
return (
<span className="mx_MNoticeTile">
{content.body}
</span>
);
},
});

View file

@ -0,0 +1,18 @@
var React = require('react');
var MTextTileController = require("../../../../src/controllers/molecules/MTextTile");
module.exports = React.createClass({
displayName: 'MTextTile',
mixins: [MTextTileController],
render: function() {
var content = this.props.mxEvent.getContent();
return (
<span className="mx_MTextTile">
{content.body}
</span>
);
},
});

View file

@ -0,0 +1,21 @@
var React = require('react');
var ComponentBroker = require('../../../../src/ComponentBroker');
var LogoutButton = ComponentBroker.get("atoms/LogoutButton");
var MatrixToolbarController = require("../../../../src/controllers/molecules/MatrixToolbar");
module.exports = React.createClass({
displayName: 'MatrixToolbar',
mixins: [MatrixToolbarController],
render: function() {
return (
<div className="mx_MatrixToolbar">
<LogoutButton />
</div>
);
}
});

View file

@ -0,0 +1,15 @@
var React = require('react');
var MemberTileController = require("../../../../src/controllers/molecules/MemberTile");
module.exports = React.createClass({
displayName: 'MemberTile',
mixins: [MemberTileController],
render: function() {
return (
<div className="mx_MemberTile">
<div className="mx_MemberTile_name">{this.props.member.name}</div>
</div>
);
}
});

View file

@ -0,0 +1,17 @@
var React = require('react');
var MessageComposerController = require("../../../../src/controllers/molecules/MessageComposer");
module.exports = React.createClass({
displayName: 'MessageComposer',
mixins: [MessageComposerController],
render: function() {
return (
<div className="mx_MessageComposer">
<textarea ref="textarea" onKeyDown={this.onKeyDown} />
</div>
);
},
});

View file

@ -0,0 +1,45 @@
var React = require('react');
var classNames = require("classnames");
var ComponentBroker = require('../../../../src/ComponentBroker');
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
var SenderProfile = ComponentBroker.get('molecules/SenderProfile');
var UnknownMessageTile = ComponentBroker.get('molecules/UnknownMessageTile');
var tileTypes = {
'm.text': ComponentBroker.get('molecules/MTextTile'),
'm.notice': ComponentBroker.get('molecules/MNoticeTile'),
'm.emote': ComponentBroker.get('molecules/MEmoteTile')
};
var MessageTileController = require("../../../../src/controllers/molecules/MessageTile");
module.exports = React.createClass({
displayName: 'MessageTile',
mixins: [MessageTileController],
render: function() {
var content = this.props.mxEvent.getContent();
var msgtype = content.msgtype;
var TileType = UnknownMessageTile;
if (msgtype && tileTypes[msgtype]) {
TileType = tileTypes[msgtype];
}
var classes = classNames({
mx_MessageTile: true,
sending: this.props.mxEvent.status == 'sending',
not_sent: this.props.mxEvent.status == 'not_sent'
});
return (
<div className={classes}>
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
<SenderProfile mxEvent={this.props.mxEvent} />
<TileType mxEvent={this.props.mxEvent} />
</div>
);
},
});

View file

@ -0,0 +1,19 @@
var React = require('react');
var ProgressBarController = require("../../../../src/controllers/molecules/ProgressBar");
module.exports = React.createClass({
displayName: 'ProgressBar',
mixins: [ProgressBarController],
render: function() {
// Would use an HTML5 progress tag but if that doesn't animate if you
// use the HTML attributes rather than styles
var progressStyle = {
width: ((this.props.value / this.props.max) * 100)+"%"
};
return (
<div className="mx_ProgressBar"><div className="mx_ProgressBar_fill" style={progressStyle}></div></div>
);
}
});

View file

@ -0,0 +1,17 @@
var React = require('react');
var RoomHeaderController = require("../../../../src/controllers/molecules/RoomHeader");
module.exports = React.createClass({
displayName: 'RoomHeader',
mixins: [RoomHeaderController],
render: function() {
return (
<div className="mx_RoomHeader">
{this.props.room.name}
</div>
);
},
});

View file

@ -0,0 +1,25 @@
var React = require('react');
var classNames = require('classnames');
var RoomTileController = require("../../../../src/controllers/molecules/RoomTile");
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
module.exports = React.createClass({
displayName: 'RoomTile',
mixins: [RoomTileController],
render: function() {
var myUserId = MatrixClientPeg.get().credentials.userId;
var classes = classNames({
'mx_RoomTile': true,
'selected': this.props.selected,
'unread': this.props.unread,
'invited': this.props.room.currentState.members[myUserId].membership == 'invite'
});
return (
<div className={classes} onClick={this.onClick}>
<div className="mx_RoomTile_name">{this.props.room.name}</div>
</div>
);
}
});

View file

@ -0,0 +1,24 @@
var React = require('react');
var SenderProfileController = require("../../../../src/controllers/molecules/SenderProfile");
module.exports = React.createClass({
displayName: 'SenderProfile',
mixins: [SenderProfileController],
render: function() {
var mxEvent = this.props.mxEvent;
var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
var msgtype = mxEvent.getContent().msgtype;
if (msgtype && msgtype == 'm.emote') {
name = ''; // emote message must include the name so don't duplicate it
}
return (
<span className="mx_SenderProfile">
{name}
</span>
);
},
});

View file

@ -0,0 +1,25 @@
var React = require('react');
var ServerConfigController = require("../../../../src/controllers/molecules/ServerConfig");
module.exports = React.createClass({
displayName: 'ServerConfig',
mixins: [ServerConfigController],
render: function() {
return (
<div className="HomeServerTextBox">
<table className="serverConfig">
<tr>
<td>Home Server URL</td>
<td><input type="text" value={this.state.hs_url} onChange={this.hsChanged} /></td>
</tr>
<tr>
<td>Identity Server URL</td>
<td><input type="text" value={this.state.is_url} onChange={this.isChanged} /></td>
</tr>
</table>
</div>
);
}
});

View file

@ -0,0 +1,16 @@
var React = require('react');
var UnknownMessageTileController = require("../../../../src/controllers/molecules/UnknownMessageTile");
module.exports = React.createClass({
displayName: 'UnknownMessageTile',
mixins: [UnknownMessageTileController],
render: function() {
return (
<span className="mx_UnknownMessageTile">
?
</span>
);
},
});

View file

@ -0,0 +1,38 @@
var React = require('react');
var MemberListController = require("../../../../src/controllers/organisms/MemberList");
var ComponentBroker = require('../../../../src/ComponentBroker');
var MemberTile = ComponentBroker.get("molecules/MemberTile");
module.exports = React.createClass({
displayName: 'MemberList',
mixins: [MemberListController],
makeMemberTiles: function() {
var that = this;
return Object.keys(that.state.memberDict).map(function(userId) {
var m = that.state.memberDict[userId];
return (
<li key={userId}>
<MemberTile
member={m}
/>
</li>
);
});
},
render: function() {
return (
<div className="mx_MemberList">
<ul>
{this.makeMemberTiles()}
</ul>
</div>
);
}
});

View file

@ -0,0 +1,20 @@
var React = require('react');
var RoomListController = require("../../../../src/controllers/organisms/RoomList");
module.exports = React.createClass({
displayName: 'RoomList',
mixins: [RoomListController],
render: function() {
return (
<div className="mx_RoomList">
<ul>
{this.makeRoomTiles()}
</ul>
</div>
);
}
});

View file

@ -0,0 +1,68 @@
var React = require('react');
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
var ComponentBroker = require('../../../../src/ComponentBroker');
var MessageTile = ComponentBroker.get('molecules/MessageTile');
var RoomHeader = ComponentBroker.get('molecules/RoomHeader');
var MemberList = ComponentBroker.get('organisms/MemberList');
var MessageComposer = ComponentBroker.get('molecules/MessageComposer');
var RoomViewController = require("../../../../src/controllers/organisms/RoomView");
var Loader = require("react-loader");
module.exports = React.createClass({
displayName: 'RoomView',
mixins: [RoomViewController],
getMessageTiles: function() {
return this.state.room.timeline.map(function(mxEv) {
return (
<li key={mxEv.getId()}><MessageTile mxEvent={mxEv} /></li>
);
});
},
render: function() {
var myUserId = MatrixClientPeg.get().credentials.userId;
if (this.state.room.currentState.members[myUserId].membership == 'invite') {
if (this.state.joining) {
return (
<div className="mx_RoomView">
<Loader />
</div>
);
} else {
var inviteEvent = this.state.room.currentState.members[myUserId].events.member.event;
// XXX: Leaving this intentionally basic for now because invites are about to change totally
var joinErrorText = this.state.joinError ? "Failed to join room!" : "";
return (
<div className="mx_RoomView">
<div className="mx_RoomView_invitePrompt">
<div>{inviteEvent.user_id} has invited you to a room</div>
<button ref="joinButton" onClick={this.onJoinButtonClicked}>Join</button>
<div className="error">{joinErrorText}</div>
</div>
</div>
);
}
} else {
return (
<div className="mx_RoomView">
<RoomHeader room={this.state.room} />
<div className="mx_RoomView_HSplit">
<ul className="mx_RoomView_MessageList" ref="messageList">
{this.getMessageTiles()}
</ul>
<MemberList roomId={this.props.roomId} key={this.props.roomId} />
</div>
<MessageComposer roomId={this.props.roomId} />
</div>
);
}
},
});

View file

@ -0,0 +1,41 @@
var React = require('react');
var ComponentBroker = require('../../../../src/ComponentBroker');
var RoomList = ComponentBroker.get('organisms/RoomList');
var RoomView = ComponentBroker.get('organisms/RoomView');
var MatrixToolbar = ComponentBroker.get('molecules/MatrixToolbar');
var Login = ComponentBroker.get('templates/Login');
var MatrixChatController = require("../../../../src/controllers/pages/MatrixChat");
// should be atomised
var Loader = require("react-loader");
module.exports = React.createClass({
displayName: 'MatrixChat',
mixins: [MatrixChatController],
render: function() {
if (this.state.logged_in && this.state.ready) {
return (
<div className="mx_MatrixChat">
<div className="mx_MatrixChat_leftPanel">
<MatrixToolbar />
<RoomList selectedRoom={this.state.currentRoom} />
</div>
<RoomView roomId={this.state.currentRoom} key={this.state.currentRoom} />
</div>
);
} else if (this.state.logged_in) {
return (
<Loader />
);
} else {
return (
<Login onLoggedIn={this.onLoggedIn} />
);
}
}
});

View file

@ -0,0 +1,38 @@
var React = require('react');
var ComponentBroker = require("../../../../src/ComponentBroker");
var ProgressBar = ComponentBroker.get("molecules/ProgressBar");
var Loader = require("react-loader");
var LoginController = require("../../../../src/controllers/templates/Login");
module.exports = React.createClass({
displayName: 'Login',
mixins: [LoginController],
loginContent: function() {
if (this.state.busy) {
return (
<Loader />
);
} else {
return (
<div>
<h1>Please log in:</h1>
{this.componentForStep(this.state.step)}
<div className="error">{this.state.errorText}</div>
</div>
);
}
},
render: function() {
return (
<div className="mx_Login">
<ProgressBar value={this.state.currentStep} max={this.state.totalSteps} />
{this.loginContent()}
</div>
);
}
});