Fossil SCM
Animation tweaks and more predictable scrolling when the user filter is cleared (always scroll to the button).
Commit
3d4101d354708a2661974aef661e68102a46eab61c4b09df2a99de748eac747a
Parent
2b3a6ed8465eb09…
2 files changed
+34
-12
+2
-2
+34
-12
| --- src/chat.js | ||
| +++ src/chat.js | ||
| @@ -202,10 +202,11 @@ | ||
| 202 | 202 | D.removeClass(this.e.inputCurrent, 'hidden'); |
| 203 | 203 | const mh2 = m.clientHeight; |
| 204 | 204 | m.scrollTo(0, sTop + (mh1-mh2)); |
| 205 | 205 | this.e.inputCurrent.value = old.value; |
| 206 | 206 | old.value = ''; |
| 207 | + D.addClassBriefly(this.e.inputCurrent, "anim-flip-v"); | |
| 207 | 208 | return this; |
| 208 | 209 | }, |
| 209 | 210 | /** |
| 210 | 211 | If passed true or no arguments, switches to multi-line mode |
| 211 | 212 | if currently in single-line mode. If passed false, switches |
| @@ -503,19 +504,25 @@ | ||
| 503 | 504 | setUserFilter: function(uname){ |
| 504 | 505 | this.filterState.activeUser = uname; |
| 505 | 506 | const mw = this.e.viewMessages.querySelectorAll('.message-widget'); |
| 506 | 507 | const self = this; |
| 507 | 508 | let eLast; |
| 508 | - mw.forEach(function(w){ | |
| 509 | - if(self.filterState.match(w.dataset.xfrom)){ | |
| 510 | - w.classList.remove('hidden'); | |
| 511 | - eLast = w; | |
| 512 | - }else{ | |
| 513 | - w.classList.add('hidden'); | |
| 514 | - } | |
| 515 | - }); | |
| 509 | + if(!uname){ | |
| 510 | + D.removeClass(Chat.e.viewMessages.querySelectorAll('.message-widget.hidden'), | |
| 511 | + 'hidden'); | |
| 512 | + }else{ | |
| 513 | + mw.forEach(function(w){ | |
| 514 | + if(self.filterState.match(w.dataset.xfrom)){ | |
| 515 | + w.classList.remove('hidden'); | |
| 516 | + eLast = w; | |
| 517 | + }else{ | |
| 518 | + w.classList.add('hidden'); | |
| 519 | + } | |
| 520 | + }); | |
| 521 | + } | |
| 516 | 522 | if(eLast) eLast.scrollIntoView(false); |
| 523 | + else this.scrollMessagesTo(1); | |
| 517 | 524 | cs.e.activeUserList.querySelectorAll('.chat-user').forEach(function(e){ |
| 518 | 525 | e.classList[uname===e.dataset.uname ? 'add' : 'remove']('selected'); |
| 519 | 526 | }); |
| 520 | 527 | return this; |
| 521 | 528 | } |
| @@ -987,11 +994,18 @@ | ||
| 987 | 994 | "Message in context", |
| 988 | 995 | function(){ |
| 989 | 996 | self.hide(); |
| 990 | 997 | Chat.setUserFilter(false); |
| 991 | 998 | eMsg.scrollIntoView(false); |
| 992 | - D.addClassBriefly(eMsg.firstElementChild, 'anim-rotate-360'); | |
| 999 | + D.addClassBriefly( | |
| 1000 | + eMsg.firstElementChild, 'anim-rotate-360' | |
| 1001 | + //eMsg.firstElementChild, 'anim-flip-v' | |
| 1002 | + //eMsg.childNodes, 'anim-rotate-360' | |
| 1003 | + //eMsg.childNodes, 'anim-flip-v' | |
| 1004 | + //eMsg, 'anim-flip-v' | |
| 1005 | + ); | |
| 1006 | + //D.addClassBriefly(eMsg.childNodes[1], 'anim-flip-h'); | |
| 993 | 1007 | }) |
| 994 | 1008 | ) |
| 995 | 1009 | ); |
| 996 | 1010 | }/*jump-to button*/ |
| 997 | 1011 | } |
| @@ -1208,17 +1222,18 @@ | ||
| 1208 | 1222 | persistentSetting: 'active-user-list', |
| 1209 | 1223 | callback: function(){ |
| 1210 | 1224 | D.toggleClass(Chat.e.activeUserListWrapper,'hidden'); |
| 1211 | 1225 | if(Chat.e.activeUserListWrapper.classList.contains('hidden')){ |
| 1212 | 1226 | /* When hiding this element, undo all filtering */ |
| 1213 | - D.removeClass(Chat.e.viewMessages.querySelectorAll('.message-widget.hidden'), 'hidden'); | |
| 1227 | + Chat.setUserFilter(false); | |
| 1214 | 1228 | /*Ideally we'd scroll the final message into view |
| 1215 | 1229 | now, but because viewMessages is currently hidden behind |
| 1216 | 1230 | viewConfig, scrolling is a no-op. */ |
| 1217 | 1231 | Chat.scrollMessagesTo(1); |
| 1218 | 1232 | }else{ |
| 1219 | 1233 | Chat.updateActiveUserList(); |
| 1234 | + D.addClassBriefly(Chat.e.activeUserListWrapper, "anim-flip-v"); | |
| 1220 | 1235 | } |
| 1221 | 1236 | } |
| 1222 | 1237 | } |
| 1223 | 1238 | }; |
| 1224 | 1239 | /* Settings menu entries... Remember that they will be rendered in |
| @@ -1249,12 +1264,13 @@ | ||
| 1249 | 1264 | label: "Timestamps in active users list", |
| 1250 | 1265 | boolValue: ()=>Chat.e.activeUserList.classList.contains('timestamps'), |
| 1251 | 1266 | persistentSetting: 'active-user-list-timestamps', |
| 1252 | 1267 | callback: function(){ |
| 1253 | 1268 | D.toggleClass(Chat.e.activeUserList,'timestamps'); |
| 1254 | - /* If the timestamp option is activated but optActiveUsers is not | |
| 1255 | - currently checked then toggle that option on as well. */ | |
| 1269 | + /* If the timestamp option is activated but | |
| 1270 | + namedOptions.activeUsers is not currently checked then | |
| 1271 | + toggle that option on as well. */ | |
| 1256 | 1272 | if(Chat.e.activeUserList.classList.contains('timestamps') |
| 1257 | 1273 | && !namedOptions.activeUsers.boolValue()){ |
| 1258 | 1274 | namedOptions.activeUsers.checkbox.checked = true; |
| 1259 | 1275 | namedOptions.activeUsers.callback(); |
| 1260 | 1276 | } |
| @@ -1591,8 +1607,14 @@ | ||
| 1591 | 1607 | Chat._gotServerError = poll.running = false; |
| 1592 | 1608 | if( window.fossil.config.chat.fromcli ){ |
| 1593 | 1609 | Chat.chatOnlyMode(true); |
| 1594 | 1610 | } |
| 1595 | 1611 | Chat.intervalTimer = setInterval(poll, 1000); |
| 1612 | + if(0){ | |
| 1613 | + const flip = (ev)=>F.dom.addClassBriefly(ev.target,'anim-flip-h'); | |
| 1614 | + document.querySelectorAll('#chat-edit-buttons button').forEach(function(e){ | |
| 1615 | + e.addEventListener('click',flip, false); | |
| 1616 | + }); | |
| 1617 | + } | |
| 1596 | 1618 | setTimeout( ()=>Chat.inputFocus(), 0 ); |
| 1597 | 1619 | F.page.chat = Chat/* enables testing the APIs via the dev tools */; |
| 1598 | 1620 | })(); |
| 1599 | 1621 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -202,10 +202,11 @@ | |
| 202 | D.removeClass(this.e.inputCurrent, 'hidden'); |
| 203 | const mh2 = m.clientHeight; |
| 204 | m.scrollTo(0, sTop + (mh1-mh2)); |
| 205 | this.e.inputCurrent.value = old.value; |
| 206 | old.value = ''; |
| 207 | return this; |
| 208 | }, |
| 209 | /** |
| 210 | If passed true or no arguments, switches to multi-line mode |
| 211 | if currently in single-line mode. If passed false, switches |
| @@ -503,19 +504,25 @@ | |
| 503 | setUserFilter: function(uname){ |
| 504 | this.filterState.activeUser = uname; |
| 505 | const mw = this.e.viewMessages.querySelectorAll('.message-widget'); |
| 506 | const self = this; |
| 507 | let eLast; |
| 508 | mw.forEach(function(w){ |
| 509 | if(self.filterState.match(w.dataset.xfrom)){ |
| 510 | w.classList.remove('hidden'); |
| 511 | eLast = w; |
| 512 | }else{ |
| 513 | w.classList.add('hidden'); |
| 514 | } |
| 515 | }); |
| 516 | if(eLast) eLast.scrollIntoView(false); |
| 517 | cs.e.activeUserList.querySelectorAll('.chat-user').forEach(function(e){ |
| 518 | e.classList[uname===e.dataset.uname ? 'add' : 'remove']('selected'); |
| 519 | }); |
| 520 | return this; |
| 521 | } |
| @@ -987,11 +994,18 @@ | |
| 987 | "Message in context", |
| 988 | function(){ |
| 989 | self.hide(); |
| 990 | Chat.setUserFilter(false); |
| 991 | eMsg.scrollIntoView(false); |
| 992 | D.addClassBriefly(eMsg.firstElementChild, 'anim-rotate-360'); |
| 993 | }) |
| 994 | ) |
| 995 | ); |
| 996 | }/*jump-to button*/ |
| 997 | } |
| @@ -1208,17 +1222,18 @@ | |
| 1208 | persistentSetting: 'active-user-list', |
| 1209 | callback: function(){ |
| 1210 | D.toggleClass(Chat.e.activeUserListWrapper,'hidden'); |
| 1211 | if(Chat.e.activeUserListWrapper.classList.contains('hidden')){ |
| 1212 | /* When hiding this element, undo all filtering */ |
| 1213 | D.removeClass(Chat.e.viewMessages.querySelectorAll('.message-widget.hidden'), 'hidden'); |
| 1214 | /*Ideally we'd scroll the final message into view |
| 1215 | now, but because viewMessages is currently hidden behind |
| 1216 | viewConfig, scrolling is a no-op. */ |
| 1217 | Chat.scrollMessagesTo(1); |
| 1218 | }else{ |
| 1219 | Chat.updateActiveUserList(); |
| 1220 | } |
| 1221 | } |
| 1222 | } |
| 1223 | }; |
| 1224 | /* Settings menu entries... Remember that they will be rendered in |
| @@ -1249,12 +1264,13 @@ | |
| 1249 | label: "Timestamps in active users list", |
| 1250 | boolValue: ()=>Chat.e.activeUserList.classList.contains('timestamps'), |
| 1251 | persistentSetting: 'active-user-list-timestamps', |
| 1252 | callback: function(){ |
| 1253 | D.toggleClass(Chat.e.activeUserList,'timestamps'); |
| 1254 | /* If the timestamp option is activated but optActiveUsers is not |
| 1255 | currently checked then toggle that option on as well. */ |
| 1256 | if(Chat.e.activeUserList.classList.contains('timestamps') |
| 1257 | && !namedOptions.activeUsers.boolValue()){ |
| 1258 | namedOptions.activeUsers.checkbox.checked = true; |
| 1259 | namedOptions.activeUsers.callback(); |
| 1260 | } |
| @@ -1591,8 +1607,14 @@ | |
| 1591 | Chat._gotServerError = poll.running = false; |
| 1592 | if( window.fossil.config.chat.fromcli ){ |
| 1593 | Chat.chatOnlyMode(true); |
| 1594 | } |
| 1595 | Chat.intervalTimer = setInterval(poll, 1000); |
| 1596 | setTimeout( ()=>Chat.inputFocus(), 0 ); |
| 1597 | F.page.chat = Chat/* enables testing the APIs via the dev tools */; |
| 1598 | })(); |
| 1599 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -202,10 +202,11 @@ | |
| 202 | D.removeClass(this.e.inputCurrent, 'hidden'); |
| 203 | const mh2 = m.clientHeight; |
| 204 | m.scrollTo(0, sTop + (mh1-mh2)); |
| 205 | this.e.inputCurrent.value = old.value; |
| 206 | old.value = ''; |
| 207 | D.addClassBriefly(this.e.inputCurrent, "anim-flip-v"); |
| 208 | return this; |
| 209 | }, |
| 210 | /** |
| 211 | If passed true or no arguments, switches to multi-line mode |
| 212 | if currently in single-line mode. If passed false, switches |
| @@ -503,19 +504,25 @@ | |
| 504 | setUserFilter: function(uname){ |
| 505 | this.filterState.activeUser = uname; |
| 506 | const mw = this.e.viewMessages.querySelectorAll('.message-widget'); |
| 507 | const self = this; |
| 508 | let eLast; |
| 509 | if(!uname){ |
| 510 | D.removeClass(Chat.e.viewMessages.querySelectorAll('.message-widget.hidden'), |
| 511 | 'hidden'); |
| 512 | }else{ |
| 513 | mw.forEach(function(w){ |
| 514 | if(self.filterState.match(w.dataset.xfrom)){ |
| 515 | w.classList.remove('hidden'); |
| 516 | eLast = w; |
| 517 | }else{ |
| 518 | w.classList.add('hidden'); |
| 519 | } |
| 520 | }); |
| 521 | } |
| 522 | if(eLast) eLast.scrollIntoView(false); |
| 523 | else this.scrollMessagesTo(1); |
| 524 | cs.e.activeUserList.querySelectorAll('.chat-user').forEach(function(e){ |
| 525 | e.classList[uname===e.dataset.uname ? 'add' : 'remove']('selected'); |
| 526 | }); |
| 527 | return this; |
| 528 | } |
| @@ -987,11 +994,18 @@ | |
| 994 | "Message in context", |
| 995 | function(){ |
| 996 | self.hide(); |
| 997 | Chat.setUserFilter(false); |
| 998 | eMsg.scrollIntoView(false); |
| 999 | D.addClassBriefly( |
| 1000 | eMsg.firstElementChild, 'anim-rotate-360' |
| 1001 | //eMsg.firstElementChild, 'anim-flip-v' |
| 1002 | //eMsg.childNodes, 'anim-rotate-360' |
| 1003 | //eMsg.childNodes, 'anim-flip-v' |
| 1004 | //eMsg, 'anim-flip-v' |
| 1005 | ); |
| 1006 | //D.addClassBriefly(eMsg.childNodes[1], 'anim-flip-h'); |
| 1007 | }) |
| 1008 | ) |
| 1009 | ); |
| 1010 | }/*jump-to button*/ |
| 1011 | } |
| @@ -1208,17 +1222,18 @@ | |
| 1222 | persistentSetting: 'active-user-list', |
| 1223 | callback: function(){ |
| 1224 | D.toggleClass(Chat.e.activeUserListWrapper,'hidden'); |
| 1225 | if(Chat.e.activeUserListWrapper.classList.contains('hidden')){ |
| 1226 | /* When hiding this element, undo all filtering */ |
| 1227 | Chat.setUserFilter(false); |
| 1228 | /*Ideally we'd scroll the final message into view |
| 1229 | now, but because viewMessages is currently hidden behind |
| 1230 | viewConfig, scrolling is a no-op. */ |
| 1231 | Chat.scrollMessagesTo(1); |
| 1232 | }else{ |
| 1233 | Chat.updateActiveUserList(); |
| 1234 | D.addClassBriefly(Chat.e.activeUserListWrapper, "anim-flip-v"); |
| 1235 | } |
| 1236 | } |
| 1237 | } |
| 1238 | }; |
| 1239 | /* Settings menu entries... Remember that they will be rendered in |
| @@ -1249,12 +1264,13 @@ | |
| 1264 | label: "Timestamps in active users list", |
| 1265 | boolValue: ()=>Chat.e.activeUserList.classList.contains('timestamps'), |
| 1266 | persistentSetting: 'active-user-list-timestamps', |
| 1267 | callback: function(){ |
| 1268 | D.toggleClass(Chat.e.activeUserList,'timestamps'); |
| 1269 | /* If the timestamp option is activated but |
| 1270 | namedOptions.activeUsers is not currently checked then |
| 1271 | toggle that option on as well. */ |
| 1272 | if(Chat.e.activeUserList.classList.contains('timestamps') |
| 1273 | && !namedOptions.activeUsers.boolValue()){ |
| 1274 | namedOptions.activeUsers.checkbox.checked = true; |
| 1275 | namedOptions.activeUsers.callback(); |
| 1276 | } |
| @@ -1591,8 +1607,14 @@ | |
| 1607 | Chat._gotServerError = poll.running = false; |
| 1608 | if( window.fossil.config.chat.fromcli ){ |
| 1609 | Chat.chatOnlyMode(true); |
| 1610 | } |
| 1611 | Chat.intervalTimer = setInterval(poll, 1000); |
| 1612 | if(0){ |
| 1613 | const flip = (ev)=>F.dom.addClassBriefly(ev.target,'anim-flip-h'); |
| 1614 | document.querySelectorAll('#chat-edit-buttons button').forEach(function(e){ |
| 1615 | e.addEventListener('click',flip, false); |
| 1616 | }); |
| 1617 | } |
| 1618 | setTimeout( ()=>Chat.inputFocus(), 0 ); |
| 1619 | F.page.chat = Chat/* enables testing the APIs via the dev tools */; |
| 1620 | })(); |
| 1621 |
+2
-2
| --- src/style.chat.css | ||
| +++ src/style.chat.css | ||
| @@ -382,11 +382,11 @@ | ||
| 382 | 382 | to { |
| 383 | 383 | transform: rotate(360deg); |
| 384 | 384 | } |
| 385 | 385 | } |
| 386 | 386 | body.chat .anim-flip-h { |
| 387 | - animation: flip-h 1s linear; | |
| 387 | + animation: flip-h 750ms linear; | |
| 388 | 388 | } |
| 389 | 389 | @keyframes flip-h{ |
| 390 | 390 | from{ |
| 391 | 391 | transform: rotateY(0deg); |
| 392 | 392 | } |
| @@ -393,15 +393,15 @@ | ||
| 393 | 393 | to{ |
| 394 | 394 | transform: rotateY(360deg); |
| 395 | 395 | } |
| 396 | 396 | } |
| 397 | 397 | body.chat .anim-flip-v { |
| 398 | - animation: flip-v 1s linear; | |
| 398 | + animation: flip-v 750ms linear; | |
| 399 | 399 | } |
| 400 | 400 | @keyframes flip-v{ |
| 401 | 401 | from{ |
| 402 | 402 | transform: rotateX(0deg); |
| 403 | 403 | } |
| 404 | 404 | to{ |
| 405 | 405 | transform: rotateX(360deg); |
| 406 | 406 | } |
| 407 | 407 | } |
| 408 | 408 |
| --- src/style.chat.css | |
| +++ src/style.chat.css | |
| @@ -382,11 +382,11 @@ | |
| 382 | to { |
| 383 | transform: rotate(360deg); |
| 384 | } |
| 385 | } |
| 386 | body.chat .anim-flip-h { |
| 387 | animation: flip-h 1s linear; |
| 388 | } |
| 389 | @keyframes flip-h{ |
| 390 | from{ |
| 391 | transform: rotateY(0deg); |
| 392 | } |
| @@ -393,15 +393,15 @@ | |
| 393 | to{ |
| 394 | transform: rotateY(360deg); |
| 395 | } |
| 396 | } |
| 397 | body.chat .anim-flip-v { |
| 398 | animation: flip-v 1s linear; |
| 399 | } |
| 400 | @keyframes flip-v{ |
| 401 | from{ |
| 402 | transform: rotateX(0deg); |
| 403 | } |
| 404 | to{ |
| 405 | transform: rotateX(360deg); |
| 406 | } |
| 407 | } |
| 408 |
| --- src/style.chat.css | |
| +++ src/style.chat.css | |
| @@ -382,11 +382,11 @@ | |
| 382 | to { |
| 383 | transform: rotate(360deg); |
| 384 | } |
| 385 | } |
| 386 | body.chat .anim-flip-h { |
| 387 | animation: flip-h 750ms linear; |
| 388 | } |
| 389 | @keyframes flip-h{ |
| 390 | from{ |
| 391 | transform: rotateY(0deg); |
| 392 | } |
| @@ -393,15 +393,15 @@ | |
| 393 | to{ |
| 394 | transform: rotateY(360deg); |
| 395 | } |
| 396 | } |
| 397 | body.chat .anim-flip-v { |
| 398 | animation: flip-v 750ms linear; |
| 399 | } |
| 400 | @keyframes flip-v{ |
| 401 | from{ |
| 402 | transform: rotateX(0deg); |
| 403 | } |
| 404 | to{ |
| 405 | transform: rotateX(360deg); |
| 406 | } |
| 407 | } |
| 408 |