mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: number field
This commit is contained in:
parent
8b7eee46bb
commit
35e27b579f
3
frontend/app_flowy/assets/images/grid/field/euro.svg
Normal file
3
frontend/app_flowy/assets/images/grid/field/euro.svg
Normal 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 |
3
frontend/app_flowy/assets/images/grid/field/numbers.svg
Normal file
3
frontend/app_flowy/assets/images/grid/field/numbers.svg
Normal 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 |
@ -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 |
3
frontend/app_flowy/assets/images/grid/field/yen.svg
Normal file
3
frontend/app_flowy/assets/images/grid/field/yen.svg
Normal 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 |
@ -152,9 +152,10 @@
|
||||
"textFieldName": "Text",
|
||||
"checkboxFieldName": "Checkbox",
|
||||
"dateFieldName": "Date",
|
||||
"numberFieldName": "Number",
|
||||
"numberFieldName": "Numbers",
|
||||
"singleSelectFieldName": "Select",
|
||||
"multiSelectFieldName": "Multiselect"
|
||||
"multiSelectFieldName": "Multiselect",
|
||||
"numberFormat": " Number format"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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: [
|
||||
|
@ -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
|
||||
|
@ -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: [
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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"),
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user