chore: show create option message on bottom

This commit is contained in:
appflowy 2022-05-06 21:07:34 +08:00
parent 228695d517
commit 18752e7af8
6 changed files with 136 additions and 28 deletions

View File

@ -181,6 +181,7 @@
"textPlaceholder": "Empty"
},
"selectOption": {
"create": "Create",
"purpleColor": "Purple",
"pinkColor": "Pink",
"lightPinkColor": "Light Pink",

View File

@ -1,9 +1,13 @@
import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
import 'select_option_service.dart';
part 'selection_editor_bloc.freezed.dart';
@ -24,14 +28,19 @@ class SelectOptionEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionE
_startListening();
},
didReceiveOptions: (_DidReceiveOptions value) {
final result = _makeOptions(state.filter, value.options);
emit(state.copyWith(
allOptions: value.options,
options: _makeOptions(state.filter, value.options),
options: result.options,
createOption: result.createOption,
selectedOptions: value.selectedOptions,
));
},
newOption: (_NewOption value) {
_createOption(value.optionName);
emit(state.copyWith(
filter: none(),
));
},
deleteOption: (_DeleteOption value) {
_deleteOption(value.option);
@ -91,16 +100,37 @@ class SelectOptionEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionE
}
void _filterOption(String optionName, Emitter<SelectOptionEditorState> emit) {
emit(state.copyWith(filter: optionName, options: _makeOptions(optionName, state.allOptions)));
final _MakeOptionResult result = _makeOptions(Some(optionName), state.allOptions);
emit(state.copyWith(
filter: Some(optionName),
options: result.options,
createOption: result.createOption,
));
}
List<SelectOption> _makeOptions(String filter, List<SelectOption> allOptions) {
_MakeOptionResult _makeOptions(Option<String> filter, List<SelectOption> allOptions) {
final List<SelectOption> options = List.from(allOptions);
if (filter.isNotEmpty) {
options.retainWhere((option) => option.name.toLowerCase().contains(filter.toLowerCase()));
}
Option<String> createOption = filter;
return options;
filter.foldRight(null, (filter, previous) {
if (filter.isNotEmpty) {
options.retainWhere((option) {
final name = option.name.toLowerCase();
final lFilter = filter.toLowerCase();
if (name == lFilter) {
createOption = none();
}
return name.contains(lFilter);
});
}
});
return _MakeOptionResult(
options: options,
createOption: createOption,
);
}
void _startListening() {
@ -135,7 +165,8 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
required List<SelectOption> options,
required List<SelectOption> allOptions,
required List<SelectOption> selectedOptions,
required String filter,
required Option<String> createOption,
required Option<String> filter,
}) = _SelectOptionEditorState;
factory SelectOptionEditorState.initial(GridSelectOptionCellContext context) {
@ -144,7 +175,18 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
options: data?.options ?? [],
allOptions: data?.options ?? [],
selectedOptions: data?.selectOptions ?? [],
filter: "",
createOption: none(),
filter: none(),
);
}
}
class _MakeOptionResult {
List<SelectOption> options;
Option<String> createOption;
_MakeOptionResult({
required this.options,
required this.createOption,
});
}

View File

