Skip to content

Commit 1b60005

Browse files
committed
feat: auto scroll to bottom after crating a new field
1 parent 5275bb6 commit 1b60005

File tree

4 files changed

+75
-28
lines changed

4 files changed

+75
-28
lines changed

frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ import 'field_type_option_editor.dart';
1313
class FieldEditor extends StatelessWidget with FlowyOverlayDelegate {
1414
final String gridId;
1515
final String fieldName;
16+
final VoidCallback? onRemoved;
1617

1718
final IFieldTypeOptionLoader typeOptionLoader;
1819
const FieldEditor({
1920
required this.gridId,
20-
required this.fieldName,
21+
this.fieldName = "",
2122
required this.typeOptionLoader,
23+
this.onRemoved,
2224
Key? key,
2325
}) : super(key: key);
2426

@@ -73,6 +75,9 @@ class FieldEditor extends StatelessWidget with FlowyOverlayDelegate {
7375

7476
@override
7577
bool asBarrier() => true;
78+
79+
@override
80+
void didRemove() => onRemoved?.call();
7681
}
7782

7883
class _FieldTypeOptionCell extends StatelessWidget {

frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_name_input.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class _FieldNameTextFieldState extends State<FieldNameTextField> {
3333
final theme = context.watch<AppTheme>();
3434
return RoundedInputField(
3535
height: 36,
36+
autoFocus: true,
3637
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
3738
controller: controller,
3839
normalBorderColor: theme.shader4,
@@ -47,7 +48,8 @@ class _FieldNameTextFieldState extends State<FieldNameTextField> {
4748
@override
4849
void didUpdateWidget(covariant FieldNameTextField oldWidget) {
4950
controller.text = widget.name;
50-
controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length));
51+
controller.selection = TextSelection.fromPosition(
52+
TextPosition(offset: controller.text.length));
5153

5254
super.didUpdateWidget(oldWidget);
5355
}

frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ class _PropertyList extends StatelessWidget {
130130
axis: Axis.vertical,
131131
controller: _scrollController,
132132
barSize: GridSize.scrollBarSize,
133+
autoHideScrollbar: false,
133134
child: ListView.separated(
134135
controller: _scrollController,
135136
itemCount: state.gridCells.length,
@@ -145,7 +146,25 @@ class _PropertyList extends StatelessWidget {
145146
),
146147
),
147148
),
148-
_CreateFieldButton(viewId: viewId),
149+
_CreateFieldButton(
150+
viewId: viewId,
151+
onCreated: () {
152+
// _scrollController.
153+
FieldEditor(
154+
gridId: viewId,
155+
typeOptionLoader: NewFieldTypeOptionLoader(gridId: viewId),
156+
onRemoved: () {
157+
WidgetsBinding.instance.addPostFrameCallback((_) {
158+
_scrollController.animateTo(
159+
_scrollController.position.maxScrollExtent,
160+
duration: const Duration(milliseconds: 250),
161+
curve: Curves.ease,
162+
);
163+
});
164+
},
165+
).show(context);
166+
},
167+
),
149168
],
150169
);
151170
},
@@ -155,7 +174,12 @@ class _PropertyList extends StatelessWidget {
155174

156175
class _CreateFieldButton extends StatelessWidget {
157176
final String viewId;
158-
const _CreateFieldButton({required this.viewId, Key? key}) : super(key: key);
177+
final VoidCallback onCreated;
178+
const _CreateFieldButton({
179+
required this.viewId,
180+
required this.onCreated,
181+
Key? key,
182+
}) : super(key: key);
159183

160184
@override
161185
Widget build(BuildContext context) {
@@ -169,11 +193,7 @@ class _CreateFieldButton extends StatelessWidget {
169193
fontSize: 12,
170194
),
171195
hoverColor: theme.shader6,
172-
onTap: () => FieldEditor(
173-
gridId: viewId,
174-
fieldName: "",
175-
typeOptionLoader: NewFieldTypeOptionLoader(gridId: viewId),
176-
).show(context),
196+
onTap: () => onCreated(),
177197
leftIcon: svgWidget("home/add"),
178198
),
179199
);

frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/scrolling/styled_scroll_bar.dart

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class StyledScrollbar extends StatefulWidget {
1414
final ScrollController controller;
1515
final Function(double)? onDrag;
1616
final bool showTrack;
17+
final bool autoHideScrollbar;
1718
final Color? handleColor;
1819
final Color? trackColor;
1920

@@ -30,6 +31,7 @@ class StyledScrollbar extends StatefulWidget {
3031
this.onDrag,
3132
this.contentSize,
3233
this.showTrack = false,
34+
this.autoHideScrollbar = true,
3335
this.handleColor,
3436
this.trackColor})
3537
: super(key: key);
@@ -48,16 +50,13 @@ class ScrollbarState extends State<StyledScrollbar> {
4850
widget.controller.addListener(() => setState(() {}));
4951
widget.controller.position.isScrollingNotifier.addListener(
5052
() {
51-
if (!mounted) {
52-
return;
53-
}
53+
if (!mounted) return;
54+
if (!widget.autoHideScrollbar) return;
5455
_hideScrollbarOperation?.cancel();
5556
if (!widget.controller.position.isScrollingNotifier.value) {
56-
// scroll is stopped
5757
_hideScrollbarOperation = CancelableOperation.fromFuture(
5858
Future.delayed(const Duration(seconds: 2), () {}),
5959
).then((_) {
60-
// Opti: hide with animation
6160
hideHandler = true;
6261
if (mounted) {
6362
setState(() {});
@@ -103,7 +102,6 @@ class ScrollbarState extends State<StyledScrollbar> {
103102
break;
104103
case Axis.horizontal:
105104
// Use supplied contentSize if we have it, otherwise just fallback to maxScrollExtents
106-
107105
if (contentSize != null && contentSize > 0) {
108106
maxExtent = contentSize - constraints.maxWidth;
109107
} else {
@@ -118,7 +116,8 @@ class ScrollbarState extends State<StyledScrollbar> {
118116
// Calculate the alignment for the handle, this is a value between 0 and 1,
119117
// it automatically takes the handle size into acct
120118
// ignore: omit_local_variable_types
121-
double handleAlignment = maxExtent == 0 ? 0 : widget.controller.offset / maxExtent;
119+
double handleAlignment =
120+
maxExtent == 0 ? 0 : widget.controller.offset / maxExtent;
122121

123122
// Convert handle alignment from [0, 1] to [-1, 1]
124123
handleAlignment *= 2.0;
@@ -127,19 +126,25 @@ class ScrollbarState extends State<StyledScrollbar> {
127126
// Calculate handleSize by comparing the total content size to our viewport
128127
var handleExtent = _viewExtent;
129128
if (contentExtent > _viewExtent) {
130-
//Make sure handle is never small than the minSize
129+
// Make sure handle is never small than the minSize
131130
handleExtent = max(60, _viewExtent * _viewExtent / contentExtent);
132131
}
132+
133133
// Hide the handle if content is < the viewExtent
134134
var showHandle = contentExtent > _viewExtent && contentExtent > 0;
135+
135136
if (hideHandler) {
136137
showHandle = false;
137138
}
138139

139140
// Handle color
140-
var handleColor = widget.handleColor ?? (theme.isDark ? theme.bg2.withOpacity(.2) : theme.bg2);
141+
var handleColor = widget.handleColor ??
142+
(theme.isDark ? theme.bg2.withOpacity(.2) : theme.bg2);
141143
// Track color
142-
var trackColor = widget.trackColor ?? (theme.isDark ? theme.bg2.withOpacity(.1) : theme.bg2.withOpacity(.3));
144+
var trackColor = widget.trackColor ??
145+
(theme.isDark
146+
? theme.bg2.withOpacity(.1)
147+
: theme.bg2.withOpacity(.3));
143148

144149
//Layout the stack, it just contains a child, and
145150
return Stack(children: <Widget>[
@@ -149,8 +154,12 @@ class ScrollbarState extends State<StyledScrollbar> {
149154
alignment: const Alignment(1, 1),
150155
child: Container(
151156
color: trackColor,
152-
width: widget.axis == Axis.vertical ? widget.size : double.infinity,
153-
height: widget.axis == Axis.horizontal ? widget.size : double.infinity,
157+
width: widget.axis == Axis.vertical
158+
? widget.size
159+
: double.infinity,
160+
height: widget.axis == Axis.horizontal
161+
? widget.size
162+
: double.infinity,
154163
),
155164
),
156165

@@ -167,10 +176,14 @@ class ScrollbarState extends State<StyledScrollbar> {
167176
// HANDLE SHAPE
168177
child: MouseHoverBuilder(
169178
builder: (_, isHovered) => Container(
170-
width: widget.axis == Axis.vertical ? widget.size : handleExtent,
171-
height: widget.axis == Axis.horizontal ? widget.size : handleExtent,
179+
width:
180+
widget.axis == Axis.vertical ? widget.size : handleExtent,
181+
height: widget.axis == Axis.horizontal
182+
? widget.size
183+
: handleExtent,
172184
decoration: BoxDecoration(
173-
color: handleColor.withOpacity(isHovered ? 1 : .85), borderRadius: Corners.s3Border),
185+
color: handleColor.withOpacity(isHovered ? 1 : .85),
186+
borderRadius: Corners.s3Border),
174187
),
175188
),
176189
),
@@ -182,15 +195,19 @@ class ScrollbarState extends State<StyledScrollbar> {
182195

183196
void _handleHorizontalDrag(DragUpdateDetails details) {
184197
var pos = widget.controller.offset;
185-
var pxRatio = (widget.controller.position.maxScrollExtent + _viewExtent) / _viewExtent;
186-
widget.controller.jumpTo((pos + details.delta.dx * pxRatio).clamp(0.0, widget.controller.position.maxScrollExtent));
198+
var pxRatio = (widget.controller.position.maxScrollExtent + _viewExtent) /
199+
_viewExtent;
200+
widget.controller.jumpTo((pos + details.delta.dx * pxRatio)
201+
.clamp(0.0, widget.controller.position.maxScrollExtent));
187202
widget.onDrag?.call(details.delta.dx);
188203
}
189204

190205
void _handleVerticalDrag(DragUpdateDetails details) {
191206
var pos = widget.controller.offset;
192-
var pxRatio = (widget.controller.position.maxScrollExtent + _viewExtent) / _viewExtent;
193-
widget.controller.jumpTo((pos + details.delta.dy * pxRatio).clamp(0.0, widget.controller.position.maxScrollExtent));
207+
var pxRatio = (widget.controller.position.maxScrollExtent + _viewExtent) /
208+
_viewExtent;
209+
widget.controller.jumpTo((pos + details.delta.dy * pxRatio)
210+
.clamp(0.0, widget.controller.position.maxScrollExtent));
194211
widget.onDrag?.call(details.delta.dy);
195212
}
196213
}
@@ -204,6 +221,7 @@ class ScrollbarListStack extends StatelessWidget {
204221
final EdgeInsets? scrollbarPadding;
205222
final Color? handleColor;
206223
final Color? trackColor;
224+
final bool autoHideScrollbar;
207225

208226
const ScrollbarListStack(
209227
{Key? key,
@@ -214,6 +232,7 @@ class ScrollbarListStack extends StatelessWidget {
214232
this.contentSize,
215233
this.scrollbarPadding,
216234
this.handleColor,
235+
this.autoHideScrollbar = true,
217236
this.trackColor})
218237
: super(key: key);
219238

@@ -238,6 +257,7 @@ class ScrollbarListStack extends StatelessWidget {
238257
contentSize: contentSize,
239258
trackColor: trackColor,
240259
handleColor: handleColor,
260+
autoHideScrollbar: autoHideScrollbar,
241261
),
242262
)
243263
// The animate will be used by the children that using styled_widget.

0 commit comments

Comments
 (0)