diff --git a/css/base.css b/css/base.css
index 98ac031c..adb69345 100644
--- a/css/base.css
+++ b/css/base.css
@@ -408,6 +408,7 @@ ul.menu > li.separator:hover {
}
.avatar-box {
+ position: relative;
flex-grow: 0;
flex-shrink: 0;
}
@@ -489,6 +490,7 @@ ul.menu > li.separator:hover {
.contact-box {
list-style: none;
+ overflow-x: hidden;
}
.contact-box > li {
@@ -498,6 +500,8 @@ ul.menu > li.separator:hover {
flex-grow: 0;
flex-shrink: 0;
border-bottom: 1px solid #eee;
+ border-collapse: collapse;
+ overflow-x: hidden;
}
.contact-box > li:hover {
background-color: #eceff1; /* blue-gray 50 highlight */
@@ -506,8 +510,19 @@ ul.menu > li.separator:hover {
background-color: #cfd8dc; /* blue-gray 100 highlight */
}
+.contact-box .text-box {
+ overflow-x: hidden;
+}
+
.contact-box .contact-title {
font-size: 1.05rem;
+ text-overflow: ellipsis;
+ overflow: hidden
+}
+
+.contact-box .contact-comment {
+ text-overflow: ellipsis;
+ overflow: hidden
}
/* The down arrow head - trigger for context menu in contacts list */
@@ -558,7 +573,7 @@ ul.menu > li.separator:hover {
border-radius: 50%;
position: absolute;
right: 0;
- bottom: 0;
+ bottom: 0.1rem;
}
.contact-box .online {
background-color: #4caf50;
@@ -619,6 +634,13 @@ ul.menu > li.separator:hover {
font-size: 4.5rem;
padding: 0;
}
+
+.avatar-upload .avatar-box {
+ position: relative;
+ width: 7rem;
+ height: 7rem;
+}
+
/* Clearfix */
.avatar-upload:after {
content: ".";
@@ -702,6 +724,7 @@ ul.menu > li.separator:hover {
#topic-caption-panel .avatar-box {
+ position: relative;
width: 3rem;
min-width: 3rem;
height: 3rem;
@@ -1073,7 +1096,6 @@ ul.tabbar li.active > a:after {
width: 100%;
padding-bottom: 100%;
border-radius: 50%;
- position: relative;
}
/* Div with letter tile content */
@@ -1214,12 +1236,85 @@ ul.tabbar li.active > a:after {
border-radius: 50%;
}
+.chip-input {
+ display: flex;
+ flex-wrap: wrap;
+ cursor: text;
+ font-size: 1.1rem;
+ font-weight: normal;
+ background: transparent;
+ border-bottom: 1px solid #ccc;
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
+ transition: all 0.2s ease-in-out;
+ width: 100%;
+ margin: 0.25rem 0;
+ padding: 0 0.125rem 0.35rem 0.125rem;
+ outline: none;
+ color: #666;
+ align-items: center;
+ justify-content: flex-start;
+ text-align: center;
+}
+
+.chip-input.focused,
+.chip-input:focus {
+ border-bottom: 1px solid #2196f3;
+ box-shadow: 0 1px 0 rgba(33, 150, 243, 0.25);
+}
+
+.chip-input > input[type="text"] {
+ display: inline-block;
+ width: auto;
+ border: none;
+ line-height: 1.8;
+ box-shadow: none;
+ min-width: 4rem;
+ flex: 1 0 auto;
+ height: 1.8rem;
+ margin: 0.125rem;
+}
+
.chip {
+ display: flex;
+ flex: 0 0 auto;
+ align-items: center;
+ height: 1.8rem;
+ line-height: 1.8;
+ border-radius: 0.9rem;
+ margin: 0.125rem;
+ background-color: #eee;
+ white-space: nowrap;
+}
+
+.chip > a {
display: inline-block;
- background-color: white;
- height: 2rem;
- line-height: 2;
- border-radius: 1rem;
- padding: 0 1rem;
- background-color: #ccc;
+ color: #ccc;
+ background-color: #aaa;
+ border-radius: 50%;
+ margin: 0.3rem;
+ width: 1.175rem;
+ height: 1.175rem;
+ line-height: 1.15;
+ text-decoration: none;
+}
+
+.chip > a:focus,
+.chip > a:hover {
+ text-decoration: none;
+ color: #ddd;
+ background-color: #bbb;
+}
+
+.chip .avatar-box {
+ position: relative;
+ width: 1.8rem;
+ min-width: 1.8rem;
+ height: 1.8rem;
+ min-height: 1.8rem;
+ font-size: 1rem;
+ margin-right: 0.2rem;
+}
+
+.chip .avatar-box .material-icons {
+ font-size: 1.8rem;
}
diff --git a/index.html b/index.html
index ade2026f..64bcac01 100644
--- a/index.html
+++ b/index.html
@@ -210,7 +210,7 @@
if (this.props.avatar === true) {
if (this.props.topic && this.props.title && this.props.title.trim()) {
var letter = this.props.title.trim().charAt(0);
- var color = "lettertile color" + (Math.abs(stringHash(this.props.topic)) % 16);
+ var color = "lettertile dark-color" + (Math.abs(stringHash(this.props.topic)) % 16);
avatar = (
)
} else {
avatar = (Tinode.getTopicType(this.props.topic) === "grp") ?
@@ -296,7 +296,6 @@
);
}
});
-
/* END InPlaceEdit */
/* BEGIN Combobox for selecting host name */
@@ -344,6 +343,98 @@
}
/* END Combobox for selecting host name */
+ /* BEGIN ChipInput: group membership widget */
+ var ChipInput = React.createClass({
+ getInitialState: function() {
+ return {
+ chips: this.props.chips,
+ placeholder: this.props.chips ? "" : this.props.prompt,
+ input: '',
+ focused: false
+ };
+ },
+
+ handleTextInput: function(e) {
+ console.log(e.target.value);
+ this.setState({input: e.target.value});
+ },
+
+ handleChipCancel: function(item) {
+ console.log("cancel " + item);
+ },
+
+ handleFocusGained: function() {
+ this.setState({focused: true});
+ },
+
+ handleFocusLost: function() {
+ this.setState({focused: false});
+ },
+
+ handleKeyDown: function(e) {
+ if (e.key === 'Backspace') {
+ if (this.state.input.length == 0 && this.state.chips.length > 0) {
+ var updated = this.state.chips.slice();
+ updated.splice(-1);
+ this.setState({chips: updated});
+ }
+ }
+ },
+
+ render: function() {
+ var chips = [];
+ var instance = this;
+ this.state.chips.map(function(item) {
+ chips.push(
+
+ );
+ });
+ var className = "chip-input" + (this.state.focused ? " focused" : "");
+ return (
+
+ {chips}
+
+
+ );
+ }
+ });
+
+ ChipInput.Chip = React.createClass({
+ handleCancel: function() {
+ this.props.onCancel(this.props.title);
+ },
+
+ render: function() {
+ return (
+
+
+
+
+
{this.props.title}
+ {this.props.onCancel ?
+
×
+ : null}
+
+ );
+ }
+ });
+ /* END ChipInput */
+
/* BEGIN Login: a login form */
var LoginView = React.createClass({
getInitialState: function() {
@@ -515,7 +606,10 @@
{this.state.dataUrl ?
:
this.props.readOnly && this.props.uid ?
- :
+
+
+
+ :
128×128
}
{this.props.readOnly ? null :
{title}
{this.props.unread > 0 ? : null}
- {this.props.comment ? {this.props.comment}
: null}
+ {this.props.comment ? {this.props.comment}
: null}
{this.props.showContextMenu ?
@@ -996,7 +1090,7 @@
// If filter function is provided, filter out the items
// which don't satisfy the condition.
- if (instance.props.filterFunc && instance.props.filter {
+ if (instance.props.filterFunc && instance.props.filter) {
var content = [key];
if (c.private) {
content.push(c.private);
@@ -1041,6 +1135,34 @@
});
/* END ContactList */
+ /* BEGIN GroupMembers: controlc for managing a list of group members */
+ var GroupManager = React.createClass({
+ getInitialState: function() {
+ return {
+ members: this.props.members || [],
+ contacts: this.props.contacts || [],
+ };
+ },
+
+ handleContactSelected: function(contact) {
+
+ },
+
+ render: function() {
+ return (
+
+
+
+
+ );
+ }
+ });
+ /* END GroupManager */
+
/* BEGIN Create new topic and invite users or send an invite */
var NewTopicView = React.createClass({
getInitialState: function() {
@@ -1161,6 +1283,7 @@
+
@@ -1232,9 +1355,11 @@
access: null,
auth: null,
anon: null,
- contactList: []
+ contactList: [],
+ showMemberPanel: false
};
},
+
componentWillMount: function() {
var topic = Tinode.getTopic(this.props.topic);
if (!topic) {
@@ -1333,7 +1458,8 @@
},
handleAddMembers: function() {
- this.props.onAddMember(this.props.topic);
+ // this.props.onAddMember(this.props.topic);
+ this.setState({showMemberPanel: true});
},
handleLeave: function() {
@@ -1357,89 +1483,94 @@
-
-
-
-
-
-
-
-
-
-
-
+ {this.state.showMemberPanel ?
+
+ :
+
-
- {this.state.address}
-
-
-
- {this.state.access}
-
-
-
-
-
- {this.state.auth || this.state.anon ?
-
-
-
-
Auth: {this.state.auth}
-
Anon: {this.state.anon}
-
+
+
+
+
+
+
+
+
+
+
+ {this.state.address}
+
+
+
+ {this.state.access}
+
+
+
+
+
+ {this.state.auth || this.state.anon ?
+
+
+
+
Auth: {this.state.auth}
+
Anon: {this.state.anon}
+
+
:
null
}
+
+ {this.state.groupTopic ?
+
+
+
+
+
+
+
+ :
+
+ }
- {this.state.groupTopic ?
-
-
-
-
-
-
-
- :
-
- }
-
+ }
);
}
@@ -2087,9 +2218,9 @@
}
},
- handleManageGroupMembers: function(topicName) {
+ //handleManageGroupMembers: function(topicName) {
// FIXME(gene): launch group member manager
- },
+ //onAddMember},
handleTopicUpdateRequest: function(topicName, pub, priv) {
var topic = Tinode.getTopic(topicName);