chore: number field

This commit is contained in:
appflowy 2022-03-27 22:38:50 +08:00
parent 8b7eee46bb
commit 35e27b579f
24 changed files with 492 additions and 167 deletions

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.9083 10.9011C10.4173 11.2751 9.83813 11.4814 9.09532 11.4814C7.93705 11.4814 6.82914 10.8367 6.45144 9.2765H10.027V8.50287H6.32554C6.31295 8.34814 6.31295 8.19341 6.31295 8.02579C6.31295 7.84527 6.32554 7.67765 6.33813 7.51003H10.027V6.73639H6.45144C6.81655 5.25358 7.82374 4.50573 9.09532 4.50573C9.72482 4.50573 10.304 4.69914 10.8201 5.06017L11.4371 4.24785C10.7194 3.73209 9.91367 3.5 9.09532 3.5C7.10612 3.5 5.72122 4.69914 5.31835 6.73639H4.5V7.51003H5.21763C5.20504 7.67765 5.20504 7.84527 5.20504 8.02579C5.20504 8.19341 5.20504 8.34814 5.21763 8.50287H4.5V9.2765H5.31835C5.7464 11.5716 7.40827 12.5 9.09532 12.5C10.0773 12.5 10.8705 12.1777 11.5 11.7264L10.9083 10.9011Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 814 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.201 6.4H3.001V12H2.081V7.384L0.953 7.704L0.729 6.92L2.201 6.4ZM3.91156 12V11.1L6.35156 8.61C6.9449 8.01667 7.24156 7.50333 7.24156 7.07C7.24156 6.73 7.13823 6.46667 6.93156 6.28C6.73156 6.08667 6.4749 5.99 6.16156 5.99C5.5749 5.99 5.14156 6.28 4.86156 6.86L3.89156 6.29C4.11156 5.82333 4.42156 5.47 4.82156 5.23C5.22156 4.99 5.6649 4.87 6.15156 4.87C6.7649 4.87 7.29156 5.06333 7.73156 5.45C8.17156 5.83667 8.39156 6.36333 8.39156 7.03C8.39156 7.74333 7.9949 8.50333 7.20156 9.31L5.62156 10.89H8.52156V12H3.91156ZM12.9025 7.032C13.5105 7.176 14.0025 7.46 14.3785 7.884C14.7625 8.3 14.9545 8.824 14.9545 9.456C14.9545 10.296 14.6705 10.956 14.1025 11.436C13.5345 11.916 12.8385 12.156 12.0145 12.156C11.3745 12.156 10.7985 12.008 10.2865 11.712C9.78253 11.416 9.41853 10.984 9.19453 10.416L10.3705 9.732C10.6185 10.452 11.1665 10.812 12.0145 10.812C12.4945 10.812 12.8745 10.692 13.1545 10.452C13.4345 10.204 13.5745 9.872 13.5745 9.456C13.5745 9.04 13.4345 8.712 13.1545 8.472C12.8745 8.232 12.4945 8.112 12.0145 8.112H11.7025L11.1505 7.284L12.9625 4.896H9.44653V3.6H14.6065V4.776L12.9025 7.032Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.716 9.828C10.716 8.292 9.6 7.896 8.124 7.416C7.128 7.092 6.396 6.828 6.396 6.072C6.396 5.376 6.984 4.968 7.824 4.968C8.412 4.968 9.216 5.22 9.84 5.676L10.44 4.944C9.9 4.512 9.096 4.176 8.352 4.056V2.508H7.572V4.008C6.12 4.092 5.304 4.98 5.304 6.132C5.304 7.56 6.516 7.98 7.824 8.388C8.928 8.748 9.612 9.036 9.612 9.9C9.612 10.644 8.964 11.088 8.076 11.088C7.308 11.088 6.444 10.752 5.772 10.092L5.136 10.836C5.844 11.532 6.684 11.904 7.572 12.012V13.476H8.352V12.024C9.84 11.928 10.716 11.052 10.716 9.828Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 641 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.2301 4.25586H10.0181L8.02613 7.93986H8.00213L5.99813 4.25586H4.78613L7.06613 8.23986H5.43413V8.95986H7.47413L7.48613 8.98386V9.82386H5.43413V10.5319H7.48613V12.4999H8.53013V10.5319H10.4981V9.82386H8.53013V8.98386L8.54213 8.95986H10.4981V8.23986H8.95013L11.2301 4.25586Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 404 B

View File

