-
Notifications
You must be signed in to change notification settings - Fork 146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Foldable widgets: added keyboard navigation, removed (broken) animation. #7336
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'dart:async'; | ||
import 'dart:html'; | ||
import 'dart:math' show min, max; | ||
|
||
|
@@ -22,41 +23,26 @@ void _setEventForFoldable() { | |
final scrollContainer = _parentWithClass(h, 'scroll-container'); | ||
if (content == null) continue; | ||
|
||
Future<void> update(bool isActive) async { | ||
// Closing is simple: no measurements, no scrolling. | ||
Future<void> toggle() async { | ||
final isActive = foldable.classes.toggle('-active'); | ||
if (!isActive) { | ||
content.style.maxHeight = '0px'; | ||
return; | ||
} | ||
|
||
/// The following coordinate and dimension measurements trigger a reflow, | ||
/// but it is acceptable, as it is local to this event processing and the | ||
/// impact is low. | ||
if (content.style.display != 'block') { | ||
content.style.display = 'block'; | ||
} | ||
// Needs to be empty to measure real dimension. | ||
content.style.maxHeight = ''; | ||
|
||
final contentHeight = content.offsetHeight; | ||
final boundingRect = content.getBoundingClientRect(); | ||
final scrollContainerHeight = scrollContainer?.clientHeight; | ||
final buttonHeight = h.offsetHeight; | ||
|
||
// Reset content state as hidden. | ||
content.style.maxHeight = '0px'; | ||
|
||
// Wait one animation frame before trigger the full height content. | ||
await window.animationFrame; | ||
content.style.maxHeight = '${contentHeight}px'; | ||
|
||
num scrollDiff = 0; | ||
if (scrollContainer != null) { | ||
// Wait one animation frame before measurements. | ||
await window.animationFrame; | ||
|
||
final boundingRect = content.getBoundingClientRect(); | ||
final scrollContainerHeight = scrollContainer.clientHeight; | ||
final buttonHeight = h.offsetHeight; | ||
|
||
/// Calculate the required amount of scrolling in order to have the | ||
/// entire content in the view, aligning it at the bottom of the visible | ||
/// scroll view. | ||
final outsideViewDiff = | ||
boundingRect.top + boundingRect.height - scrollContainerHeight!; | ||
boundingRect.top + boundingRect.height - scrollContainerHeight; | ||
|
||
/// Limit the maximum scrolling to the screen height minus the button | ||
/// component's height, in order to make sure it will be still visible | ||
|
@@ -65,30 +51,31 @@ void _setEventForFoldable() { | |
|
||
/// Scroll the smaller amount of the two. | ||
scrollDiff = max(0, min(screenLimit, outsideViewDiff)); | ||
} | ||
|
||
/// Do not scroll if the difference is small, otherwise scroll in small | ||
/// steps synchronized to the animation frames. | ||
if (scrollDiff > 8) { | ||
final originalScrollTop = scrollContainer!.scrollTop; | ||
final maxSteps = 20; | ||
for (var i = 1; i <= maxSteps; i++) { | ||
if (i > 1) await window.animationFrame; | ||
final nextPos = originalScrollTop + (scrollDiff * i / maxSteps); | ||
scrollContainer.scrollTo(0, nextPos); | ||
/// Do not scroll if the difference is small, otherwise scroll in small | ||
/// steps synchronized to the animation frames. | ||
if (scrollDiff > 8) { | ||
final originalScrollTop = scrollContainer.scrollTop; | ||
final maxSteps = 20; | ||
for (var i = 1; i <= maxSteps; i++) { | ||
if (i > 1) await window.animationFrame; | ||
final nextPos = originalScrollTop + (scrollDiff * i / maxSteps); | ||
scrollContainer.scrollTo(0, nextPos); | ||
} | ||
} | ||
} | ||
|
||
// Wait one animation frame before enabling the content to resize on its own. | ||
await window.animationFrame; | ||
content.style.maxHeight = 'none'; | ||
} | ||
|
||
h.querySelector('.foldable-icon')!.attributes['tabindex'] = '0'; | ||
|
||
// listen on trigger events | ||
h.onClick.listen((e) async { | ||
// Toggle state. | ||
final isActive = foldable.classes.toggle('-active'); | ||
await update(isActive); | ||
e.preventDefault(); | ||
await toggle(); | ||
}); | ||
h.onKeyDown.where((e) => e.key == 'Enter').listen((e) async { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps also spacebar? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wondering about this, whether we should add |
||
e.preventDefault(); | ||
await toggle(); | ||
}); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand all this scrolling logic...
Do we really need it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is for the mobile view: when the navigation menu is at the bottom, and it is clicked, there is no indication that something has been displayed under the fold. Scrolling to it makes the new content visible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just use scrollIntoView?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we have tried it and it worked with full-page elements, but had issues with embedded-scroll of the navigation. It may be worth to re-evaluate it again, a lot of time has passed since.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#7342 to track it separately