chore: config field editor

This commit is contained in:
appflowy 2022-03-22 20:51:15 +08:00
parent 2bbcfaf3ce
commit 188302d4ba
36 changed files with 504 additions and 232 deletions

View File

@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 4.3999H4.11111H13" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.77775 4.4V3.2C5.77775 2.88174 5.89481 2.57652 6.10319 2.35147C6.31156 2.12643 6.59418 2 6.88886 2H9.11108C9.40577 2 9.68838 2.12643 9.89676 2.35147C10.1051 2.57652 10.2222 2.88174 10.2222 3.2V4.4M11.8889 4.4V12.8C11.8889 13.1183 11.7718 13.4235 11.5634 13.6485C11.3551 13.8736 11.0724 14 10.7778 14H5.2222C4.92751 14 4.64489 13.8736 4.43652 13.6485C4.22815 13.4235 4.11108 13.1183 4.11108 12.8V4.4H11.8889Z" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.88892 7.3999V10.9999" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.11108 7.3999V10.9999" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 886 B

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.9743 6.33301H7.35889C6.79245 6.33301 6.33325 6.7922 6.33325 7.35865V11.974C6.33325 12.5405 6.79245 12.9997 7.35889 12.9997H11.9743C12.5407 12.9997 12.9999 12.5405 12.9999 11.974V7.35865C12.9999 6.7922 12.5407 6.33301 11.9743 6.33301Z" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.53846 9.66667H4.02564C3.75362 9.66667 3.49275 9.55861 3.3004 9.36626C3.10806 9.17392 3 8.91304 3 8.64103V4.02564C3 3.75362 3.10806 3.49275 3.3004 3.3004C3.49275 3.10806 3.75362 3 4.02564 3H8.64103C8.91304 3 9.17392 3.10806 9.36626 3.3004C9.55861 3.49275 9.66667 3.75362 9.66667 4.02564V4.53846" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.12265 11.5847C5.92255 12.1165 6.88538 12.5 8.00024 12.5C10.4842 12.5 12.2135 10.596 13.0675 9.39083C13.6624 8.55146 13.6624 7.44854 13.0675 6.60917C12.7341 6.13867 12.2673 5.56168 11.6743 5.03305L10.9661 5.74127C11.4908 6.20089 11.9225 6.72296 12.2516 7.18736C12.601 7.68035 12.601 8.31965 12.2516 8.81264C11.4276 9.97552 9.9599 11.5 8.00024 11.5C7.19618 11.5 6.47495 11.2434 5.84702 10.8603L5.12265 11.5847ZM5.03441 10.2587L4.32618 10.967C3.73316 10.4383 3.26636 9.86133 2.93294 9.39083C2.33811 8.55146 2.33811 7.44854 2.93294 6.60917C3.78701 5.40397 5.51627 3.5 8.00024 3.5C9.1151 3.5 10.0779 3.88354 10.8778 4.4153L10.1535 5.13966C9.52554 4.75665 8.80431 4.5 8.00024 4.5C6.04059 4.5 4.57293 6.02448 3.74884 7.18736C3.39948 7.68035 3.39948 8.31965 3.74884 8.81264C4.07794 9.27704 4.50968 9.79911 5.03441 10.2587ZM6.99269 9.71466C7.28548 9.8954 7.62952 10 8.00036 10C9.09422 10 9.95491 9.08996 9.95491 8C9.95491 7.64165 9.86187 7.30275 9.69811 7.00924L8.93118 7.77618C8.94668 7.84779 8.95491 7.92265 8.95491 8C8.95491 8.5669 8.51315 9 8.00036 9C7.91225 9 7.82623 8.98721 7.7442 8.96316L6.99269 9.71466ZM7.06951 8.22363L6.30253 8.99061C6.13882 8.69713 6.04582 8.35829 6.04582 8C6.04582 6.91005 6.9065 6 8.00036 6C8.37114 6 8.71513 6.10456 9.00789 6.28525L8.25635 7.03679C8.17436 7.01277 8.08841 7 8.00036 7C7.48757 7 7.04582 7.4331 7.04582 8C7.04582 8.07728 7.05403 8.15208 7.06951 8.22363Z" fill="#333333"/>
<path d="M11.667 3.33398L3.33366 11.6673" stroke="#333333" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 11.7778L3 4" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.5 4.5L6 8L9.5 11.5" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 8L13 8" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 378 B

