Skip to content

Commit a0d253b

Browse files
authored
Merge pull request #957 from AppFlowy-IO/feat/board_setting
chore: show board setting
2 parents cb06722 + a7cbb3d commit a0d253b

File tree

4 files changed

+320
-17
lines changed

4 files changed

+320
-17
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import 'package:flutter_bloc/flutter_bloc.dart';
2+
import 'package:freezed_annotation/freezed_annotation.dart';
3+
import 'dart:async';
4+
import 'package:dartz/dartz.dart';
5+
6+
part 'board_setting_bloc.freezed.dart';
7+
8+
class BoardSettingBloc extends Bloc<BoardSettingEvent, BoardSettingState> {
9+
final String gridId;
10+
BoardSettingBloc({required this.gridId})
11+
: super(BoardSettingState.initial()) {
12+
on<BoardSettingEvent>(
13+
(event, emit) async {
14+
event.when(performAction: (action) {
15+
emit(state.copyWith(selectedAction: Some(action)));
16+
});
17+
},
18+
);
19+
}
20+
21+
@override
22+
Future<void> close() async {
23+
return super.close();
24+
}
25+
}
26+
27+
@freezed
28+
class BoardSettingEvent with _$BoardSettingEvent {
29+
const factory BoardSettingEvent.performAction(BoardSettingAction action) =
30+
_PerformAction;
31+
}
32+
33+
@freezed
34+
class BoardSettingState with _$BoardSettingState {
35+
const factory BoardSettingState({
36+
required Option<BoardSettingAction> selectedAction,
37+
}) = _BoardSettingState;
38+
39+
factory BoardSettingState.initial() => BoardSettingState(
40+
selectedAction: none(),
41+
);
42+
}
43+
44+
enum BoardSettingAction {
45+
properties,
46+
}

frontend/app_flowy/lib/plugins/board/presentation/board_page.dart

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import '../../grid/application/row/row_cache.dart';
2424
import '../application/board_bloc.dart';
2525
import 'card/card.dart';
2626
import 'card/card_cell_builder.dart';
27+
import 'toolbar/board_toolbar.dart';
2728

