fix: various grid ui issues (#6182)

* fix: delete field confirmation dialog only closes top most popover

* fix: prioritize single-line checklist items

* chore: wrap text toggle persist

* test: update integration tests

* chore: delete conflicting outputs on freezed

* chore: slightly make field editor faster

* chore: use standard dialog componet

* chore: enable multiline checklist tasks on mobile

* chore: Update frontend/appflowy_flutter/lib/plugins/database/application/field/field_editor_bloc.dart

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>

* chore: code cleanup

* fix: create field from row detail and add test

* chore: allow opening related database from editor

* test: integration test flake

---------

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>
This commit is contained in:
Richard Shiue 2024-09-05 13:54:50 +08:00 committed by GitHub
parent aa621289e9
commit 0fd0483302
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 325 additions and 307 deletions

View File

@ -22,7 +22,7 @@ void main() {
const fieldName = "test change field"; const fieldName = "test change field";
await tester.createField( await tester.createField(
FieldType.RichText, FieldType.RichText,
fieldName, name: fieldName,
layout: ViewLayoutPB.Board, layout: ViewLayoutPB.Board,
); );
await tester.tapButton(card1); await tester.tapButton(card1);

View File

@ -1,3 +1,4 @@
import 'package:appflowy/util/field_type_extension.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
@ -41,7 +42,7 @@ void main() {
name: 'my grid', name: 'my grid',
layout: ViewLayoutPB.Grid, layout: ViewLayoutPB.Grid,
); );
await tester.createField(FieldType.RichText, 'description'); await tester.createField(FieldType.RichText, name: 'description');
await tester.editCell( await tester.editCell(
rowIndex: 0, rowIndex: 0,
@ -81,7 +82,7 @@ void main() {
const fieldType = FieldType.Number; const fieldType = FieldType.Number;
// Create a number field // Create a number field
await tester.createField(fieldType, fieldType.name); await tester.createField(fieldType);
await tester.editCell( await tester.editCell(
rowIndex: 0, rowIndex: 0,
@ -157,7 +158,7 @@ void main() {
const fieldType = FieldType.CreatedTime; const fieldType = FieldType.CreatedTime;
// Create a create time field // Create a create time field
// The create time field is not editable // The create time field is not editable
await tester.createField(fieldType, fieldType.name); await tester.createField(fieldType);
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType); await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
@ -175,7 +176,7 @@ void main() {
const fieldType = FieldType.LastEditedTime; const fieldType = FieldType.LastEditedTime;
// Create a last time field // Create a last time field
// The last time field is not editable // The last time field is not editable
await tester.createField(fieldType, fieldType.name); await tester.createField(fieldType);
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType); await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
@ -191,7 +192,7 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.DateTime; const fieldType = FieldType.DateTime;
await tester.createField(fieldType, fieldType.name); await tester.createField(fieldType);
// Tap the cell to invoke the field editor // Tap the cell to invoke the field editor
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType); await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
@ -366,7 +367,7 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.MultiSelect; const fieldType = FieldType.MultiSelect;
await tester.createField(fieldType, fieldType.name); await tester.createField(fieldType, name: fieldType.i18n);
// Tap the cell to invoke the selection option editor // Tap the cell to invoke the selection option editor
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType); await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
@ -449,7 +450,7 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.Checklist; const fieldType = FieldType.Checklist;
await tester.createField(fieldType, fieldType.name); await tester.createField(fieldType);
// assert that there is no progress bar in the grid // assert that there is no progress bar in the grid
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null); tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);

View File

@ -1,12 +1,13 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart'; import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
import 'package:appflowy/plugins/database/widgets/field/type_option_editor/select/select_option.dart'; import 'package:appflowy/plugins/database/widgets/field/type_option_editor/select/select_option.dart';
import 'package:appflowy/util/field_type_extension.dart'; import 'package:appflowy/util/field_type_extension.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
import 'package:intl/intl.dart';
import '../../shared/database_test_op.dart'; import '../../shared/database_test_op.dart';
import '../../shared/util.dart'; import '../../shared/util.dart';
@ -56,11 +57,22 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// create a field // create a field
await tester.createField(FieldType.Checklist, 'checklist'); await tester.createField(FieldType.Checklist);
tester.findFieldWithName(FieldType.Checklist.i18n);
// check the field is created successfully // editing field type during field creation should change title
tester.findFieldWithName('checklist'); await tester.createField(FieldType.MultiSelect);
await tester.pumpAndSettle(); tester.findFieldWithName(FieldType.MultiSelect.i18n);
// not if the user changes the title manually though
const name = "New field";
await tester.createField(FieldType.DateTime);
await tester.tapGridFieldWithName(FieldType.DateTime.i18n);
await tester.renameField(name);
await tester.tapEditFieldButton();
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(FieldType.URL);
tester.findFieldWithName(name);
}); });
testWidgets('delete field', (tester) async { testWidgets('delete field', (tester) async {
@ -70,14 +82,14 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// create a field // create a field
await tester.createField(FieldType.Checkbox, 'New field 1'); await tester.createField(FieldType.Checkbox, name: 'New field 1');
// Delete the field // Delete the field
await tester.tapGridFieldWithName('New field 1'); await tester.tapGridFieldWithName('New field 1');
await tester.tapDeletePropertyButton(); await tester.tapDeletePropertyButton();
// confirm delete // confirm delete
await tester.tapDialogOkButton(); await tester.tapButtonWithName(LocaleKeys.space_delete.tr());
tester.noFieldWithName('New field 1'); tester.noFieldWithName('New field 1');
await tester.pumpAndSettle(); await tester.pumpAndSettle();
@ -90,10 +102,7 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// create a field // create a field
await tester.scrollToRight(find.byType(GridPage)); await tester.createField(FieldType.RichText, name: 'New field 1');
await tester.tapNewPropertyButton();
await tester.renameField('New field 1');
await tester.dismissFieldEditor();
// duplicate the field // duplicate the field
await tester.tapGridFieldWithName('New field 1'); await tester.tapGridFieldWithName('New field 1');
@ -126,26 +135,6 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
testWidgets('create checklist field', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.scrollToRight(find.byType(GridPage));
await tester.tapNewPropertyButton();
// Open the type option menu
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(FieldType.Checklist);
// After update the field type, the cells should be updated
await tester.findCellByFieldType(FieldType.Checklist);
await tester.pumpAndSettle();
});
testWidgets('create list of fields', (tester) async { testWidgets('create list of fields', (tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton(); await tester.tapAnonymousSignInButton();
@ -162,18 +151,10 @@ void main() {
FieldType.CreatedTime, FieldType.CreatedTime,
FieldType.Checkbox, FieldType.Checkbox,
]) { ]) {
await tester.scrollToRight(find.byType(GridPage)); await tester.createField(fieldType);
await tester.tapNewPropertyButton();
await tester.renameField(fieldType.name);
// Open the type option menu
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(fieldType);
await tester.dismissFieldEditor();
// After update the field type, the cells should be updated // After update the field type, the cells should be updated
await tester.findCellByFieldType(fieldType); tester.findCellByFieldType(fieldType);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
} }
}); });
@ -190,15 +171,7 @@ void main() {
FieldType.Checklist, FieldType.Checklist,
FieldType.URL, FieldType.URL,
]) { ]) {
// create the field await tester.createField(fieldType);
await tester.scrollToRight(find.byType(GridPage));
await tester.tapNewPropertyButton();
await tester.renameField(fieldType.i18n);
// change field type
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(fieldType);
await tester.dismissFieldEditor();
// open the field editor // open the field editor
await tester.tapGridFieldWithName(fieldType.i18n); await tester.tapGridFieldWithName(fieldType.i18n);
@ -218,11 +191,7 @@ void main() {
await tester.scrollToRight(find.byType(GridPage)); await tester.scrollToRight(find.byType(GridPage));
// create a number field // create a number field
await tester.tapNewPropertyButton(); await tester.createField(FieldType.Number);
await tester.renameField("Number");
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(FieldType.Number);
await tester.dismissFieldEditor();
// enter some data into the first number cell // enter some data into the first number cell
await tester.editCell( await tester.editCell(
@ -243,7 +212,7 @@ void main() {
); );
// open editor and change number format // open editor and change number format
await tester.tapGridFieldWithName('Number'); await tester.tapGridFieldWithName(FieldType.Number.i18n);
await tester.tapEditFieldButton(); await tester.tapEditFieldButton();
await tester.changeNumberFieldFormat(); await tester.changeNumberFieldFormat();
await tester.dismissFieldEditor(); await tester.dismissFieldEditor();
@ -292,11 +261,7 @@ void main() {
await tester.scrollToRight(find.byType(GridPage)); await tester.scrollToRight(find.byType(GridPage));
// create a date field // create a date field
await tester.tapNewPropertyButton(); await tester.createField(FieldType.DateTime);
await tester.renameField(FieldType.DateTime.i18n);
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(FieldType.DateTime);
await tester.dismissFieldEditor();
// edit the first date cell // edit the first date cell
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime); await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);

View File

@ -1,3 +1,6 @@
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/desktop_field_cell.dart';
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
import 'package:appflowy/util/field_type_extension.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:appflowy/plugins/database/widgets/row/row_banner.dart'; import 'package:appflowy/plugins/database/widgets/row/row_banner.dart';
@ -121,15 +124,24 @@ void main() {
FieldType.Checkbox, FieldType.Checkbox,
]) { ]) {
await tester.tapRowDetailPageCreatePropertyButton(); await tester.tapRowDetailPageCreatePropertyButton();
await tester.renameField(fieldType.name);
// Open the type option menu // Open the type option menu
await tester.tapSwitchFieldTypeButton(); await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(fieldType); await tester.selectFieldType(fieldType);
final field = find.descendant(
of: find.byType(RowDetailPage),
matching: find.byWidgetPredicate(
(widget) =>
widget is FieldCellButton &&
widget.field.name == fieldType.i18n,
),
);
expect(field, findsOneWidget);
// After update the field type, the cells should be updated // After update the field type, the cells should be updated
await tester.findCellByFieldType(fieldType); tester.findCellByFieldType(fieldType);
await tester.scrollRowDetailByOffset(const Offset(0, -50)); await tester.scrollRowDetailByOffset(const Offset(0, -50));
} }
}); });

