mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: show hidden fields in row detail page (#3545)
This commit is contained in:
parent
6198c3907f
commit
0738b5f87d
@ -146,7 +146,7 @@ void main() {
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Assert that the first field in the row details page is the select
|
||||
// option tyoe
|
||||
// option type
|
||||
tester.assertFirstFieldInRowDetailByType(FieldType.SingleSelect);
|
||||
|
||||
// Reorder first field in list
|
||||
@ -168,6 +168,54 @@ void main() {
|
||||
tester.assertFirstFieldInRowDetailByType(FieldType.SingleSelect);
|
||||
});
|
||||
|
||||
testWidgets('hide and show hidden fields', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Assert that the show hidden fields button isn't visible
|
||||
tester.assertToggleShowHiddenFieldsVisibility(false);
|
||||
|
||||
// Hide the first field in the field list
|
||||
await tester.tapGridFieldWithNameInRowDetailPage("Type");
|
||||
await tester.tapHidePropertyButtonInFieldEditor();
|
||||
|
||||
// Assert that the field is now hidden
|
||||
tester.noFieldWithName("Type");
|
||||
|
||||
// Assert that the show hidden fields button appears
|
||||
tester.assertToggleShowHiddenFieldsVisibility(true);
|
||||
|
||||
// Click on the show hidden fields button
|
||||
await tester.toggleShowHiddenFields();
|
||||
|
||||
// Assert that the hidden field is shown again and that the show
|
||||
// hidden fields button is still present
|
||||
tester.findFieldWithName("Type");
|
||||
tester.assertToggleShowHiddenFieldsVisibility(true);
|
||||
|
||||
// Click hide hidden fields
|
||||
await tester.toggleShowHiddenFields();
|
||||
|
||||
// Assert that the hidden field has vanished
|
||||
tester.noFieldWithName("Type");
|
||||
|
||||
// Click show hidden fields
|
||||
await tester.toggleShowHiddenFields();
|
||||
|
||||
// delete the hidden field
|
||||
await tester.tapGridFieldWithNameInRowDetailPage("Type");
|
||||
await tester.tapDeletePropertyInFieldEditor();
|
||||
|
||||
// Assert that the that the show hidden fields button is gone
|
||||
tester.assertToggleShowHiddenFieldsVisibility(false);
|
||||
});
|
||||
|
||||
testWidgets('check document exists in row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -55,6 +55,7 @@ import 'package:appflowy/plugins/database_view/widgets/row/row_property.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/setting/database_setting.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/emoji_picker/emoji_menu_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
@ -673,6 +674,31 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
void assertToggleShowHiddenFieldsVisibility(bool shown) {
|
||||
final button = find.byType(ToggleHiddenFieldsVisibilityButton);
|
||||
if (shown) {
|
||||
expect(button, findsOneWidget);
|
||||
} else {
|
||||
expect(button, findsNothing);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> toggleShowHiddenFields() async {
|
||||
final button = find.byType(ToggleHiddenFieldsVisibilityButton);
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
Future<void> tapDeletePropertyInFieldEditor() async {
|
||||
final deleteButton = find.byType(DeleteFieldButton);
|
||||
await tapButton(deleteButton);
|
||||
|
||||
final confirmButton = find.descendant(
|
||||
of: find.byType(NavigatorAlertDialog),
|
||||
matching: find.byType(PrimaryTextButton),
|
||||
);
|
||||
await tapButton(confirmButton);
|
||||
}
|
||||
|
||||
Future<void> scrollGridByOffset(Offset offset) async {
|
||||
await drag(find.byType(GridPage), offset);
|
||||
await pumpAndSettle();
|
||||
@ -746,7 +772,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> tapHidePropertyButtonInFieldEditor() async {
|
||||
final button = find.byType(HideFieldButton);
|
||||
final button = find.byType(FieldVisibilityToggleButton);
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
@ -899,7 +925,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> assertRowCountInGridPage(int num) async {
|
||||
final text = find.text('${rowCountString()} $num',findRichText: true);
|
||||
final text = find.text('${rowCountString()} $num', findRichText: true);
|
||||
expect(text, findsOneWidget);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/timestamp_entities.pb.dart';
|
||||
@ -73,4 +74,13 @@ class DatabaseCellContext with _$DatabaseCellContext {
|
||||
|
||||
/// Only the primary field can have an emoji.
|
||||
String? get emoji => fieldInfo.field.isPrimary ? rowMeta.icon : null;
|
||||
|
||||
/// Determines whether a database cell context should be visible.
|
||||
/// It will be visible when the field is not hidden or when hidden fields
|
||||
/// should be shown.
|
||||
bool isVisible({bool showHiddenFields = false}) {
|
||||
return fieldInfo.visibility != null &&
|
||||
(showHiddenFields ||
|
||||
fieldInfo.visibility != FieldVisibility.AlwaysHidden);
|
||||
}
|
||||
}
|
||||
|
@ -15,16 +15,16 @@ class FieldActionSheetBloc
|
||||
final FieldSettingsBackendService fieldSettingsService;
|
||||
|
||||
FieldActionSheetBloc({required FieldContext fieldCellContext})
|
||||
: fieldId = fieldCellContext.field.id,
|
||||
: fieldId = fieldCellContext.fieldInfo.id,
|
||||
fieldService = FieldBackendService(
|
||||
viewId: fieldCellContext.viewId,
|
||||
fieldId: fieldCellContext.field.id,
|
||||
fieldId: fieldCellContext.fieldInfo.id,
|
||||
),
|
||||
fieldSettingsService =
|
||||
FieldSettingsBackendService(viewId: fieldCellContext.viewId),
|
||||
super(
|
||||
FieldActionSheetState.initial(
|
||||
TypeOptionPB.create()..field_2 = fieldCellContext.field,
|
||||
TypeOptionPB.create()..field_2 = fieldCellContext.fieldInfo.field,
|
||||
),
|
||||
) {
|
||||
on<FieldActionSheetEvent>(
|
||||
|
@ -15,13 +15,14 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
|
||||
final FieldBackendService _fieldBackendSvc;
|
||||
|
||||
FieldCellBloc({
|
||||
required FieldContext cellContext,
|
||||
}) : _fieldListener = SingleFieldListener(fieldId: cellContext.field.id),
|
||||
required FieldContext fieldContext,
|
||||
}) : _fieldListener =
|
||||
SingleFieldListener(fieldId: fieldContext.fieldInfo.id),
|
||||
_fieldBackendSvc = FieldBackendService(
|
||||
viewId: cellContext.viewId,
|
||||
fieldId: cellContext.field.id,
|
||||
viewId: fieldContext.viewId,
|
||||
fieldId: fieldContext.fieldInfo.id,
|
||||
),
|
||||
super(FieldCellState.initial(cellContext)) {
|
||||
super(FieldCellState.initial(fieldContext)) {
|
||||
on<FieldCellEvent>(
|
||||
(event, emit) async {
|
||||
event.when(
|
||||
@ -29,7 +30,7 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
|
||||
_startListening();
|
||||
},
|
||||
didReceiveFieldUpdate: (field) {
|
||||
emit(state.copyWith(field: cellContext.field));
|
||||
emit(state.copyWith(field: fieldContext.fieldInfo.field));
|
||||
},
|
||||
onResizeStart: () {
|
||||
emit(state.copyWith(resizeStart: state.width));
|
||||
@ -88,8 +89,8 @@ class FieldCellState with _$FieldCellState {
|
||||
|
||||
factory FieldCellState.initial(FieldContext cellContext) => FieldCellState(
|
||||
viewId: cellContext.viewId,
|
||||
field: cellContext.field,
|
||||
width: cellContext.field.width.toDouble(),
|
||||
field: cellContext.fieldInfo.field,
|
||||
width: cellContext.fieldInfo.field.width.toDouble(),
|
||||
resizeStart: 0,
|
||||
);
|
||||
}
|
||||
|
@ -367,28 +367,13 @@ class FieldController {
|
||||
|
||||
/// Listen for field changes in the backend.
|
||||
void _listenOnFieldChanges() {
|
||||
void deleteFields(List<FieldIdPB> deletedFields) {
|
||||
if (deletedFields.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final List<FieldInfo> newFields = fieldInfos;
|
||||
final Map<String, FieldIdPB> deletedFieldMap = {
|
||||
for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
|
||||
};
|
||||
|
||||
newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
|
||||
_fieldNotifier.fieldInfos = newFields;
|
||||
}
|
||||
|
||||
Future<FieldInfo> attachFieldSettings(FieldInfo fieldInfo) async {
|
||||
return _fieldSettingsBackendSvc
|
||||
.getFieldSettings(fieldInfo.id)
|
||||
.then((result) {
|
||||
final fieldSettings = result.fold(
|
||||
(fieldSettings) => fieldSettings,
|
||||
(err) {
|
||||
return null;
|
||||
},
|
||||
(err) => null,
|
||||
);
|
||||
if (fieldSettings == null) {
|
||||
return fieldInfo;
|
||||
@ -400,9 +385,25 @@ class FieldController {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> insertFields(List<IndexFieldPB> insertedFields) async {
|
||||
List<FieldInfo> deleteFields(List<FieldIdPB> deletedFields) {
|
||||
if (deletedFields.isEmpty) {
|
||||
return fieldInfos;
|
||||
}
|
||||
final List<FieldInfo> newFields = fieldInfos;
|
||||
final Map<String, FieldIdPB> deletedFieldMap = {
|
||||
for (final fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
|
||||
};
|
||||
|
||||
newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
|
||||
return newFields;
|
||||
}
|
||||
|
||||
Future<List<FieldInfo>> insertFields(
|
||||
List<IndexFieldPB> insertedFields,
|
||||
List<FieldInfo> fieldInfos,
|
||||
) async {
|
||||
if (insertedFields.isEmpty) {
|
||||
return;
|
||||
return fieldInfos;
|
||||
}
|
||||
final List<FieldInfo> newFieldInfos = fieldInfos;
|
||||
for (final indexField in insertedFields) {
|
||||
@ -414,32 +415,32 @@ class FieldController {
|
||||
newFieldInfos.add(fieldInfo);
|
||||
}
|
||||
}
|
||||
_fieldNotifier.fieldInfos = newFieldInfos;
|
||||
return newFieldInfos;
|
||||
}
|
||||
|
||||
Future<List<FieldInfo>> updateFields(List<FieldPB> updatedFieldPBs) async {
|
||||
Future<(List<FieldInfo>, List<FieldInfo>)> updateFields(
|
||||
List<FieldPB> updatedFieldPBs,
|
||||
List<FieldInfo> fieldInfos,
|
||||
) async {
|
||||
if (updatedFieldPBs.isEmpty) {
|
||||
return [];
|
||||
return (<FieldInfo>[], fieldInfos);
|
||||
}
|
||||
|
||||
final List<FieldInfo> newFields = fieldInfos;
|
||||
final List<FieldInfo> newFieldInfo = fieldInfos;
|
||||
final List<FieldInfo> updatedFields = [];
|
||||
for (final updatedFieldPB in updatedFieldPBs) {
|
||||
final index =
|
||||
newFields.indexWhere((field) => field.id == updatedFieldPB.id);
|
||||
newFieldInfo.indexWhere((field) => field.id == updatedFieldPB.id);
|
||||
if (index != -1) {
|
||||
newFields.removeAt(index);
|
||||
newFieldInfo.removeAt(index);
|
||||
final initial = FieldInfo.initial(updatedFieldPB);
|
||||
final fieldInfo = await attachFieldSettings(initial);
|
||||
newFields.insert(index, fieldInfo);
|
||||
newFieldInfo.insert(index, fieldInfo);
|
||||
updatedFields.add(fieldInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedFields.isNotEmpty) {
|
||||
_fieldNotifier.fieldInfos = newFields;
|
||||
}
|
||||
return updatedFields;
|
||||
return (updatedFields, newFieldInfo);
|
||||
}
|
||||
|
||||
// Listen on field's changes
|
||||
@ -450,10 +451,14 @@ class FieldController {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
deleteFields(changeset.deletedFields);
|
||||
insertFields(changeset.insertedFields);
|
||||
List<FieldInfo> updatedFields;
|
||||
List<FieldInfo> fieldInfos = deleteFields(changeset.deletedFields);
|
||||
fieldInfos =
|
||||
await insertFields(changeset.insertedFields, fieldInfos);
|
||||
(updatedFields, fieldInfos) =
|
||||
await updateFields(changeset.updatedFields, fieldInfos);
|
||||
|
||||
final updatedFields = await updateFields(changeset.updatedFields);
|
||||
_fieldNotifier.fieldInfos = fieldInfos;
|
||||
for (final listener in _updatedFieldCallbacks.values) {
|
||||
listener(updatedFields);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
@ -108,6 +109,6 @@ class FieldBackendService {
|
||||
class FieldContext with _$FieldContext {
|
||||
const factory FieldContext({
|
||||
required String viewId,
|
||||
required FieldPB field,
|
||||
required FieldInfo fieldInfo,
|
||||
}) = _FieldCellContext;
|
||||
}
|
||||
|
@ -20,7 +20,14 @@ class FieldSettingsBackendService {
|
||||
|
||||
return DatabaseEventGetFieldSettings(payload).send().then((result) {
|
||||
return result.fold(
|
||||
(fieldSettings) => left(fieldSettings.items.first),
|
||||
(repeatedFieldSettings) {
|
||||
final fieldSetting = repeatedFieldSettings.items.first;
|
||||
if (!fieldSetting.hasVisibility()) {
|
||||
fieldSetting.visibility = FieldVisibility.AlwaysShown;
|
||||
}
|
||||
|
||||
return left(fieldSetting);
|
||||
},
|
||||
(r) => right(r),
|
||||
);
|
||||
});
|
||||
@ -31,7 +38,18 @@ class FieldSettingsBackendService {
|
||||
|
||||
return DatabaseEventGetAllFieldSettings(payload).send().then((result) {
|
||||
return result.fold(
|
||||
(fieldSettings) => left(fieldSettings.items),
|
||||
(repeatedFieldSettings) {
|
||||
final fieldSettings = <FieldSettingsPB>[];
|
||||
|
||||
for (final fieldSetting in repeatedFieldSettings.items) {
|
||||
if (!fieldSetting.hasVisibility()) {
|
||||
fieldSetting.visibility = FieldVisibility.AlwaysShown;
|
||||
}
|
||||
fieldSettings.add(fieldSetting);
|
||||
}
|
||||
|
||||
return left(fieldSettings);
|
||||
},
|
||||
(r) => right(r),
|
||||
);
|
||||
});
|
||||
|
@ -246,14 +246,11 @@ class RowCache {
|
||||
// ignore: prefer_collection_literals
|
||||
final cellContextMap = CellContextByFieldId();
|
||||
for (final fieldInfo in _fieldDelegate.fieldInfos) {
|
||||
if (fieldInfo.visibility != null &&
|
||||
fieldInfo.visibility != FieldVisibility.AlwaysHidden) {
|
||||
cellContextMap[fieldInfo.id] = DatabaseCellContext(
|
||||
rowMeta: rowMeta,
|
||||
viewId: viewId,
|
||||
fieldInfo: fieldInfo,
|
||||
);
|
||||
}
|
||||
cellContextMap[fieldInfo.id] = DatabaseCellContext(
|
||||
rowMeta: rowMeta,
|
||||
viewId: viewId,
|
||||
fieldInfo: fieldInfo,
|
||||
);
|
||||
}
|
||||
return cellContextMap;
|
||||
}
|
||||
|
@ -23,12 +23,14 @@ class CalendarEventEditorBloc
|
||||
await event.when(
|
||||
initial: () {
|
||||
_startListening();
|
||||
final cells = rowController.loadData();
|
||||
final cells = rowController
|
||||
.loadData()
|
||||
.values
|
||||
.where((cellContext) => cellContext.isVisible())
|
||||
.toList();
|
||||
if (!isClosed) {
|
||||
add(
|
||||
CalendarEventEditorEvent.didReceiveCellDatas(
|
||||
cells.values.toList(),
|
||||
),
|
||||
CalendarEventEditorEvent.didReceiveCellDatas(cells),
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -47,8 +49,11 @@ class CalendarEventEditorBloc
|
||||
rowController.addListener(
|
||||
onRowChanged: (cells, reason) {
|
||||
if (!isClosed) {
|
||||
final cellData = cells.values
|
||||
.where((cellContext) => cellContext.isVisible())
|
||||
.toList();
|
||||
add(
|
||||
CalendarEventEditorEvent.didReceiveCellDatas(cells.values.toList()),
|
||||
CalendarEventEditorEvent.didReceiveCellDatas(cellData),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -39,6 +39,8 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
_rowBackendSvc.createRowAfterRow(rowId);
|
||||
},
|
||||
didReceiveCells: (cellByFieldId, reason) async {
|
||||
cellByFieldId
|
||||
.removeWhere((_, cellContext) => !cellContext.isVisible());
|
||||
final cells = cellByFieldId.values
|
||||
.map((e) => GridCellEquatable(e.fieldInfo))
|
||||
.toList();
|
||||
@ -106,16 +108,18 @@ class RowState with _$RowState {
|
||||
|
||||
factory RowState.initial(
|
||||
CellContextByFieldId cellByFieldId,
|
||||
) =>
|
||||
RowState(
|
||||
cellByFieldId: cellByFieldId,
|
||||
cells: UnmodifiableListView(
|
||||
cellByFieldId.values
|
||||
.map((e) => GridCellEquatable(e.fieldInfo))
|
||||
.toList(),
|
||||
),
|
||||
rowSource: const RowSourece.disk(),
|
||||
);
|
||||
) {
|
||||
cellByFieldId.removeWhere((_, cellContext) => !cellContext.isVisible());
|
||||
return RowState(
|
||||
cellByFieldId: cellByFieldId,
|
||||
cells: UnmodifiableListView(
|
||||
cellByFieldId.values
|
||||
.map((e) => GridCellEquatable(e.fieldInfo))
|
||||
.toList(),
|
||||
),
|
||||
rowSource: const RowSourece.disk(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GridCellEquatable extends Equatable {
|
||||
|
@ -14,49 +14,73 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
|
||||
RowDetailBloc({
|
||||
required this.rowController,
|
||||
}) : super(RowDetailState.initial()) {
|
||||
}) : super(RowDetailState.initial(rowController.loadData())) {
|
||||
on<RowDetailEvent>(
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
await _startListening();
|
||||
final cells = rowController.loadData();
|
||||
if (!isClosed) {
|
||||
add(RowDetailEvent.didReceiveCellDatas(cells.values.toList()));
|
||||
}
|
||||
},
|
||||
didReceiveCellDatas: (cells) {
|
||||
emit(state.copyWith(cells: cells));
|
||||
didReceiveCellDatas: (visibleCells, allCells, numHiddenFields) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
visibleCells: visibleCells,
|
||||
allCells: allCells,
|
||||
numHiddenFields: numHiddenFields,
|
||||
),
|
||||
);
|
||||
},
|
||||
deleteField: (fieldId) {
|
||||
_fieldBackendService(fieldId).deleteField();
|
||||
final fieldService = FieldBackendService(
|
||||
viewId: rowController.viewId,
|
||||
fieldId: fieldId,
|
||||
);
|
||||
fieldService.deleteField();
|
||||
},
|
||||
showField: (fieldId) async {
|
||||
toggleFieldVisibility: (fieldId) async {
|
||||
final fieldInfo = state.allCells
|
||||
.where((cellContext) => cellContext.fieldId == fieldId)
|
||||
.first
|
||||
.fieldInfo;
|
||||
final fieldVisibility =
|
||||
fieldInfo.visibility == FieldVisibility.AlwaysShown
|
||||
? FieldVisibility.AlwaysHidden
|
||||
: FieldVisibility.AlwaysShown;
|
||||
final result =
|
||||
await FieldSettingsBackendService(viewId: rowController.viewId)
|
||||
.updateFieldSettings(
|
||||
fieldId: fieldId,
|
||||
fieldVisibility: FieldVisibility.AlwaysShown,
|
||||
fieldVisibility: fieldVisibility,
|
||||
);
|
||||
result.fold(
|
||||
(l) {},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
},
|
||||
hideField: (fieldId) async {
|
||||
final result =
|
||||
await FieldSettingsBackendService(viewId: rowController.viewId)
|
||||
.updateFieldSettings(
|
||||
fieldId: fieldId,
|
||||
fieldVisibility: FieldVisibility.AlwaysHidden,
|
||||
);
|
||||
result.fold(
|
||||
(l) {},
|
||||
(err) => Log.error(err),
|
||||
reorderField:
|
||||
(reorderedFieldId, targetFieldId, fromIndex, toIndex) async {
|
||||
await _reorderField(
|
||||
reorderedFieldId,
|
||||
targetFieldId,
|
||||
fromIndex,
|
||||
toIndex,
|
||||
emit,
|
||||
);
|
||||
},
|
||||
reorderField: (fieldId, fromIndex, toIndex) async {
|
||||
await _reorderField(fieldId, fromIndex, toIndex, emit);
|
||||
toggleHiddenFieldVisibility: () {
|
||||
final showHiddenFields = !state.showHiddenFields;
|
||||
final visibleCells = List<DatabaseCellContext>.from(state.allCells);
|
||||
visibleCells.retainWhere(
|
||||
(cellContext) =>
|
||||
!cellContext.fieldInfo.isPrimary &&
|
||||
cellContext.isVisible(showHiddenFields: showHiddenFields),
|
||||
);
|
||||
emit(
|
||||
state.copyWith(
|
||||
showHiddenFields: showHiddenFields,
|
||||
visibleCells: visibleCells,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -71,36 +95,60 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
|
||||
Future<void> _startListening() async {
|
||||
rowController.addListener(
|
||||
onRowChanged: (cells, reason) {
|
||||
if (!isClosed) {
|
||||
add(RowDetailEvent.didReceiveCellDatas(cells.values.toList()));
|
||||
onRowChanged: (cellMap, reason) {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
final allCells = cellMap.values.toList();
|
||||
int numHiddenFields = 0;
|
||||
final visibleCells = <DatabaseCellContext>[];
|
||||
for (final cell in allCells) {
|
||||
final isPrimary = cell.fieldInfo.isPrimary;
|
||||
|
||||
if (cell.isVisible(showHiddenFields: state.showHiddenFields) &&
|
||||
!isPrimary) {
|
||||
visibleCells.add(cell);
|
||||
}
|
||||
|
||||
if (!cell.isVisible() && !isPrimary) {
|
||||
numHiddenFields++;
|
||||
}
|
||||
}
|
||||
|
||||
add(
|
||||
RowDetailEvent.didReceiveCellDatas(
|
||||
visibleCells,
|
||||
allCells,
|
||||
numHiddenFields,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
FieldBackendService _fieldBackendService(String fieldId) {
|
||||
return FieldBackendService(
|
||||
viewId: rowController.viewId,
|
||||
fieldId: fieldId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _reorderField(
|
||||
String fieldId,
|
||||
String reorderedFieldId,
|
||||
String targetFieldId,
|
||||
int fromIndex,
|
||||
int toIndex,
|
||||
Emitter<RowDetailState> emit,
|
||||
) async {
|
||||
final cells = List<DatabaseCellContext>.from(state.cells);
|
||||
final cells = List<DatabaseCellContext>.from(state.visibleCells);
|
||||
cells.insert(toIndex, cells.removeAt(fromIndex));
|
||||
emit(state.copyWith(cells: cells));
|
||||
emit(state.copyWith(visibleCells: cells));
|
||||
|
||||
final fieldService =
|
||||
FieldBackendService(viewId: rowController.viewId, fieldId: fieldId);
|
||||
final fromIndexInAllFields =
|
||||
state.allCells.indexWhere((cell) => cell.fieldId == reorderedFieldId);
|
||||
final toIndexInAllFields =
|
||||
state.allCells.indexWhere((cell) => cell.fieldId == targetFieldId);
|
||||
|
||||
final fieldService = FieldBackendService(
|
||||
viewId: rowController.viewId,
|
||||
fieldId: reorderedFieldId,
|
||||
);
|
||||
final result = await fieldService.moveField(
|
||||
fromIndex,
|
||||
toIndex,
|
||||
fromIndexInAllFields,
|
||||
toIndexInAllFields,
|
||||
);
|
||||
result.fold((l) {}, (err) => Log.error(err));
|
||||
}
|
||||
@ -110,25 +158,54 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
class RowDetailEvent with _$RowDetailEvent {
|
||||
const factory RowDetailEvent.initial() = _Initial;
|
||||
const factory RowDetailEvent.deleteField(String fieldId) = _DeleteField;
|
||||
const factory RowDetailEvent.showField(String fieldId) = _ShowField;
|
||||
const factory RowDetailEvent.hideField(String fieldId) = _HideField;
|
||||
const factory RowDetailEvent.toggleFieldVisibility(String fieldId) =
|
||||
_ToggleFieldVisibility;
|
||||
const factory RowDetailEvent.reorderField(
|
||||
String fieldId,
|
||||
String reorderFieldID,
|
||||
String targetFieldID,
|
||||
int fromIndex,
|
||||
int toIndex,
|
||||
) = _ReorderField;
|
||||
const factory RowDetailEvent.toggleHiddenFieldVisibility() =
|
||||
_ToggleHiddenFieldVisibility;
|
||||
const factory RowDetailEvent.didReceiveCellDatas(
|
||||
List<DatabaseCellContext> gridCells,
|
||||
List<DatabaseCellContext> visibleCells,
|
||||
List<DatabaseCellContext> allCells,
|
||||
int numHiddenFields,
|
||||
) = _DidReceiveCellDatas;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RowDetailState with _$RowDetailState {
|
||||
const factory RowDetailState({
|
||||
required List<DatabaseCellContext> cells,
|
||||
required List<DatabaseCellContext> visibleCells,
|
||||
required List<DatabaseCellContext> allCells,
|
||||
required bool showHiddenFields,
|
||||
required int numHiddenFields,
|
||||
}) = _RowDetailState;
|
||||
|
||||
factory RowDetailState.initial() => RowDetailState(
|
||||
cells: List.empty(),
|
||||
);
|
||||
factory RowDetailState.initial(CellContextByFieldId cellByFieldId) {
|
||||
final allCells = cellByFieldId.values.toList();
|
||||
int numHiddenFields = 0;
|
||||
final visibleCells = <DatabaseCellContext>[];
|
||||
for (final cell in allCells) {
|
||||
final isVisible = cell.isVisible();
|
||||
final isPrimary = cell.fieldInfo.isPrimary;
|
||||
|
||||
if (isVisible && !isPrimary) {
|
||||
visibleCells.add(cell);
|
||||
}
|
||||
|
||||
if (!isVisible && !isPrimary) {
|
||||
numHiddenFields++;
|
||||
}
|
||||
}
|
||||
|
||||
return RowDetailState(
|
||||
visibleCells: visibleCells,
|
||||
allCells: allCells,
|
||||
showHiddenFields: false,
|
||||
numHiddenFields: numHiddenFields,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ class _GridFieldCellState extends State<GridFieldCell> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) {
|
||||
return FieldCellBloc(cellContext: widget.cellContext);
|
||||
return FieldCellBloc(fieldContext: widget.cellContext);
|
||||
},
|
||||
child: BlocBuilder<FieldCellBloc, FieldCellState>(
|
||||
builder: (context, state) {
|
||||
@ -54,7 +54,7 @@ class _GridFieldCellState extends State<GridFieldCell> {
|
||||
);
|
||||
},
|
||||
child: FieldCellButton(
|
||||
field: widget.cellContext.field,
|
||||
field: widget.cellContext.fieldInfo.field,
|
||||
onTap: () => popoverController.show(),
|
||||
),
|
||||
);
|
||||
|
@ -34,14 +34,14 @@ class _GridFieldCellActionSheetState extends State<GridFieldCellActionSheet> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_showFieldEditor) {
|
||||
final field = widget.cellContext.field;
|
||||
return SizedBox(
|
||||
width: 400,
|
||||
child: FieldEditor(
|
||||
viewId: widget.cellContext.viewId,
|
||||
fieldInfo: widget.cellContext.fieldInfo,
|
||||
typeOptionLoader: FieldTypeOptionLoader(
|
||||
viewId: widget.cellContext.viewId,
|
||||
field: field,
|
||||
field: widget.cellContext.fieldInfo.field,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -96,8 +96,8 @@ class _EditFieldButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _FieldOperationList extends StatelessWidget {
|
||||
final FieldContext fieldInfo;
|
||||
const _FieldOperationList(this.fieldInfo, {Key? key}) : super(key: key);
|
||||
final FieldContext fieldContext;
|
||||
const _FieldOperationList(this.fieldContext, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -128,7 +128,7 @@ class _FieldOperationList extends StatelessWidget {
|
||||
bool enable = true;
|
||||
|
||||
// If the field is primary, delete and duplicate are disabled.
|
||||
if (fieldInfo.field.isPrimary) {
|
||||
if (fieldContext.fieldInfo.isPrimary) {
|
||||
switch (action) {
|
||||
case FieldAction.hide:
|
||||
break;
|
||||
@ -145,7 +145,7 @@ class _FieldOperationList extends StatelessWidget {
|
||||
child: SizedBox(
|
||||
height: GridSize.popoverItemHeight,
|
||||
child: FieldActionCell(
|
||||
fieldInfo: fieldInfo,
|
||||
fieldInfo: fieldContext,
|
||||
action: action,
|
||||
enable: enable,
|
||||
),
|
||||
@ -217,7 +217,7 @@ extension _FieldActionExtension on FieldAction {
|
||||
}
|
||||
}
|
||||
|
||||
void run(BuildContext context, FieldContext fieldInfo) {
|
||||
void run(BuildContext context, FieldContext fieldContext) {
|
||||
switch (this) {
|
||||
case FieldAction.hide:
|
||||
context
|
||||
@ -228,8 +228,8 @@ extension _FieldActionExtension on FieldAction {
|
||||
PopoverContainer.of(context).close();
|
||||
|
||||
FieldBackendService(
|
||||
viewId: fieldInfo.viewId,
|
||||
fieldId: fieldInfo.field.id,
|
||||
viewId: fieldContext.viewId,
|
||||
fieldId: fieldContext.fieldInfo.id,
|
||||
).duplicateField();
|
||||
|
||||
break;
|
||||
@ -240,8 +240,8 @@ extension _FieldActionExtension on FieldAction {
|
||||
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
|
||||
confirm: () {
|
||||
FieldBackendService(
|
||||
viewId: fieldInfo.viewId,
|
||||
fieldId: fieldInfo.field.id,
|
||||
viewId: fieldContext.viewId,
|
||||
fieldId: fieldContext.fieldInfo.field.id,
|
||||
).deleteField();
|
||||
},
|
||||
).show(context);
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_editor_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
@ -19,17 +21,19 @@ class FieldEditor extends StatefulWidget {
|
||||
final String viewId;
|
||||
final bool isGroupingField;
|
||||
final Function(String)? onDeleted;
|
||||
final Function(String)? onHidden;
|
||||
final Function(String)? onToggleVisibility;
|
||||
final FieldTypeOptionLoader typeOptionLoader;
|
||||
final FieldInfo? fieldInfo;
|
||||
|
||||
const FieldEditor({
|
||||
required this.viewId,
|
||||
required this.typeOptionLoader,
|
||||
this.fieldInfo,
|
||||
this.isGroupingField = false,
|
||||
this.onDeleted,
|
||||
this.onHidden,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
this.onToggleVisibility,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _FieldEditorState();
|
||||
@ -53,14 +57,14 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool requireSpace = widget.onDeleted != null ||
|
||||
widget.onHidden != null ||
|
||||
widget.onToggleVisibility != null ||
|
||||
!widget.typeOptionLoader.field.isPrimary;
|
||||
|
||||
final List<Widget> children = [
|
||||
FieldNameTextField(popoverMutex: popoverMutex),
|
||||
if (requireSpace) const VSpace(4),
|
||||
if (widget.onDeleted != null) _addDeleteFieldButton(),
|
||||
if (widget.onHidden != null) _addHideFieldButton(),
|
||||
if (widget.onToggleVisibility != null) _addHideFieldButton(),
|
||||
if (!widget.typeOptionLoader.field.isPrimary)
|
||||
FieldTypeOptionCell(popoverMutex: popoverMutex),
|
||||
];
|
||||
@ -88,7 +92,7 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
builder: (context, state) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: _DeleteFieldButton(
|
||||
child: DeleteFieldButton(
|
||||
popoverMutex: popoverMutex,
|
||||
onDeleted: () {
|
||||
state.field.fold(
|
||||
@ -107,12 +111,14 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
builder: (context, state) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: HideFieldButton(
|
||||
child: FieldVisibilityToggleButton(
|
||||
isFieldHidden:
|
||||
widget.fieldInfo!.visibility == FieldVisibility.AlwaysHidden,
|
||||
popoverMutex: popoverMutex,
|
||||
onHidden: () {
|
||||
onTap: () {
|
||||
state.field.fold(
|
||||
() => Log.error('Can not hidden the field'),
|
||||
(field) => widget.onHidden?.call(field.id),
|
||||
(field) => widget.onToggleVisibility?.call(field.id),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -218,15 +224,16 @@ class _FieldNameTextFieldState extends State<FieldNameTextField> {
|
||||
}
|
||||
}
|
||||
|
||||
class _DeleteFieldButton extends StatelessWidget {
|
||||
@visibleForTesting
|
||||
class DeleteFieldButton extends StatelessWidget {
|
||||
final PopoverMutex popoverMutex;
|
||||
final VoidCallback? onDeleted;
|
||||
|
||||
const _DeleteFieldButton({
|
||||
const DeleteFieldButton({
|
||||
required this.popoverMutex,
|
||||
required this.onDeleted,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -253,15 +260,17 @@ class _DeleteFieldButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
class HideFieldButton extends StatelessWidget {
|
||||
class FieldVisibilityToggleButton extends StatelessWidget {
|
||||
final bool isFieldHidden;
|
||||
final PopoverMutex popoverMutex;
|
||||
final VoidCallback? onHidden;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const HideFieldButton({
|
||||
const FieldVisibilityToggleButton({
|
||||
required this.isFieldHidden,
|
||||
required this.popoverMutex,
|
||||
required this.onHidden,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
required this.onTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -270,10 +279,13 @@ class HideFieldButton extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
final Widget button = FlowyButton(
|
||||
text: FlowyText.medium(
|
||||
LocaleKeys.grid_field_hide.tr(),
|
||||
isFieldHidden
|
||||
? LocaleKeys.grid_field_show.tr()
|
||||
: LocaleKeys.grid_field_hide.tr(),
|
||||
),
|
||||
leftIcon: const FlowySvg(FlowySvgs.hide_s),
|
||||
onTap: () => onHidden?.call(),
|
||||
leftIcon:
|
||||
FlowySvg(isFieldHidden ? FlowySvgs.show_m : FlowySvgs.hide_m),
|
||||
onTap: onTap,
|
||||
onHover: (_) => popoverMutex.close(),
|
||||
);
|
||||
return SizedBox(height: GridSize.popoverItemHeight, child: button);
|
||||
|
@ -98,12 +98,12 @@ class _GridHeaderState extends State<_GridHeader> {
|
||||
.map(
|
||||
(field) => FieldContext(
|
||||
viewId: widget.viewId,
|
||||
field: field.field,
|
||||
fieldInfo: field,
|
||||
),
|
||||
)
|
||||
.map(
|
||||
(ctx) => GridFieldCell(
|
||||
key: _getKeyById(ctx.field.id),
|
||||
key: _getKeyById(ctx.fieldInfo.id),
|
||||
cellContext: ctx,
|
||||
),
|
||||
)
|
||||
@ -136,7 +136,7 @@ class _GridHeaderState extends State<_GridHeader> {
|
||||
int newIndex,
|
||||
) {
|
||||
if (cells.length > oldIndex) {
|
||||
final field = cells[oldIndex].cellContext.field;
|
||||
final field = cells[oldIndex].cellContext.fieldInfo.field;
|
||||
context
|
||||
.read<GridHeaderBloc>()
|
||||
.add(GridHeaderEvent.moveField(field, oldIndex, newIndex));
|
||||
|
@ -93,6 +93,8 @@ List<DatabaseCellContext> _makeCells(
|
||||
CellContextByFieldId originalCellMap,
|
||||
) {
|
||||
final List<DatabaseCellContext> cells = [];
|
||||
originalCellMap
|
||||
.removeWhere((fieldId, cellContext) => !cellContext.isVisible());
|
||||
for (final entry in originalCellMap.entries) {
|
||||
// Filter out the cell if it's fieldId equal to the groupFieldId
|
||||
if (groupFieldId != null) {
|
||||
|
@ -151,6 +151,7 @@ class _GridPropertyCellState extends State<GridPropertyCell> {
|
||||
popupBuilder: (BuildContext context) {
|
||||
return FieldEditor(
|
||||
viewId: widget.viewId,
|
||||
fieldInfo: widget.fieldInfo,
|
||||
typeOptionLoader: FieldTypeOptionLoader(
|
||||
viewId: widget.viewId,
|
||||
field: widget.fieldInfo.field,
|
||||
|
@ -37,9 +37,8 @@ class RowPropertyList extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<RowDetailBloc, RowDetailState>(
|
||||
buildWhen: (previous, current) => previous.cells != current.cells,
|
||||
builder: (context, state) {
|
||||
final children = state.cells
|
||||
final children = state.visibleCells
|
||||
.where((element) => !element.fieldInfo.field.isPrimary)
|
||||
.mapIndexed(
|
||||
(index, cell) => _PropertyCell(
|
||||
@ -50,18 +49,26 @@ class RowPropertyList extends StatelessWidget {
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return ReorderableListView(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
final reorderedField = children[oldIndex].cellContext.fieldId;
|
||||
_reorderField(
|
||||
context,
|
||||
state.cells,
|
||||
reorderedField,
|
||||
oldIndex,
|
||||
newIndex,
|
||||
);
|
||||
// when reorderiing downwards, need to update index
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex--;
|
||||
}
|
||||
final reorderedFieldId = children[oldIndex].cellContext.fieldId;
|
||||
final targetFieldId = children[newIndex].cellContext.fieldId;
|
||||
|
||||
context.read<RowDetailBloc>().add(
|
||||
RowDetailEvent.reorderField(
|
||||
reorderedFieldId,
|
||||
targetFieldId,
|
||||
oldIndex,
|
||||
newIndex,
|
||||
),
|
||||
);
|
||||
},
|
||||
buildDefaultDragHandles: false,
|
||||
proxyDecorator: (child, index, animation) => Material(
|
||||
@ -84,41 +91,22 @@ class RowPropertyList extends StatelessWidget {
|
||||
),
|
||||
footer: Padding(
|
||||
padding: const EdgeInsets.only(left: 20),
|
||||
child: CreateRowFieldButton(viewId: viewId),
|
||||
child: Column(
|
||||
children: [
|
||||
if (context.read<RowDetailBloc>().state.numHiddenFields != 0)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(bottom: 4.0),
|
||||
child: ToggleHiddenFieldsVisibilityButton(),
|
||||
),
|
||||
CreateRowFieldButton(viewId: viewId),
|
||||
],
|
||||
),
|
||||
),
|
||||
children: children,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _reorderField(
|
||||
BuildContext context,
|
||||
List<DatabaseCellContext> cells,
|
||||
String reorderedFieldId,
|
||||
int oldIndex,
|
||||
int newIndex,
|
||||
) {
|
||||
// when reorderiing downwards, need to update index
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex--;
|
||||
}
|
||||
|
||||
// also update index when the index is after the index of the primary field
|
||||
// in the original list of DatabaseCellContext's
|
||||
final primaryFieldIndex =
|
||||
cells.indexWhere((element) => element.fieldInfo.isPrimary);
|
||||
if (oldIndex >= primaryFieldIndex) {
|
||||
oldIndex++;
|
||||
}
|
||||
if (newIndex >= primaryFieldIndex) {
|
||||
newIndex++;
|
||||
}
|
||||
|
||||
context.read<RowDetailBloc>().add(
|
||||
RowDetailEvent.reorderField(reorderedFieldId, oldIndex, newIndex),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PropertyCell extends StatefulWidget {
|
||||
@ -208,14 +196,17 @@ class _PropertyCellState extends State<_PropertyCell> {
|
||||
Widget buildFieldEditor() {
|
||||
return FieldEditor(
|
||||
viewId: widget.cellContext.viewId,
|
||||
fieldInfo: widget.cellContext.fieldInfo,
|
||||
isGroupingField: widget.cellContext.fieldInfo.isGroupField,
|
||||
typeOptionLoader: FieldTypeOptionLoader(
|
||||
viewId: widget.cellContext.viewId,
|
||||
field: widget.cellContext.fieldInfo.field,
|
||||
),
|
||||
onHidden: (fieldId) {
|
||||
onToggleVisibility: (fieldId) {
|
||||
_popoverController.close();
|
||||
context.read<RowDetailBloc>().add(RowDetailEvent.hideField(fieldId));
|
||||
context
|
||||
.read<RowDetailBloc>()
|
||||
.add(RowDetailEvent.toggleFieldVisibility(fieldId));
|
||||
},
|
||||
onDeleted: (fieldId) {
|
||||
_popoverController.close();
|
||||
@ -288,6 +279,43 @@ GridCellStyle? _customCellStyle(FieldType fieldType) {
|
||||
throw UnimplementedError;
|
||||
}
|
||||
|
||||
class ToggleHiddenFieldsVisibilityButton extends StatelessWidget {
|
||||
const ToggleHiddenFieldsVisibilityButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<RowDetailBloc, RowDetailState>(
|
||||
builder: (context, state) {
|
||||
final text = switch (state.showHiddenFields) {
|
||||
false => LocaleKeys.grid_rowPage_showHiddenFields
|
||||
.plural(state.numHiddenFields),
|
||||
true => LocaleKeys.grid_rowPage_hideHiddenFields
|
||||
.plural(state.numHiddenFields),
|
||||
};
|
||||
|
||||
return SizedBox(
|
||||
height: 30,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(text, color: Theme.of(context).hintColor),
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
leftIcon: RotatedBox(
|
||||
quarterTurns: state.showHiddenFields ? 1 : 3,
|
||||
child: FlowySvg(
|
||||
FlowySvgs.arrow_left_s,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
|
||||
onTap: () => context.read<RowDetailBloc>().add(
|
||||
const RowDetailEvent.toggleHiddenFieldVisibility(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CreateRowFieldButton extends StatefulWidget {
|
||||
final String viewId;
|
||||
|
||||
|
@ -140,8 +140,8 @@ class BoardTestContext {
|
||||
}
|
||||
|
||||
FieldContext singleSelectFieldCellContext() {
|
||||
final field = singleSelectFieldContext().field;
|
||||
return FieldContext(viewId: gridView.id, field: field);
|
||||
final fieldInfo = singleSelectFieldContext();
|
||||
return FieldContext(viewId: gridView.id, fieldInfo: fieldInfo);
|
||||
}
|
||||
|
||||
FieldInfo textFieldContext() {
|
||||
|
@ -23,8 +23,8 @@ void main() {
|
||||
blocTest(
|
||||
'update field width',
|
||||
build: () => FieldCellBloc(
|
||||
cellContext: FieldContext(
|
||||
field: context.fieldContexts[0].field,
|
||||
fieldContext: FieldContext(
|
||||
fieldInfo: context.fieldContexts[0],
|
||||
viewId: context.gridView.id,
|
||||
),
|
||||
)..add(const FieldCellEvent.initial()),
|
||||
@ -42,8 +42,8 @@ void main() {
|
||||
blocTest(
|
||||
'field width should not be lesser than 50px',
|
||||
build: () => FieldCellBloc(
|
||||
cellContext: FieldContext(
|
||||
field: context.fieldContexts[0].field,
|
||||
fieldContext: FieldContext(
|
||||
fieldInfo: context.fieldContexts[0],
|
||||
viewId: context.gridView.id,
|
||||
),
|
||||
)..add(const FieldCellEvent.initial()),
|
||||
|
@ -89,8 +89,8 @@ class GridTestContext {
|
||||
}
|
||||
|
||||
FieldContext singleSelectFieldCellContext() {
|
||||
final field = singleSelectFieldContext().field;
|
||||
return FieldContext(viewId: gridView.id, field: field);
|
||||
final fieldInfo = singleSelectFieldContext();
|
||||
return FieldContext(viewId: gridView.id, fieldInfo: fieldInfo);
|
||||
}
|
||||
|
||||
FieldInfo textFieldContext() {
|
||||
|
@ -411,6 +411,7 @@
|
||||
},
|
||||
"field": {
|
||||
"hide": "Hide",
|
||||
"show": "Show",
|
||||
"insertLeft": "Insert Left",
|
||||
"insertRight": "Insert Right",
|
||||
"duplicate": "Duplicate",
|
||||
@ -447,6 +448,19 @@
|
||||
"deleteFieldPromptMessage": "Are you sure? This property will be deleted",
|
||||
"newColumn": "New Column"
|
||||
},
|
||||
"rowPage": {
|
||||
"newField": "Add a new field",
|
||||
"showHiddenFields": {
|
||||
"one": "Show {} hidden field",
|
||||
"many": "Show {} hidden fields",
|
||||
"other": "Show {} hidden fields"
|
||||
},
|
||||
"hideHiddenFields": {
|
||||
"one": "Hide {} hidden field",
|
||||
"many": "Hide {} hidden fields",
|
||||
"other": "Hide {} hidden fields"
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"ascending": "Ascending",
|
||||
"descending": "Descending",
|
||||
|
Loading…
Reference in New Issue
Block a user