View File

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 11.7778L13 4" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.5 4.5L10 8L6.5 11.5" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 8L3 8" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 381 B

View File

@ -141,5 +141,14 @@
"lightLabel": "Light Mode",
"darkLabel": "Dark Mode"
}
},
"grid": {
"field": {
"hide": "Hide",
"insertLeft": "Insert Left",
"insertRight": "Insert Right",
"duplicate": "Duplicate",
"delete": "Delete"
}
}
}

View File

@ -3,7 +3,7 @@ import 'package:app_flowy/user/application/user_service.dart';
import 'package:app_flowy/workspace/application/app/prelude.dart';
import 'package:app_flowy/workspace/application/doc/prelude.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:app_flowy/workspace/application/grid/row_listener.dart';
import 'package:app_flowy/workspace/application/grid/row/row_listener.dart';
import 'package:app_flowy/workspace/application/trash/prelude.dart';
import 'package:app_flowy/workspace/application/workspace/prelude.dart';
import 'package:app_flowy/workspace/application/view/prelude.dart';
@ -101,10 +101,17 @@ class HomeDepsResolver {
),
);
getIt.registerFactoryParam<ColumnBloc, List<Field>, void>(
(data, _) => ColumnBloc(
getIt.registerFactoryParam<GridHeaderBloc, List<Field>, void>(
(data, _) => GridHeaderBloc(
data: GridColumnData(fields: data),
service: ColumnService(),
service: FieldService(),
),
);
getIt.registerFactoryParam<FieldEditBloc, Field, void>(
(field, _) => FieldEditBloc(
field: field,
service: FieldService(),
),
);

View File

@ -174,7 +174,7 @@ class PasswordTextField extends StatelessWidget {
obscureHideIcon: svg("home/show"),
hintText: LocaleKeys.signIn_passwordHint.tr(),
normalBorderColor: theme.shader4,
highlightBorderColor: theme.red,
errorBorderColor: theme.red,
cursorColor: theme.main1,
errorText: context.read<SignInBloc>().state.passwordError.fold(() => "", (error) => error),
onChanged: (value) => context.read<SignInBloc>().add(SignInEvent.passwordChanged(value)),
@ -199,7 +199,7 @@ class EmailTextField extends StatelessWidget {
hintText: LocaleKeys.signIn_emailHint.tr(),
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
normalBorderColor: theme.shader4,
highlightBorderColor: theme.red,
errorBorderColor: theme.red,
cursorColor: theme.main1,
errorText: context.read<SignInBloc>().state.emailError.fold(() => "", (error) => error),
onChanged: (value) => context.read<SignInBloc>().add(SignInEvent.emailChanged(value)),

View File

@ -139,7 +139,7 @@ class PasswordTextField extends StatelessWidget {
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
hintText: LocaleKeys.signUp_passwordHint.tr(),
normalBorderColor: theme.shader4,
highlightBorderColor: theme.red,
errorBorderColor: theme.red,
cursorColor: theme.main1,
errorText: context.read<SignUpBloc>().state.passwordError.fold(() => "", (error) => error),
onChanged: (value) => context.read<SignUpBloc>().add(SignUpEvent.passwordChanged(value)),
@ -167,7 +167,7 @@ class RepeatPasswordTextField extends StatelessWidget {
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
hintText: LocaleKeys.signUp_repeatPasswordHint.tr(),
normalBorderColor: theme.shader4,
highlightBorderColor: theme.red,
errorBorderColor: theme.red,
cursorColor: theme.main1,
errorText: context.read<SignUpBloc>().state.repeatPasswordError.fold(() => "", (error) => error),
onChanged: (value) => context.read<SignUpBloc>().add(SignUpEvent.repeatPasswordChanged(value)),
@ -192,7 +192,7 @@ class EmailTextField extends StatelessWidget {
hintText: LocaleKeys.signUp_emailHint.tr(),
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
normalBorderColor: theme.shader4,
highlightBorderColor: theme.red,
errorBorderColor: theme.red,
cursorColor: theme.main1,
errorText: context.read<SignUpBloc>().state.emailError.fold(() => "", (error) => error),
onChanged: (value) => context.read<SignUpBloc>().add(SignUpEvent.emailChanged(value)),

View File

@ -1,4 +1,4 @@
import 'package:app_flowy/workspace/application/grid/row_service.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';

View File

@ -1,42 +0,0 @@
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'column_service.dart';
import 'data.dart';
part 'column_bloc.freezed.dart';
class ColumnBloc extends Bloc<ColumnEvent, ColumnState> {
final ColumnService service;
final GridColumnData data;
ColumnBloc({required this.data, required this.service}) : super(ColumnState.initial(data.fields)) {
on<ColumnEvent>(
(event, emit) async {
await event.map(
initial: (_InitialColumn value) async {},
createColumn: (_CreateColumn value) {},
);
},
);
}
@override
Future<void> close() async {
return super.close();
}
}
@freezed
abstract class ColumnEvent with _$ColumnEvent {
const factory ColumnEvent.initial() = _InitialColumn;
const factory ColumnEvent.createColumn() = _CreateColumn;
}
@freezed
abstract class ColumnState with _$ColumnState {
const factory ColumnState({required List<Field> fields}) = _ColumnState;
factory ColumnState.initial(List<Field> fields) => ColumnState(fields: fields);
}

View File

@ -1 +0,0 @@
class ColumnService {}

View File

@ -0,0 +1,58 @@
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'field_service.dart';
part 'field_edit_bloc.freezed.dart';
class FieldEditBloc extends Bloc<FieldEditEvent, FieldEditState> {
final FieldService service;
FieldEditBloc({required Field field, required this.service}) : super(FieldEditState.initial(field)) {
on<FieldEditEvent>(
(event, emit) async {
await event.map(
initial: (_InitialField value) {},
createField: (_CreateField value) {},
updateFieldName: (_UpdateFieldName value) {
//
},
hideField: (_HideField value) {},
deleteField: (_DeleteField value) {},
insertField: (_InsertField value) {},
duplicateField: (_DuplicateField value) {},
);
},
);
}
@override
Future<void> close() async {
return super.close();
}
}
@freezed
class FieldEditEvent with _$FieldEditEvent {
const factory FieldEditEvent.initial() = _InitialField;
const factory FieldEditEvent.createField() = _CreateField;
const factory FieldEditEvent.updateFieldName(String name) = _UpdateFieldName;
const factory FieldEditEvent.hideField() = _HideField;
const factory FieldEditEvent.duplicateField() = _DuplicateField;
const factory FieldEditEvent.insertField({required bool onLeft}) = _InsertField;
const factory FieldEditEvent.deleteField() = _DeleteField;
}
@freezed
class FieldEditState with _$FieldEditState {
const factory FieldEditState({
required Field field,
required String errorText,
}) = _FieldEditState;
factory FieldEditState.initial(Field field) => FieldEditState(
field: field,
errorText: '',
);
}

View File

@ -0,0 +1 @@
class FieldService {}

View File

@ -0,0 +1,42 @@
import 'package:app_flowy/workspace/application/grid/data.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'field_service.dart';
part 'grid_header_bloc.freezed.dart';
class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
final FieldService service;
final GridColumnData data;
GridHeaderBloc({required this.data, required this.service}) : super(GridHeaderState.initial(data.fields)) {
on<GridHeaderEvent>(
(event, emit) async {
await event.map(
initial: (_InitialHeader value) async {},
createField: (_CreateField value) {},
);
},
);
}
@override
Future<void> close() async {
return super.close();
}
}
@freezed
class GridHeaderEvent with _$GridHeaderEvent {
const factory GridHeaderEvent.initial() = _InitialHeader;
const factory GridHeaderEvent.createField() = _CreateField;
}
@freezed
class GridHeaderState with _$GridHeaderState {
const factory GridHeaderState({required List<Field> fields}) = _GridHeaderState;
factory GridHeaderState.initial(List<Field> fields) => GridHeaderState(fields: fields);
}

View File

@ -1,10 +1,11 @@
export 'grid_bloc.dart';
export 'row_bloc.dart';
export 'row_service.dart';
export 'row/row_bloc.dart';
export 'row/row_service.dart';
export 'grid_service.dart';
export 'data.dart';
export 'column_service.dart';
export 'column_bloc.dart';
export 'field/field_service.dart';
export 'field/grid_header_bloc.dart';
export 'field/field_edit_bloc.dart';
export 'cell_bloc/text_cell_bloc.dart';
export 'cell_bloc/number_cell_bloc.dart';
export 'cell_bloc/selection_cell_bloc.dart';

View File

@ -1,9 +1,9 @@
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'grid_service.dart';
import 'row_listener.dart';
import 'row_service.dart';

View File

@ -3,7 +3,7 @@ import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'grid_service.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
class RowService {
final GridRowData rowData;

View File

@ -21,6 +21,11 @@ class GridSize {
vertical: GridSize.cellContentPadding,
);
static EdgeInsets get fieldContentInsets => EdgeInsets.symmetric(
horizontal: GridSize.cellContentPadding,
vertical: GridSize.cellContentPadding,
);
static EdgeInsets get footerContentInsets => EdgeInsets.fromLTRB(
0,
GridSize.headerContainerPadding,

View File

@ -1,4 +1,4 @@
import 'package:app_flowy/workspace/application/grid/row_service.dart';
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
import 'package:flutter/widgets.dart';
import 'checkbox_cell.dart';

View File

@ -1,6 +1,5 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart';
import 'package:app_flowy/workspace/application/grid/row_service.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

View File

@ -1,6 +1,5 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/cell_bloc/date_cell_bloc.dart';
import 'package:app_flowy/workspace/application/grid/row_service.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

View File

@ -1,6 +1,5 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/cell_bloc/number_cell_bloc.dart';
import 'package:app_flowy/workspace/application/grid/row_service.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

View File

@ -1,8 +1,6 @@
import 'dart:async';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/cell_bloc/text_cell_bloc.dart';
import 'package:app_flowy/workspace/application/grid/row_service.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

View File

@ -1 +1,208 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
class FieldEditor extends StatelessWidget {
final Field field;
const FieldEditor({required this.field, Key? key}) : super(key: key);
static void show(BuildContext context, Field field) {
final editor = FieldEditor(field: field);
FlowyOverlay.of(context).insertWithAnchor(
widget: OverlayContainer(child: editor),
identifier: editor.identifier(),
anchorContext: context,
anchorDirection: AnchorDirection.bottomWithLeftAligned,
style: FlowyOverlayStyle(blur: false),
);
}
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return BlocProvider(
create: (context) => getIt<FieldEditBloc>(param1: field)..add(const FieldEditEvent.initial()),
child: Container(
color: theme.surface,
constraints: BoxConstraints.loose(const Size(300, 200)),
child: SingleChildScrollView(
child: Column(children: [
const FieldNameTextField(),
// FieldTypeSwitcher(),
const VSpace(10),
FieldOperationList(
onTap: () {
FlowyOverlay.of(context).remove(identifier());
},
),
]),
),
),
);
}
String identifier() {
return toString();
}
}
class FieldNameTextField extends StatelessWidget {
const FieldNameTextField({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return BlocBuilder<FieldEditBloc, FieldEditState>(
buildWhen: ((previous, current) => previous.field.name == current.field.name),
builder: (context, state) {
return RoundedInputField(
height: 36,
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
initialValue: state.field.name,
normalBorderColor: theme.shader4,
errorBorderColor: theme.red,
focusBorderColor: theme.main1,
cursorColor: theme.main1,
errorText: state.errorText,
onChanged: (value) {
context.read<FieldEditBloc>().add(FieldEditEvent.updateFieldName(value));
},
);
},
);
}
}
class FieldTypeSwitcher extends StatelessWidget {
const FieldTypeSwitcher({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return FlowyButton(
text: FlowyText.medium(context.read<FieldEditBloc>().state.field.name, fontSize: 12),
hoverColor: theme.hover,
onTap: () {},
leftIcon: svg("editor/details", color: theme.iconColor),
);
}
}
class FieldOperationList extends StatelessWidget {
final VoidCallback onTap;
const FieldOperationList({required this.onTap, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final children = FieldAction.values
.map((action) => FieldActionItem(
action: action,
onTap: onTap,
))
.toList();
return GridView(
// https://api.flutter.dev/flutter/widgets/AnimatedList/shrinkWrap.html
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 4.0,
mainAxisSpacing: 8,
),
children: children,
);
}
}
class FieldActionItem extends StatelessWidget {
final VoidCallback onTap;
final FieldAction action;
const FieldActionItem({required this.action, required this.onTap, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return FlowyButton(
text: FlowyText.medium(action.title(), fontSize: 12),
hoverColor: theme.hover,
onTap: () {
action.run(context);
onTap();
},
leftIcon: svg(action.iconName(), color: theme.iconColor),
);
}
}
enum FieldAction {
hide,
insertLeft,
duplicate,
insertRight,
delete,
}
extension _FieldActionExtension on FieldAction {
String iconName() {
switch (this) {
case FieldAction.hide:
return 'grid/hide';
case FieldAction.insertLeft:
return 'grid/left';
case FieldAction.insertRight:
return 'grid/right';
case FieldAction.duplicate:
return 'grid/duplicate';
case FieldAction.delete:
return 'grid/delete';
}
}
String title() {
switch (this) {
case FieldAction.hide:
return LocaleKeys.grid_field_hide.tr();
case FieldAction.insertLeft:
return LocaleKeys.grid_field_insertLeft.tr();
case FieldAction.insertRight:
return LocaleKeys.grid_field_insertRight.tr();
case FieldAction.duplicate:
return LocaleKeys.grid_field_duplicate.tr();
case FieldAction.delete:
return LocaleKeys.grid_field_delete.tr();
}
}
void run(BuildContext context) {
final bloc = context.read<FieldEditBloc>();
switch (this) {
case FieldAction.hide:
bloc.add(const FieldEditEvent.hideField());
break;
case FieldAction.insertLeft:
bloc.add(const FieldEditEvent.insertField(onLeft: true));
break;
case FieldAction.insertRight:
bloc.add(const FieldEditEvent.insertField(onLeft: false));
break;
case FieldAction.duplicate:
bloc.add(const FieldEditEvent.duplicateField());
break;
case FieldAction.delete:
bloc.add(const FieldEditEvent.deleteField());
break;
}
}
}

View File

@ -1,5 +1,5 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/column_bloc.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
@ -44,8 +44,8 @@ class GridHeader extends StatelessWidget {
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return BlocProvider(
create: (context) => getIt<ColumnBloc>(param1: fields)..add(const ColumnEvent.initial()),
child: BlocBuilder<ColumnBloc, ColumnState>(
create: (context) => getIt<GridHeaderBloc>(param1: fields)..add(const GridHeaderEvent.initial()),
child: BlocBuilder<GridHeaderBloc, GridHeaderState>(
builder: (context, state) {
final headers = state.fields
.map(
@ -111,7 +111,7 @@ class CreateColumnButton extends StatelessWidget {
return FlowyButton(
text: const FlowyText.medium('New column', fontSize: 12),
hoverColor: theme.hover,
onTap: () => context.read<ColumnBloc>().add(const ColumnEvent.createColumn()),
onTap: () => context.read<GridHeaderBloc>().add(const GridHeaderEvent.createField()),
leftIcon: svg("home/add"),
);
}

View File

@ -1,5 +1,5 @@
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:app_flowy/workspace/presentation/widgets/pop_up_window.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
@ -8,6 +8,8 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'field_editor.dart';
class HeaderCell extends StatelessWidget {
final Field field;
const HeaderCell(this.field, {Key? key}) : super(key: key);
@ -16,44 +18,17 @@ class HeaderCell extends StatelessWidget {
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return FlowyButton(
text: Padding(padding: GridSize.cellContentInsets, child: FlowyText.medium(field.name, fontSize: 12)),
text: Padding(
padding: GridSize.cellContentInsets,
child: FlowyText.medium(field.name, fontSize: 12),
),
hoverColor: theme.hover,
onTap: () {
FlowyPoppuWindow.show(
context,
size: Size(300, 100),
child: CusTextField(),
);
// StyledDialog(
// child: SingleChildScrollView(
// child: Container(
// color: Colors.red,
// child: TextField(),
// ),
// ),
// ).show(context);
},
onTap: () => FieldEditor.show(context, field),
rightIcon: svg("editor/details", color: theme.iconColor),
);
}
}
class CusTextField extends StatelessWidget {
const CusTextField({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return Container(
color: theme.bg3,
child: TextField(
decoration: InputDecoration(hintText: 'Please enter a text'),
onSubmitted: print,
));
}
}
class HeaderCellContainer extends StatelessWidget {
final HeaderCell child;
final double width;

View File

@ -22,10 +22,7 @@ class FlowyPoppuWindow extends StatelessWidget {
}) async {
final window = await getWindowInfo();
FlowyOverlay.of(context).insertWithRect(
widget: SizedBox.fromSize(
size: size,
child: FlowyPoppuWindow(child: child),
),
widget: FlowyPoppuWindow(child: child),
identifier: 'FlowyPoppuWindow',
anchorPosition: Offset(-size.width / 2.0, -size.height / 2.0),
anchorSize: window.frame.size,

View File

@ -5,6 +5,8 @@ import 'package:flowy_infra_ui/src/flowy_overlay/layout.dart';
import 'package:flutter/material.dart';
import 'dart:ui';
export './overlay_container.dart';
/// Specifies how overlay are anchored to the SourceWidget
enum AnchorDirection {
// Corner aligned with a corner of the SourceWidget

View File

@ -35,36 +35,29 @@ class ListOverlay extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
const padding = EdgeInsets.symmetric(horizontal: 6, vertical: 6);
double totalHeight = height + padding.vertical;
if (footer != null) {
totalHeight = totalHeight + footer!.height + footer!.padding.vertical;
}
return Material(
type: MaterialType.transparency,
child: Container(
decoration: FlowyDecoration.decoration(theme.surface, theme.shadowColor.withOpacity(0.1)),
constraints: BoxConstraints.tight(Size(width, totalHeight)),
child: Padding(
padding: padding,
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
itemBuilder: itemBuilder,
itemCount: itemCount,
controller: controller,
),
if (footer != null)
Padding(
padding: footer!.padding,
child: footer!.widget,
),
],
return OverlayContainer(
constraints: BoxConstraints.tight(Size(width, totalHeight)),
padding: padding,
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
itemBuilder: itemBuilder,
itemCount: itemCount,
controller: controller,
),
),
if (footer != null)
Padding(
padding: footer!.padding,
child: footer!.widget,
),
],
),
);
}

View File

@ -0,0 +1,30 @@
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/decoration.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class OverlayContainer extends StatelessWidget {
final Widget child;
final BoxConstraints? constraints;
final EdgeInsets padding;
const OverlayContainer({
required this.child,
this.constraints,
this.padding = const EdgeInsets.all(12),
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return Material(
type: MaterialType.transparency,
child: Container(
padding: padding,
decoration: FlowyDecoration.decoration(theme.surface, theme.shadowColor.withOpacity(0.1)),
constraints: constraints,
child: child,
),
);
}
}

View File

@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:flowy_infra/time/duration.dart';
typedef HoverBuilder = Widget Function(BuildContext context, bool onHover);
typedef IsOnSelected = bool Function();
class FlowyHover extends StatefulWidget {
@ -29,23 +28,20 @@ class _FlowyHoverState extends State<FlowyHover> {
Widget build(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (p) => setOnHover(true),
onExit: (p) => setOnHover(false),
onEnter: (p) => setState(() => _onHover = true),
onExit: (p) => setState(() => _onHover = false),
child: render(),
);
}
void setOnHover(bool value) => setState(() => _onHover = value);
Widget render() {
var showHover = _onHover;
if (showHover == false && widget.isOnSelected != null) {
if (!showHover && widget.isOnSelected != null) {
showHover = widget.isOnSelected!();
}
if (showHover) {
return FlowyHoverBackground(
return FlowyHoverContainer(
config: widget.config,
child: widget.builder(context, _onHover),
);
@ -68,12 +64,11 @@ class HoverDisplayConfig {
required this.hoverColor});
}
class FlowyHoverBackground extends StatelessWidget {
class FlowyHoverContainer extends StatelessWidget {
final HoverDisplayConfig config;
final Widget child;
const FlowyHoverBackground({
const FlowyHoverContainer({
Key? key,
required this.child,
required this.config,

View File

@ -1,85 +1,105 @@
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/widget/rounded_button.dart';
import 'package:flowy_infra_ui/widget/text_field_container.dart';
import 'package:flutter/material.dart';
import 'package:flowy_infra/time/duration.dart';
// ignore: must_be_immutable
class RoundedInputField extends StatefulWidget {
final String? hintText;
final IconData? icon;
final bool obscureText;
final Widget? obscureIcon;
final Widget? obscureHideIcon;
final Color normalBorderColor;
final Color highlightBorderColor;
final Color errorBorderColor;
final Color cursorColor;
final Color? focusBorderColor;
final String errorText;
final TextStyle style;
final ValueChanged<String>? onChanged;
final String? initialValue;
late bool enableObscure;
var _text = "";
final EdgeInsets margin;
final EdgeInsets padding;
final EdgeInsets contentPadding;
final double height;
RoundedInputField({
const RoundedInputField({
Key? key,
this.hintText,
this.errorText = "",
this.initialValue,
this.icon,
this.obscureText = false,
this.obscureIcon,
this.obscureHideIcon,
this.onChanged,
this.normalBorderColor = Colors.transparent,
this.highlightBorderColor = Colors.transparent,
this.errorBorderColor = Colors.transparent,
this.focusBorderColor,
this.cursorColor = Colors.black,
this.style = const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
}) : super(key: key) {
enableObscure = obscureText;
}
this.margin = EdgeInsets.zero,
this.padding = EdgeInsets.zero,
this.contentPadding = const EdgeInsets.symmetric(horizontal: 10),
this.height = 48,
}) : super(key: key);
@override
State<RoundedInputField> createState() => _RoundedInputFieldState();
}
class _RoundedInputFieldState extends State<RoundedInputField> {
String inputText = "";
bool obscuteText = false;
@override
void initState() {
obscuteText = widget.obscureText;
super.initState();
}
@override
Widget build(BuildContext context) {
final Icon? newIcon = widget.icon == null
? null
: Icon(
widget.icon!,
color: const Color(0xFF6F35A5),
);
var borderColor = widget.normalBorderColor;
var focusBorderColor = widget.focusBorderColor ?? borderColor;
if (widget.errorText.isNotEmpty) {
borderColor = widget.highlightBorderColor;
borderColor = widget.errorBorderColor;
focusBorderColor = borderColor;
}
List<Widget> children = [
TextFieldContainer(
height: 48,
borderRadius: Corners.s10Border,
borderColor: borderColor,
Container(
margin: widget.margin,
padding: widget.padding,
height: widget.height,
child: TextFormField(
initialValue: widget.initialValue,
onChanged: (value) {
widget._text = value;
inputText = value;
if (widget.onChanged != null) {
widget.onChanged!(value);
}
setState(() {});
},
cursorColor: widget.cursorColor,
obscureText: widget.enableObscure,
obscureText: obscuteText,
decoration: InputDecoration(
icon: newIcon,
contentPadding: widget.contentPadding,
hintText: widget.hintText,
hintStyle: TextStyle(color: widget.normalBorderColor),
border: InputBorder.none,
suffixIcon: suffixIcon(),
border: OutlineInputBorder(
borderSide: BorderSide(
color: borderColor,
width: 1.0,
),
borderRadius: Corners.s10Border,
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: focusBorderColor,
width: 1.0,
),
borderRadius: Corners.s10Border,
),
suffixIcon: obscureIcon(),
),
),
),
@ -100,39 +120,32 @@ class _RoundedInputFieldState extends State<RoundedInputField> {
return AnimatedSize(
duration: .4.seconds,
curve: Curves.easeInOut,
child: Column(
children: children,
),
child: Column(children: children),
);
}
Widget? suffixIcon() {
Widget? obscureIcon() {
if (widget.obscureText == false) {
return null;
}
if (widget._text.isEmpty) {
return SizedBox.fromSize(size: const Size.square(16));
const double iconWidth = 16;
if (inputText.isEmpty) {
return SizedBox.fromSize(size: const Size.square(iconWidth));
}
assert(widget.obscureIcon != null && widget.obscureHideIcon != null);
Widget? icon;
if (widget.obscureText == true) {
assert(widget.obscureIcon != null && widget.obscureHideIcon != null);
if (widget.enableObscure) {
icon = widget.obscureIcon!;
} else {
icon = widget.obscureHideIcon!;
}
}
if (icon == null) {
return null;
if (obscuteText) {
icon = widget.obscureIcon!;
} else {
icon = widget.obscureHideIcon!;
}
return RoundedImageButton(
size: 16,
size: iconWidth,
press: () {
widget.enableObscure = !widget.enableObscure;
obscuteText = !obscuteText;
setState(() {});
},
child: icon,

View File

@ -1,40 +0,0 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class TextFieldContainer extends StatelessWidget {
final Widget child;
final BorderRadius borderRadius;
final Color borderColor;
final double? height;
final double? width;
const TextFieldContainer({
Key? key,
required this.child,
this.borderRadius = BorderRadius.zero,
this.borderColor = Colors.white,
this.height,
this.width,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 10),
padding: const EdgeInsets.symmetric(horizontal: 15),
height: height,
width: width,
decoration: BoxDecoration(
border: Border.all(color: borderColor),
color: Colors.white,
borderRadius: borderRadius,
),
child: Align(alignment: Alignment.center, child: child),
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Widget>('child', child));
}
}

View File

@ -111,6 +111,7 @@ flutter:
- assets/images/
- assets/images/home/
- assets/images/editor/
- assets/images/grid/
- assets/translations/
# - images/a_dot_ham.jpeg