View File

@ -74,7 +74,6 @@ import 'package:appflowy/util/field_type_extension.dart';
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/clear_date_button.dart'; import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/clear_date_button.dart';
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/date_type_option_button.dart'; import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/date_type_option_button.dart';
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/reminder_selector.dart'; import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/reminder_selector.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/pop_up_action.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart'; import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
@ -476,7 +475,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
await pumpAndSettle(); await pumpAndSettle();
if (enter) { if (enter) {
await testTextInput.receiveAction(TextInputAction.done); await testTextInput.receiveAction(TextInputAction.done);
await pumpAndSettle(); await pumpAndSettle(const Duration(milliseconds: 500));
} else { } else {
await tapButton( await tapButton(
find.descendant( find.descendant(
@ -629,12 +628,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
(w) => w is FieldActionCell && w.action == FieldAction.delete, (w) => w is FieldActionCell && w.action == FieldAction.delete,
); );
await tapButton(deleteButton); await tapButton(deleteButton);
await tapButtonWithName(LocaleKeys.space_delete.tr());
final confirmButton = find.descendant(
of: find.byType(NavigatorAlertDialog),
matching: find.byType(PrimaryTextButton),
);
await tapButton(confirmButton);
} }
Future<void> scrollRowDetailByOffset(Offset offset) async { Future<void> scrollRowDetailByOffset(Offset offset) async {
@ -788,7 +782,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
/// Each field has its own cell, so we can find the corresponding cell by /// Each field has its own cell, so we can find the corresponding cell by
/// the field type after create a new field. /// the field type after create a new field.
Future<void> findCellByFieldType(FieldType fieldType) async { void findCellByFieldType(FieldType fieldType) {
final finder = finderForFieldType(fieldType); final finder = finderForFieldType(fieldType);
expect(finder, findsWidgets); expect(finder, findsWidgets);
} }
@ -894,18 +888,19 @@ extension AppFlowyDatabaseTest on WidgetTester {
} }
Future<void> createField( Future<void> createField(
FieldType fieldType, FieldType fieldType, {
String name, { String? name,
ViewLayoutPB layout = ViewLayoutPB.Grid, ViewLayoutPB layout = ViewLayoutPB.Grid,
}) async { }) async {
if (layout == ViewLayoutPB.Grid) { if (layout == ViewLayoutPB.Grid) {
await scrollToRight(find.byType(GridPage)); await scrollToRight(find.byType(GridPage));
} }
await tapNewPropertyButton(); await tapNewPropertyButton();
await renameField(name); if (name != null) {
await renameField(name);
}
await tapSwitchFieldTypeButton(); await tapSwitchFieldTypeButton();
await selectFieldType(fieldType); await selectFieldType(fieldType);
await dismissFieldEditor();
} }
Future<void> tapDatabaseSettingButton() async { Future<void> tapDatabaseSettingButton() async {

View File

@ -62,7 +62,7 @@ class _QuickEditFieldState extends State<QuickEditField> {
create: (_) => FieldEditorBloc( create: (_) => FieldEditorBloc(
viewId: widget.viewId, viewId: widget.viewId,
fieldController: widget.fieldController, fieldController: widget.fieldController,
field: widget.fieldInfo.field, fieldInfo: widget.fieldInfo,
isNew: false, isNew: false,
), ),
child: BlocConsumer<FieldEditorBloc, FieldEditorState>( child: BlocConsumer<FieldEditorBloc, FieldEditorState>(

View File

@ -18,32 +18,33 @@ part 'field_editor_bloc.freezed.dart';
class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> { class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
FieldEditorBloc({ FieldEditorBloc({
required this.viewId, required this.viewId,
required this.fieldInfo,
required this.fieldController, required this.fieldController,
this.onFieldInserted, this.onFieldInserted,
required FieldPB field,
required this.isNew, required this.isNew,
}) : fieldId = field.id, }) : _fieldService = FieldBackendService(
fieldService = FieldBackendService(
viewId: viewId, viewId: viewId,
fieldId: field.id, fieldId: fieldInfo.id,
), ),
fieldSettingsService = FieldSettingsBackendService(viewId: viewId), fieldSettingsService = FieldSettingsBackendService(viewId: viewId),
super(FieldEditorState(field: FieldInfo.initial(field))) { super(FieldEditorState(field: fieldInfo)) {
_dispatch(); _dispatch();
_startListening(); _startListening();
_init(); _init();
} }
final String viewId; final String viewId;
final String fieldId; final FieldInfo fieldInfo;
final bool isNew; final bool isNew;
final FieldController fieldController; final FieldController fieldController;
final FieldBackendService fieldService; final FieldBackendService _fieldService;
final FieldSettingsBackendService fieldSettingsService; final FieldSettingsBackendService fieldSettingsService;
final void Function(String newFieldId)? onFieldInserted; final void Function(String newFieldId)? onFieldInserted;
late final OnReceiveField _listener; late final OnReceiveField _listener;
String get fieldId => fieldInfo.id;
@override @override
Future<void> close() { Future<void> close() {
fieldController.removeSingleFieldListener( fieldController.removeSingleFieldListener(
@ -66,13 +67,13 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
fieldName = fieldType.i18n; fieldName = fieldType.i18n;
} }
await fieldService.updateType( await _fieldService.updateType(
fieldType: fieldType, fieldType: fieldType,
fieldName: fieldName, fieldName: fieldName,
); );
}, },
renameField: (newName) async { renameField: (newName) async {
final result = await fieldService.updateField(name: newName); final result = await _fieldService.updateField(name: newName);
_logIfError(result); _logIfError(result);
emit(state.copyWith(wasRenameManually: true)); emit(state.copyWith(wasRenameManually: true));
}, },
@ -85,14 +86,14 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
_logIfError(result); _logIfError(result);
}, },
insertLeft: () async { insertLeft: () async {
final result = await fieldService.createBefore(); final result = await _fieldService.createBefore();
result.fold( result.fold(
(newField) => onFieldInserted?.call(newField.id), (newField) => onFieldInserted?.call(newField.id),
(err) => Log.error("Failed creating field $err"), (err) => Log.error("Failed creating field $err"),
); );
}, },
insertRight: () async { insertRight: () async {
final result = await fieldService.createAfter(); final result = await _fieldService.createAfter();
result.fold( result.fold(
(newField) => onFieldInserted?.call(newField.id), (newField) => onFieldInserted?.call(newField.id),
(err) => Log.error("Failed creating field $err"), (err) => Log.error("Failed creating field $err"),
@ -106,7 +107,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
? FieldVisibility.AlwaysShown ? FieldVisibility.AlwaysShown
: FieldVisibility.AlwaysHidden; : FieldVisibility.AlwaysHidden;
final result = await fieldSettingsService.updateFieldSettings( final result = await fieldSettingsService.updateFieldSettings(
fieldId: state.field.id, fieldId: fieldId,
fieldVisibility: newVisibility, fieldVisibility: newVisibility,
); );
_logIfError(result); _logIfError(result);

View File

@ -82,6 +82,15 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
), ),
); );
}, },
startEditingField: (fieldId) {
emit(state.copyWith(editingFieldId: fieldId));
},
startEditingNewField: (fieldId) {
emit(state.copyWith(editingFieldId: fieldId, newFieldId: fieldId));
},
endEditingField: () {
emit(state.copyWith(editingFieldId: "", newFieldId: ""));
},
); );
}, },
); );
@ -219,6 +228,16 @@ class RowDetailEvent with _$RowDetailEvent {
/// Used to hide/show the hidden fields in the row detail page /// Used to hide/show the hidden fields in the row detail page
const factory RowDetailEvent.toggleHiddenFieldVisibility() = const factory RowDetailEvent.toggleHiddenFieldVisibility() =
_ToggleHiddenFieldVisibility; _ToggleHiddenFieldVisibility;
/// Begin editing an event;
const factory RowDetailEvent.startEditingField(String fieldId) =
_StartEditingField;
const factory RowDetailEvent.startEditingNewField(String fieldId) =
_StartEditingNewField;
/// End editing an event
const factory RowDetailEvent.endEditingField() = _EndEditingField;
} }
@freezed @freezed
@ -228,6 +247,8 @@ class RowDetailState with _$RowDetailState {
required List<CellContext> visibleCells, required List<CellContext> visibleCells,
required bool showHiddenFields, required bool showHiddenFields,
required int numHiddenFields, required int numHiddenFields,
required String editingFieldId,
required String newFieldId,
}) = _RowDetailState; }) = _RowDetailState;
factory RowDetailState.initial() => const RowDetailState( factory RowDetailState.initial() => const RowDetailState(
@ -235,5 +256,7 @@ class RowDetailState with _$RowDetailState {
visibleCells: [], visibleCells: [],
showHiddenFields: false, showHiddenFields: false,
numHiddenFields: 0, numHiddenFields: 0,
editingFieldId: "",
newFieldId: "",
); );
} }

