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 = (
{letter}
) } 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.sharer ? + + person_add Add members + + : null} + {!this.state.owner ? + + exit_to_app Leave + + : null} +
+ +
+ : + + }
- {this.state.groupTopic ? -
-
- -
-
- {this.state.sharer ? - - person_add Add members - - : null} - {!this.state.owner ? - - exit_to_app Leave - - : null} -
- -
- : - - } -
+ }
); } @@ -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);