mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: enable number filter (#4653)
* chore: enable filtering by number field type * chore: code cleanup * fix: integration test * chore: remove unnecessary async from event handler
This commit is contained in:
parent
1311e2d379
commit
c159a5e42b
@ -9,7 +9,7 @@ import '../util/database_test_op.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('database filter', () {
|
||||
group('grid filter:', () {
|
||||
testWidgets('add text filter', (tester) async {
|
||||
await tester.openV020database();
|
||||
|
||||
|
@ -47,9 +47,12 @@ class FieldInfo with _$FieldInfo {
|
||||
}
|
||||
|
||||
bool get canCreateFilter {
|
||||
if (hasFilter) return false;
|
||||
if (hasFilter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (field.fieldType) {
|
||||
case FieldType.Number:
|
||||
case FieldType.Checkbox:
|
||||
case FieldType.MultiSelect:
|
||||
case FieldType.RichText:
|
||||
@ -62,7 +65,9 @@ class FieldInfo with _$FieldInfo {
|
||||
}
|
||||
|
||||
bool get canCreateSort {
|
||||
if (hasSort) return false;
|
||||
if (hasSort) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (field.fieldType) {
|
||||
case FieldType.RichText:
|
||||
|
@ -0,0 +1,117 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/filter/filter_listener.dart';
|
||||
import 'package:appflowy/plugins/database/application/filter/filter_service.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/filter_info.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'number_filter_editor_bloc.freezed.dart';
|
||||
|
||||
class NumberFilterEditorBloc
|
||||
extends Bloc<NumberFilterEditorEvent, NumberFilterEditorState> {
|
||||
NumberFilterEditorBloc({required this.filterInfo})
|
||||
: _filterBackendSvc = FilterBackendService(viewId: filterInfo.viewId),
|
||||
_listener = FilterListener(
|
||||
viewId: filterInfo.viewId,
|
||||
filterId: filterInfo.filter.id,
|
||||
),
|
||||
super(NumberFilterEditorState.initial(filterInfo)) {
|
||||
_dispatch();
|
||||
_startListening();
|
||||
}
|
||||
|
||||
final FilterInfo filterInfo;
|
||||
final FilterBackendService _filterBackendSvc;
|
||||
final FilterListener _listener;
|
||||
|
||||
void _dispatch() {
|
||||
on<NumberFilterEditorEvent>(
|
||||
(event, emit) async {
|
||||
event.when(
|
||||
didReceiveFilter: (filter) {
|
||||
final filterInfo = state.filterInfo.copyWith(filter: filter);
|
||||
emit(
|
||||
state.copyWith(
|
||||
filterInfo: filterInfo,
|
||||
filter: filterInfo.numberFilter()!,
|
||||
),
|
||||
);
|
||||
},
|
||||
updateCondition: (NumberFilterConditionPB condition) {
|
||||
_filterBackendSvc.insertNumberFilter(
|
||||
filterId: filterInfo.filter.id,
|
||||
fieldId: filterInfo.fieldInfo.id,
|
||||
condition: condition,
|
||||
content: state.filter.content,
|
||||
);
|
||||
},
|
||||
updateContent: (content) {
|
||||
_filterBackendSvc.insertNumberFilter(
|
||||
filterId: filterInfo.filter.id,
|
||||
fieldId: filterInfo.fieldInfo.id,
|
||||
condition: state.filter.condition,
|
||||
content: content,
|
||||
);
|
||||
},
|
||||
delete: () {
|
||||
_filterBackendSvc.deleteFilter(
|
||||
fieldId: filterInfo.fieldInfo.id,
|
||||
filterId: filterInfo.filter.id,
|
||||
fieldType: filterInfo.fieldInfo.fieldType,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_listener.start(
|
||||
onDeleted: () {
|
||||
if (!isClosed) {
|
||||
add(const NumberFilterEditorEvent.delete());
|
||||
}
|
||||
},
|
||||
onUpdated: (filter) {
|
||||
if (!isClosed) {
|
||||
add(NumberFilterEditorEvent.didReceiveFilter(filter));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _listener.stop();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class NumberFilterEditorEvent with _$NumberFilterEditorEvent {
|
||||
const factory NumberFilterEditorEvent.didReceiveFilter(FilterPB filter) =
|
||||
_DidReceiveFilter;
|
||||
const factory NumberFilterEditorEvent.updateCondition(
|
||||
NumberFilterConditionPB condition,
|
||||
) = _UpdateCondition;
|
||||
const factory NumberFilterEditorEvent.updateContent(String content) =
|
||||
_UpdateContent;
|
||||
const factory NumberFilterEditorEvent.delete() = _Delete;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class NumberFilterEditorState with _$NumberFilterEditorState {
|
||||
const factory NumberFilterEditorState({
|
||||
required FilterInfo filterInfo,
|
||||
required NumberFilterPB filter,
|
||||
}) = _NumberFilterEditorState;
|
||||
|
||||
factory NumberFilterEditorState.initial(FilterInfo filterInfo) {
|
||||
return NumberFilterEditorState(
|
||||
filterInfo: filterInfo,
|
||||
filter: filterInfo.numberFilter()!,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,15 +1,227 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/filter/number_filter_editor_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../condition_button.dart';
|
||||
import '../disclosure_button.dart';
|
||||
import '../filter_info.dart';
|
||||
import 'choicechip.dart';
|
||||
|
||||
class NumberFilterChoicechip extends StatelessWidget {
|
||||
const NumberFilterChoicechip({required this.filterInfo, super.key});
|
||||
class NumberFilterChoiceChip extends StatefulWidget {
|
||||
const NumberFilterChoiceChip({
|
||||
super.key,
|
||||
required this.filterInfo,
|
||||
});
|
||||
|
||||
final FilterInfo filterInfo;
|
||||
|
||||
@override
|
||||
State<NumberFilterChoiceChip> createState() => _NumberFilterChoiceChipState();
|
||||
}
|
||||
|
||||
class _NumberFilterChoiceChipState extends State<NumberFilterChoiceChip> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChoiceChipButton(filterInfo: filterInfo);
|
||||
return BlocProvider(
|
||||
create: (_) => NumberFilterEditorBloc(
|
||||
filterInfo: widget.filterInfo,
|
||||
),
|
||||
child: BlocBuilder<NumberFilterEditorBloc, NumberFilterEditorState>(
|
||||
builder: (context, state) {
|
||||
return AppFlowyPopover(
|
||||
constraints: BoxConstraints.loose(const Size(200, 100)),
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
popupBuilder: (_) {
|
||||
return BlocProvider.value(
|
||||
value: context.read<NumberFilterEditorBloc>(),
|
||||
child: const NumberFilterEditor(),
|
||||
);
|
||||
},
|
||||
child: ChoiceChipButton(
|
||||
filterInfo: state.filterInfo,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NumberFilterEditor extends StatefulWidget {
|
||||
const NumberFilterEditor({super.key});
|
||||
|
||||
@override
|
||||
State<NumberFilterEditor> createState() => _NumberFilterEditorState();
|
||||
}
|
||||
|
||||
class _NumberFilterEditorState extends State<NumberFilterEditor> {
|
||||
final popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<NumberFilterEditorBloc, NumberFilterEditorState>(
|
||||
builder: (context, state) {
|
||||
final List<Widget> children = [
|
||||
_buildFilterPanel(context, state),
|
||||
if (state.filter.condition != NumberFilterConditionPB.NumberIsEmpty &&
|
||||
state.filter.condition !=
|
||||
NumberFilterConditionPB.NumberIsNotEmpty) ...[
|
||||
const VSpace(4),
|
||||
_buildFilterNumberField(context, state),
|
||||
],
|
||||
];
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
|
||||
child: IntrinsicHeight(child: Column(children: children)),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFilterPanel(
|
||||
BuildContext context,
|
||||
NumberFilterEditorState state,
|
||||
) {
|
||||
return SizedBox(
|
||||
height: 20,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FlowyText(
|
||||
state.filterInfo.fieldInfo.name,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const HSpace(4),
|
||||
Expanded(
|
||||
child: NumberFilterConditionPBList(
|
||||
filterInfo: state.filterInfo,
|
||||
popoverMutex: popoverMutex,
|
||||
onCondition: (condition) {
|
||||
context
|
||||
.read<NumberFilterEditorBloc>()
|
||||
.add(NumberFilterEditorEvent.updateCondition(condition));
|
||||
},
|
||||
),
|
||||
),
|
||||
const HSpace(4),
|
||||
DisclosureButton(
|
||||
popoverMutex: popoverMutex,
|
||||
onAction: (action) {
|
||||
switch (action) {
|
||||
case FilterDisclosureAction.delete:
|
||||
context
|
||||
.read<NumberFilterEditorBloc>()
|
||||
.add(const NumberFilterEditorEvent.delete());
|
||||
break;
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFilterNumberField(
|
||||
BuildContext context,
|
||||
NumberFilterEditorState state,
|
||||
) {
|
||||
return FlowyTextField(
|
||||
text: state.filter.content,
|
||||
hintText: LocaleKeys.grid_settings_typeAValue.tr(),
|
||||
debounceDuration: const Duration(milliseconds: 300),
|
||||
autoFocus: false,
|
||||
onChanged: (text) {
|
||||
context
|
||||
.read<NumberFilterEditorBloc>()
|
||||
.add(NumberFilterEditorEvent.updateContent(text));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NumberFilterConditionPBList extends StatelessWidget {
|
||||
const NumberFilterConditionPBList({
|
||||
super.key,
|
||||
required this.filterInfo,
|
||||
required this.popoverMutex,
|
||||
required this.onCondition,
|
||||
});
|
||||
|
||||
final FilterInfo filterInfo;
|
||||
final PopoverMutex popoverMutex;
|
||||
final Function(NumberFilterConditionPB) onCondition;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final numberFilter = filterInfo.numberFilter()!;
|
||||
return PopoverActionList<ConditionWrapper>(
|
||||
asBarrier: true,
|
||||
mutex: popoverMutex,
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
actions: NumberFilterConditionPB.values
|
||||
.map(
|
||||
(action) => ConditionWrapper(
|
||||
action,
|
||||
numberFilter.condition == action,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
buildChild: (controller) {
|
||||
return ConditionButton(
|
||||
conditionName: numberFilter.condition.filterName,
|
||||
onTap: () => controller.show(),
|
||||
);
|
||||
},
|
||||
onSelected: (action, controller) {
|
||||
onCondition(action.inner);
|
||||
controller.close();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConditionWrapper extends ActionCell {
|
||||
ConditionWrapper(this.inner, this.isSelected);
|
||||
|
||||
final NumberFilterConditionPB inner;
|
||||
final bool isSelected;
|
||||
|
||||
@override
|
||||
Widget? rightIcon(Color iconColor) =>
|
||||
isSelected ? const FlowySvg(FlowySvgs.check_s) : null;
|
||||
|
||||
@override
|
||||
String get name => inner.filterName;
|
||||
}
|
||||
|
||||
extension NumberFilterConditionPBExtension on NumberFilterConditionPB {
|
||||
String get filterName {
|
||||
return switch (this) {
|
||||
NumberFilterConditionPB.Equal => LocaleKeys.grid_numberFilter_equal.tr(),
|
||||
NumberFilterConditionPB.NotEqual =>
|
||||
LocaleKeys.grid_numberFilter_notEqual.tr(),
|
||||
NumberFilterConditionPB.LessThan =>
|
||||
LocaleKeys.grid_numberFilter_lessThan.tr(),
|
||||
NumberFilterConditionPB.LessThanOrEqualTo =>
|
||||
LocaleKeys.grid_numberFilter_lessThanOrEqualTo.tr(),
|
||||
NumberFilterConditionPB.GreaterThan =>
|
||||
LocaleKeys.grid_numberFilter_greaterThan.tr(),
|
||||
NumberFilterConditionPB.GreaterThanOrEqualTo =>
|
||||
LocaleKeys.grid_numberFilter_greaterThanOrEqualTo.tr(),
|
||||
NumberFilterConditionPB.NumberIsEmpty =>
|
||||
LocaleKeys.grid_numberFilter_isEmpty.tr(),
|
||||
NumberFilterConditionPB.NumberIsNotEmpty =>
|
||||
LocaleKeys.grid_numberFilter_isNotEmpty.tr(),
|
||||
_ => "",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -27,51 +27,37 @@ class SelectOptionFilterList extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) {
|
||||
late SelectOptionFilterListBloc bloc;
|
||||
if (filterInfo.fieldInfo.fieldType == FieldType.SingleSelect) {
|
||||
bloc = SelectOptionFilterListBloc(
|
||||
selectedOptionIds: selectedOptionIds,
|
||||
delegate:
|
||||
SingleSelectOptionFilterDelegateImpl(filterInfo: filterInfo),
|
||||
);
|
||||
} else {
|
||||
bloc = SelectOptionFilterListBloc(
|
||||
selectedOptionIds: selectedOptionIds,
|
||||
delegate:
|
||||
MultiSelectOptionFilterDelegateImpl(filterInfo: filterInfo),
|
||||
);
|
||||
}
|
||||
|
||||
bloc.add(const SelectOptionFilterListEvent.initial());
|
||||
return bloc;
|
||||
return SelectOptionFilterListBloc(
|
||||
selectedOptionIds: selectedOptionIds,
|
||||
delegate: filterInfo.fieldInfo.fieldType == FieldType.SingleSelect
|
||||
? SingleSelectOptionFilterDelegateImpl(filterInfo: filterInfo)
|
||||
: MultiSelectOptionFilterDelegateImpl(filterInfo: filterInfo),
|
||||
)..add(const SelectOptionFilterListEvent.initial());
|
||||
},
|
||||
child:
|
||||
BlocListener<SelectOptionFilterListBloc, SelectOptionFilterListState>(
|
||||
BlocConsumer<SelectOptionFilterListBloc, SelectOptionFilterListState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.selectedOptionIds != current.selectedOptionIds,
|
||||
listener: (context, state) {
|
||||
onSelectedOptions(state.selectedOptionIds.toList());
|
||||
},
|
||||
child: BlocBuilder<SelectOptionFilterListBloc,
|
||||
SelectOptionFilterListState>(
|
||||
builder: (context, state) {
|
||||
return ListView.separated(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: state.visibleOptions.length,
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final option = state.visibleOptions[index];
|
||||
return SelectOptionFilterCell(
|
||||
option: option.optionPB,
|
||||
isSelected: option.isSelected,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
builder: (context, state) {
|
||||
return ListView.separated(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: state.visibleOptions.length,
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final option = state.visibleOptions[index];
|
||||
return SelectOptionFilterCell(
|
||||
option: option.optionPB,
|
||||
isSelected: option.isSelected,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,5 @@
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/checkbox_filter.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_filter.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/date_filter.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_filter.pbserver.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/text_filter.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/util.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
|
||||
class FilterInfo {
|
||||
FilterInfo(this.viewId, this.filter, this.fieldInfo);
|
||||
@ -27,44 +21,39 @@ class FilterInfo {
|
||||
String get fieldId => filter.fieldId;
|
||||
|
||||
DateFilterPB? dateFilter() {
|
||||
if (![
|
||||
FieldType.DateTime,
|
||||
FieldType.LastEditedTime,
|
||||
FieldType.CreatedTime,
|
||||
].contains(filter.fieldType)) {
|
||||
return null;
|
||||
}
|
||||
return DateFilterPB.fromBuffer(filter.data);
|
||||
return filter.fieldType == FieldType.DateTime
|
||||
? DateFilterPB.fromBuffer(filter.data)
|
||||
: null;
|
||||
}
|
||||
|
||||
TextFilterPB? textFilter() {
|
||||
if (filter.fieldType != FieldType.RichText) {
|
||||
return null;
|
||||
}
|
||||
return TextFilterPB.fromBuffer(filter.data);
|
||||
return filter.fieldType == FieldType.RichText
|
||||
? TextFilterPB.fromBuffer(filter.data)
|
||||
: null;
|
||||
}
|
||||
|
||||
CheckboxFilterPB? checkboxFilter() {
|
||||
if (filter.fieldType != FieldType.Checkbox) {
|
||||
return null;
|
||||
}
|
||||
return CheckboxFilterPB.fromBuffer(filter.data);
|
||||
return filter.fieldType == FieldType.Checkbox
|
||||
? CheckboxFilterPB.fromBuffer(filter.data)
|
||||
: null;
|
||||
}
|
||||
|
||||
SelectOptionFilterPB? selectOptionFilter() {
|
||||
if (filter.fieldType == FieldType.SingleSelect ||
|
||||
filter.fieldType == FieldType.MultiSelect) {
|
||||
return SelectOptionFilterPB.fromBuffer(filter.data);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return filter.fieldType == FieldType.SingleSelect ||
|
||||
filter.fieldType == FieldType.MultiSelect
|
||||
? SelectOptionFilterPB.fromBuffer(filter.data)
|
||||
: null;
|
||||
}
|
||||
|
||||
ChecklistFilterPB? checklistFilter() {
|
||||
if (filter.fieldType == FieldType.Checklist) {
|
||||
return ChecklistFilterPB.fromBuffer(filter.data);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return filter.fieldType == FieldType.Checklist
|
||||
? ChecklistFilterPB.fromBuffer(filter.data)
|
||||
: null;
|
||||
}
|
||||
|
||||
NumberFilterPB? numberFilter() {
|
||||
return filter.fieldType == FieldType.Number
|
||||
? NumberFilterPB.fromBuffer(filter.data)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
@ -17,31 +17,18 @@ class FilterMenuItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return buildFilterChoicechip(filterInfo);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildFilterChoicechip(FilterInfo filterInfo) {
|
||||
switch (filterInfo.fieldInfo.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return CheckboxFilterChoicechip(filterInfo: filterInfo);
|
||||
case FieldType.DateTime:
|
||||
case FieldType.LastEditedTime:
|
||||
case FieldType.CreatedTime:
|
||||
return DateFilterChoicechip(filterInfo: filterInfo);
|
||||
case FieldType.MultiSelect:
|
||||
return SelectOptionFilterChoicechip(filterInfo: filterInfo);
|
||||
case FieldType.Number:
|
||||
return NumberFilterChoicechip(filterInfo: filterInfo);
|
||||
case FieldType.RichText:
|
||||
return TextFilterChoicechip(filterInfo: filterInfo);
|
||||
case FieldType.SingleSelect:
|
||||
return SelectOptionFilterChoicechip(filterInfo: filterInfo);
|
||||
case FieldType.URL:
|
||||
return URLFilterChoicechip(filterInfo: filterInfo);
|
||||
case FieldType.Checklist:
|
||||
return ChecklistFilterChoicechip(filterInfo: filterInfo);
|
||||
default:
|
||||
return const SizedBox();
|
||||
return switch (filterInfo.fieldInfo.fieldType) {
|
||||
FieldType.Checkbox => CheckboxFilterChoicechip(filterInfo: filterInfo),
|
||||
FieldType.DateTime => DateFilterChoicechip(filterInfo: filterInfo),
|
||||
FieldType.MultiSelect =>
|
||||
SelectOptionFilterChoicechip(filterInfo: filterInfo),
|
||||
FieldType.Number => NumberFilterChoiceChip(filterInfo: filterInfo),
|
||||
FieldType.RichText => TextFilterChoicechip(filterInfo: filterInfo),
|
||||
FieldType.SingleSelect =>
|
||||
SelectOptionFilterChoicechip(filterInfo: filterInfo),
|
||||
FieldType.URL => URLFilterChoicechip(filterInfo: filterInfo),
|
||||
FieldType.Checklist => ChecklistFilterChoicechip(filterInfo: filterInfo),
|
||||
_ => const SizedBox(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -540,6 +540,16 @@
|
||||
"empty": "Is empty",
|
||||
"notEmpty": "Is not empty"
|
||||
},
|
||||
"numberFilter": {
|
||||
"equal": "Equals",
|
||||
"notEqual": "Does not equal",
|
||||
"lessThan": "Is less than",
|
||||
"greaterThan": "Is greater than",
|
||||
"lessThanOrEqualTo": "Is less than or equal to",
|
||||
"greaterThanOrEqualTo": "Is greater than or equal to",
|
||||
"isEmpty": "Is empty",
|
||||
"isNotEmpty": "Is not empty"
|
||||
},
|
||||
"field": {
|
||||
"hide": "Hide",
|
||||
"show": "Show",
|
||||
@ -1262,4 +1272,4 @@
|
||||
"userIcon": "User icon"
|
||||
},
|
||||
"noLogFiles": "There're no log files"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user