View File

@ -86,7 +86,7 @@ class _GridFieldCellState extends State<GridFieldCell> {
return FieldEditor( return FieldEditor(
viewId: widget.viewId, viewId: widget.viewId,
fieldController: widget.fieldController, fieldController: widget.fieldController,
field: widget.fieldInfo.field, fieldInfo: widget.fieldInfo,
isNewField: widget.isNew, isNewField: widget.isNew,
initialPage: widget.isNew initialPage: widget.isNew
? FieldEditorPage.details ? FieldEditorPage.details

View File

@ -174,7 +174,7 @@ class _CellTrailing extends StatelessWidget {
} }
} }
class CreateFieldButton extends StatefulWidget { class CreateFieldButton extends StatelessWidget {
const CreateFieldButton({ const CreateFieldButton({
super.key, super.key,
required this.viewId, required this.viewId,
@ -184,11 +184,6 @@ class CreateFieldButton extends StatefulWidget {
final String viewId; final String viewId;
final void Function(String fieldId) onFieldCreated; final void Function(String fieldId) onFieldCreated;
@override
State<CreateFieldButton> createState() => _CreateFieldButtonState();
}
class _CreateFieldButtonState extends State<CreateFieldButton> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FlowyButton( return FlowyButton(
@ -202,10 +197,10 @@ class _CreateFieldButtonState extends State<CreateFieldButton> {
hoverColor: AFThemeExtension.of(context).greyHover, hoverColor: AFThemeExtension.of(context).greyHover,
onTap: () async { onTap: () async {
final result = await FieldBackendService.createField( final result = await FieldBackendService.createField(
viewId: widget.viewId, viewId: viewId,
); );
result.fold( result.fold(
(field) => widget.onFieldCreated(field.id), (field) => onFieldCreated(field.id),
(err) => Log.error("Failed to create field type option: $err"), (err) => Log.error("Failed to create field type option: $err"),
); );
}, },

View File

@ -174,6 +174,8 @@ class _ChecklistItemState extends State<ChecklistItem> {
meta: Platform.isMacOS, meta: Platform.isMacOS,
control: !Platform.isMacOS, control: !Platform.isMacOS,
): const _SelectTaskIntent(), ): const _SelectTaskIntent(),
const SingleActivator(LogicalKeyboardKey.enter):
const _EndEditingTaskIntent(),
const SingleActivator(LogicalKeyboardKey.escape): const SingleActivator(LogicalKeyboardKey.escape):
const _EndEditingTaskIntent(), const _EndEditingTaskIntent(),
}; };

View File

@ -138,7 +138,7 @@ class _ChecklistItemState extends State<_ChecklistItem> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 5), padding: const EdgeInsets.symmetric(horizontal: 5),
height: 44, constraints: const BoxConstraints(minHeight: 44),
child: Row( child: Row(
children: [ children: [
InkWell( InkWell(
@ -164,6 +164,8 @@ class _ChecklistItemState extends State<_ChecklistItem> {
controller: _textController, controller: _textController,
focusNode: _focusNode, focusNode: _focusNode,
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
enabledBorder: InputBorder.none, enabledBorder: InputBorder.none,

View File

@ -2,8 +2,15 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/application/field/type_option/relation_type_option_cubit.dart'; import 'package:appflowy/plugins/database/application/field/type_option/relation_type_option_cubit.dart';
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart';
import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart';
import 'package:appflowy/plugins/database/widgets/row/relation_row_detail.dart'; import 'package:appflowy/plugins/database/widgets/row/relation_row_detail.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -126,7 +133,7 @@ class _RelationCellEditorContentState
shrinkWrap: true, shrinkWrap: true,
slivers: [ slivers: [
_CellEditorTitle( _CellEditorTitle(
databaseName: widget.relatedDatabaseMeta.databaseName, databaseMeta: widget.relatedDatabaseMeta,
), ),
_SearchField( _SearchField(
focusNode: focusNode, focusNode: focusNode,
@ -204,10 +211,10 @@ class _RelationCellEditorContentState
class _CellEditorTitle extends StatelessWidget { class _CellEditorTitle extends StatelessWidget {
const _CellEditorTitle({ const _CellEditorTitle({
required this.databaseName, required this.databaseMeta,
}); });
final String databaseName; final DatabaseMeta databaseMeta;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -223,15 +230,20 @@ class _CellEditorTitle extends StatelessWidget {
fontSize: 11, fontSize: 11,
color: Theme.of(context).hintColor, color: Theme.of(context).hintColor,
), ),
Padding( MouseRegion(
padding: const EdgeInsets.symmetric( cursor: SystemMouseCursors.click,
horizontal: 4, child: GestureDetector(
vertical: 2, onTap: () => _openRelatedDatbase(context),
), child: Padding(
child: FlowyText.regular( padding:
databaseName, const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
fontSize: 11, child: FlowyText.regular(
overflow: TextOverflow.ellipsis, databaseMeta.databaseName,
fontSize: 11,
overflow: TextOverflow.ellipsis,
decoration: TextDecoration.underline,
),
),
), ),
), ),
], ],
@ -239,6 +251,28 @@ class _CellEditorTitle extends StatelessWidget {
), ),
); );
} }
void _openRelatedDatbase(BuildContext context) {
FolderEventGetView(ViewIdPB(value: databaseMeta.inlineViewId))
.send()
.then((result) {
result.fold(
(view) {
PopoverContainer.of(context).closeAll();
Navigator.of(context).maybePop();
getIt<TabsBloc>().add(
TabsEvent.openPlugin(
plugin: DatabaseTabBarViewPlugin(
view: view,
pluginType: view.pluginType,
),
),
);
},
(err) => Log.error(err),
);
});
}
} }
class _SearchField extends StatelessWidget { class _SearchField extends StatelessWidget {

View File

@ -31,7 +31,7 @@ class FieldEditor extends StatefulWidget {
const FieldEditor({ const FieldEditor({
super.key, super.key,
required this.viewId, required this.viewId,
required this.field, required this.fieldInfo,
required this.fieldController, required this.fieldController,
required this.isNewField, required this.isNewField,
this.initialPage = FieldEditorPage.details, this.initialPage = FieldEditorPage.details,
@ -39,7 +39,7 @@ class FieldEditor extends StatefulWidget {
}); });
final String viewId; final String viewId;
final FieldPB field; final FieldInfo fieldInfo;
final FieldController fieldController; final FieldController fieldController;
final FieldEditorPage initialPage; final FieldEditorPage initialPage;
final void Function(String fieldId)? onFieldInserted; final void Function(String fieldId)? onFieldInserted;
@ -51,13 +51,13 @@ class FieldEditor extends StatefulWidget {
class _FieldEditorState extends State<FieldEditor> { class _FieldEditorState extends State<FieldEditor> {
late FieldEditorPage _currentPage; late FieldEditorPage _currentPage;
late final TextEditingController textController; late final TextEditingController textController =
TextEditingController(text: widget.fieldInfo.name);
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_currentPage = widget.initialPage; _currentPage = widget.initialPage;
textController = TextEditingController(text: widget.field.name);
} }
@override @override
@ -71,14 +71,14 @@ class _FieldEditorState extends State<FieldEditor> {
return BlocProvider( return BlocProvider(
create: (_) => FieldEditorBloc( create: (_) => FieldEditorBloc(
viewId: widget.viewId, viewId: widget.viewId,
field: widget.field, fieldInfo: widget.fieldInfo,
fieldController: widget.fieldController, fieldController: widget.fieldController,
onFieldInserted: widget.onFieldInserted, onFieldInserted: widget.onFieldInserted,
isNew: widget.isNewField, isNew: widget.isNewField,
), ),
child: _currentPage == FieldEditorPage.details child: _currentPage == FieldEditorPage.general
? _fieldDetails() ? _fieldGeneral()
: _fieldGeneral(), : _fieldDetails(),
); );
} }
@ -120,6 +120,21 @@ class _FieldEditorState extends State<FieldEditor> {
); );
} }
Widget _actionCell(FieldAction action) {
return BlocBuilder<FieldEditorBloc, FieldEditorState>(
builder: (context, state) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: FieldActionCell(
viewId: widget.viewId,
fieldInfo: state.field,
action: action,
),
);
},
);
}
Widget _fieldDetails() { Widget _fieldDetails() {
return SizedBox( return SizedBox(
width: 260, width: 260,
@ -129,19 +144,6 @@ class _FieldEditorState extends State<FieldEditor> {
), ),
); );
} }
Widget _actionCell(FieldAction action) {
return BlocBuilder<FieldEditorBloc, FieldEditorState>(
builder: (context, state) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: FieldActionCell(
viewId: widget.viewId,
fieldInfo: state.field,
action: action,
),
),
);
}
} }
class _EditFieldButton extends StatelessWidget { class _EditFieldButton extends StatelessWidget {
@ -322,32 +324,33 @@ enum FieldAction {
); );
break; break;
case FieldAction.clearData: case FieldAction.clearData:
NavigatorAlertDialog( PopoverContainer.of(context).closeAll();
constraints: const BoxConstraints( showCancelAndConfirmDialog(
maxWidth: 250, context: context,
maxHeight: 260, title: LocaleKeys.grid_field_label.tr(),
), description: LocaleKeys.grid_field_clearFieldPromptMessage.tr(),
title: LocaleKeys.grid_field_clearFieldPromptMessage.tr(), confirmLabel: LocaleKeys.button_confirm.tr(),
confirm: () { onConfirm: () {
FieldBackendService.clearField( FieldBackendService.clearField(
viewId: viewId, viewId: viewId,
fieldId: fieldInfo.id, fieldId: fieldInfo.id,
); );
}, },
).show(context); );
PopoverContainer.of(context).close();
break; break;
case FieldAction.delete: case FieldAction.delete:
NavigatorAlertDialog( PopoverContainer.of(context).closeAll();
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(), showConfirmDeletionDialog(
confirm: () { context: context,
name: LocaleKeys.grid_field_label.tr(),
description: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
onConfirm: () {
FieldBackendService.deleteField( FieldBackendService.deleteField(
viewId: viewId, viewId: viewId,
fieldId: fieldInfo.id, fieldId: fieldInfo.id,
); );
}, },
).show(context); );
PopoverContainer.of(context).close();
break; break;
case FieldAction.wrap: case FieldAction.wrap:
context context
@ -574,7 +577,10 @@ class _FieldNameTextFieldState extends State<FieldNameTextField> {
} }
class SwitchFieldButton extends StatefulWidget { class SwitchFieldButton extends StatefulWidget {
const SwitchFieldButton({super.key, required this.popoverMutex}); const SwitchFieldButton({
super.key,
required this.popoverMutex,
});
final PopoverMutex popoverMutex; final PopoverMutex popoverMutex;

View File

@ -14,7 +14,6 @@ import 'package:appflowy/plugins/database/grid/presentation/widgets/header/deskt
import 'package:appflowy/plugins/database/widgets/field/field_editor.dart'; import 'package:appflowy/plugins/database/widgets/field/field_editor.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_button.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_button.dart';
import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log; import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
@ -130,50 +129,11 @@ class _PropertyCell extends StatefulWidget {
class _PropertyCellState extends State<_PropertyCell> { class _PropertyCellState extends State<_PropertyCell> {
final PopoverController _popoverController = PopoverController(); final PopoverController _popoverController = PopoverController();
final PopoverController _fieldPopoverController = PopoverController();
final ValueNotifier<bool> _isFieldHover = ValueNotifier(false); final ValueNotifier<bool> _isFieldHover = ValueNotifier(false);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final dragThumb = MouseRegion(
cursor: Platform.isWindows
? SystemMouseCursors.click
: SystemMouseCursors.grab,
child: SizedBox(
width: 16,
height: 30,
child: AppFlowyPopover(
controller: _fieldPopoverController,
constraints: BoxConstraints.loose(const Size(240, 600)),
margin: EdgeInsets.zero,
triggerActions: PopoverTriggerFlags.none,
direction: PopoverDirection.bottomWithLeftAligned,
popupBuilder: (popoverContext) => FieldEditor(
viewId: widget.fieldController.viewId,
field: widget.fieldController
.getField(widget.cellContext.fieldId)!
.field,
fieldController: widget.fieldController,
isNewField: false,
),
child: ValueListenableBuilder(
valueListenable: _isFieldHover,
builder: (_, isHovering, child) =>
isHovering ? child! : const SizedBox.shrink(),
child: BlockActionButton(
onTap: () => _fieldPopoverController.show(),
svg: FlowySvgs.drag_element_s,
richMessage: TextSpan(
text: LocaleKeys.grid_rowPage_fieldDragElementTooltip.tr(),
style: context.tooltipTextStyle(),
),
),
),
),
),
);
final cell = widget.cellBuilder.buildStyled( final cell = widget.cellBuilder.buildStyled(
widget.cellContext, widget.cellContext,
EditableCellStyle.desktopRowDetail, EditableCellStyle.desktopRowDetail,
@ -210,53 +170,12 @@ class _PropertyCellState extends State<_PropertyCell> {
return ReorderableDragStartListener( return ReorderableDragStartListener(
index: widget.index, index: widget.index,
enabled: value, enabled: value,
child: dragThumb, child: _buildDragHandle(context),
); );
}, },
), ),
const HSpace(4), const HSpace(4),
BlocSelector<RowDetailBloc, RowDetailState, FieldInfo?>( _buildFieldButton(context),
selector: (state) => state.fields.firstWhereOrNull(
(fieldInfo) => fieldInfo.field.id == widget.cellContext.fieldId,
),
builder: (context, fieldInfo) {
if (fieldInfo == null) {
return const SizedBox.shrink();
}
return AppFlowyPopover(
controller: _popoverController,
constraints: BoxConstraints.loose(const Size(240, 600)),
margin: EdgeInsets.zero,
triggerActions: PopoverTriggerFlags.none,
direction: PopoverDirection.bottomWithLeftAligned,
popupBuilder: (popoverContext) => FieldEditor(
viewId: widget.fieldController.viewId,
field: fieldInfo.field,
fieldController: widget.fieldController,
isNewField: false,
),
child: SizedBox(
width: 160,
height: 30,
child: Tooltip(
waitDuration: const Duration(seconds: 1),
preferBelow: false,
verticalOffset: 15,
message: fieldInfo.name,
child: FieldCellButton(
field: fieldInfo.field,
onTap: () => _popoverController.show(),
radius: BorderRadius.circular(6),
margin: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 6,
),
),
),
),
);
},
),
const HSpace(8), const HSpace(8),
Expanded(child: gesture), Expanded(child: gesture),
], ],
@ -264,6 +183,96 @@ class _PropertyCellState extends State<_PropertyCell> {
), ),
); );
} }
Widget _buildDragHandle(BuildContext context) {
return MouseRegion(
cursor: Platform.isWindows
? SystemMouseCursors.click
: SystemMouseCursors.grab,
child: SizedBox(
width: 16,
height: 30,
child: BlocListener<RowDetailBloc, RowDetailState>(
listenWhen: (previous, current) =>
previous.editingFieldId != current.editingFieldId,
listener: (context, state) {
if (state.editingFieldId == widget.cellContext.fieldId) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_popoverController.show();
});
}
},
child: ValueListenableBuilder(
valueListenable: _isFieldHover,
builder: (_, isHovering, child) =>
isHovering ? child! : const SizedBox.shrink(),
child: BlockActionButton(
onTap: () => context.read<RowDetailBloc>().add(
RowDetailEvent.startEditingField(
widget.cellContext.fieldId,
),
),
svg: FlowySvgs.drag_element_s,
richMessage: TextSpan(
text: LocaleKeys.grid_rowPage_fieldDragElementTooltip.tr(),
style: context.tooltipTextStyle(),
),
),
),
),
),
);
}
Widget _buildFieldButton(BuildContext context) {
return BlocSelector<RowDetailBloc, RowDetailState, FieldInfo?>(
selector: (state) => state.fields.firstWhereOrNull(
(fieldInfo) => fieldInfo.field.id == widget.cellContext.fieldId,
),
builder: (context, fieldInfo) {
if (fieldInfo == null) {
return const SizedBox.shrink();
}
return AppFlowyPopover(
controller: _popoverController,
constraints: BoxConstraints.loose(const Size(240, 600)),
margin: EdgeInsets.zero,
triggerActions: PopoverTriggerFlags.none,
direction: PopoverDirection.bottomWithLeftAligned,
onClose: () => context
.read<RowDetailBloc>()
.add(const RowDetailEvent.endEditingField()),
popupBuilder: (popoverContext) => FieldEditor(
viewId: widget.fieldController.viewId,
fieldInfo: fieldInfo,
fieldController: widget.fieldController,
isNewField: context.watch<RowDetailBloc>().state.newFieldId ==
widget.cellContext.fieldId,
),
child: SizedBox(
width: 160,
height: 30,
child: Tooltip(
waitDuration: const Duration(seconds: 1),
preferBelow: false,
verticalOffset: 15,
message: fieldInfo.name,
child: FieldCellButton(
field: fieldInfo.field,
onTap: () => context.read<RowDetailBloc>().add(
RowDetailEvent.startEditingField(
widget.cellContext.fieldId,
),
),
radius: BorderRadius.circular(6),
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
),
),
),
);
},
);
}
} }
class ToggleHiddenFieldsVisibilityButton extends StatelessWidget { class ToggleHiddenFieldsVisibilityButton extends StatelessWidget {
@ -357,7 +366,7 @@ class ToggleHiddenFieldsVisibilityButton extends StatelessWidget {
} }
} }
class CreateRowFieldButton extends StatefulWidget { class CreateRowFieldButton extends StatelessWidget {
const CreateRowFieldButton({ const CreateRowFieldButton({
super.key, super.key,
required this.viewId, required this.viewId,
@ -367,61 +376,35 @@ class CreateRowFieldButton extends StatefulWidget {
final String viewId; final String viewId;
final FieldController fieldController; final FieldController fieldController;
@override
State<CreateRowFieldButton> createState() => _CreateRowFieldButtonState();
}
class _CreateRowFieldButtonState extends State<CreateRowFieldButton> {
final PopoverController popoverController = PopoverController();
FieldPB? createdField;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppFlowyPopover( return SizedBox(
constraints: BoxConstraints.loose(const Size(240, 200)), height: 30,
controller: popoverController, child: FlowyButton(
direction: PopoverDirection.topWithLeftAligned, margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
triggerActions: PopoverTriggerFlags.none, text: FlowyText.medium(
margin: EdgeInsets.zero, lineHeight: 1.0,
child: SizedBox( LocaleKeys.grid_field_newProperty.tr(),
height: 30, color: Theme.of(context).hintColor,
child: FlowyButton( ),
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 6), hoverColor: AFThemeExtension.of(context).lightGreyHover,
text: FlowyText.medium( onTap: () async {
lineHeight: 1.0, final result = await FieldBackendService.createField(
LocaleKeys.grid_field_newProperty.tr(), viewId: viewId,
color: Theme.of(context).hintColor, );
), await Future.delayed(const Duration(milliseconds: 50));
hoverColor: AFThemeExtension.of(context).lightGreyHover, result.fold(
onTap: () async { (field) => context
final result = await FieldBackendService.createField( .read<RowDetailBloc>()
viewId: widget.viewId, .add(RowDetailEvent.startEditingNewField(field.id)),
); (err) => Log.error("Failed to create field type option: $err"),
result.fold( );
(newField) { },
createdField = newField; leftIcon: FlowySvg(
popoverController.show(); FlowySvgs.add_m,
}, color: Theme.of(context).hintColor,
(r) => Log.error("Failed to create field type option: $r"),
);
},
leftIcon: FlowySvg(
FlowySvgs.add_m,
color: Theme.of(context).hintColor,
),
), ),
), ),
popupBuilder: (BuildContext popoverContext) {
if (createdField == null) {
return const SizedBox.shrink();
}
return FieldEditor(
viewId: widget.viewId,
field: createdField!,
fieldController: widget.fieldController,
isNewField: true,
);
},
); );
} }
} }

View File

@ -204,7 +204,7 @@ class _DatabasePropertyCellState extends State<DatabasePropertyCell> {
popupBuilder: (BuildContext context) { popupBuilder: (BuildContext context) {
return FieldEditor( return FieldEditor(
viewId: widget.viewId, viewId: widget.viewId,
field: widget.fieldInfo.field, fieldInfo: widget.fieldInfo,
fieldController: widget.fieldController, fieldController: widget.fieldController,
isNewField: false, isNewField: false,
); );

View File

@ -133,10 +133,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: bidi name: bidi
sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63" sha256: "9a712c7ddf708f7c41b1923aa83648a3ed44cfd75b04f72d598c45e5be287f9d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.10" version: "2.0.12"
bitsdojo_window: bitsdojo_window:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -34,7 +34,7 @@ void main() {
final editorBloc = FieldEditorBloc( final editorBloc = FieldEditorBloc(
viewId: context.gridView.id, viewId: context.gridView.id,
field: fieldInfo.field, fieldInfo: fieldInfo,
fieldController: context.fieldController, fieldController: context.fieldController,
isNew: false, isNew: false,
); );

View File

@ -78,15 +78,13 @@ class BoardTestContext {
FieldEditorBloc makeFieldEditor({ FieldEditorBloc makeFieldEditor({
required FieldInfo fieldInfo, required FieldInfo fieldInfo,
}) { }) =>
final editorBloc = FieldEditorBloc( FieldEditorBloc(
viewId: databaseController.viewId, viewId: databaseController.viewId,
fieldController: fieldController, fieldController: fieldController,
field: fieldInfo.field, fieldInfo: fieldInfo,
isNew: false, isNew: false,
); );
return editorBloc;
}
CellController makeCellControllerFromFieldId(String fieldId) { CellController makeCellControllerFromFieldId(String fieldId) {
return makeCellController( return makeCellController(

View File

@ -10,7 +10,7 @@ Future<FieldEditorBloc> createEditorBloc(AppFlowyGridTest gridTest) async {
return FieldEditorBloc( return FieldEditorBloc(
viewId: context.gridView.id, viewId: context.gridView.id,
fieldController: context.fieldController, fieldController: context.fieldController,
field: fieldInfo.field, fieldInfo: fieldInfo,
isNew: false, isNew: false,
); );
} }

View File

@ -102,7 +102,7 @@ Future<FieldEditorBloc> createFieldEditor({
return FieldEditorBloc( return FieldEditorBloc(
viewId: databaseController.viewId, viewId: databaseController.viewId,
fieldController: databaseController.fieldController, fieldController: databaseController.fieldController,
field: field, fieldInfo: databaseController.fieldController.getField(field.id)!,
isNew: true, isNew: true,
); );
}, },

View File

@ -1298,6 +1298,7 @@
"isNotEmpty": "Is not empty" "isNotEmpty": "Is not empty"
}, },
"field": { "field": {
"label": "Property",
"hide": "Hide", "hide": "Hide",
"show": "Show", "show": "Show",
"insertLeft": "Insert Left", "insertLeft": "Insert Left",
@ -1351,7 +1352,7 @@
"editProperty": "Edit property", "editProperty": "Edit property",
"newProperty": "New property", "newProperty": "New property",
"openRowDocument": "Open as a page", "openRowDocument": "Open as a page",
"deleteFieldPromptMessage": "Are you sure? This property will be deleted", "deleteFieldPromptMessage": "Are you sure? This property and all its data will be deleted",
"clearFieldPromptMessage": "Are you sure? All cells in this column will be emptied", "clearFieldPromptMessage": "Are you sure? All cells in this column will be emptied",
"newColumn": "New Column", "newColumn": "New Column",
"format": "Format", "format": "Format",

View File

@ -110,7 +110,7 @@ fi
if [ "$verbose" = true ]; then if [ "$verbose" = true ]; then
dart run build_runner build -d & dart run build_runner build -d &
else else
dart run build_runner build >/dev/null 2>&1 & dart run build_runner build -d >/dev/null 2>&1 &
fi fi
# Get the PID of the background process # Get the PID of the background process