Skip to content

Commit cfe490b

Browse files
committed
ShowDialogAction: bottom sheet and side sheet presentations, dialog alignment and insets
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyH8twyrBoe4wFCTw78Cz5
1 parent 7f67b5c commit cfe490b

3 files changed

Lines changed: 125 additions & 1 deletion

File tree

‎lib/src/api/models/action/show_dialog_action.dart‎

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,24 @@ import '../models.dart';
66

77
part 'show_dialog_action.g.dart';
88

9-
/// An action that navigates to a new page.
9+
/// Where a [ShowDialogAction] puts its canvas.
10+
enum DialogPresentation {
11+
/// A dialog at the canvas's design size: centered, or placed by
12+
/// [ShowDialogAction.alignment] and [ShowDialogAction.insets].
13+
dialog,
14+
15+
/// A modal sheet across the bottom of the screen, as wide as the screen and
16+
/// as tall as the canvas (at most 88% of the screen). The canvas draws the
17+
/// sheet's surface, such as rounded top corners and a grab handle.
18+
bottomSheet,
19+
20+
/// A modal sheet along the right edge, as wide as the canvas and as tall
21+
/// as the screen less [ShowDialogAction.insets].
22+
sideSheet,
23+
}
24+
25+
/// An action that shows a canvas over the current one: as a dialog, a bottom
26+
/// sheet or a side sheet.
1027
@JsonSerializable()
1128
class ShowDialogAction extends ActionModel with Equatable, SerializableMixin {
1229
/// Destination canvas node's ID.
@@ -24,13 +41,26 @@ class ShowDialogAction extends ActionModel with Equatable, SerializableMixin {
2441
/// Whether to show a close button on the dialog.
2542
final bool showCloseButton;
2643

44+
/// How the canvas is shown.
45+
final DialogPresentation presentation;
46+
47+
/// Where a dialog sits on the screen, e.g. bottom left for a popover next
48+
/// to a sidebar. Null centers it. Ignored by sheets.
49+
final AlignmentModel? alignment;
50+
51+
/// Space kept between the canvas and the screen edges it is aligned to.
52+
final EdgeInsetsModel insets;
53+
2754
/// Creates a new [ShowDialogAction].
2855
ShowDialogAction({
2956
required this.destinationId,
3057
Map<String, String>? params,
3158
this.barrierColor = ColorRGBA.black54,
3259
this.barrierDismissible = true,
3360
this.showCloseButton = false,
61+
this.presentation = DialogPresentation.dialog,
62+
this.alignment,
63+
this.insets = EdgeInsetsModel.zero,
3464
super.nonBlocking,
3565
super.enabled,
3666
}) : params = {...params ?? {}},
@@ -43,6 +73,9 @@ class ShowDialogAction extends ActionModel with Equatable, SerializableMixin {
4373
barrierDismissible,
4474
barrierColor,
4575
showCloseButton,
76+
presentation,
77+
alignment,
78+
insets,
4679
];
4780

4881
/// Duplicates this [ShowDialogAction] with given data overrides.
@@ -52,12 +85,18 @@ class ShowDialogAction extends ActionModel with Equatable, SerializableMixin {
5285
bool? barrierDismissible,
5386
ColorRGBA? barrierColor,
5487
bool? showCloseButton,
88+
DialogPresentation? presentation,
89+
AlignmentModel? alignment,
90+
EdgeInsetsModel? insets,
5591
}) => ShowDialogAction(
5692
destinationId: destinationId ?? this.destinationId,
5793
params: {...this.params, ...params ?? {}},
5894
barrierDismissible: barrierDismissible ?? this.barrierDismissible,
5995
barrierColor: barrierColor ?? this.barrierColor,
6096
showCloseButton: showCloseButton ?? this.showCloseButton,
97+
presentation: presentation ?? this.presentation,
98+
alignment: alignment ?? this.alignment,
99+
insets: insets ?? this.insets,
61100
);
62101

63102
/// Creates a new [ShowDialogAction] instance from a JSON data.

‎lib/src/api/models/action/show_dialog_action.g.dart‎

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎test/show_dialog_action_test.dart‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import 'package:codelessly_api/codelessly_api.dart';
2+
import 'package:test/test.dart';
3+
4+
void main() {
5+
test('a plain dialog keeps its old JSON', () {
6+
final ActionModel action = ActionModel.fromJson({
7+
'type': 'showDialog',
8+
'destinationId': 'c1',
9+
});
10+
expect(action, isA<ShowDialogAction>());
11+
final ShowDialogAction dialog = action as ShowDialogAction;
12+
expect(dialog.presentation, DialogPresentation.dialog);
13+
expect(dialog.alignment, isNull);
14+
expect(dialog.insets, EdgeInsetsModel.zero);
15+
expect(dialog.toJson(), {
16+
'type': 'showDialog',
17+
'destinationId': 'c1',
18+
'params': <String, dynamic>{},
19+
});
20+
});
21+
22+
test('sheets and placed dialogs round-trip', () {
23+
for (final Map<String, dynamic> json in <Map<String, dynamic>>[
24+
{
25+
'type': 'showDialog',
26+
'destinationId': 'session',
27+
'params': {'id': r'${item.id}'},
28+
'presentation': 'bottomSheet',
29+
},
30+
{
31+
'type': 'showDialog',
32+
'destinationId': 'session',
33+
'params': <String, dynamic>{},
34+
'presentation': 'sideSheet',
35+
'insets': 10,
36+
},
37+
{
38+
'type': 'showDialog',
39+
'destinationId': 'settings',
40+
'params': <String, dynamic>{},
41+
'alignment': {
42+
'data': [-1, 1],
43+
},
44+
'insets': [96, 0, 0, 16],
45+
},
46+
]) {
47+
final ShowDialogAction action =
48+
ActionModel.fromJson(json) as ShowDialogAction;
49+
expect(ActionModel.fromJson(action.toJson()), action, reason: '$json');
50+
}
51+
final ShowDialogAction popover = ActionModel.fromJson({
52+
'type': 'showDialog',
53+
'destinationId': 'settings',
54+
'alignment': {
55+
'data': [-1, 1],
56+
},
57+
'insets': [96, 0, 0, 16],
58+
}) as ShowDialogAction;
59+
expect(popover.alignment, AlignmentModel.bottomLeft);
60+
expect(popover.insets.l, 96);
61+
expect(popover.insets.b, 16);
62+
});
63+
}

0 commit comments

Comments
 (0)