Skip to content
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.9.0

- **FEAT**: Added submit button to multi screen form.
- **FEAT**: Added `ServerpodTuiText` to render multi line texts without leading and trailing whitespace after the first line.
- **FIX**: Removed leading and trailing whitespace from multi line form description text.

## 0.8.0

- **FEAT**: `AlertLine` copy/dismiss hints are now clickable.
Expand Down
1 change: 1 addition & 0 deletions lib/components.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export 'src/components/radio_button.dart';
export 'src/components/shimmer.dart';
export 'src/components/spinner.dart';
export 'src/components/tab_bar.dart';
export 'src/components/text.dart';
export 'src/components/tip.dart';
export 'src/components/unconstrained_box.dart';
export 'src/components/wrap.dart';
3 changes: 2 additions & 1 deletion lib/src/components/form/configuration.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:nocterm/nocterm.dart';
import 'package:serverpod_tui/src/components/checkbox.dart';
import 'package:serverpod_tui/src/components/radio_button.dart';
import 'package:serverpod_tui/src/components/text.dart';
import 'package:serverpod_tui/src/components/wrap.dart';
import 'package:serverpod_tui/src/form/config.dart';
import 'package:serverpod_tui/src/form/config_option.dart';
Expand Down Expand Up @@ -34,7 +35,7 @@ class FormConfigurationLayout extends StatelessComponent {
child,
if (config.description case final FormDescription description) ...[
SizedBox(height: description.spacing),
Text(
ServerpodTuiText(
description.label,
style: const TextStyle(
color: Color.defaultColor,
Expand Down
1 change: 1 addition & 0 deletions lib/src/components/form/form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Form extends StatelessComponent {
LogicalKey nextButtonActivationKey,
VoidCallback? onSubmit,
String? summaryDescription,
String? submitButtonLabel,
}) = MultiScreenForm;

final FormState state;
Expand Down
81 changes: 59 additions & 22 deletions lib/src/components/form/multi_screen_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class MultiScreenForm extends Form {
this.nextButtonActivationKey = LogicalKey.space,
super.onSubmit,
this.summaryDescription,
this.submitButtonLabel,
}) : super(state: state);

/// State for multi screen form.
Expand All @@ -36,6 +37,10 @@ class MultiScreenForm extends Form {
/// Optional description for the summary screen.
final String? summaryDescription;

/// Optional label for the summary screen action button.
/// Defaults to 'Submit' when not provided.
final String? submitButtonLabel;

@override
Component build(BuildContext context) {
if (state.isSummary) {
Expand All @@ -45,12 +50,14 @@ class MultiScreenForm extends Form {
padding: padding,
description: summaryDescription,
scrollController: scrollController,
onSubmit: onSubmit,
backButtonActivationKey: backButtonActivationKey,
nextButtonActivationKey: nextButtonActivationKey,
submitButtonLabel: submitButtonLabel,
);
}

final config = state.configurations[state.currentScreenIndex];
final showMultiScreenNavigationButtons =
!state.isSummary && !state.hasSingleScreen;

return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
Expand Down Expand Up @@ -82,13 +89,14 @@ class MultiScreenForm extends Form {
),
),
),
if (showMultiScreenNavigationButtons)
_MultiScreenNavigationButtons(
state: state,
rebuild: rebuild,
backButtonActivationKey: backButtonActivationKey,
nextButtonActivationKey: nextButtonActivationKey,
),
_MultiScreenNavigationButtons(
state: state,
rebuild: rebuild,
backButtonActivationKey: backButtonActivationKey,
nextButtonActivationKey: nextButtonActivationKey,
onSubmit: onSubmit,
submitButtonLabel: submitButtonLabel,
),
],
);
}
Expand All @@ -100,38 +108,51 @@ class _MultiScreenNavigationButtons extends StatelessComponent {
required this.rebuild,
required this.backButtonActivationKey,
required this.nextButtonActivationKey,
this.onSubmit,
this.submitButtonLabel,
});

final MultiScreenFormState state;
final VoidCallback rebuild;
final LogicalKey backButtonActivationKey;
final LogicalKey nextButtonActivationKey;
final VoidCallback? onSubmit;

/// Label for the button to submit the form.
/// Defaults to 'Submit'.
final String? submitButtonLabel;

@override
Component build(BuildContext context) {
final isFirstScreen = state.currentScreenIndex == 0;
final onLastScreen = state.hasSingleScreen || state.isSummary;

return Align(
alignment: Alignment.bottomRight,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (!isFirstScreen) ...[
TextButton(
name: 'Back',
activationKeys: [backButtonActivationKey],
onActivate: (_) {
state.previousScreen();
rebuild();
},
focused: state.focusOnButton && state.focusedButtonIndex == 0,
),
const SizedBox(width: 1),
],
TextButton(
name: 'Back',
activationKeys: [backButtonActivationKey],
onActivate: (_) {
state.previousScreen();
rebuild();
},
enabled: !isFirstScreen,
focused: state.focusOnButton && state.focusedButtonIndex == 0,
),
const SizedBox(width: 1),
TextButton(
name: 'Next',
name: onLastScreen ? submitButtonLabel ?? 'Submit' : 'Next',
activationKeys: [nextButtonActivationKey],
onActivate: (_) {
state.nextScreen();
if (onLastScreen) {
onSubmit?.call();
} else {
state.nextScreen();
}
rebuild();
},
focused: state.focusOnButton && state.focusedButtonIndex == 1,
Expand All @@ -150,13 +171,21 @@ class _SummaryScreen extends StatelessComponent {
required this.padding,
required this.scrollController,
this.description,
this.onSubmit,
this.backButtonActivationKey = LogicalKey.space,
this.nextButtonActivationKey = LogicalKey.space,
this.submitButtonLabel,
});

final MultiScreenFormState state;
final VoidCallback rebuild;
final EdgeInsets padding;
final ScrollController scrollController;
final String? description;
final VoidCallback? onSubmit;
final LogicalKey backButtonActivationKey;
final LogicalKey nextButtonActivationKey;
final String? submitButtonLabel;

@override
Component build(BuildContext context) {
Expand Down Expand Up @@ -194,6 +223,14 @@ class _SummaryScreen extends StatelessComponent {
),
),
),
_MultiScreenNavigationButtons(
state: state,
rebuild: rebuild,
backButtonActivationKey: backButtonActivationKey,
nextButtonActivationKey: nextButtonActivationKey,
onSubmit: onSubmit,
submitButtonLabel: submitButtonLabel,
),
],
);
}
Expand Down
Loading
Loading