2829
class BoardPage extends StatelessWidget {
2930
final ViewPB view;
@@ -100,25 +101,34 @@ class _BoardContentState extends State<BoardContent> {
100101
buildWhen: (previous, current) =>
101102
previous.groupIds.length != current.groupIds.length,
102103
builder: (context, state) {
104+
final theme = context.read<AppTheme>();
103105
return Container(
104-
color: Colors.white,
106+
color: theme.surface,
105107
child: Padding(
106-
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
107-
child: AFBoard(
108-
scrollManager: scrollManager,
109-
scrollController: scrollController,
110-
dataController: context.read<BoardBloc>().boardController,
111-
headerBuilder: _buildHeader,
112-
footBuilder: _buildFooter,
113-
cardBuilder: (_, column, columnItem) => _buildCard(
114-
context,
115-
column,
116-
columnItem,
117-
),
118-
columnConstraints: const BoxConstraints.tightFor(width: 300),
119-
config: AFBoardConfig(
120-
columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
121-
),
108+
padding: const EdgeInsets.symmetric(horizontal: 20),
109+
child: Column(
110+
children: [
111+
const _ToolbarBlocAdaptor(),
112+
Expanded(
113+
child: AFBoard(
114+
scrollManager: scrollManager,
115+
scrollController: scrollController,
116+
dataController: context.read<BoardBloc>().boardController,
117+
headerBuilder: _buildHeader,
118+
footBuilder: _buildFooter,
119+
cardBuilder: (_, column, columnItem) => _buildCard(
120+
context,
121+
column,
122+
columnItem,
123+
),
124+
columnConstraints:
125+
const BoxConstraints.tightFor(width: 300),
126+
config: AFBoardConfig(
127+
columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
128+
),
129+
),
130+
),
131+
],
122132
),
123133
),
124134
);
@@ -277,6 +287,25 @@ class _BoardContentState extends State<BoardContent> {
277287
}
278288
}
279289

290+
class _ToolbarBlocAdaptor extends StatelessWidget {
291+
const _ToolbarBlocAdaptor({Key? key}) : super(key: key);
292+
293+
@override
294+
Widget build(BuildContext context) {
295+
return BlocBuilder<BoardBloc, BoardState>(
296+
builder: (context, state) {
297+
final bloc = context.read<BoardBloc>();
298+
final toolbarContext = BoardToolbarContext(
299+
viewId: bloc.gridId,
300+
fieldCache: bloc.fieldCache,
301+
);
302+
303+
return BoardToolbar(toolbarContext: toolbarContext);
304+
},
305+
);
306+
}
307+
}
308+
280309
extension HexColor on Color {
281310
static Color fromHex(String hexString) {
282311
final buffer = StringBuffer();
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import 'package:app_flowy/generated/locale_keys.g.dart';
2+
import 'package:app_flowy/plugins/board/application/toolbar/board_setting_bloc.dart';
3+
import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
4+
import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
5+
import 'package:app_flowy/plugins/grid/presentation/widgets/toolbar/grid_property.dart';
6+
import 'package:easy_localization/easy_localization.dart';
7+
import 'package:flowy_infra/image.dart';
8+
import 'package:flowy_infra/theme.dart';
9+
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
10+
import 'package:flowy_infra_ui/style_widget/button.dart';
11+
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
12+
import 'package:flowy_infra_ui/style_widget/text.dart';
13+
import 'package:flowy_infra_ui/widget/spacing.dart';
14+
import 'package:flutter/material.dart';
15+
import 'package:flutter_bloc/flutter_bloc.dart';
16+
17+
import 'board_toolbar.dart';
18+
19+
class BoardSettingContext {
20+
final String viewId;
21+
final GridFieldCache fieldCache;
22+
BoardSettingContext({
23+
required this.viewId,
24+
required this.fieldCache,
25+
});
26+
27+
factory BoardSettingContext.from(BoardToolbarContext toolbarContext) =>
28+
BoardSettingContext(
29+
viewId: toolbarContext.viewId,
30+
fieldCache: toolbarContext.fieldCache,
31+
);
32+
}
33+
34+
class BoardSettingList extends StatelessWidget {
35+
final BoardSettingContext settingContext;
36+
final Function(BoardSettingAction, BoardSettingContext) onAction;
37+
const BoardSettingList({
38+
required this.settingContext,
39+
required this.onAction,
40+
Key? key,
41+
}) : super(key: key);
42+
43+
@override
44+
Widget build(BuildContext context) {
45+
return BlocProvider(
46+
create: (context) => BoardSettingBloc(gridId: settingContext.viewId),
47+
child: BlocListener<BoardSettingBloc, BoardSettingState>(
48+
listenWhen: (previous, current) =>
49+
previous.selectedAction != current.selectedAction,
50+
listener: (context, state) {
51+
state.selectedAction.foldLeft(null, (_, action) {
52+
FlowyOverlay.of(context).remove(identifier());
53+
onAction(action, settingContext);
54+
});
55+
},
56+
child: BlocBuilder<BoardSettingBloc, BoardSettingState>(
57+
builder: (context, state) {
58+
return _renderList();
59+
},
60+
),
61+
),
62+
);
63+
}
64+
65+
Widget _renderList() {
66+
final cells = BoardSettingAction.values.map((action) {
67+
return _SettingItem(action: action);
68+
}).toList();
69+
70+
return SizedBox(
71+
width: 140,
72+
child: ListView.separated(
73+
shrinkWrap: true,
74+
controller: ScrollController(),
75+
itemCount: cells.length,
76+
separatorBuilder: (context, index) {
77+
return VSpace(GridSize.typeOptionSeparatorHeight);
78+
},
79+
physics: StyledScrollPhysics(),
80+
itemBuilder: (BuildContext context, int index) {
81+
return cells[index];
82+
},
83+
),
84+
);
85+
}
86+
87+
static void show(BuildContext context, BoardSettingContext settingContext) {
88+
final list = BoardSettingList(
89+
settingContext: settingContext,
90+
onAction: (action, settingContext) {
91+
switch (action) {
92+
case BoardSettingAction.properties:
93+
GridPropertyList(
94+
gridId: settingContext.viewId,
95+
fieldCache: settingContext.fieldCache)
96+
.show(context);
97+
break;
98+
}
99+
},
100+
);
101+
102+
FlowyOverlay.of(context).insertWithAnchor(
103+
widget: OverlayContainer(
104+
constraints: BoxConstraints.loose(const Size(140, 400)),
105+
child: list,
106+
),
107+
identifier: identifier(),
108+
anchorContext: context,
109+
anchorDirection: AnchorDirection.bottomRight,
110+
style: FlowyOverlayStyle(blur: false),
111+
);
112+
}
113+
114+
static String identifier() {
115+
return (BoardSettingList).toString();
116+
}
117+
}
118+
119+
class _SettingItem extends StatelessWidget {
120+
final BoardSettingAction action;
121+
122+
const _SettingItem({
123+
required this.action,
124+
Key? key,
125+
}) : super(key: key);
126+
127+
@override
128+
Widget build(BuildContext context) {
129+
final theme = context.read<AppTheme>();
130+
final isSelected = context
131+
.read<BoardSettingBloc>()
132+
.state
133+
.selectedAction
134+
.foldLeft(false, (_, selectedAction) => selectedAction == action);
135+
136+
return SizedBox(
137+
height: 30,
138+
child: FlowyButton(
139+
isSelected: isSelected,
140+
text: FlowyText.medium(action.title(),
141+
fontSize: 12, color: theme.textColor),
142+
hoverColor: theme.hover,
143+
onTap: () {
144+
context
145+
.read<BoardSettingBloc>()
146+
.add(BoardSettingEvent.performAction(action));
147+
},
148+
leftIcon: svgWidget(action.iconName(), color: theme.iconColor),
149+
),
150+
);
151+
}
152+
}
153+
154+
extension _GridSettingExtension on BoardSettingAction {
155+
String iconName() {
156+
switch (this) {
157+
case BoardSettingAction.properties:
158+
return 'grid/setting/properties';
159+
}
160+
}
161+
162+
String title() {
163+
switch (this) {
164+
case BoardSettingAction.properties:
165+
return LocaleKeys.grid_settings_Properties.tr();
166+
}
167+
}
168+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
2+
import 'package:flowy_infra/image.dart';
3+
import 'package:flowy_infra/theme.dart';
4+
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
5+
import 'package:flutter/widgets.dart';
6+
import 'package:provider/provider.dart';
7+
8+
import 'board_setting.dart';
9+
10+
class BoardToolbarContext {
11+
final String viewId;
12+
final GridFieldCache fieldCache;
13+
14+
BoardToolbarContext({
15+
required this.viewId,
16+
required this.fieldCache,
17+
});
18+
}
19+
20+
class BoardToolbar extends StatelessWidget {
21+
final BoardToolbarContext toolbarContext;
22+
const BoardToolbar({
23+
required this.toolbarContext,
24+
Key? key,
25+
}) : super(key: key);
26+
27+
@override
28+
Widget build(BuildContext context) {
29+
return SizedBox(
30+
height: 40,
31+
child: Row(
32+
children: [
33+
_SettingButton(
34+
settingContext: BoardSettingContext.from(toolbarContext),
35+
),
36+
],
37+
),
38+
);
39+
}
40+
}
41+
42+
class _SettingButton extends StatelessWidget {
43+
final BoardSettingContext settingContext;
44+
const _SettingButton({required this.settingContext, Key? key})
45+
: super(key: key);
46+
47+
@override
48+
Widget build(BuildContext context) {
49+
final theme = context.read<AppTheme>();
50+
return FlowyIconButton(
51+
hoverColor: theme.hover,
52+
width: 22,
53+
onPressed: () => BoardSettingList.show(context, settingContext),
54+
icon: Padding(
55+
padding: const EdgeInsets.symmetric(vertical: 3.0, horizontal: 3.0),
56+
child: svgWidget("grid/setting/setting"),
57+
),
58+
);
59+
}
60+
}

0 commit comments

Comments
 (0)