mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-11-03 03:24:08 +00:00
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:
parent
aa621289e9
commit
0fd0483302
@ -22,7 +22,7 @@ void main() {
|
||||
const fieldName = "test change field";
|
||||
await tester.createField(
|
||||
FieldType.RichText,
|
||||
fieldName,
|
||||
name: fieldName,
|
||||
layout: ViewLayoutPB.Board,
|
||||
);
|
||||
await tester.tapButton(card1);
|
||||
|
||||
@ -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-folder/protobuf.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@ -41,7 +42,7 @@ void main() {
|
||||
name: 'my grid',
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
await tester.createField(FieldType.RichText, 'description');
|
||||
await tester.createField(FieldType.RichText, name: 'description');
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
@ -81,7 +82,7 @@ void main() {
|
||||
const fieldType = FieldType.Number;
|
||||
|
||||
// Create a number field
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
await tester.createField(fieldType);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
@ -157,7 +158,7 @@ void main() {
|
||||
const fieldType = FieldType.CreatedTime;
|
||||
// Create a create time field
|
||||
// The create time field is not editable
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
await tester.createField(fieldType);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
|
||||
@ -175,7 +176,7 @@ void main() {
|
||||
const fieldType = FieldType.LastEditedTime;
|
||||
// Create a last time field
|
||||
// The last time field is not editable
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
await tester.createField(fieldType);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
|
||||
@ -191,7 +192,7 @@ void main() {
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.DateTime;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
await tester.createField(fieldType);
|
||||
|
||||
// Tap the cell to invoke the field editor
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
@ -366,7 +367,7 @@ void main() {
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
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
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
@ -449,7 +450,7 @@ void main() {
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.Checklist;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
await tester.createField(fieldType);
|
||||
|
||||
// assert that there is no progress bar in the grid
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);
|
||||
|
||||
@ -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/widgets/field/type_option_editor/select/select_option.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-folder/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../shared/database_test_op.dart';
|
||||
import '../../shared/util.dart';
|
||||
@ -56,11 +57,22 @@ void main() {
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// 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
|
||||
tester.findFieldWithName('checklist');
|
||||
await tester.pumpAndSettle();
|
||||
// editing field type during field creation should change title
|
||||
await tester.createField(FieldType.MultiSelect);
|
||||
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 {
|
||||
@ -70,14 +82,14 @@ void main() {
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.createField(FieldType.Checkbox, 'New field 1');
|
||||
await tester.createField(FieldType.Checkbox, name: 'New field 1');
|
||||
|
||||
// Delete the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
await tester.tapDeletePropertyButton();
|
||||
|
||||
// confirm delete
|
||||
await tester.tapDialogOkButton();
|
||||
await tester.tapButtonWithName(LocaleKeys.space_delete.tr());
|
||||
|
||||
tester.noFieldWithName('New field 1');
|
||||
await tester.pumpAndSettle();
|
||||
@ -90,10 +102,7 @@ void main() {
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField('New field 1');
|
||||
await tester.dismissFieldEditor();
|
||||
await tester.createField(FieldType.RichText, name: 'New field 1');
|
||||
|
||||
// duplicate the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
@ -126,26 +135,6 @@ void main() {
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
@ -162,18 +151,10 @@ void main() {
|
||||
FieldType.CreatedTime,
|
||||
FieldType.Checkbox,
|
||||
]) {
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField(fieldType.name);
|
||||
|
||||
// Open the type option menu
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
|
||||
await tester.selectFieldType(fieldType);
|
||||
await tester.dismissFieldEditor();
|
||||
await tester.createField(fieldType);
|
||||
|
||||
// After update the field type, the cells should be updated
|
||||
await tester.findCellByFieldType(fieldType);
|
||||
tester.findCellByFieldType(fieldType);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
});
|
||||
@ -190,15 +171,7 @@ void main() {
|
||||
FieldType.Checklist,
|
||||
FieldType.URL,
|
||||
]) {
|
||||
// create the field
|
||||
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();
|
||||
await tester.createField(fieldType);
|
||||
|
||||
// open the field editor
|
||||
await tester.tapGridFieldWithName(fieldType.i18n);
|
||||
@ -218,11 +191,7 @@ void main() {
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
|
||||
// create a number field
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField("Number");
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.Number);
|
||||
await tester.dismissFieldEditor();
|
||||
await tester.createField(FieldType.Number);
|
||||
|
||||
// enter some data into the first number cell
|
||||
await tester.editCell(
|
||||
@ -243,7 +212,7 @@ void main() {
|
||||
);
|
||||
|
||||
// open editor and change number format
|
||||
await tester.tapGridFieldWithName('Number');
|
||||
await tester.tapGridFieldWithName(FieldType.Number.i18n);
|
||||
await tester.tapEditFieldButton();
|
||||
await tester.changeNumberFieldFormat();
|
||||
await tester.dismissFieldEditor();
|
||||
@ -292,11 +261,7 @@ void main() {
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
|
||||
// create a date field
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField(FieldType.DateTime.i18n);
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.DateTime);
|
||||
await tester.dismissFieldEditor();
|
||||
await tester.createField(FieldType.DateTime);
|
||||
|
||||
// edit the first date cell
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
|
||||
|
||||
@ -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:appflowy/plugins/database/widgets/row/row_banner.dart';
|
||||
@ -121,15 +124,24 @@ void main() {
|
||||
FieldType.Checkbox,
|
||||
]) {
|
||||
await tester.tapRowDetailPageCreatePropertyButton();
|
||||
await tester.renameField(fieldType.name);
|
||||
|
||||
// Open the type option menu
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
|
||||
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
|
||||
await tester.findCellByFieldType(fieldType);
|
||||
tester.findCellByFieldType(fieldType);
|
||||
await tester.scrollRowDetailByOffset(const Offset(0, -50));
|
||||
}
|
||||
});
|
||||
|
||||
@ -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/date_type_option_button.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/toggle/toggle.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
@ -476,7 +475,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await pumpAndSettle();
|
||||
if (enter) {
|
||||
await testTextInput.receiveAction(TextInputAction.done);
|
||||
await pumpAndSettle();
|
||||
await pumpAndSettle(const Duration(milliseconds: 500));
|
||||
} else {
|
||||
await tapButton(
|
||||
find.descendant(
|
||||
@ -629,12 +628,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
(w) => w is FieldActionCell && w.action == FieldAction.delete,
|
||||
);
|
||||
await tapButton(deleteButton);
|
||||
|
||||
final confirmButton = find.descendant(
|
||||
of: find.byType(NavigatorAlertDialog),
|
||||
matching: find.byType(PrimaryTextButton),
|
||||
);
|
||||
await tapButton(confirmButton);
|
||||
await tapButtonWithName(LocaleKeys.space_delete.tr());
|
||||
}
|
||||
|
||||
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
|
||||
/// the field type after create a new field.
|
||||
Future<void> findCellByFieldType(FieldType fieldType) async {
|
||||
void findCellByFieldType(FieldType fieldType) {
|
||||
final finder = finderForFieldType(fieldType);
|
||||
expect(finder, findsWidgets);
|
||||
}
|
||||
@ -894,18 +888,19 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> createField(
|
||||
FieldType fieldType,
|
||||
String name, {
|
||||
FieldType fieldType, {
|
||||
String? name,
|
||||
ViewLayoutPB layout = ViewLayoutPB.Grid,
|
||||
}) async {
|
||||
if (layout == ViewLayoutPB.Grid) {
|
||||
await scrollToRight(find.byType(GridPage));
|
||||
}
|
||||
await tapNewPropertyButton();
|
||||
await renameField(name);
|
||||
if (name != null) {
|
||||
await renameField(name);
|
||||
}
|
||||
await tapSwitchFieldTypeButton();
|
||||
await selectFieldType(fieldType);
|
||||
await dismissFieldEditor();
|
||||
}
|
||||
|
||||
Future<void> tapDatabaseSettingButton() async {
|
||||
|
||||
@ -62,7 +62,7 @@ class _QuickEditFieldState extends State<QuickEditField> {
|
||||
create: (_) => FieldEditorBloc(
|
||||
viewId: widget.viewId,
|
||||
fieldController: widget.fieldController,
|
||||
field: widget.fieldInfo.field,
|
||||
fieldInfo: widget.fieldInfo,
|
||||
isNew: false,
|
||||
),
|
||||
child: BlocConsumer<FieldEditorBloc, FieldEditorState>(
|
||||
|
||||
@ -18,32 +18,33 @@ part 'field_editor_bloc.freezed.dart';
|
||||
class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
FieldEditorBloc({
|
||||
required this.viewId,
|
||||
required this.fieldInfo,
|
||||
required this.fieldController,
|
||||
this.onFieldInserted,
|
||||
required FieldPB field,
|
||||
required this.isNew,
|
||||
}) : fieldId = field.id,
|
||||
fieldService = FieldBackendService(
|
||||
}) : _fieldService = FieldBackendService(
|
||||
viewId: viewId,
|
||||
fieldId: field.id,
|
||||
fieldId: fieldInfo.id,
|
||||
),
|
||||
fieldSettingsService = FieldSettingsBackendService(viewId: viewId),
|
||||
super(FieldEditorState(field: FieldInfo.initial(field))) {
|
||||
super(FieldEditorState(field: fieldInfo)) {
|
||||
_dispatch();
|
||||
_startListening();
|
||||
_init();
|
||||
}
|
||||
|
||||
final String viewId;
|
||||
final String fieldId;
|
||||
final FieldInfo fieldInfo;
|
||||
final bool isNew;
|
||||
final FieldController fieldController;
|
||||
final FieldBackendService fieldService;
|
||||
final FieldBackendService _fieldService;
|
||||
final FieldSettingsBackendService fieldSettingsService;
|
||||
final void Function(String newFieldId)? onFieldInserted;
|
||||
|
||||
late final OnReceiveField _listener;
|
||||
|
||||
String get fieldId => fieldInfo.id;
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
fieldController.removeSingleFieldListener(
|
||||
@ -66,13 +67,13 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
fieldName = fieldType.i18n;
|
||||
}
|
||||
|
||||
await fieldService.updateType(
|
||||
await _fieldService.updateType(
|
||||
fieldType: fieldType,
|
||||
fieldName: fieldName,
|
||||
);
|
||||
},
|
||||
renameField: (newName) async {
|
||||
final result = await fieldService.updateField(name: newName);
|
||||
final result = await _fieldService.updateField(name: newName);
|
||||
_logIfError(result);
|
||||
emit(state.copyWith(wasRenameManually: true));
|
||||
},
|
||||
@ -85,14 +86,14 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
_logIfError(result);
|
||||
},
|
||||
insertLeft: () async {
|
||||
final result = await fieldService.createBefore();
|
||||
final result = await _fieldService.createBefore();
|
||||
result.fold(
|
||||
(newField) => onFieldInserted?.call(newField.id),
|
||||
(err) => Log.error("Failed creating field $err"),
|
||||
);
|
||||
},
|
||||
insertRight: () async {
|
||||
final result = await fieldService.createAfter();
|
||||
final result = await _fieldService.createAfter();
|
||||
result.fold(
|
||||
(newField) => onFieldInserted?.call(newField.id),
|
||||
(err) => Log.error("Failed creating field $err"),
|
||||
@ -106,7 +107,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
? FieldVisibility.AlwaysShown
|
||||
: FieldVisibility.AlwaysHidden;
|
||||
final result = await fieldSettingsService.updateFieldSettings(
|
||||
fieldId: state.field.id,
|
||||
fieldId: fieldId,
|
||||
fieldVisibility: newVisibility,
|
||||
);
|
||||
_logIfError(result);
|
||||
|
||||
@ -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
|
||||
const factory RowDetailEvent.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
|
||||
@ -228,6 +247,8 @@ class RowDetailState with _$RowDetailState {
|
||||
required List<CellContext> visibleCells,
|
||||
required bool showHiddenFields,
|
||||
required int numHiddenFields,
|
||||
required String editingFieldId,
|
||||
required String newFieldId,
|
||||
}) = _RowDetailState;
|
||||
|
||||
factory RowDetailState.initial() => const RowDetailState(
|
||||
@ -235,5 +256,7 @@ class RowDetailState with _$RowDetailState {
|
||||
visibleCells: [],
|
||||
showHiddenFields: false,
|
||||
numHiddenFields: 0,
|
||||
editingFieldId: "",
|
||||
newFieldId: "",
|
||||
);
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ class _GridFieldCellState extends State<GridFieldCell> {
|
||||
return FieldEditor(
|
||||
viewId: widget.viewId,
|
||||
fieldController: widget.fieldController,
|
||||
field: widget.fieldInfo.field,
|
||||
fieldInfo: widget.fieldInfo,
|
||||
isNewField: widget.isNew,
|
||||
initialPage: widget.isNew
|
||||
? FieldEditorPage.details
|
||||
|
||||
@ -174,7 +174,7 @@ class _CellTrailing extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class CreateFieldButton extends StatefulWidget {
|
||||
class CreateFieldButton extends StatelessWidget {
|
||||
const CreateFieldButton({
|
||||
super.key,
|
||||
required this.viewId,
|
||||
@ -184,11 +184,6 @@ class CreateFieldButton extends StatefulWidget {
|
||||
final String viewId;
|
||||
final void Function(String fieldId) onFieldCreated;
|
||||
|
||||
@override
|
||||
State<CreateFieldButton> createState() => _CreateFieldButtonState();
|
||||
}
|
||||
|
||||
class _CreateFieldButtonState extends State<CreateFieldButton> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyButton(
|
||||
@ -202,10 +197,10 @@ class _CreateFieldButtonState extends State<CreateFieldButton> {
|
||||
hoverColor: AFThemeExtension.of(context).greyHover,
|
||||
onTap: () async {
|
||||
final result = await FieldBackendService.createField(
|
||||
viewId: widget.viewId,
|
||||
viewId: viewId,
|
||||
);
|
||||
result.fold(
|
||||
(field) => widget.onFieldCreated(field.id),
|
||||
(field) => onFieldCreated(field.id),
|
||||
(err) => Log.error("Failed to create field type option: $err"),
|
||||
);
|
||||
},
|
||||
|
||||
@ -174,6 +174,8 @@ class _ChecklistItemState extends State<ChecklistItem> {
|
||||
meta: Platform.isMacOS,
|
||||
control: !Platform.isMacOS,
|
||||
): const _SelectTaskIntent(),
|
||||
const SingleActivator(LogicalKeyboardKey.enter):
|
||||
const _EndEditingTaskIntent(),
|
||||
const SingleActivator(LogicalKeyboardKey.escape):
|
||||
const _EndEditingTaskIntent(),
|
||||
};
|
||||
|
||||
@ -138,7 +138,7 @@ class _ChecklistItemState extends State<_ChecklistItem> {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
height: 44,
|
||||
constraints: const BoxConstraints(minHeight: 44),
|
||||
child: Row(
|
||||
children: [
|
||||
InkWell(
|
||||
@ -164,6 +164,8 @@ class _ChecklistItemState extends State<_ChecklistItem> {
|
||||
controller: _textController,
|
||||
focusNode: _focusNode,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
|
||||
@ -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/grid/presentation/layout/sizes.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/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-folder/protobuf.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
@ -126,7 +133,7 @@ class _RelationCellEditorContentState
|
||||
shrinkWrap: true,
|
||||
slivers: [
|
||||
_CellEditorTitle(
|
||||
databaseName: widget.relatedDatabaseMeta.databaseName,
|
||||
databaseMeta: widget.relatedDatabaseMeta,
|
||||
),
|
||||
_SearchField(
|
||||
focusNode: focusNode,
|
||||
@ -204,10 +211,10 @@ class _RelationCellEditorContentState
|
||||
|
||||
class _CellEditorTitle extends StatelessWidget {
|
||||
const _CellEditorTitle({
|
||||
required this.databaseName,
|
||||
required this.databaseMeta,
|
||||
});
|
||||
|
||||
final String databaseName;
|
||||
final DatabaseMeta databaseMeta;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -223,15 +230,20 @@ class _CellEditorTitle extends StatelessWidget {
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4,
|
||||
vertical: 2,
|
||||
),
|
||||
child: FlowyText.regular(
|
||||
databaseName,
|
||||
fontSize: 11,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () => _openRelatedDatbase(context),
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||
child: FlowyText.regular(
|
||||
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 {
|
||||
|
||||
@ -31,7 +31,7 @@ class FieldEditor extends StatefulWidget {
|
||||
const FieldEditor({
|
||||
super.key,
|
||||
required this.viewId,
|
||||
required this.field,
|
||||
required this.fieldInfo,
|
||||
required this.fieldController,
|
||||
required this.isNewField,
|
||||
this.initialPage = FieldEditorPage.details,
|
||||
@ -39,7 +39,7 @@ class FieldEditor extends StatefulWidget {
|
||||
});
|
||||
|
||||
final String viewId;
|
||||
final FieldPB field;
|
||||
final FieldInfo fieldInfo;
|
||||
final FieldController fieldController;
|
||||
final FieldEditorPage initialPage;
|
||||
final void Function(String fieldId)? onFieldInserted;
|
||||
@ -51,13 +51,13 @@ class FieldEditor extends StatefulWidget {
|
||||
|
||||
class _FieldEditorState extends State<FieldEditor> {
|
||||
late FieldEditorPage _currentPage;
|
||||
late final TextEditingController textController;
|
||||
late final TextEditingController textController =
|
||||
TextEditingController(text: widget.fieldInfo.name);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_currentPage = widget.initialPage;
|
||||
textController = TextEditingController(text: widget.field.name);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -71,14 +71,14 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
return BlocProvider(
|
||||
create: (_) => FieldEditorBloc(
|
||||
viewId: widget.viewId,
|
||||
field: widget.field,
|
||||
fieldInfo: widget.fieldInfo,
|
||||
fieldController: widget.fieldController,
|
||||
onFieldInserted: widget.onFieldInserted,
|
||||
isNew: widget.isNewField,
|
||||
),
|
||||
child: _currentPage == FieldEditorPage.details
|
||||
? _fieldDetails()
|
||||
: _fieldGeneral(),
|
||||
child: _currentPage == FieldEditorPage.general
|
||||
? _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() {
|
||||
return SizedBox(
|
||||
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 {
|
||||
@ -322,32 +324,33 @@ enum FieldAction {
|
||||
);
|
||||
break;
|
||||
case FieldAction.clearData:
|
||||
NavigatorAlertDialog(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 250,
|
||||
maxHeight: 260,
|
||||
),
|
||||
title: LocaleKeys.grid_field_clearFieldPromptMessage.tr(),
|
||||
confirm: () {
|
||||
PopoverContainer.of(context).closeAll();
|
||||
showCancelAndConfirmDialog(
|
||||
context: context,
|
||||
title: LocaleKeys.grid_field_label.tr(),
|
||||
description: LocaleKeys.grid_field_clearFieldPromptMessage.tr(),
|
||||
confirmLabel: LocaleKeys.button_confirm.tr(),
|
||||
onConfirm: () {
|
||||
FieldBackendService.clearField(
|
||||
viewId: viewId,
|
||||
fieldId: fieldInfo.id,
|
||||
);
|
||||
},
|
||||
).show(context);
|
||||
PopoverContainer.of(context).close();
|
||||
);
|
||||
break;
|
||||
case FieldAction.delete:
|
||||
NavigatorAlertDialog(
|
||||
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
|
||||
confirm: () {
|
||||
PopoverContainer.of(context).closeAll();
|
||||
showConfirmDeletionDialog(
|
||||
context: context,
|
||||
name: LocaleKeys.grid_field_label.tr(),
|
||||
description: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
|
||||
onConfirm: () {
|
||||
FieldBackendService.deleteField(
|
||||
viewId: viewId,
|
||||
fieldId: fieldInfo.id,
|
||||
);
|
||||
},
|
||||
).show(context);
|
||||
PopoverContainer.of(context).close();
|
||||
);
|
||||
break;
|
||||
case FieldAction.wrap:
|
||||
context
|
||||
@ -574,7 +577,10 @@ class _FieldNameTextFieldState extends State<FieldNameTextField> {
|
||||
}
|
||||
|
||||
class SwitchFieldButton extends StatefulWidget {
|
||||
const SwitchFieldButton({super.key, required this.popoverMutex});
|
||||
const SwitchFieldButton({
|
||||
super.key,
|
||||
required this.popoverMutex,
|
||||
});
|
||||
|
||||
final PopoverMutex popoverMutex;
|
||||
|
||||
|
||||
@ -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/document/presentation/editor_plugins/actions/block_action_button.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_popover/appflowy_popover.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
@ -130,50 +129,11 @@ class _PropertyCell extends StatefulWidget {
|
||||
|
||||
class _PropertyCellState extends State<_PropertyCell> {
|
||||
final PopoverController _popoverController = PopoverController();
|
||||
final PopoverController _fieldPopoverController = PopoverController();
|
||||
|
||||
final ValueNotifier<bool> _isFieldHover = ValueNotifier(false);
|
||||
|
||||
@override
|
||||
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(
|
||||
widget.cellContext,
|
||||
EditableCellStyle.desktopRowDetail,
|
||||
@ -210,53 +170,12 @@ class _PropertyCellState extends State<_PropertyCell> {
|
||||
return ReorderableDragStartListener(
|
||||
index: widget.index,
|
||||
enabled: value,
|
||||
child: dragThumb,
|
||||
child: _buildDragHandle(context),
|
||||
);
|
||||
},
|
||||
),
|
||||
const HSpace(4),
|
||||
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,
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
_buildFieldButton(context),
|
||||
const HSpace(8),
|
||||
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 {
|
||||
@ -357,7 +366,7 @@ class ToggleHiddenFieldsVisibilityButton extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class CreateRowFieldButton extends StatefulWidget {
|
||||
class CreateRowFieldButton extends StatelessWidget {
|
||||
const CreateRowFieldButton({
|
||||
super.key,
|
||||
required this.viewId,
|
||||
@ -367,61 +376,35 @@ class CreateRowFieldButton extends StatefulWidget {
|
||||
final String viewId;
|
||||
final FieldController fieldController;
|
||||
|
||||
@override
|
||||
State<CreateRowFieldButton> createState() => _CreateRowFieldButtonState();
|
||||
}
|
||||
|
||||
class _CreateRowFieldButtonState extends State<CreateRowFieldButton> {
|
||||
final PopoverController popoverController = PopoverController();
|
||||
FieldPB? createdField;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppFlowyPopover(
|
||||
constraints: BoxConstraints.loose(const Size(240, 200)),
|
||||
controller: popoverController,
|
||||
direction: PopoverDirection.topWithLeftAligned,
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
margin: EdgeInsets.zero,
|
||||
child: SizedBox(
|
||||
height: 30,
|
||||
child: FlowyButton(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
|
||||
text: FlowyText.medium(
|
||||
lineHeight: 1.0,
|
||||
LocaleKeys.grid_field_newProperty.tr(),
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
onTap: () async {
|
||||
final result = await FieldBackendService.createField(
|
||||
viewId: widget.viewId,
|
||||
);
|
||||
result.fold(
|
||||
(newField) {
|
||||
createdField = newField;
|
||||
popoverController.show();
|
||||
},
|
||||
(r) => Log.error("Failed to create field type option: $r"),
|
||||
);
|
||||
},
|
||||
leftIcon: FlowySvg(
|
||||
FlowySvgs.add_m,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
return SizedBox(
|
||||
height: 30,
|
||||
child: FlowyButton(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
|
||||
text: FlowyText.medium(
|
||||
lineHeight: 1.0,
|
||||
LocaleKeys.grid_field_newProperty.tr(),
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
onTap: () async {
|
||||
final result = await FieldBackendService.createField(
|
||||
viewId: viewId,
|
||||
);
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
result.fold(
|
||||
(field) => context
|
||||
.read<RowDetailBloc>()
|
||||
.add(RowDetailEvent.startEditingNewField(field.id)),
|
||||
(err) => Log.error("Failed to create field type option: $err"),
|
||||
);
|
||||
},
|
||||
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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ class _DatabasePropertyCellState extends State<DatabasePropertyCell> {
|
||||
popupBuilder: (BuildContext context) {
|
||||
return FieldEditor(
|
||||
viewId: widget.viewId,
|
||||
field: widget.fieldInfo.field,
|
||||
fieldInfo: widget.fieldInfo,
|
||||
fieldController: widget.fieldController,
|
||||
isNewField: false,
|
||||
);
|
||||
|
||||
@ -133,10 +133,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bidi
|
||||
sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63"
|
||||
sha256: "9a712c7ddf708f7c41b1923aa83648a3ed44cfd75b04f72d598c45e5be287f9d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.10"
|
||||
version: "2.0.12"
|
||||
bitsdojo_window:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@ -34,7 +34,7 @@ void main() {
|
||||
|
||||
final editorBloc = FieldEditorBloc(
|
||||
viewId: context.gridView.id,
|
||||
field: fieldInfo.field,
|
||||
fieldInfo: fieldInfo,
|
||||
fieldController: context.fieldController,
|
||||
isNew: false,
|
||||
);
|
||||
|
||||
@ -78,15 +78,13 @@ class BoardTestContext {
|
||||
|
||||
FieldEditorBloc makeFieldEditor({
|
||||
required FieldInfo fieldInfo,
|
||||
}) {
|
||||
final editorBloc = FieldEditorBloc(
|
||||
viewId: databaseController.viewId,
|
||||
fieldController: fieldController,
|
||||
field: fieldInfo.field,
|
||||
isNew: false,
|
||||
);
|
||||
return editorBloc;
|
||||
}
|
||||
}) =>
|
||||
FieldEditorBloc(
|
||||
viewId: databaseController.viewId,
|
||||
fieldController: fieldController,
|
||||
fieldInfo: fieldInfo,
|
||||
isNew: false,
|
||||
);
|
||||
|
||||
CellController makeCellControllerFromFieldId(String fieldId) {
|
||||
return makeCellController(
|
||||
|
||||
@ -10,7 +10,7 @@ Future<FieldEditorBloc> createEditorBloc(AppFlowyGridTest gridTest) async {
|
||||
return FieldEditorBloc(
|
||||
viewId: context.gridView.id,
|
||||
fieldController: context.fieldController,
|
||||
field: fieldInfo.field,
|
||||
fieldInfo: fieldInfo,
|
||||
isNew: false,
|
||||
);
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ Future<FieldEditorBloc> createFieldEditor({
|
||||
return FieldEditorBloc(
|
||||
viewId: databaseController.viewId,
|
||||
fieldController: databaseController.fieldController,
|
||||
field: field,
|
||||
fieldInfo: databaseController.fieldController.getField(field.id)!,
|
||||
isNew: true,
|
||||
);
|
||||
},
|
||||
|
||||
@ -1298,6 +1298,7 @@
|
||||
"isNotEmpty": "Is not empty"
|
||||
},
|
||||
"field": {
|
||||
"label": "Property",
|
||||
"hide": "Hide",
|
||||
"show": "Show",
|
||||
"insertLeft": "Insert Left",
|
||||
@ -1351,7 +1352,7 @@
|
||||
"editProperty": "Edit property",
|
||||
"newProperty": "New property",
|
||||
"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",
|
||||
"newColumn": "New Column",
|
||||
"format": "Format",
|
||||
|
||||
@ -110,7 +110,7 @@ fi
|
||||
if [ "$verbose" = true ]; then
|
||||
dart run build_runner build -d &
|
||||
else
|
||||
dart run build_runner build >/dev/null 2>&1 &
|
||||
dart run build_runner build -d >/dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
# Get the PID of the background process
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user