diff --git a/demo/lib/components/alert.dart b/demo/lib/components/alert.dart new file mode 100644 index 0000000..1f67ccf --- /dev/null +++ b/demo/lib/components/alert.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:mix/mix.dart'; +import 'package:remix_ui/remix_ui.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +@widgetbook.UseCase( + name: 'interactive playground', + type: RemixAlert, +) +Widget buildCheckboxUseCase(BuildContext context) { + return Center( + child: SizedBox( + width: 300, + child: RemixAlert( + title: StyledText( + context.knobs.string( + label: 'Title', + initialValue: 'Error', + ), + ), + subtitle: StyledText( + context.knobs.string( + label: 'Subtitle', + initialValue: 'Your session has expired. Please log in again.', + ), + ), + leading: context.knobs.boolean( + label: 'Leading', + initialValue: false, + ) + ? const StyledIcon(Icons.warning_amber_rounded) + : null, + ), + ), + ); +} diff --git a/demo/lib/main.directories.g.dart b/demo/lib/main.directories.g.dart index 1e1e610..43adeb8 100644 --- a/demo/lib/main.directories.g.dart +++ b/demo/lib/main.directories.g.dart @@ -9,19 +9,32 @@ // ************************************************************************** // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:demo/components/avatar.dart' as _i2; -import 'package:demo/components/badge.dart' as _i3; -import 'package:demo/components/button.dart' as _i4; -import 'package:demo/components/checkbox.dart' as _i5; -import 'package:demo/components/list_tile.dart' as _i6; -import 'package:demo/components/radio.dart' as _i7; -import 'package:demo/components/switch.dart' as _i8; +import 'package:demo/components/alert.dart' as _i2; +import 'package:demo/components/avatar.dart' as _i3; +import 'package:demo/components/badge.dart' as _i4; +import 'package:demo/components/button.dart' as _i5; +import 'package:demo/components/checkbox.dart' as _i6; +import 'package:demo/components/list_tile.dart' as _i7; +import 'package:demo/components/radio.dart' as _i8; +import 'package:demo/components/switch.dart' as _i9; import 'package:widgetbook/widgetbook.dart' as _i1; final directories = <_i1.WidgetbookNode>[ _i1.WidgetbookFolder( name: 'components', children: [ + _i1.WidgetbookFolder( + name: 'alert', + children: [ + _i1.WidgetbookLeafComponent( + name: 'RemixAlert', + useCase: _i1.WidgetbookUseCase( + name: 'interactive playground', + builder: _i2.buildCheckboxUseCase, + ), + ) + ], + ), _i1.WidgetbookFolder( name: 'avatar', children: [ @@ -29,7 +42,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'RemixAvatar', useCase: _i1.WidgetbookUseCase( name: 'interactive playground', - builder: _i2.buildCheckboxUseCase, + builder: _i3.buildCheckboxUseCase, ), ) ], @@ -41,7 +54,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'RemixBadge', useCase: _i1.WidgetbookUseCase( name: 'interactive playground', - builder: _i3.buildCheckboxUseCase, + builder: _i4.buildCheckboxUseCase, ), ) ], @@ -53,7 +66,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'RemixButton', useCase: _i1.WidgetbookUseCase( name: 'interactive playground', - builder: _i4.buildButtonUseCase, + builder: _i5.buildButtonUseCase, ), ) ], @@ -65,7 +78,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'RemixCheckbox', useCase: _i1.WidgetbookUseCase( name: 'interactive playground', - builder: _i5.buildCheckboxUseCase, + builder: _i6.buildCheckboxUseCase, ), ) ], @@ -77,7 +90,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'RemixListTile', useCase: _i1.WidgetbookUseCase( name: 'interactive playground', - builder: _i6.buildCheckboxUseCase, + builder: _i7.buildCheckboxUseCase, ), ) ], @@ -89,7 +102,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'RemixRadio', useCase: _i1.WidgetbookUseCase( name: 'interactive playground', - builder: _i7.buildRadioUseCase, + builder: _i8.buildRadioUseCase, ), ) ], @@ -101,7 +114,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'RemixSwitch', useCase: _i1.WidgetbookUseCase( name: 'interactive playground', - builder: _i8.buildRadioUseCase, + builder: _i9.buildRadioUseCase, ), ) ], diff --git a/lib/components/alert/alert.dart b/lib/components/alert/alert.dart new file mode 100644 index 0000000..d850403 --- /dev/null +++ b/lib/components/alert/alert.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:mix/mix.dart'; +import 'package:remix_ui/components/alert/alert.style.dart'; +import 'package:remix_ui/components/card/card.style.dart'; + +import '../../utils/component_recipe.dart'; + +class RemixAlert extends StatelessWidget + implements RemixComponentRecipe { + const RemixAlert({ + super.key, + this.leading, + this.title, + this.subtitle, + this.style, + this.variants = const [], + }); + + final Widget? leading; + final Widget? title; + final Widget? subtitle; + + @override + final AlertStyles? style; + + @override + final List variants; + + AlertStyles buildStyle(List variants) { + final result = style == null ? AlertStyles.base() : style!; + return result.applyVariants(variants); + } + + @override + Widget build(BuildContext context) { + final style = buildStyle(variants); + + return HBox( + style: style.outerRowContainer, + children: [ + if (leading != null) + MixProvider.build( + context, + style: style.icon, + builder: (_) => leading!, + ), + VBox( + style: style.innerColumnContainer, + children: [ + if (title != null) + MixProvider.build( + context, + style: style.title, + builder: (_) => title!, + ), + if (subtitle != null) + MixProvider.build( + context, + style: style.subtitle, + builder: (_) => subtitle!, + ), + ], + ), + ], + ); + } +} diff --git a/lib/components/alert/alert.style.dart b/lib/components/alert/alert.style.dart new file mode 100644 index 0000000..e9bb26a --- /dev/null +++ b/lib/components/alert/alert.style.dart @@ -0,0 +1,104 @@ +import 'package:mix/mix.dart'; + +class AlertStyles extends StyleRecipe { + const AlertStyles({ + this.outerRowContainer = const Style.empty(), + this.innerColumnContainer = const Style.empty(), + this.title = const Style.empty(), + this.subtitle = const Style.empty(), + this.icon = const Style.empty(), + }); + + final Style outerRowContainer; + final Style innerColumnContainer; + final Style title; + final Style subtitle; + final Style icon; + + factory AlertStyles.base() { + return AlertStyles( + outerRowContainer: _outerRowContainer(), + innerColumnContainer: _innerColumnContainer(), + title: _title(), + subtitle: _subtitle(), + icon: _icon(), + ); + } + + @override + AlertStyles applyVariants(List variants) { + return AlertStyles( + outerRowContainer: outerRowContainer.applyVariants(variants), + innerColumnContainer: innerColumnContainer.applyVariants(variants), + title: title.applyVariants(variants), + subtitle: subtitle.applyVariants(variants), + icon: icon.applyVariants(variants), + ); + } + + @override + AlertStyles merge(AlertStyles? other) { + if (other == null) return this; + return copyWith( + outerRowContainer: outerRowContainer.merge(other.outerRowContainer), + innerColumnContainer: + innerColumnContainer.merge(other.innerColumnContainer), + title: title.merge(other.title), + subtitle: subtitle.merge(other.subtitle), + icon: icon.merge(other.icon), + ); + } + + @override + AlertStyles copyWith({ + Style? outerRowContainer, + Style? innerColumnContainer, + Style? title, + Style? subtitle, + Style? icon, + }) { + return AlertStyles( + outerRowContainer: outerRowContainer ?? this.outerRowContainer, + innerColumnContainer: innerColumnContainer ?? this.innerColumnContainer, + title: title ?? this.title, + subtitle: subtitle ?? this.subtitle, + icon: icon ?? this.icon, + ); + } +} + +Style _outerRowContainer() => Style( + flex.gap(8), + box.padding(16), + box.borderRadius(8), + box.border.width(1), + box.border.color.redAccent(), + flex.mainAxisSize.min(), + flex.mainAxisAlignment.start(), + flex.crossAxisAlignment.start(), + ); + +Style _innerColumnContainer() => Style( + flex.gap(2), + flex.mainAxisSize.min(), + flex.mainAxisAlignment.start(), + flex.crossAxisAlignment.start(), + flexible.expanded(), + ); + +Style _title() => Style( + text.style.fontSize(14), + text.style.fontWeight.w600(), + text.style.color.redAccent(), + ); + +Style _subtitle() => Style( + text.style.fontSize(14), + text.style.fontWeight.normal(), + text.style.color.redAccent(), + ); + +Style _icon() => Style( + icon.size(20), + icon.color.redAccent(), + ); diff --git a/lib/remix_ui.dart b/lib/remix_ui.dart index e134e5e..750555c 100644 --- a/lib/remix_ui.dart +++ b/lib/remix_ui.dart @@ -1,5 +1,7 @@ library remix_ui; +export 'components/alert/alert.dart'; +export 'components/alert/alert.style.dart'; export 'components/button/button.dart'; export 'components/button/button.style.dart'; export 'components/button/button.variants.dart';