@ -152,9 +152,10 @@
"textFieldName": "Text",
"checkboxFieldName": "Checkbox",
"dateFieldName": "Date",
"numberFieldName": "Number",
"numberFieldName": "Numbers",
"singleSelectFieldName": "Select",
"multiSelectFieldName": "Multiselect"
"multiSelectFieldName": "Multiselect",
"numberFormat": " Number format"
}
}
}

View File

@ -18,6 +18,7 @@ import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
import 'package:get_it/get_it.dart';
@ -221,7 +222,7 @@ void _resolveGridDeps(GetIt getIt) {
() => DateTypeOptionBloc(),
);
getIt.registerFactory<NumberTypeOptionBloc>(
() => NumberTypeOptionBloc(),
getIt.registerFactoryParam<NumberTypeOptionBloc, NumberTypeOption, void>(
(typeOption, _) => NumberTypeOptionBloc(typeOption: typeOption),
);
}

View File

@ -16,25 +16,29 @@ class EditFieldBloc extends Bloc<EditFieldEvent, EditFieldState> {
(event, emit) async {
await event.map(
initial: (_InitialField value) {},
updateFieldName: (_UpdateFieldName value) {
//
updateFieldName: (_UpdateFieldName value) async {
final result = await service.updateField(fieldId: field.id, name: value.name);
result.fold(
(l) => null,
(err) => Log.error(err),
);
},
hideField: (_HideField value) async {
final result = await service.updateField(fieldId: value.fieldId, visibility: false);
final result = await service.updateField(fieldId: field.id, visibility: false);
result.fold(
(l) => null,
(err) => Log.error(err),
);
},
deleteField: (_DeleteField value) async {
final result = await service.deleteField(fieldId: value.fieldId);
final result = await service.deleteField(fieldId: field.id);
result.fold(
(l) => null,
(err) => Log.error(err),
);
},
duplicateField: (_DuplicateField value) async {
final result = await service.duplicateField(fieldId: value.fieldId);
final result = await service.duplicateField(fieldId: field.id);
result.fold(
(l) => null,
(err) => Log.error(err),
@ -56,9 +60,9 @@ class EditFieldBloc extends Bloc<EditFieldEvent, EditFieldState> {
class EditFieldEvent with _$EditFieldEvent {
const factory EditFieldEvent.initial() = _InitialField;
const factory EditFieldEvent.updateFieldName(String name) = _UpdateFieldName;
const factory EditFieldEvent.hideField(String fieldId) = _HideField;
const factory EditFieldEvent.duplicateField(String fieldId) = _DuplicateField;
const factory EditFieldEvent.deleteField(String fieldId) = _DeleteField;
const factory EditFieldEvent.hideField() = _HideField;
const factory EditFieldEvent.duplicateField() = _DuplicateField;
const factory EditFieldEvent.deleteField() = _DeleteField;
const factory EditFieldEvent.saveField() = _SaveField;
}
@ -67,10 +71,12 @@ class EditFieldState with _$EditFieldState {
const factory EditFieldState({
required EditFieldContext editContext,
required String errorText,
required String fieldName,
}) = _EditFieldState;
factory EditFieldState.initial(EditFieldContext editContext) => EditFieldState(
editContext: editContext,
errorText: '',
fieldName: editContext.gridField.name,
);
}

View File

@ -1,4 +1,6 @@
import 'package:app_flowy/workspace/application/grid/data.dart';
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.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';
@ -9,24 +11,43 @@ part 'grid_header_bloc.freezed.dart';
class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
final FieldService service;
final GridFieldsListener fieldListener;
GridHeaderBloc({
required GridHeaderData data,
required this.service,
}) : super(GridHeaderState.initial(data.fields)) {
}) : fieldListener = GridFieldsListener(gridId: data.gridId),
super(GridHeaderState.initial(data.fields)) {
on<GridHeaderEvent>(
(event, emit) async {
await event.map(
initial: (_InitialHeader value) async {},
initial: (_InitialHeader value) async {
_startListening();
},
createField: (_CreateField value) {},
insertField: (_InsertField value) {},
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
emit(state.copyWith(fields: value.fields));
},
);
},
);
}
Future<void> _startListening() async {
fieldListener.updateFieldsNotifier.addPublishListener((result) {
result.fold(
(fields) => add(GridHeaderEvent.didReceiveFieldUpdate(fields)),
(err) => Log.error(err),
);
});
fieldListener.start();
}
@override
Future<void> close() async {
await fieldListener.stop();
return super.close();
}
}
@ -36,6 +57,7 @@ class GridHeaderEvent with _$GridHeaderEvent {
const factory GridHeaderEvent.initial() = _InitialHeader;
const factory GridHeaderEvent.createField() = _CreateField;
const factory GridHeaderEvent.insertField({required bool onLeft}) = _InsertField;
const factory GridHeaderEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
}
@freezed

View File

@ -9,14 +9,14 @@ import 'dart:async';
import 'dart:typed_data';
import 'package:app_flowy/core/notification_helper.dart';
// typedef RowsUpdateNotifierValue = Either<List<GridRowData>, FlowyError>;
typedef UpdateFieldNotifiedValue = Either<List<Field>, FlowyError>;
class GridListener {
class GridFieldsListener {
final String gridId;
PublishNotifier<Either<List<Field>, FlowyError>> fieldsUpdateNotifier = PublishNotifier();
PublishNotifier<UpdateFieldNotifiedValue> updateFieldsNotifier = PublishNotifier();
StreamSubscription<SubscribeObject>? _subscription;
GridNotificationParser? _parser;
GridListener({required this.gridId});
GridFieldsListener({required this.gridId});
void start() {
_parser = GridNotificationParser(
@ -33,8 +33,8 @@ class GridListener {
switch (ty) {
case GridNotification.DidUpdateFields:
result.fold(
(payload) => fieldsUpdateNotifier.value = left(RepeatedField.fromBuffer(payload).items),
(error) => fieldsUpdateNotifier.value = right(error),
(payload) => updateFieldsNotifier.value = left(RepeatedField.fromBuffer(payload).items),
(error) => updateFieldsNotifier.value = right(error),
);
break;
default:
@ -45,6 +45,6 @@ class GridListener {
Future<void> stop() async {
_parser = null;
await _subscription?.cancel();
fieldsUpdateNotifier.dispose();
updateFieldsNotifier.dispose();
}
}

View File

@ -29,6 +29,9 @@ class SwitchFieldTypeBloc extends Bloc<SwitchFieldTypeEvent, SwitchFieldTypeStat
(err) => Log.error(err),
);
},
didUpdateTypeOptionData: (_DidUpdateTypeOptionData value) {
emit(state.copyWith(typeOptionData: value.typeOptionData));
},
);
},
);
@ -43,6 +46,7 @@ class SwitchFieldTypeBloc extends Bloc<SwitchFieldTypeEvent, SwitchFieldTypeStat
@freezed
class SwitchFieldTypeEvent with _$SwitchFieldTypeEvent {
const factory SwitchFieldTypeEvent.toFieldType(FieldType fieldType) = _ToFieldType;
const factory SwitchFieldTypeEvent.didUpdateTypeOptionData(Uint8List typeOptionData) = _DidUpdateTypeOptionData;
}
@freezed

View File

@ -1,20 +1,20 @@
import 'dart:typed_data';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
part 'number_bloc.freezed.dart';
class NumberTypeOptionBloc extends Bloc<NumberTypeOptionEvent, NumberTypeOptionState> {
NumberTypeOptionBloc() : super(NumberTypeOptionState.initial()) {
NumberTypeOptionBloc({required NumberTypeOption typeOption}) : super(NumberTypeOptionState.initial(typeOption)) {
on<NumberTypeOptionEvent>(
(event, emit) async {
await event.map(
initial: (_InitialField value) async {},
didSelectFormat: (_DidSelectFormat value) {
state.typeOption.format = value.format;
emit(state);
},
);
},
);
@ -28,12 +28,17 @@ class NumberTypeOptionBloc extends Bloc<NumberTypeOptionEvent, NumberTypeOptionS
@freezed
class NumberTypeOptionEvent with _$NumberTypeOptionEvent {
const factory NumberTypeOptionEvent.initial(Uint8List? typeOptionData) = _InitialField;
const factory NumberTypeOptionEvent.initial() = _InitialField;
const factory NumberTypeOptionEvent.didSelectFormat(NumberFormat format) = _DidSelectFormat;
}
@freezed
class NumberTypeOptionState with _$NumberTypeOptionState {
const factory NumberTypeOptionState() = _NumberTypeOptionState;
const factory NumberTypeOptionState({
required NumberTypeOption typeOption,
}) = _NumberTypeOptionState;
factory NumberTypeOptionState.initial() => NumberTypeOptionState();
factory NumberTypeOptionState.initial(NumberTypeOption typeOption) => NumberTypeOptionState(
typeOption: typeOption,
);
}

View File

@ -8,7 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:equatable/equatable.dart';
import 'grid_block_service.dart';
import 'grid_listenr.dart';
import 'field/grid_listenr.dart';
import 'grid_service.dart';
part 'grid_bloc.freezed.dart';
@ -16,11 +16,11 @@ part 'grid_bloc.freezed.dart';
class GridBloc extends Bloc<GridEvent, GridState> {
final View view;
final GridService service;
late GridListener _gridListener;
late GridFieldsListener _fieldListener;
late GridBlockService _blockService;
GridBloc({required this.view, required this.service}) : super(GridState.initial()) {
_gridListener = GridListener(gridId: view.id);
_fieldListener = GridFieldsListener(gridId: view.id);
on<GridEvent>(
(event, emit) async {
@ -34,10 +34,10 @@ class GridBloc extends Bloc<GridEvent, GridState> {
delete: (_Delete value) {},
rename: (_Rename value) {},
updateDesc: (_Desc value) {},
rowsDidUpdate: (_RowsDidUpdate value) {
didReceiveRowUpdate: (_DidReceiveRowUpdate value) {
emit(state.copyWith(rows: value.rows));
},
fieldsDidUpdate: (_FieldsDidUpdate value) {
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
emit(state.copyWith(fields: value.fields));
},
);
@ -47,19 +47,19 @@ class GridBloc extends Bloc<GridEvent, GridState> {
@override
Future<void> close() async {
await _gridListener.stop();
await _fieldListener.stop();
await _blockService.stop();
return super.close();
}
Future<void> _initGrid(Emitter<GridState> emit) async {
_gridListener.fieldsUpdateNotifier.addPublishListener((result) {
_fieldListener.updateFieldsNotifier.addPublishListener((result) {
result.fold(
(fields) => add(GridEvent.fieldsDidUpdate(fields)),
(fields) => add(GridEvent.didReceiveFieldUpdate(fields)),
(err) => Log.error(err),
);
});
_gridListener.start();
_fieldListener.start();
await _loadGrid(emit);
}
@ -72,7 +72,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
_blockService.blocksUpdateNotifier?.addPublishListener((result) {
result.fold(
(blockMap) => add(GridEvent.rowsDidUpdate(_buildRows(blockMap))),
(blockMap) => add(GridEvent.didReceiveRowUpdate(_buildRows(blockMap))),
(err) => Log.error('$err'),
);
});
@ -128,8 +128,8 @@ class GridEvent with _$GridEvent {
const factory GridEvent.updateDesc(String gridId, String desc) = _Desc;
const factory GridEvent.delete(String gridId) = _Delete;
const factory GridEvent.createRow() = _CreateRow;
const factory GridEvent.rowsDidUpdate(List<GridBlockRow> rows) = _RowsDidUpdate;
const factory GridEvent.fieldsDidUpdate(List<Field> fields) = _FieldsDidUpdate;
const factory GridEvent.didReceiveRowUpdate(List<GridBlockRow> rows) = _DidReceiveRowUpdate;
const factory GridEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
}
@freezed

View File

@ -1,5 +1,6 @@
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
import 'package:app_flowy/workspace/application/grid/grid_bloc.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
@ -17,7 +18,7 @@ typedef CellDataMap = HashMap<String, GridCellData>;
class RowBloc extends Bloc<RowEvent, RowState> {
final RowService rowService;
final RowListener rowlistener;
final RowFieldListener fieldListener;
final GridFieldsListener fieldListener;
RowBloc({required GridRowData rowData, required this.rowlistener})
: rowService = RowService(
@ -25,7 +26,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
blockId: rowData.blockId,
rowId: rowData.rowId,
),
fieldListener = RowFieldListener(
fieldListener = GridFieldsListener(
gridId: rowData.gridId,
),
super(RowState.initial(rowData)) {
@ -65,8 +66,8 @@ class RowBloc extends Bloc<RowEvent, RowState> {
@override
Future<void> close() async {
await rowlistener.close();
await fieldListener.close();
await rowlistener.stop();
await fieldListener.stop();
return super.close();
}
@ -89,7 +90,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
);
});
fieldListener.updateFieldNotifier.addPublishListener((result) {
fieldListener.updateFieldsNotifier.addPublishListener((result) {
result.fold(
(fields) => add(RowEvent.didReceiveFieldUpdate(fields)),
(err) => Log.error(err),

View File

@ -46,49 +46,10 @@ class RowListener {
}
}
Future<void> close() async {
Future<void> stop() async {
_parser = null;
await _subscription?.cancel();
updateCellNotifier.dispose();
updateRowNotifier.dispose();
}
}
class RowFieldListener {
final String gridId;
PublishNotifier<UpdateFieldNotifiedValue> updateFieldNotifier = PublishNotifier();
StreamSubscription<SubscribeObject>? _subscription;
GridNotificationParser? _parser;
RowFieldListener({required this.gridId});
void start() {
_parser = GridNotificationParser(
id: gridId,
callback: (ty, result) {
_handleObservableType(ty, result);
},
);
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
void _handleObservableType(GridNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case GridNotification.DidUpdateFields:
result.fold(
(payload) => updateFieldNotifier.value = left(RepeatedField.fromBuffer(payload).items),
(error) => updateFieldNotifier.value = right(error),
);
break;
default:
break;
}
}
Future<void> close() async {
_parser = null;
await _subscription?.cancel();
updateFieldNotifier.dispose();
}
}

View File

@ -1,6 +1,5 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/grid_bloc.dart';
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';
@ -97,7 +96,6 @@ class _FlowyGridState extends State<FlowyGrid> {
child: ScrollConfiguration(
behavior: const ScrollBehavior().copyWith(scrollbars: false),
child: CustomScrollView(
shrinkWrap: true,
physics: StyledScrollPhysics(),
controller: _scrollController.verticalController,
slivers: [

View File

@ -30,10 +30,6 @@ class _GridRowWidgetState extends State<GridRowWidget> {
Widget build(BuildContext context) {
return BlocProvider.value(
value: _rowBloc,
child: MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (p) => _rowBloc.add(const RowEvent.activeRow()),
onExit: (p) => _rowBloc.add(const RowEvent.disactiveRow()),
child: BlocBuilder<RowBloc, RowState>(
buildWhen: (p, c) => p.rowHeight != c.rowHeight,
builder: (context, state) {
@ -50,8 +46,31 @@ class _GridRowWidgetState extends State<GridRowWidget> {
);
},
),
),
);
// return BlocProvider.value(
// value: _rowBloc,
// child: MouseRegion(
// cursor: SystemMouseCursors.click,
// onEnter: (p) => _rowBloc.add(const RowEvent.activeRow()),
// onExit: (p) => _rowBloc.add(const RowEvent.disactiveRow()),
// child: BlocBuilder<RowBloc, RowState>(
// buildWhen: (p, c) => p.rowHeight != c.rowHeight,
// builder: (context, state) {
// return SizedBox(
// height: _rowBloc.state.rowHeight,
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.stretch,
// children: const [
// _RowLeading(),
// _RowCells(),
// _RowTrailing(),
// ],
// ),
// );
// },
// ),
// ),
// );
}
@override

View File

@ -52,7 +52,7 @@ class _CreateFieldPannelWidget extends StatelessWidget {
child: BlocBuilder<CreateFieldBloc, CreateFieldState>(
builder: (context, state) {
return state.field.fold(
() => const SizedBox(),
() => const SizedBox(width: 200),
(field) => ListView(
shrinkWrap: true,
children: [

View File

@ -95,10 +95,10 @@ class _FieldNameTextField extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<EditFieldBloc, EditFieldState>(
buildWhen: ((previous, current) => previous.editContext.gridField.name == current.editContext.gridField.name),
buildWhen: ((previous, current) => previous.fieldName == current.fieldName),
builder: (context, state) {
return FieldNameTextField(
name: state.editContext.gridField.name,
name: state.fieldName,
errorText: state.errorText,
onNameChanged: (newName) {
context.read<EditFieldBloc>().add(EditFieldEvent.updateFieldName(newName));

View File

@ -46,7 +46,7 @@ class FieldActionItem extends StatelessWidget {
text: FlowyText.medium(action.title(), fontSize: 12),
hoverColor: theme.hover,
onTap: () {
action.run(context, fieldId);
action.run(context);
onTap();
},
leftIcon: svg(action.iconName(), color: theme.iconColor),
@ -83,16 +83,16 @@ extension _FieldActionExtension on FieldAction {
}
}
void run(BuildContext context, String fieldId) {
void run(BuildContext context) {
switch (this) {
case FieldAction.hide:
context.read<EditFieldBloc>().add(EditFieldEvent.hideField(fieldId));
context.read<EditFieldBloc>().add(const EditFieldEvent.hideField());
break;
case FieldAction.duplicate:
context.read<EditFieldBloc>().add(EditFieldEvent.duplicateField(fieldId));
context.read<EditFieldBloc>().add(const EditFieldEvent.duplicateField());
break;
case FieldAction.delete:
context.read<EditFieldBloc>().add(EditFieldEvent.deleteField(fieldId));
context.read<EditFieldBloc>().add(const EditFieldEvent.deleteField());
break;
}
}

View File

@ -11,12 +11,13 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pbserver.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'type_option/number.dart';
typedef SelectFieldCallback = void Function(Field, Uint8List);
class FieldTypeSwitcher extends StatelessWidget {
@ -40,8 +41,11 @@ class FieldTypeSwitcher extends StatelessWidget {
];
final builder = _makeTypeOptionBuild(
state.field.fieldType,
state.typeOptionData,
fieldType: state.field.fieldType,
typeOptionData: state.typeOptionData,
typeOptionDataCallback: (newTypeOptionData) {
context.read<SwitchFieldTypeBloc>().add(SwitchFieldTypeEvent.didUpdateTypeOptionData(newTypeOptionData));
},
);
final typeOptionWidget = builder.customWidget;
@ -77,7 +81,6 @@ class FieldTypeSwitcher extends StatelessWidget {
}
abstract class TypeOptionBuilder {
Uint8List? get typeOptionData;
Widget? get customWidget;
}
@ -85,7 +88,14 @@ abstract class TypeOptionWidget extends StatelessWidget {
const TypeOptionWidget({Key? key}) : super(key: key);
}
TypeOptionBuilder _makeTypeOptionBuild(FieldType fieldType, Uint8List typeOptionData) {
typedef TypeOptionData = Uint8List;
typedef TypeOptionDataCallback = void Function(TypeOptionData typeOptionData);
TypeOptionBuilder _makeTypeOptionBuild({
required FieldType fieldType,
required TypeOptionData typeOptionData,
required TypeOptionDataCallback typeOptionDataCallback,
}) {
switch (fieldType) {
case FieldType.Checkbox:
return CheckboxTypeOptionBuilder(typeOptionData);
@ -94,7 +104,7 @@ TypeOptionBuilder _makeTypeOptionBuild(FieldType fieldType, Uint8List typeOption
case FieldType.MultiSelect:
return MultiSelectTypeOptionBuilder(typeOptionData);
case FieldType.Number:
return NumberTypeOptionBuilder(typeOptionData);
return NumberTypeOptionBuilder(typeOptionData, typeOptionDataCallback);
case FieldType.RichText:
return RichTextTypeOptionBuilder(typeOptionData);
case FieldType.SingleSelect:
@ -107,46 +117,16 @@ TypeOptionBuilder _makeTypeOptionBuild(FieldType fieldType, Uint8List typeOption
class RichTextTypeOptionBuilder extends TypeOptionBuilder {
RichTextTypeOption typeOption;
RichTextTypeOptionBuilder(Uint8List typeOptionData) : typeOption = RichTextTypeOption.fromBuffer(typeOptionData);
@override
Uint8List? get typeOptionData => typeOption.writeToBuffer();
RichTextTypeOptionBuilder(TypeOptionData typeOptionData) : typeOption = RichTextTypeOption.fromBuffer(typeOptionData);
@override
Widget? get customWidget => null;
}
class NumberTypeOptionBuilder extends TypeOptionBuilder {
NumberTypeOption typeOption;
NumberTypeOptionBuilder(Uint8List typeOptionData) : typeOption = NumberTypeOption.fromBuffer(typeOptionData);
@override
Uint8List? get typeOptionData => typeOption.writeToBuffer();
@override
Widget? get customWidget => const NumberTypeOptionWidget();
}
class NumberTypeOptionWidget extends TypeOptionWidget {
const NumberTypeOptionWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<NumberTypeOptionBloc>(),
child: Container(height: 30, color: Colors.green),
);
}
}
class DateTypeOptionBuilder extends TypeOptionBuilder {
DateTypeOption typeOption;
DateTypeOptionBuilder(Uint8List typeOptionData) : typeOption = DateTypeOption.fromBuffer(typeOptionData);
@override
Uint8List? get typeOptionData => typeOption.writeToBuffer();
DateTypeOptionBuilder(TypeOptionData typeOptionData) : typeOption = DateTypeOption.fromBuffer(typeOptionData);
@override
Widget? get customWidget => const DateTypeOptionWidget();
@ -167,10 +147,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
class CheckboxTypeOptionBuilder extends TypeOptionBuilder {
CheckboxTypeOption typeOption;
CheckboxTypeOptionBuilder(Uint8List typeOptionData) : typeOption = CheckboxTypeOption.fromBuffer(typeOptionData);
@override
Uint8List? get typeOptionData => throw UnimplementedError();
CheckboxTypeOptionBuilder(TypeOptionData typeOptionData) : typeOption = CheckboxTypeOption.fromBuffer(typeOptionData);
@override
Widget? get customWidget => null;
@ -179,12 +156,9 @@ class CheckboxTypeOptionBuilder extends TypeOptionBuilder {
class SingleSelectTypeOptionBuilder extends TypeOptionBuilder {
SingleSelectTypeOption typeOption;
SingleSelectTypeOptionBuilder(Uint8List typeOptionData)
SingleSelectTypeOptionBuilder(TypeOptionData typeOptionData)
: typeOption = SingleSelectTypeOption.fromBuffer(typeOptionData);
@override
Uint8List? get typeOptionData => typeOption.writeToBuffer();
@override
Widget? get customWidget => const SingleSelectTypeOptionWidget();
}
@ -204,12 +178,9 @@ class SingleSelectTypeOptionWidget extends TypeOptionWidget {
class MultiSelectTypeOptionBuilder extends TypeOptionBuilder {
MultiSelectTypeOption typeOption;
MultiSelectTypeOptionBuilder(Uint8List typeOptionData)
MultiSelectTypeOptionBuilder(TypeOptionData typeOptionData)
: typeOption = MultiSelectTypeOption.fromBuffer(typeOptionData);
@override
Uint8List? get typeOptionData => typeOption.writeToBuffer();
@override
Widget? get customWidget => const MultiSelectTypeOptionWidget();
}

View File

@ -0,0 +1,123 @@
import 'package:app_flowy/startup/startup.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';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.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 'create_field_pannel.dart';
import 'grid_header_cell.dart';
class GridHeaderDelegate extends SliverPersistentHeaderDelegate {
final String gridId;
final List<Field> fields;
GridHeaderDelegate({required this.gridId, required this.fields});
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return GridHeader(gridId: gridId, fields: fields, key: ObjectKey(fields));
}
@override
double get maxExtent => GridSize.headerHeight;
@override
double get minExtent => GridSize.headerHeight;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
if (oldDelegate is GridHeaderDelegate) {
return fields != oldDelegate.fields;
}
return false;
}
}
class GridHeader extends StatelessWidget {
final List<Field> fields;
final String gridId;
const GridHeader({required this.gridId, required this.fields, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return BlocProvider(
create: (context) {
final bloc = getIt<GridHeaderBloc>(param1: gridId, param2: fields);
bloc.add(const GridHeaderEvent.initial());
return bloc;
},
child: BlocBuilder<GridHeaderBloc, GridHeaderState>(
builder: (context, state) {
final cells = state.fields.map(
(field) => GridHeaderCell(
GridFieldData(gridId: gridId, field: field),
),
);
final row = Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const _HeaderLeading(),
...cells,
_HeaderTrailing(gridId: gridId),
],
);
return Container(color: theme.surface, child: row);
},
),
);
}
}
class _HeaderLeading extends StatelessWidget {
const _HeaderLeading({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: GridSize.leadingHeaderPadding,
);
}
}
class _HeaderTrailing extends StatelessWidget {
final String gridId;
const _HeaderTrailing({required this.gridId, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
return Container(
width: GridSize.trailHeaderPadding,
decoration: BoxDecoration(
border: Border(top: borderSide, bottom: borderSide),
),
padding: GridSize.headerContentInsets,
child: CreateFieldButton(gridId: gridId),
);
}
}
class CreateFieldButton extends StatelessWidget {
final String gridId;
const CreateFieldButton({required this.gridId, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return FlowyButton(
text: const FlowyText.medium('New column', fontSize: 12),
hoverColor: theme.hover,
onTap: () => CreateFieldPannel(gridId: gridId).show(context, gridId),
leftIcon: svg("home/add"),
);
}
}

View File

@ -0,0 +1,36 @@
import 'package:app_flowy/workspace/application/grid/field/field_service.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';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'edit_field_pannel.dart';
class GridHeaderCell extends StatelessWidget {
final GridFieldData fieldData;
const GridHeaderCell(this.fieldData, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
final button = FlowyButton(
hoverColor: theme.hover,
onTap: () => EditFieldPannel.show(context, fieldData),
rightIcon: svg("editor/details", color: theme.iconColor),
text: Padding(padding: GridSize.cellContentInsets, child: FlowyText.medium(fieldData.field.name, fontSize: 12)),
);
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
final decoration = BoxDecoration(border: Border(top: borderSide, right: borderSide, bottom: borderSide));
return Container(
width: fieldData.field.width.toDouble(),
decoration: decoration,
padding: GridSize.headerContentInsets,
child: button,
);
}
}

View File

@ -0,0 +1,165 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/field/type_option/number_bloc.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.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/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:easy_localization/easy_localization.dart' hide NumberFormat;
import 'package:app_flowy/generated/locale_keys.g.dart';
class NumberTypeOptionBuilder extends TypeOptionBuilder {
NumberTypeOption typeOption;
TypeOptionDataCallback typeOptionDataCallback;
NumberTypeOptionBuilder(
TypeOptionData typeOptionData,
this.typeOptionDataCallback,
) : typeOption = NumberTypeOption.fromBuffer(typeOptionData);
@override
Widget? get customWidget => NumberTypeOptionWidget(
typeOption: typeOption,
updateCallback: typeOptionDataCallback,
);
}
class NumberTypeOptionWidget extends TypeOptionWidget {
final TypeOptionDataCallback updateCallback;
final NumberTypeOption typeOption;
const NumberTypeOptionWidget({required this.typeOption, required this.updateCallback, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return BlocProvider(
create: (context) => getIt<NumberTypeOptionBloc>(param1: typeOption),
child: SizedBox(
height: 36,
child: BlocConsumer<NumberTypeOptionBloc, NumberTypeOptionState>(
listener: (context, state) => updateCallback(state.typeOption.writeToBuffer()),
builder: (context, state) {
return FlowyButton(
text: FlowyText.medium(LocaleKeys.grid_field_numberFormat.tr(), fontSize: 12),
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
hoverColor: theme.hover,
onTap: () {
NumberFormatList.show(context, (format) {
context.read<NumberTypeOptionBloc>().add(NumberTypeOptionEvent.didSelectFormat(format));
});
},
rightIcon: svg("grid/more", color: theme.iconColor),
);
},
),
),
);
}
}
typedef _SelectNumberFormatCallback = Function(NumberFormat format);
class NumberFormatList extends StatelessWidget {
final _SelectNumberFormatCallback onSelected;
const NumberFormatList({required this.onSelected, Key? key}) : super(key: key);
static void show(BuildContext context, _SelectNumberFormatCallback onSelected) {
final list = NumberFormatList(onSelected: onSelected);
FlowyOverlay.of(context).insertWithAnchor(
widget: OverlayContainer(
child: list,
constraints: BoxConstraints.loose(const Size(140, 300)),
),
identifier: list.identifier(),
anchorContext: context,
anchorDirection: AnchorDirection.leftWithCenterAligned,
style: FlowyOverlayStyle(blur: false),
anchorOffset: const Offset(-20, 0),
);
}
@override
Widget build(BuildContext context) {
final formatItems = NumberFormat.values.map((format) {
return NumberFormatItem(
format: format,
onSelected: (format) {
onSelected(format);
FlowyOverlay.of(context).remove(identifier());
});
}).toList();
return ListView.separated(
shrinkWrap: true,
controller: ScrollController(),
separatorBuilder: (context, index) {
return const VSpace(10);
},
itemCount: formatItems.length,
itemBuilder: (BuildContext context, int index) {
return formatItems[index];
},
);
}
String identifier() {
return toString();
}
}
class NumberFormatItem extends StatelessWidget {
final NumberFormat format;
final Function(NumberFormat format) onSelected;
const NumberFormatItem({required this.format, required this.onSelected, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return SizedBox(
height: 26,
child: FlowyButton(
text: FlowyText.medium(format.title(), fontSize: 12),
hoverColor: theme.hover,
onTap: () => onSelected(format),
leftIcon: svg(format.iconName(), color: theme.iconColor),
),
);
}
}
extension NumberFormatExtension on NumberFormat {
String title() {
switch (this) {
case NumberFormat.CNY:
return "Yen";
case NumberFormat.EUR:
return "Euro";
case NumberFormat.Number:
return "Numbers";
case NumberFormat.USD:
return "US Dollar";
default:
throw UnimplementedError;
}
}
String iconName() {
switch (this) {
case NumberFormat.CNY:
return "grid/field/yen";
case NumberFormat.EUR:
return "grid/field/euro";
case NumberFormat.Number:
return "grid/field/numbers";
case NumberFormat.USD:
return "grid/field/us_dollar";
default:
throw UnimplementedError;
}
}
}