Skip to content
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

fix: [MDS-624] Fix Accordion content outside version focus and hover #231

Merged
merged 4 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 113 additions & 158 deletions lib/src/widgets/accordion/accordion.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'package:moon_design/src/theme/tokens/transitions.dart';
import 'package:moon_design/src/utils/color_tween_premul.dart';
import 'package:moon_design/src/utils/extensions.dart';
import 'package:moon_design/src/utils/squircle/squircle_border.dart';
import 'package:moon_design/src/widgets/common/effects/focus_effect.dart';
import 'package:moon_design/src/widgets/common/base_control.dart';
import 'package:moon_design/src/widgets/common/icons/icons.dart';
import 'package:moon_design/src/widgets/common/icons/moon_icon.dart';

Expand Down Expand Up @@ -233,10 +233,6 @@ class MoonAccordion<T> extends StatefulWidget {
class _MoonAccordionState<T> extends State<MoonAccordion<T>> with TickerProviderStateMixin {
static final Animatable<double> _halfTween = Tween<double>(begin: 0.0, end: 0.5);

late final Map<Type, Action<Intent>> _actions = {
ActivateIntent: CallbackAction<Intent>(onInvoke: (_) => _handleTap())
};

late MoonAccordionSizeProperties _effectiveMoonAccordionSize;
late BorderRadiusGeometry _effectiveBorderRadius;
late EdgeInsetsGeometry _effectiveHeaderPadding;
Expand Down Expand Up @@ -265,37 +261,11 @@ class _MoonAccordionState<T> extends State<MoonAccordion<T>> with TickerProvider
Color? _effectiveHoverEffectColor;

bool _isExpanded = false;
bool _isFocused = false;
bool _isHovered = false;

FocusNode get _effectiveFocusNode => widget.focusNode ?? (_focusNode ??= FocusNode());

void _handleHover(bool hover) {
if (hover != _isHovered) {
setState(() => _isHovered = hover);

if (hover) {
_hoverAnimationController!.forward();
} else {
_hoverAnimationController!.reverse();
}
}
}

void _handleFocus(bool focus) {
if (focus != _isFocused) {
setState(() => _isFocused = focus);

if (focus) {
_hoverAnimationController!.forward();
} else {
_hoverAnimationController!.reverse();
}
}
}

void _handleFocusChange(bool hasFocus) {
setState(() => _isFocused = hasFocus);
void _handleActiveState(bool isActive) {
isActive ? _hoverAnimationController!.forward() : _hoverAnimationController!.reverse();
}

void _handleTap() {
Expand Down Expand Up @@ -417,42 +387,46 @@ class _MoonAccordionState<T> extends State<MoonAccordion<T>> with TickerProvider
);
}

Widget _buildHeader({bool isContentOutsideHeader = false, required Widget child}) {
Widget _buildDecorationContainer({required Widget child}) {
final Color effectiveBorderColor =
widget.borderColor ?? context.moonTheme?.accordionTheme.colors.borderColor ?? MoonColors.light.beerus;

final List<BoxShadow> effectiveShadows =
widget.shadows ?? context.moonTheme?.accordionTheme.shadows.shadows ?? MoonShadows.light.sm;

return RepaintBoundary(
child: AnimatedBuilder(
animation: _hoverAnimationController!,
builder: (context, child) {
return Container(
height: isContentOutsideHeader ? _effectiveHeaderHeight : null,
padding: isContentOutsideHeader ? _resolvedDirectionalHeaderPadding : null,
clipBehavior: widget.clipBehavior ?? Clip.none,
decoration: widget.decoration ??
((widget.hasContentOutside && isContentOutsideHeader) ||
(!widget.hasContentOutside && !isContentOutsideHeader)
? ShapeDecoration(
color: _hoverColor!.value,
shadows: effectiveShadows,
shape: MoonSquircleBorder(
side: widget.showBorder ? BorderSide(color: effectiveBorderColor) : BorderSide.none,
borderRadius: _effectiveBorderRadius.squircleBorderRadius(context),
),
)
: null),
child: child,
);
},
child: child,
),
return MoonBaseControl(
borderRadius: _effectiveBorderRadius.squircleBorderRadius(context),
autofocus: widget.autofocus,
focusNode: _effectiveFocusNode,
onTap: _handleTap,
builder: (context, isEnabled, isHovered, isFocused, isPressed) {
final bool isActive = isHovered || isFocused;
_handleActiveState(isActive);

return AnimatedBuilder(
animation: _hoverAnimationController!,
builder: (context, child) {
return Container(
clipBehavior: widget.clipBehavior ?? Clip.none,
decoration: widget.decoration ??
ShapeDecoration(
color: _hoverColor!.value,
shadows: effectiveShadows,
shape: MoonSquircleBorder(
side: widget.showBorder ? BorderSide(color: effectiveBorderColor) : BorderSide.none,
borderRadius: _effectiveBorderRadius.squircleBorderRadius(context),
),
),
child: child,
);
},
child: child,
);
},
);
}

Widget _buildChildren(BuildContext context, Widget? rootChild) {
Widget _buildContent(BuildContext context, Widget? rootChild) {
_effectiveHoverEffectColor ??= context.moonEffects?.controlHoverEffect.primaryHoverColor ??
MoonEffectsTheme(tokens: MoonTokens.light).controlHoverEffect.primaryHoverColor;

Expand All @@ -466,21 +440,6 @@ class _MoonAccordionState<T> extends State<MoonAccordion<T>> with TickerProvider

_resolvedDirectionalHeaderPadding = _effectiveHeaderPadding.resolve(Directionality.of(context));

final double effectiveFocusEffectExtent = context.moonEffects?.controlFocusEffect.effectExtent ??
MoonEffectsTheme(tokens: MoonTokens.light).controlFocusEffect.effectExtent;

final Color effectiveFocusEffectColor = context.moonEffects?.controlFocusEffect.effectColor ??
MoonEffectsTheme(tokens: MoonTokens.light).controlFocusEffect.effectColor;

final Duration effectiveFocusEffectDuration = context.moonEffects?.controlFocusEffect.effectDuration ??
MoonEffectsTheme(tokens: MoonTokens.light).controlFocusEffect.effectDuration;

final Curve effectiveFocusEffectCurve = context.moonEffects?.controlFocusEffect.effectCurve ??
MoonEffectsTheme(tokens: MoonTokens.light).controlFocusEffect.effectCurve;

final Color effectiveHoverEffectColor = context.moonEffects?.controlHoverEffect.primaryHoverColor ??
MoonEffectsTheme(tokens: MoonTokens.light).controlHoverEffect.primaryHoverColor;

final Color effectiveBackgroundColor =
widget.backgroundColor ?? context.moonTheme?.accordionTheme.colors.backgroundColor ?? MoonColors.light.gohan;

Expand Down Expand Up @@ -509,13 +468,15 @@ class _MoonAccordionState<T> extends State<MoonAccordion<T>> with TickerProvider
context.moonTheme?.accordionTheme.colors.expandedTrailingColor ??
MoonColors.light.textPrimary;

final TextStyle effectiveHeaderTextStyle = _effectiveMoonAccordionSize.headerTextStyle;

final Color effectiveContentTextColor =
context.moonTheme?.accordionTheme.colors.contentColor ?? MoonColors.light.textPrimary;

final TextStyle effectiveHeaderTextStyle = _effectiveMoonAccordionSize.headerTextStyle;
final TextStyle effectiveContentTextStyle = _effectiveMoonAccordionSize.contentTextStyle;

final Color effectiveHoverEffectColor = context.moonEffects?.controlHoverEffect.primaryHoverColor ??
MoonEffectsTheme(tokens: MoonTokens.light).controlHoverEffect.primaryHoverColor;

final Duration effectiveHoverEffectDuration = context.moonEffects?.controlHoverEffect.hoverDuration ??
MoonEffectsTheme(tokens: MoonTokens.light).controlHoverEffect.hoverDuration;

Expand Down Expand Up @@ -563,90 +524,82 @@ class _MoonAccordionState<T> extends State<MoonAccordion<T>> with TickerProvider
..begin = effectiveTrailingColor
..end = effectiveExpandedTrailingColor;

return Semantics(
label: widget.semanticLabel,
enabled: _isExpanded,
focused: _isFocused,
child: FocusableActionDetector(
actions: _actions,
focusNode: _effectiveFocusNode,
autofocus: widget.autofocus,
onFocusChange: _handleFocusChange,
onShowFocusHighlight: _handleFocus,
onShowHoverHighlight: _handleHover,
child: MoonFocusEffect(
show: _isFocused,
childBorderRadius: _effectiveBorderRadius,
effectColor: effectiveFocusEffectColor,
effectDuration: effectiveFocusEffectDuration,
effectCurve: effectiveFocusEffectCurve,
effectExtent: effectiveFocusEffectExtent,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: _handleTap,
child: _buildHeader(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_buildHeader(
isContentOutsideHeader: true,
child: Row(
children: [
if (widget.leading != null)
IconTheme(
data: IconThemeData(color: _leadingColor!.value),
child: DefaultTextStyle(
style: effectiveHeaderTextStyle.copyWith(color: _leadingColor!.value),
child: Padding(
padding: EdgeInsetsDirectional.only(end: _resolvedDirectionalHeaderPadding.left),
child: widget.leading,
),
),
),
IconTheme(
data: IconThemeData(color: _titleColor!.value),
child: DefaultTextStyle(
style: effectiveHeaderTextStyle.copyWith(color: _titleColor!.value),
child: Expanded(child: widget.title),
),
),
IconTheme(
data: IconThemeData(color: _trailingColor!.value),
child: DefaultTextStyle(
style: effectiveHeaderTextStyle.copyWith(color: _trailingColor!.value),
child: widget.trailing ?? _buildIcon(context)!,
),
),
],
),
),
ClipRect(
child: Column(
children: [
IconTheme(
data: IconThemeData(color: effectiveContentTextColor),
child: DefaultTextStyle(
style: effectiveContentTextStyle.copyWith(color: effectiveContentTextColor),
child: Align(
alignment: widget.expandedAlignment ?? Alignment.topCenter,
heightFactor: _expansionCurvedAnimation!.value,
child: rootChild,
),
),
),
],
),
),
],
final Widget header = SizedBox(
height: _effectiveHeaderHeight,
child: Padding(
padding: _resolvedDirectionalHeaderPadding,
child: Row(
children: [
if (widget.leading != null)
IconTheme(
data: IconThemeData(color: _leadingColor!.value),
child: DefaultTextStyle(
style: effectiveHeaderTextStyle.copyWith(color: _leadingColor!.value),
child: Padding(
padding: EdgeInsetsDirectional.only(end: _resolvedDirectionalHeaderPadding.left),
child: widget.leading,
),
),
),
IconTheme(
data: IconThemeData(color: _titleColor!.value),
child: DefaultTextStyle(
style: effectiveHeaderTextStyle.copyWith(color: _titleColor!.value),
child: Expanded(child: widget.title),
),
),
IconTheme(
data: IconThemeData(color: _trailingColor!.value),
child: DefaultTextStyle(
style: effectiveHeaderTextStyle.copyWith(color: _trailingColor!.value),
child: widget.trailing ?? _buildIcon(context)!,
),
),
],
),
),
);

final Widget childWrapper = ClipRect(
child: IconTheme(
data: IconThemeData(color: effectiveContentTextColor),
child: DefaultTextStyle(
style: effectiveContentTextStyle.copyWith(color: effectiveContentTextColor),
child: Align(
alignment: widget.expandedAlignment ?? Alignment.topCenter,
heightFactor: _expansionCurvedAnimation!.value,
child: rootChild,
),
),
),
);

return switch (widget.hasContentOutside) {
true => Semantics(
label: widget.semanticLabel,
enabled: _isExpanded,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_buildDecorationContainer(child: header),
childWrapper,
],
),
),
false => Semantics(
label: widget.semanticLabel,
enabled: _isExpanded,
child: _buildDecorationContainer(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
header,
childWrapper,
],
),
),
),
};
}

@override
Expand Down Expand Up @@ -682,10 +635,12 @@ class _MoonAccordionState<T> extends State<MoonAccordion<T>> with TickerProvider
),
);

return AnimatedBuilder(
animation: _expansionAnimationController!.view,
builder: _buildChildren,
child: shouldRemoveChildren ? null : result,
return RepaintBoundary(
child: AnimatedBuilder(
animation: _expansionAnimationController!.view,
builder: _buildContent,
child: shouldRemoveChildren ? null : result,
),
);
}
}
Loading