mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-09-26 00:40:13 +00:00
Merge pull request #957 from AppFlowy-IO/feat/board_setting
chore: show board setting
This commit is contained in:
commit
a0d253b889
@ -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<BoardSettingEvent, BoardSettingState> {
|
||||||
|
final String gridId;
|
||||||
|
BoardSettingBloc({required this.gridId})
|
||||||
|
: super(BoardSettingState.initial()) {
|
||||||
|
on<BoardSettingEvent>(
|
||||||
|
(event, emit) async {
|
||||||
|
event.when(performAction: (action) {
|
||||||
|
emit(state.copyWith(selectedAction: Some(action)));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> 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<BoardSettingAction> selectedAction,
|
||||||
|
}) = _BoardSettingState;
|
||||||
|
|
||||||
|
factory BoardSettingState.initial() => BoardSettingState(
|
||||||
|
selectedAction: none(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BoardSettingAction {
|
||||||
|
properties,
|
||||||
|
}
|
@ -24,6 +24,7 @@ import '../../grid/application/row/row_cache.dart';
|
|||||||
import '../application/board_bloc.dart';
|
import '../application/board_bloc.dart';
|
||||||
import 'card/card.dart';
|
import 'card/card.dart';
|
||||||
import 'card/card_cell_builder.dart';
|
import 'card/card_cell_builder.dart';
|
||||||
|
import 'toolbar/board_toolbar.dart';
|
||||||
|
|
||||||
class BoardPage extends StatelessWidget {
|
class BoardPage extends StatelessWidget {
|
||||||
final ViewPB view;
|
final ViewPB view;
|
||||||
@ -100,25 +101,34 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
buildWhen: (previous, current) =>
|
buildWhen: (previous, current) =>
|
||||||
previous.groupIds.length != current.groupIds.length,
|
previous.groupIds.length != current.groupIds.length,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
final theme = context.read<AppTheme>();
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.white,
|
color: theme.surface,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: AFBoard(
|
child: Column(
|
||||||
scrollManager: scrollManager,
|
children: [
|
||||||
scrollController: scrollController,
|
const _ToolbarBlocAdaptor(),
|
||||||
dataController: context.read<BoardBloc>().boardController,
|
Expanded(
|
||||||
headerBuilder: _buildHeader,
|
child: AFBoard(
|
||||||
footBuilder: _buildFooter,
|
scrollManager: scrollManager,
|
||||||
cardBuilder: (_, column, columnItem) => _buildCard(
|
scrollController: scrollController,
|
||||||
context,
|
dataController: context.read<BoardBloc>().boardController,
|
||||||
column,
|
headerBuilder: _buildHeader,
|
||||||
columnItem,
|
footBuilder: _buildFooter,
|
||||||
),
|
cardBuilder: (_, column, columnItem) => _buildCard(
|
||||||
columnConstraints: const BoxConstraints.tightFor(width: 300),
|
context,
|
||||||
config: AFBoardConfig(
|
column,
|
||||||
columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
|
columnItem,
|
||||||
),
|
),
|
||||||
|
columnConstraints:
|
||||||
|
const BoxConstraints.tightFor(width: 300),
|
||||||
|
config: AFBoardConfig(
|
||||||
|
columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -277,6 +287,25 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ToolbarBlocAdaptor extends StatelessWidget {
|
||||||
|
const _ToolbarBlocAdaptor({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<BoardBloc, BoardState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final bloc = context.read<BoardBloc>();
|
||||||
|
final toolbarContext = BoardToolbarContext(
|
||||||
|
viewId: bloc.gridId,
|
||||||
|
fieldCache: bloc.fieldCache,
|
||||||
|
);
|
||||||
|
|
||||||
|
return BoardToolbar(toolbarContext: toolbarContext);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension HexColor on Color {
|
extension HexColor on Color {
|
||||||
static Color fromHex(String hexString) {
|
static Color fromHex(String hexString) {
|
||||||
final buffer = StringBuffer();
|
final buffer = StringBuffer();
|
||||||
|
@ -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<BoardSettingBloc, BoardSettingState>(
|
||||||
|
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<BoardSettingBloc, BoardSettingState>(
|
||||||
|
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<AppTheme>();
|
||||||
|
final isSelected = context
|
||||||
|
.read<BoardSettingBloc>()
|
||||||
|
.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<BoardSettingBloc>()
|
||||||
|
.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<AppTheme>();
|
||||||
|
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"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user