chore: auto resize row height

This commit is contained in:
appflowy 2022-04-16 09:25:12 +08:00
parent f3c82f5c30
commit d4de5767a6
10 changed files with 192 additions and 94 deletions

View File

@ -21,6 +21,17 @@ class RowService {
return GridEventCreateRow(payload).send();
}
Future<Either<Unit, FlowyError>> moveRow(String rowId, int fromIndex, int toIndex) {
final payload = MoveItemPayload.create()
..gridId = gridId
..itemId = rowId
..ty = MoveItemType.MoveRow
..fromIndex = fromIndex
..toIndex = toIndex;
return GridEventMoveItem(payload).send();
}
Future<Either<Row, FlowyError>> getRow() {
final payload = RowIdentifierPayload.create()
..gridId = gridId

View File

@ -107,7 +107,7 @@ class _FlowyGridState extends State<FlowyGrid> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _GridToolbarAdaptor(),
_gridHeader(context, state.gridId, contentWidth),
_gridHeader(context, state.gridId),
Flexible(child: child),
],
);
@ -147,16 +147,12 @@ class _FlowyGridState extends State<FlowyGrid> {
);
}
Widget _gridHeader(BuildContext context, String gridId, double contentWidth) {
Widget _gridHeader(BuildContext context, String gridId) {
final fieldCache = context.read<GridBloc>().fieldCache;
return SizedBox(
width: contentWidth,
child: GridHeaderSliverAdaptor(
gridId: gridId,
fieldCache: fieldCache,
anchorScrollController: headerScrollController,
),
return GridHeaderSliverAdaptor(
gridId: gridId,
fieldCache: fieldCache,
anchorScrollController: headerScrollController,
);
}
}

View File

@ -33,9 +33,7 @@ class CellContainer extends StatelessWidget {
child: Consumer<CellStateNotifier>(
builder: (context, state, _) {
return Container(
constraints: BoxConstraints(
maxWidth: width,
),
constraints: BoxConstraints(maxWidth: width, maxHeight: 42),
decoration: _makeBoxDecoration(context, state),
padding: GridSize.cellContentInsets,
child: Center(child: child),

View File

@ -6,6 +6,7 @@ import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'field_type_extension.dart';
@ -28,49 +29,19 @@ class GridFieldCell extends StatelessWidget {
final button = FlowyButton(
hoverColor: theme.shader6,
onTap: () => _showActionSheet(context),
// rightIcon: svgWidget("editor/details", color: theme.iconColor),
leftIcon: svgWidget(state.field.fieldType.iconName(), color: theme.iconColor),
text: FlowyText.medium(state.field.name, fontSize: 12),
padding: GridSize.cellContentInsets,
);
final line = InkWell(
onTap: () {},
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onHorizontalDragCancel: () {},
onHorizontalDragUpdate: (value) {
context.read<FieldCellBloc>().add(FieldCellEvent.updateWidth(value.delta.dx));
},
child: FlowyHover(
style: HoverStyle(
hoverColor: theme.main1,
borderRadius: BorderRadius.zero,
contentMargin: const EdgeInsets.only(left: 5),
),
builder: (_, onHover) => const SizedBox(width: 2),
),
),
);
const line = Positioned(top: 0, bottom: 0, right: 0, child: _DragToExpandLine());
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
final decoration = BoxDecoration(
border: Border(
top: borderSide,
right: borderSide,
bottom: borderSide,
));
return Container(
return _CellContainer(
width: state.field.width.toDouble(),
decoration: decoration,
child: ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: Stack(
alignment: Alignment.centerRight,
fit: StackFit.expand,
children: [button, Positioned(top: 0, bottom: 0, right: 0, child: line)],
),
child: Stack(
alignment: Alignment.centerRight,
fit: StackFit.expand,
children: [button, line],
),
);
},
@ -98,3 +69,65 @@ class GridFieldCell extends StatelessWidget {
).show(context);
}
}
class _CellContainer extends StatelessWidget {
final Widget child;
final double width;
const _CellContainer({
required this.child,
required this.width,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
final decoration = BoxDecoration(
border: Border(
top: borderSide,
right: borderSide,
bottom: borderSide,
));
return Container(
width: width,
decoration: decoration,
child: ConstrainedBox(constraints: const BoxConstraints.expand(), child: child),
);
}
}
class _DragToExpandLine extends StatelessWidget {
const _DragToExpandLine({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return InkWell(
onTap: () {},
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onHorizontalDragCancel: () {},
onHorizontalDragUpdate: (value) {
// context.read<FieldCellBloc>().add(FieldCellEvent.updateWidth(value.delta.dx));
Log.info(value);
},
onHorizontalDragEnd: (end) {
Log.info(end);
},
child: FlowyHover(
style: HoverStyle(
hoverColor: theme.main1,
borderRadius: BorderRadius.zero,
contentMargin: const EdgeInsets.only(left: 5),
),
builder: (_, onHover) => const SizedBox(width: 2),
),
),
);
}
}

View File

@ -32,15 +32,21 @@ class _GridHeaderSliverAdaptorState extends State<GridHeaderSliverAdaptor> {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) =>
getIt<GridHeaderBloc>(param1: widget.gridId, param2: widget.fieldCache)..add(const GridHeaderEvent.initial()),
create: (context) {
final bloc = getIt<GridHeaderBloc>(param1: widget.gridId, param2: widget.fieldCache);
bloc.add(const GridHeaderEvent.initial());
return bloc;
},
child: BlocBuilder<GridHeaderBloc, GridHeaderState>(
buildWhen: (previous, current) => previous.fields.length != current.fields.length,
builder: (context, state) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: widget.anchorScrollController,
child: SizedBox(height: GridSize.headerHeight, child: _GridHeader(gridId: widget.gridId)),
child: SizedBox(
height: GridSize.headerHeight,
child: _GridHeader(gridId: widget.gridId),
),
);
// return SliverPersistentHeader(
@ -54,32 +60,6 @@ class _GridHeaderSliverAdaptorState extends State<GridHeaderSliverAdaptor> {
}
}
class SliverHeaderDelegateImplementation extends SliverPersistentHeaderDelegate {
final String gridId;
final List<Field> fields;
SliverHeaderDelegateImplementation({required this.gridId, required this.fields});
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return _GridHeader(gridId: gridId);
}
@override
double get maxExtent => GridSize.headerHeight;
@override
double get minExtent => GridSize.headerHeight;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
if (oldDelegate is SliverHeaderDelegateImplementation) {
return fields.length != oldDelegate.fields.length;
}
return true;
}
}
class _GridHeader extends StatefulWidget {
final String gridId;
const _GridHeader({Key? key, required this.gridId}) : super(key: key);
@ -177,3 +157,29 @@ class CreateFieldButton extends StatelessWidget {
);
}
}
class SliverHeaderDelegateImplementation extends SliverPersistentHeaderDelegate {
final String gridId;
final List<Field> fields;
SliverHeaderDelegateImplementation({required this.gridId, required this.fields});
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return _GridHeader(gridId: gridId);
}
@override
double get maxExtent => GridSize.headerHeight;
@override
double get minExtent => GridSize.headerHeight;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
if (oldDelegate is SliverHeaderDelegateImplementation) {
return fields.length != oldDelegate.fields.length;
}
return true;
}
}

