mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: show create option message on bottom
This commit is contained in:
parent
228695d517
commit
18752e7af8
@ -181,6 +181,7 @@
|
||||
"textPlaceholder": "Empty"
|
||||
},
|
||||
"selectOption": {
|
||||
"create": "Create",
|
||||
"purpleColor": "Purple",
|
||||
"pinkColor": "Pink",
|
||||
"lightPinkColor": "Light Pink",
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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: (_) {},
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user