From a7cbb3d31aaa624cba770c73b3ac248cd14ac742 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 31 Aug 2022 16:25:00 +0800 Subject: [PATCH] chore: show board setting --- .../toolbar/board_setting_bloc.dart | 46 +++++ .../board/presentation/board_page.dart | 63 +++++-- .../presentation/toolbar/board_setting.dart | 168 ++++++++++++++++++ .../presentation/toolbar/board_toolbar.dart | 60 +++++++ 4 files changed, 320 insertions(+), 17 deletions(-) create mode 100644 frontend/app_flowy/lib/plugins/board/application/toolbar/board_setting_bloc.dart create mode 100644 frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart create mode 100644 frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart diff --git a/frontend/app_flowy/lib/plugins/board/application/toolbar/board_setting_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/toolbar/board_setting_bloc.dart new file mode 100644 index 0000000000..480b3a4768 --- /dev/null +++ b/frontend/app_flowy/lib/plugins/board/application/toolbar/board_setting_bloc.dart @@ -0,0 +1,46 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'dart:async'; +import 'package:dartz/dartz.dart'; + +part 'board_setting_bloc.freezed.dart'; + +class BoardSettingBloc extends Bloc { + final String gridId; + BoardSettingBloc({required this.gridId}) + : super(BoardSettingState.initial()) { + on( + (event, emit) async { + event.when(performAction: (action) { + emit(state.copyWith(selectedAction: Some(action))); + }); + }, + ); + } + + @override + Future close() async { + return super.close(); + } +} + +@freezed +class BoardSettingEvent with _$BoardSettingEvent { + const factory BoardSettingEvent.performAction(BoardSettingAction action) = + _PerformAction; +} + +@freezed +class BoardSettingState with _$BoardSettingState { + const factory BoardSettingState({ + required Option selectedAction, + }) = _BoardSettingState; + + factory BoardSettingState.initial() => BoardSettingState( + selectedAction: none(), + ); +} + +enum BoardSettingAction { + properties, +} diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index bc8a4fcfa7..7235217d4c 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -24,6 +24,7 @@ import '../../grid/application/row/row_cache.dart'; import '../application/board_bloc.dart'; import 'card/card.dart'; import 'card/card_cell_builder.dart'; +import 'toolbar/board_toolbar.dart'; class BoardPage extends StatelessWidget { final ViewPB view; @@ -100,25 +101,34 @@ class _BoardContentState extends State { buildWhen: (previous, current) => previous.groupIds.length != current.groupIds.length, builder: (context, state) { + final theme = context.read(); return Container( - color: Colors.white, + color: theme.surface, child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20), - child: AFBoard( - scrollManager: scrollManager, - scrollController: scrollController, - dataController: context.read().boardController, - headerBuilder: _buildHeader, - footBuilder: _buildFooter, - cardBuilder: (_, column, columnItem) => _buildCard( - context, - column, - columnItem, - ), - columnConstraints: const BoxConstraints.tightFor(width: 300), - config: AFBoardConfig( - columnBackgroundColor: HexColor.fromHex('#F7F8FC'), - ), + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + children: [ + const _ToolbarBlocAdaptor(), + Expanded( + child: AFBoard( + scrollManager: scrollManager, + scrollController: scrollController, + dataController: context.read().boardController, + headerBuilder: _buildHeader, + footBuilder: _buildFooter, + cardBuilder: (_, column, columnItem) => _buildCard( + context, + column, + columnItem, + ), + columnConstraints: + const BoxConstraints.tightFor(width: 300), + config: AFBoardConfig( + columnBackgroundColor: HexColor.fromHex('#F7F8FC'), + ), + ), + ), + ], ), ), ); @@ -277,6 +287,25 @@ class _BoardContentState extends State { } } +class _ToolbarBlocAdaptor extends StatelessWidget { + const _ToolbarBlocAdaptor({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + final bloc = context.read(); + final toolbarContext = BoardToolbarContext( + viewId: bloc.gridId, + fieldCache: bloc.fieldCache, + ); + + return BoardToolbar(toolbarContext: toolbarContext); + }, + ); + } +} + extension HexColor on Color { static Color fromHex(String hexString) { final buffer = StringBuffer(); diff --git a/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart b/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart new file mode 100644 index 0000000000..76ab265a90 --- /dev/null +++ b/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart @@ -0,0 +1,168 @@ +import 'package:app_flowy/generated/locale_keys.g.dart'; +import 'package:app_flowy/plugins/board/application/toolbar/board_setting_bloc.dart'; +import 'package:app_flowy/plugins/grid/application/field/field_cache.dart'; +import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart'; +import 'package:app_flowy/plugins/grid/presentation/widgets/toolbar/grid_property.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flowy_infra_ui/style_widget/button.dart'; +import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flowy_infra_ui/widget/spacing.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'board_toolbar.dart'; + +class BoardSettingContext { + final String viewId; + final GridFieldCache fieldCache; + BoardSettingContext({ + required this.viewId, + required this.fieldCache, + }); + + factory BoardSettingContext.from(BoardToolbarContext toolbarContext) => + BoardSettingContext( + viewId: toolbarContext.viewId, + fieldCache: toolbarContext.fieldCache, + ); +} + +class BoardSettingList extends StatelessWidget { + final BoardSettingContext settingContext; + final Function(BoardSettingAction, BoardSettingContext) onAction; + const BoardSettingList({ + required this.settingContext, + required this.onAction, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => BoardSettingBloc(gridId: settingContext.viewId), + child: BlocListener( + listenWhen: (previous, current) => + previous.selectedAction != current.selectedAction, + listener: (context, state) { + state.selectedAction.foldLeft(null, (_, action) { + FlowyOverlay.of(context).remove(identifier()); + onAction(action, settingContext); + }); + }, + child: BlocBuilder( + builder: (context, state) { + return _renderList(); + }, + ), + ), + ); + } + + Widget _renderList() { + final cells = BoardSettingAction.values.map((action) { + return _SettingItem(action: action); + }).toList(); + + return SizedBox( + width: 140, + child: ListView.separated( + shrinkWrap: true, + controller: ScrollController(), + itemCount: cells.length, + separatorBuilder: (context, index) { + return VSpace(GridSize.typeOptionSeparatorHeight); + }, + physics: StyledScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return cells[index]; + }, + ), + ); + } + + static void show(BuildContext context, BoardSettingContext settingContext) { + final list = BoardSettingList( + settingContext: settingContext, + onAction: (action, settingContext) { + switch (action) { + case BoardSettingAction.properties: + GridPropertyList( + gridId: settingContext.viewId, + fieldCache: settingContext.fieldCache) + .show(context); + break; + } + }, + ); + + FlowyOverlay.of(context).insertWithAnchor( + widget: OverlayContainer( + constraints: BoxConstraints.loose(const Size(140, 400)), + child: list, + ), + identifier: identifier(), + anchorContext: context, + anchorDirection: AnchorDirection.bottomRight, + style: FlowyOverlayStyle(blur: false), + ); + } + + static String identifier() { + return (BoardSettingList).toString(); + } +} + +class _SettingItem extends StatelessWidget { + final BoardSettingAction action; + + const _SettingItem({ + required this.action, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.read(); + final isSelected = context + .read() + .state + .selectedAction + .foldLeft(false, (_, selectedAction) => selectedAction == action); + + return SizedBox( + height: 30, + child: FlowyButton( + isSelected: isSelected, + text: FlowyText.medium(action.title(), + fontSize: 12, color: theme.textColor), + hoverColor: theme.hover, + onTap: () { + context + .read() + .add(BoardSettingEvent.performAction(action)); + }, + leftIcon: svgWidget(action.iconName(), color: theme.iconColor), + ), + ); + } +} + +extension _GridSettingExtension on BoardSettingAction { + String iconName() { + switch (this) { + case BoardSettingAction.properties: + return 'grid/setting/properties'; + } + } + + String title() { + switch (this) { + case BoardSettingAction.properties: + return LocaleKeys.grid_settings_Properties.tr(); + } + } +} diff --git a/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart b/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart new file mode 100644 index 0000000000..fae27851a9 --- /dev/null +++ b/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart @@ -0,0 +1,60 @@ +import 'package:app_flowy/plugins/grid/application/field/field_cache.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/style_widget/icon_button.dart'; +import 'package:flutter/widgets.dart'; +import 'package:provider/provider.dart'; + +import 'board_setting.dart'; + +class BoardToolbarContext { + final String viewId; + final GridFieldCache fieldCache; + + BoardToolbarContext({ + required this.viewId, + required this.fieldCache, + }); +} + +class BoardToolbar extends StatelessWidget { + final BoardToolbarContext toolbarContext; + const BoardToolbar({ + required this.toolbarContext, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 40, + child: Row( + children: [ + _SettingButton( + settingContext: BoardSettingContext.from(toolbarContext), + ), + ], + ), + ); + } +} + +class _SettingButton extends StatelessWidget { + final BoardSettingContext settingContext; + const _SettingButton({required this.settingContext, Key? key}) + : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.read(); + return FlowyIconButton( + hoverColor: theme.hover, + width: 22, + onPressed: () => BoardSettingList.show(context, settingContext), + icon: Padding( + padding: const EdgeInsets.symmetric(vertical: 3.0, horizontal: 3.0), + child: svgWidget("grid/setting/setting"), + ), + ); + } +}