View File

@ -44,10 +44,11 @@ class _GridRowWidgetState extends State<GridRowWidget> {
child: BlocBuilder<RowBloc, RowState>(
buildWhen: (p, c) => p.rowData.height != c.rowData.height,
builder: (context, state) {
return SizedBox(
height: _rowBloc.state.rowData.height,
return LimitedBox(
maxHeight: 200,
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
_RowLeading(),
_RowCells(),
@ -147,7 +148,11 @@ class _RowCells extends StatelessWidget {
buildWhen: (previous, current) => previous.cellDataMap != current.cellDataMap,
builder: (context, state) {
final List<Widget> children = state.cellDataMap.fold(() => [], _toCells);
return Row(children: children);
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: children,
);
},
);
}

View File

@ -51,16 +51,16 @@ impl ClientGridBlockMetaEditor {
let mut row_count = 0;
let mut row_index = None;
let _ = self
.modify(|pad| {
.modify(|block_pad| {
if let Some(start_row_id) = start_row_id.as_ref() {
match pad.index_of_row(start_row_id) {
match block_pad.index_of_row(start_row_id) {
None => {}
Some(index) => row_index = Some(index + 1),
}
}
let change = pad.add_row_meta(row, start_row_id)?;
row_count = pad.number_of_rows();
let change = block_pad.add_row_meta(row, start_row_id)?;
row_count = block_pad.number_of_rows();
Ok(change)
})
.await?;
@ -71,9 +71,9 @@ impl ClientGridBlockMetaEditor {
pub async fn delete_rows(&self, ids: Vec<Cow<'_, String>>) -> FlowyResult<i32> {
let mut row_count = 0;
let _ = self
.modify(|pad| {
let changeset = pad.delete_rows(ids)?;
row_count = pad.number_of_rows();
.modify(|block_pad| {
let changeset = block_pad.delete_rows(ids)?;
row_count = block_pad.number_of_rows();
Ok(changeset)
})
.await?;
@ -81,7 +81,14 @@ impl ClientGridBlockMetaEditor {
}
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
let _ = self.modify(|pad| Ok(pad.update_row(changeset)?)).await?;
let _ = self.modify(|block_pad| Ok(block_pad.update_row(changeset)?)).await?;
Ok(())
}
pub async fn move_row(&self, row_id: &str, from: usize, to: usize) -> FlowyResult<()> {
let _ = self
.modify(|block_pad| Ok(block_pad.move_row(row_id, from, to)?))
.await?;
Ok(())
}

View File

@ -154,6 +154,32 @@ impl GridBlockMetaEditorManager {
Ok(changesets)
}
pub(crate) async fn move_row(&self, row_id: &str, from: usize, to: usize) -> FlowyResult<()> {
let editor = self.get_editor_from_row_id(row_id).await?;
let _ = editor.move_row(row_id, from, to).await?;
match editor.get_row_metas(Some(vec![Cow::Borrowed(row_id)])).await?.pop() {
None => {}
Some(row_meta) => {
let row_order = RowOrder::from(&row_meta);
let insert_row = IndexRowOrder {
row_order: row_order.clone(),
index: Some(to as i32),
};
let notified_changeset = GridRowsChangeset {
block_id: editor.block_id.clone(),
inserted_rows: vec![insert_row],
deleted_rows: vec![row_order],
updated_rows: vec![],
};
let _ = self.notify_did_update_rows(notified_changeset).await?;
}
}
Ok(())
}
pub async fn update_cell(&self, changeset: CellChangeset) -> FlowyResult<()> {
let row_id = changeset.row_id.clone();
let editor = self.get_editor_from_row_id(&row_id).await?;

View File

@ -388,7 +388,7 @@ impl ClientGridEditor {
self.move_field(&params.item_id, params.from_index, params.to_index)
.await
}
MoveItemType::MoveRow => self.move_row(params.from_index, params.to_index, &params.item_id).await,
MoveItemType::MoveRow => self.move_row(&params.item_id, params.from_index, params.to_index).await,
}
}
@ -411,9 +411,12 @@ impl ClientGridEditor {
Ok(())
}
pub async fn move_row(&self, from: i32, to: i32, row_id: &str) -> FlowyResult<()> {
// GridRowsChangeset
todo!()
pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> {
let _ = self
.block_meta_manager
.move_row(row_id, from as usize, to as usize)
.await?;
Ok(())
}
pub async fn delta_bytes(&self) -> Bytes {

View File

@ -149,6 +149,19 @@ impl GridBlockMetaPad {
})
}
pub fn move_row(&mut self, row_id: &str, from: usize, to: usize) -> CollaborateResult<Option<GridBlockMetaChange>> {
self.modify(|row_metas| {
if let Some(position) = row_metas.iter().position(|row_meta| row_meta.id == row_id) {
debug_assert_eq!(from, position);
let row_meta = row_metas.remove(position);
row_metas.insert(to, row_meta);
Ok(Some(()))
} else {
Ok(None)
}
})
}
pub fn modify<F>(&mut self, f: F) -> CollaborateResult<Option<GridBlockMetaChange>>
where
F: for<'a> FnOnce(&'a mut Vec<Arc<RowMeta>>) -> CollaborateResult<Option<()>>,