@ -60,17 +60,35 @@ extension SelectOptionColorExtension on SelectOptionColor {
}
class SelectOptionTag extends StatelessWidget {
final SelectOption option;
final String name;
final Color color;
final bool isSelected;
const SelectOptionTag({required this.option, this.isSelected = false, Key? key}) : super(key: key);
const SelectOptionTag({
required this.name,
required this.color,
this.isSelected = false,
Key? key,
}) : super(key: key);
factory SelectOptionTag.fromSelectOption({
required BuildContext context,
required SelectOption option,
bool isSelected = false,
}) {
return SelectOptionTag(
name: option.name,
color: option.color.make(context),
isSelected: isSelected,
);
}
@override
Widget build(BuildContext context) {
return ChoiceChip(
pressElevation: 1,
label: FlowyText.medium(option.name, fontSize: 12),
selectedColor: option.color.make(context),
backgroundColor: option.color.make(context),
label: FlowyText.medium(name, fontSize: 12),
selectedColor: color,
backgroundColor: color,
labelPadding: const EdgeInsets.symmetric(horizontal: 6),
selected: true,
onSelected: (_) {},

View File

@ -150,7 +150,14 @@ class _SelectOptionCell extends StatelessWidget {
child: FlowyText.medium(cellStyle!.placeholder, fontSize: 14, color: theme.shader3),
);
} else {
final tags = selectOptions.map((option) => SelectOptionTag(option: option)).toList();
final tags = selectOptions
.map(
(option) => SelectOptionTag.fromSelectOption(
context: context,
option: option,
),
)
.toList();
child = Align(
alignment: Alignment.centerLeft,
child: Wrap(children: tags, spacing: 4, runSpacing: 4),

View File

@ -104,9 +104,18 @@ class _OptionList extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<SelectOptionEditorBloc, SelectOptionEditorState>(
builder: (context, state) {
final cells = state.options.map((option) {
List<Widget> cells = [];
cells.addAll(state.options.map((option) {
return _SelectOptionCell(option, state.selectedOptions.contains(option));
}).toList();
}).toList());
state.createOption.fold(
() => null,
(createOption) {
cells.add(_CreateOptionCell(name: createOption));
},
);
final list = ListView.separated(
shrinkWrap: true,
controller: ScrollController(),
@ -119,7 +128,11 @@ class _OptionList extends StatelessWidget {
return cells[index];
},
);
return list;
return Padding(
padding: const EdgeInsets.all(3.0),
child: list,
);
},
);
}
@ -177,6 +190,30 @@ class _Title extends StatelessWidget {
}
}
class _CreateOptionCell extends StatelessWidget {
final String name;
const _CreateOptionCell({required this.name, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return Row(
children: [
FlowyText.medium(
LocaleKeys.grid_selectOption_create.tr(),
fontSize: 12,
color: theme.shader3,
),
const HSpace(10),
SelectOptionTag(
name: name,
color: theme.shader6,
),
],
);
}
}
class _SelectOptionCell extends StatelessWidget {
final SelectOption option;
final bool isSelected;
@ -206,7 +243,11 @@ class _SelectOptionCell extends StatelessWidget {
style: HoverStyle(hoverColor: theme.hover),
builder: (_, onHover) {
List<Widget> children = [
SelectOptionTag(option: option, isSelected: isSelected),
SelectOptionTag(
name: option.name,
color: option.color.make(context),
isSelected: isSelected,
),
const Spacer(),
];
@ -223,10 +264,7 @@ class _SelectOptionCell extends StatelessWidget {
));
}
return Padding(
padding: const EdgeInsets.all(3.0),
child: Row(children: children),
);
return Row(children: children);
},
);
}

View File

@ -76,7 +76,7 @@ class SelectOptionTextField extends StatelessWidget {
borderRadius: Corners.s10Border,
),
isDense: true,
prefixIcon: _renderTags(sc),
prefixIcon: _renderTags(context, sc),
hintText: LocaleKeys.grid_selectOption_searchOption.tr(),
prefixIconConstraints: BoxConstraints(maxWidth: distanceToText),
focusedBorder: OutlineInputBorder(
@ -90,12 +90,14 @@ class SelectOptionTextField extends StatelessWidget {
);
}
Widget? _renderTags(ScrollController sc) {
Widget? _renderTags(BuildContext context, ScrollController sc) {
if (selectedOptionMap.isEmpty) {
return null;
}
final children = selectedOptionMap.values.map((option) => SelectOptionTag(option: option)).toList();
final children = selectedOptionMap.values
.map((option) => SelectOptionTag.fromSelectOption(context: context, option: option))
.toList();
return Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(