mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: fixed grid toolbar
This commit is contained in:
parent
a707410548
commit
b0e07c952e
@ -2,7 +2,7 @@ import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dar
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell, Field;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
@ -35,6 +35,10 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||
content: value.cell.content,
|
||||
));
|
||||
},
|
||||
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
|
||||
emit(state.copyWith(field: value.field));
|
||||
_loadCellData();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -58,7 +62,7 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||
|
||||
_fieldListener.updateFieldNotifier.addPublishListener((result) {
|
||||
result.fold(
|
||||
(field) => _loadCellData(),
|
||||
(field) => add(DateCellEvent.didReceiveFieldUpdate(field)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
@ -97,6 +101,7 @@ class DateCellEvent with _$DateCellEvent {
|
||||
const factory DateCellEvent.initial() = _InitialCell;
|
||||
const factory DateCellEvent.selectDay(DateTime day) = _SelectDay;
|
||||
const factory DateCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
|
||||
const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
@ -104,11 +109,13 @@ class DateCellState with _$DateCellState {
|
||||
const factory DateCellState({
|
||||
required CellData cellData,
|
||||
required String content,
|
||||
required Field field,
|
||||
DateTime? selectedDay,
|
||||
}) = _DateCellState;
|
||||
|
||||
factory DateCellState.initial(CellData cellData) => DateCellState(
|
||||
cellData: cellData,
|
||||
field: cellData.field,
|
||||
content: cellData.cell?.content ?? "",
|
||||
);
|
||||
}
|
||||
|
@ -20,6 +20,15 @@ class FieldService {
|
||||
return GridEventSwitchToField(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<EditFieldContext, FlowyError>> getEditFieldContext(String fieldId, FieldType fieldType) {
|
||||
final payload = GetEditFieldContextPayload.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..fieldType = fieldType;
|
||||
|
||||
return GridEventGetEditFieldContext(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> updateField({
|
||||
required String fieldId,
|
||||
String? name,
|
||||
|
@ -40,7 +40,7 @@ class _GridPageState extends State<GridPage> {
|
||||
return state.loadingState.map(
|
||||
loading: (_) => const Center(child: CircularProgressIndicator.adaptive()),
|
||||
finish: (result) => result.successOrFail.fold(
|
||||
(_) => const FlowyGrid(),
|
||||
(_) => FlowyGrid(),
|
||||
(err) => FlowyErrorPage(err.toString()),
|
||||
),
|
||||
);
|
||||
@ -65,64 +65,61 @@ class _GridPageState extends State<GridPage> {
|
||||
}
|
||||
}
|
||||
|
||||
class FlowyGrid extends StatefulWidget {
|
||||
const FlowyGrid({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_FlowyGridState createState() => _FlowyGridState();
|
||||
}
|
||||
|
||||
class _FlowyGridState extends State<FlowyGrid> {
|
||||
class FlowyGrid extends StatelessWidget {
|
||||
final _scrollController = GridScrollController();
|
||||
final _key = GlobalKey<SliverAnimatedListState>();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
FlowyGrid({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<GridBloc, GridState>(
|
||||
buildWhen: (previous, current) => previous.fields.length != current.fields.length,
|
||||
builder: (context, state) {
|
||||
if (state.fields.isEmpty) {
|
||||
return const Center(child: CircularProgressIndicator.adaptive());
|
||||
}
|
||||
|
||||
final child = SizedBox(
|
||||
width: GridLayout.headerWidth(state.fields),
|
||||
child: ScrollConfiguration(
|
||||
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
||||
child: CustomScrollView(
|
||||
physics: StyledScrollPhysics(),
|
||||
controller: _scrollController.verticalController,
|
||||
slivers: [
|
||||
const _GridToolbarAdaptor(),
|
||||
GridHeader(gridId: state.gridId, fields: List.from(state.fields)),
|
||||
_GridRows(),
|
||||
const SliverToBoxAdapter(child: GridFooter()),
|
||||
],
|
||||
),
|
||||
),
|
||||
final child = _wrapScrollView(
|
||||
state.fields,
|
||||
[
|
||||
_GridHeader(gridId: state.gridId, fields: List.from(state.fields)),
|
||||
_GridRows(),
|
||||
const _GridFooter(),
|
||||
],
|
||||
);
|
||||
|
||||
return _wrapScrollbar(child);
|
||||
return Column(children: [
|
||||
const _GridToolbarAdaptor(),
|
||||
Flexible(child: child),
|
||||
]);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _wrapScrollbar(Widget child) {
|
||||
Widget _wrapScrollView(
|
||||
List<Field> fields,
|
||||
List<Widget> slivers,
|
||||
) {
|
||||
final verticalScrollView = ScrollConfiguration(
|
||||
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
||||
child: CustomScrollView(
|
||||
physics: StyledScrollPhysics(),
|
||||
controller: _scrollController.verticalController,
|
||||
slivers: slivers,
|
||||
),
|
||||
);
|
||||
|
||||
final sizedVerticalScrollView = SizedBox(
|
||||
width: GridLayout.headerWidth(fields),
|
||||
child: verticalScrollView,
|
||||
);
|
||||
|
||||
final horizontalScrollView = StyledSingleChildScrollView(
|
||||
controller: _scrollController.horizontalController,
|
||||
axis: Axis.horizontal,
|
||||
child: sizedVerticalScrollView,
|
||||
);
|
||||
|
||||
return ScrollbarListStack(
|
||||
axis: Axis.vertical,
|
||||
controller: _scrollController.verticalController,
|
||||
barSize: GridSize.scrollBarSize,
|
||||
child: StyledSingleChildScrollView(
|
||||
controller: _scrollController.horizontalController,
|
||||
axis: Axis.horizontal,
|
||||
child: child,
|
||||
),
|
||||
child: horizontalScrollView,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -140,14 +137,27 @@ class _GridToolbarAdaptor extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
builder: (context, toolbarContext) {
|
||||
return SliverToBoxAdapter(
|
||||
child: GridToolbar(toolbarContext: toolbarContext),
|
||||
);
|
||||
return GridToolbar(toolbarContext: toolbarContext);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GridHeader extends StatelessWidget {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
const _GridHeader({Key? key, required this.gridId, required this.fields}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverPersistentHeader(
|
||||
delegate: GridHeaderSliverAdaptor(gridId: gridId, fields: List.from(fields)),
|
||||
floating: true,
|
||||
pinned: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GridRows extends StatelessWidget {
|
||||
final _key = GlobalKey<SliverAnimatedListState>();
|
||||
_GridRows({Key? key}) : super(key: key);
|
||||
@ -191,3 +201,28 @@ class _GridRows extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GridFooter extends StatelessWidget {
|
||||
const _GridFooter({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(bottom: 200),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: GridSize.footerHeight,
|
||||
child: Padding(
|
||||
padding: GridSize.headerContentInsets,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: GridSize.leadingHeaderPadding),
|
||||
const SizedBox(width: 120, child: GridAddRowButton()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,13 @@ final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day);
|
||||
class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
|
||||
final void Function(DateTime) onSelected;
|
||||
final VoidCallback onDismissed;
|
||||
const _CellCalendar({required this.onSelected, required this.onDismissed, Key? key}) : super(key: key);
|
||||
final bool includeTime;
|
||||
const _CellCalendar({
|
||||
required this.onSelected,
|
||||
required this.onDismissed,
|
||||
required this.includeTime,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<_CellCalendar> createState() => _CellCalendarState();
|
||||
@ -86,7 +92,11 @@ class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
|
||||
}) async {
|
||||
_CellCalendar.remove(context);
|
||||
|
||||
final calendar = _CellCalendar(onSelected: onSelected, onDismissed: onDismissed);
|
||||
final calendar = _CellCalendar(
|
||||
onSelected: onSelected,
|
||||
onDismissed: onDismissed,
|
||||
includeTime: false,
|
||||
);
|
||||
// const size = Size(460, 400);
|
||||
// final window = await getWindowInfo();
|
||||
// FlowyOverlay.of(context).insertWithRect(
|
||||
@ -105,11 +115,11 @@ class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: calendar,
|
||||
constraints: BoxConstraints.loose(const Size(300, 320)),
|
||||
constraints: BoxConstraints.tight(const Size(320, 320)),
|
||||
),
|
||||
identifier: _CellCalendar.identifier(),
|
||||
anchorContext: context,
|
||||
anchorDirection: AnchorDirection.bottomWithCenterAligned,
|
||||
anchorDirection: AnchorDirection.leftWithCenterAligned,
|
||||
style: FlowyOverlayStyle(blur: false),
|
||||
delegate: calendar,
|
||||
);
|
||||
|
@ -7,28 +7,8 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class GridFooter extends StatelessWidget {
|
||||
const GridFooter({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: GridSize.footerHeight,
|
||||
child: Padding(
|
||||
padding: GridSize.headerContentInsets,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: GridSize.leadingHeaderPadding),
|
||||
const SizedBox(width: 120, child: _AddRowButton()),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AddRowButton extends StatelessWidget {
|
||||
const _AddRowButton({Key? key}) : super(key: key);
|
||||
class GridAddRowButton extends StatelessWidget {
|
||||
const GridAddRowButton({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -12,30 +12,32 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'field_editor.dart';
|
||||
import 'field_cell.dart';
|
||||
|
||||
class GridHeader extends StatelessWidget {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
const GridHeader({Key? key, required this.gridId, required this.fields}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverPersistentHeader(
|
||||
delegate: _GridHeaderDelegate(gridId: gridId, fields: List.from(fields)),
|
||||
floating: true,
|
||||
pinned: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GridHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
class GridHeaderSliverAdaptor extends SliverPersistentHeaderDelegate {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
|
||||
_GridHeaderDelegate({required this.gridId, required this.fields});
|
||||
GridHeaderSliverAdaptor({required this.gridId, required this.fields});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
return _GridHeaderWidget(gridId: gridId, fields: fields, key: ObjectKey(fields));
|
||||
final cells = fields.map(
|
||||
(field) => GridFieldCell(
|
||||
GridFieldCellContext(gridId: gridId, field: field),
|
||||
),
|
||||
);
|
||||
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const _CellLeading(),
|
||||
...cells,
|
||||
_CellTrailing(gridId: gridId),
|
||||
],
|
||||
key: ObjectKey(fields),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -46,40 +48,13 @@ class _GridHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
|
||||
@override
|
||||
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
|
||||
if (oldDelegate is _GridHeaderDelegate) {
|
||||
if (oldDelegate is GridHeaderSliverAdaptor) {
|
||||
return fields.length != oldDelegate.fields.length;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class _GridHeaderWidget extends StatelessWidget {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
|
||||
const _GridHeaderWidget({required this.gridId, required this.fields, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cells = fields.map(
|
||||
(field) => GridFieldCell(
|
||||
GridFieldCellContext(gridId: gridId, field: field),
|
||||
),
|
||||
);
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const _CellLeading(),
|
||||
...cells,
|
||||
_CellTrailing(gridId: gridId),
|
||||
],
|
||||
);
|
||||
|
||||
// return Container(height: GridSize.headerHeight, color: theme.surface, child: row);
|
||||
}
|
||||
}
|
||||
|
||||
class _CellLeading extends StatelessWidget {
|
||||
const _CellLeading({Key? key}) : super(key: key);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user