Skip to content

Commit

Permalink
Merge pull request AppFlowy-IO#472 from AppFlowy-IO/feat_row_expand
Browse files Browse the repository at this point in the history
Feat: auto expand row height
  • Loading branch information
appflowy authored Apr 30, 2022
2 parents 5e8c890 + 3435e42 commit 2848ecb
Show file tree
Hide file tree
Showing 16 changed files with 410 additions and 307 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -365,4 +365,11 @@ class GridCell with _$GridCell {
required Field field,
Cell? cell,
}) = _GridCell;

// ignore: unused_element
const GridCell._();

String cellId() {
return rowId + field.id + "${field.fieldType}";
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
Expand Down Expand Up @@ -28,7 +30,13 @@ class RowBloc extends Bloc<RowEvent, RowState> {
_rowService.createRow();
},
didReceiveCellDatas: (_DidReceiveCellDatas value) async {
emit(state.copyWith(cellDataMap: value.cellData));
final fields = value.gridCellMap.values.map((e) => CellSnapshot(e.field)).toList();
final snapshots = UnmodifiableListView(fields);
emit(state.copyWith(
gridCellMap: value.gridCellMap,
snapshots: snapshots,
changeReason: value.reason,
));
},
);
},
Expand All @@ -47,7 +55,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
Future<void> _startListening() async {
_rowListenFn = _rowCache.addRowListener(
rowId: state.rowData.rowId,
onUpdated: (cellDatas) => add(RowEvent.didReceiveCellDatas(cellDatas)),
onUpdated: (cellDatas, reason) => add(RowEvent.didReceiveCellDatas(cellDatas, reason)),
listenWhen: () => !isClosed,
);
}
Expand All @@ -57,18 +65,35 @@ class RowBloc extends Bloc<RowEvent, RowState> {
class RowEvent with _$RowEvent {
const factory RowEvent.initial() = _InitialRow;
const factory RowEvent.createRow() = _CreateRow;
const factory RowEvent.didReceiveCellDatas(GridCellMap cellData) = _DidReceiveCellDatas;
const factory RowEvent.didReceiveCellDatas(GridCellMap gridCellMap, GridRowChangeReason reason) =
_DidReceiveCellDatas;
}

@freezed
class RowState with _$RowState {
const factory RowState({
required GridRow rowData,
required GridCellMap cellDataMap,
required GridCellMap gridCellMap,
required UnmodifiableListView<CellSnapshot> snapshots,
GridRowChangeReason? changeReason,
}) = _RowState;

factory RowState.initial(GridRow rowData, GridCellMap cellDataMap) => RowState(
rowData: rowData,
cellDataMap: cellDataMap,
gridCellMap: cellDataMap,
snapshots: UnmodifiableListView(cellDataMap.values.map((e) => CellSnapshot(e.field)).toList()),
);
}

class CellSnapshot extends Equatable {
final Field _field;

const CellSnapshot(Field field) : _field = field;

@override
List<Object?> get props => [
_field.id,
_field.fieldType,
_field.visibility,
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
Future<void> _startListening() async {
_rowListenFn = _rowCache.addRowListener(
rowId: rowData.rowId,
onUpdated: (cellDatas) => add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())),
onUpdated: (cellDatas, reason) => add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())),
listenWhen: () => !isClosed,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class GridRowCache {

RowUpdateCallback addRowListener({
required String rowId,
void Function(GridCellMap)? onUpdated,
void Function(GridCellMap, GridRowChangeReason)? onUpdated,
bool Function()? listenWhen,
}) {
listenrHandler() async {
Expand All @@ -99,7 +99,7 @@ class GridRowCache {
final row = _rowsNotifier.rowDataWithId(rowId);
if (row != null) {
final GridCellMap cellDataMap = _makeGridCells(rowId, row);
onUpdated(cellDataMap);
onUpdated(cellDataMap, _rowsNotifier._changeReason);
}
}

Expand Down Expand Up @@ -339,7 +339,7 @@ class GridRow with _$GridRow {
const factory GridRow({
required String gridId,
required String rowId,
required List<Field> fields,
required UnmodifiableListView<Field> fields,
required double height,
Row? data,
}) = _GridRow;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class GridSize {
static double get leadingHeaderPadding => 50 * scale;
static double get trailHeaderPadding => 140 * scale;
static double get headerContainerPadding => 0 * scale;
static double get cellHPadding => 10 * scale;
static double get cellHPadding => 8 * scale;
static double get cellVPadding => 8 * scale;
static double get typeOptionItemHeight => 32 * scale;
static double get typeOptionSeparatorHeight => 6 * scale;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
import 'package:flutter/widgets.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:styled_widget/styled_widget.dart';
import 'checkbox_cell.dart';
import 'date_cell.dart';
import 'number_cell.dart';
import 'selection_cell/selection_cell.dart';
import 'text_cell.dart';

GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {GridCellStyle? style}) {
final key = ValueKey(gridCell.rowId + gridCell.field.id);
final key = ValueKey(gridCell.cellId());

final cellContextBuilder = GridCellContextBuilder(gridCell: gridCell, cellCache: cellCache);

Expand Down Expand Up @@ -51,9 +57,157 @@ abstract class GridCellWidget extends HoverWidget {
}

class GridCellRequestFocusNotifier extends ChangeNotifier {
VoidCallback? _listener;

@override
void addListener(VoidCallback listener) {
if (_listener != null) {
removeListener(_listener!);
}

_listener = listener;
super.addListener(listener);
}

void removeAllListener() {
if (_listener != null) {
removeListener(_listener!);
}
}

void notify() {
notifyListeners();
}
}

abstract class GridCellStyle {}

class CellSingleFocusNode extends FocusNode {
VoidCallback? _listener;

void setSingleListener(VoidCallback listener) {
if (_listener != null) {
removeListener(_listener!);
}

_listener = listener;
super.addListener(listener);
}

void removeSingleListener() {
if (_listener != null) {
removeListener(_listener!);
}
}
}

class CellStateNotifier extends ChangeNotifier {
bool _isFocus = false;
bool _onEnter = false;

set isFocus(bool value) {
if (_isFocus != value) {
_isFocus = value;
notifyListeners();
}
}

set onEnter(bool value) {
if (_onEnter != value) {
_onEnter = value;
notifyListeners();
}
}

bool get isFocus => _isFocus;

bool get onEnter => _onEnter;
}

class CellContainer extends StatelessWidget {
final GridCellWidget child;
final Widget? expander;
final double width;
final RegionStateNotifier rowStateNotifier;
const CellContainer({
Key? key,
required this.child,
required this.width,
required this.rowStateNotifier,
this.expander,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return ChangeNotifierProxyProvider<RegionStateNotifier, CellStateNotifier>(
create: (_) => CellStateNotifier(),
update: (_, row, cell) => cell!..onEnter = row.onEnter,
child: Selector<CellStateNotifier, bool>(
selector: (context, notifier) => notifier.isFocus,
builder: (context, isFocus, _) {
Widget container = Center(child: child);
child.onFocus.addListener(() {
Provider.of<CellStateNotifier>(context, listen: false).isFocus = child.onFocus.value;
});

if (expander != null) {
container = _CellEnterRegion(child: container, expander: expander!);
}

return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => child.requestFocus.notify(),
child: Container(
constraints: BoxConstraints(maxWidth: width, minHeight: 46),
decoration: _makeBoxDecoration(context, isFocus),
padding: GridSize.cellContentInsets,
child: container,
),
);
},
),
);
}

BoxDecoration _makeBoxDecoration(BuildContext context, bool isFocus) {
final theme = context.watch<AppTheme>();
if (isFocus) {
final borderSide = BorderSide(color: theme.main1, width: 1.0);
return BoxDecoration(border: Border.fromBorderSide(borderSide));
} else {
final borderSide = BorderSide(color: theme.shader5, width: 1.0);
return BoxDecoration(border: Border(right: borderSide, bottom: borderSide));
}
}
}

class _CellEnterRegion extends StatelessWidget {
final Widget child;
final Widget expander;
const _CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Selector<CellStateNotifier, bool>(
selector: (context, notifier) => notifier.onEnter,
builder: (context, onEnter, _) {
List<Widget> children = [child];
if (onEnter) {
children.add(expander.positioned(right: 0));
}

return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (p) => Provider.of<CellStateNotifier>(context, listen: false).onEnter = true,
onExit: (p) => Provider.of<CellStateNotifier>(context, listen: false).onEnter = false,
child: Stack(
alignment: AlignmentDirectional.center,
fit: StackFit.expand,
// alignment: AlignmentDirectional.centerEnd,
children: children,
),
);
},
);
}
}
Loading

0 comments on commit 2848ecb

Please sign in to comment.