Browse Source

chore: select option color

appflowy 3 years ago
parent
commit
4b27fef76e
16 changed files with 343 additions and 108 deletions
  1. 66 0
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/edit_option_bloc.dart
  2. 1 1
      frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/right_click_action.dart
  3. 1 3
      frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/disclosure_action.dart
  4. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart
  5. 3 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/create_field_pannel.dart
  6. 41 53
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/edit_option_pannel.dart
  7. 1 1
      frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart
  8. 39 11
      frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_overlay.dart
  9. 8 4
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/selection_type_option.pb.dart
  10. 33 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/selection_type_option.pbenum.dart
  11. 20 2
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/selection_type_option.pbjson.dart
  12. 1 0
      frontend/rust-lib/Cargo.lock
  13. 1 0
      frontend/rust-lib/flowy-grid/Cargo.toml
  14. 92 28
      frontend/rust-lib/flowy-grid/src/protobuf/model/selection_type_option.rs
  15. 12 1
      frontend/rust-lib/flowy-grid/src/protobuf/proto/selection_type_option.proto
  16. 23 3
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs

+ 66 - 0
frontend/app_flowy/lib/workspace/application/grid/field/type_option/edit_option_bloc.dart

@@ -0,0 +1,66 @@
+import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'dart:async';
+import 'package:protobuf/protobuf.dart';
+import 'package:dartz/dartz.dart';
+part 'edit_option_bloc.freezed.dart';
+
+class EditOptionBloc extends Bloc<EditOptionEvent, EditOptionState> {
+  EditOptionBloc({required SelectOption option}) : super(EditOptionState.initial(option)) {
+    on<EditOptionEvent>(
+      (event, emit) async {
+        event.map(
+          updateName: (_UpdateName value) {
+            emit(state.copyWith(option: _updateName(value.name)));
+          },
+          updateColor: (_UpdateColor value) {
+            emit(state.copyWith(option: _updateColor(value.color)));
+          },
+          delete: (_Delete value) {
+            emit(state.copyWith(deleted: const Some(true)));
+          },
+        );
+      },
+    );
+  }
+
+  @override
+  Future<void> close() async {
+    return super.close();
+  }
+
+  SelectOption _updateColor(SelectOptionColor color) {
+    state.option.freeze();
+    return state.option.rebuild((option) {
+      option.color = color;
+    });
+  }
+
+  SelectOption _updateName(String name) {
+    state.option.freeze();
+    return state.option.rebuild((option) {
+      option.name = name;
+    });
+  }
+}
+
+@freezed
+class EditOptionEvent with _$EditOptionEvent {
+  const factory EditOptionEvent.updateName(String name) = _UpdateName;
+  const factory EditOptionEvent.updateColor(SelectOptionColor color) = _UpdateColor;
+  const factory EditOptionEvent.delete() = _Delete;
+}
+
+@freezed
+class EditOptionState with _$EditOptionState {
+  const factory EditOptionState({
+    required SelectOption option,
+    required Option<bool> deleted,
+  }) = _EditOptionState;
+
+  factory EditOptionState.initial(SelectOption option) => EditOptionState(
+        option: option,
+        deleted: none(),
+      );
+}

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/right_click_action.dart

@@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
 
 import 'header.dart';
 
-class AppDisclosureActionSheet with ActionList<DisclosureActionWrapper> implements FlowyOverlayDelegate {
+class AppDisclosureActionSheet with ActionList<DisclosureActionWrapper>, FlowyOverlayDelegate {
   final Function(dartz.Option<AppDisclosureAction>) onSelected;
   final _items = AppDisclosureAction.values.map((action) => DisclosureActionWrapper(action)).toList();
 

+ 1 - 3
frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/disclosure_action.dart

@@ -11,9 +11,7 @@ import 'item.dart';
 
 // [[Widget: LifeCycle]]
 // https://flutterbyexample.com/lesson/stateful-widget-lifecycle
-class ViewDisclosureButton extends StatelessWidget
-    with ActionList<ViewDisclosureActionWrapper>
-    implements FlowyOverlayDelegate {
+class ViewDisclosureButton extends StatelessWidget with ActionList<ViewDisclosureActionWrapper>, FlowyOverlayDelegate {
   final Function() onTap;
   final Function(dartz.Option<ViewDisclosureAction>) onSelected;
   final _items = ViewDisclosureAction.values.map((action) => ViewDisclosureActionWrapper(action)).toList();

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart

@@ -195,7 +195,7 @@ class DocumentShareButton extends StatelessWidget {
   }
 }
 
-class ShareActions with ActionList<ShareActionWrapper> implements FlowyOverlayDelegate {
+class ShareActions with ActionList<ShareActionWrapper>, FlowyOverlayDelegate {
   final Function(dartz.Option<ShareAction>) onSelected;
   final _items = ShareAction.values.map((action) => ShareActionWrapper(action)).toList();
 

+ 3 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/create_field_pannel.dart

@@ -38,6 +38,9 @@ class CreateFieldPannel extends FlowyOverlayDelegate {
   void didRemove() {
     _createFieldBloc.add(const CreateFieldEvent.done());
   }
+
+  @override
+  bool asBarrier() => true;
 }
 
 class _CreateFieldPannelWidget extends StatelessWidget {

+ 41 - 53
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/edit_option_pannel.dart

@@ -50,13 +50,16 @@ class EditSelectOptionPannel extends StatelessWidget {
               const SliverToBoxAdapter(child: VSpace(10)),
               const SliverToBoxAdapter(child: _DeleteTag()),
               const SliverToBoxAdapter(child: TypeOptionSeparator()),
-              const SliverToBoxAdapter(child: SelectOptionColorList()),
+              SliverToBoxAdapter(child: SelectOptionColorList(selectedColor: state.option.color)),
             ];
 
-            return CustomScrollView(
-              slivers: slivers,
-              controller: ScrollController(),
-              physics: StyledScrollPhysics(),
+            return SizedBox(
+              width: 160,
+              child: CustomScrollView(
+                slivers: slivers,
+                controller: ScrollController(),
+                physics: StyledScrollPhysics(),
+              ),
             );
           },
         ),
@@ -102,19 +105,13 @@ class _OptionNameTextField extends StatelessWidget {
 }
 
 class SelectOptionColorList extends StatelessWidget {
-  const SelectOptionColorList({Key? key}) : super(key: key);
+  final SelectOptionColor selectedColor;
+  const SelectOptionColorList({required this.selectedColor, Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
-    final optionItems = SelectOptionColor.values.map((option) {
-      // Color color = option.color();
-      // var hex = option.color.value.toRadixString(16);
-      // if (hex.startsWith('ff')) {
-      //   hex = hex.substring(2);
-      // }
-      // hex = '#$hex';
-
-      return _SelectOptionColorItem(option: option, isSelected: true);
+    final optionItems = SelectOptionColor.values.map((color) {
+      return _SelectOptionColorItem(color: color, isSelected: selectedColor == color);
     }).toList();
 
     return Column(
@@ -150,24 +147,23 @@ class SelectOptionColorList extends StatelessWidget {
 }
 
 class _SelectOptionColorItem extends StatelessWidget {
-  final SelectOptionColor option;
+  final SelectOptionColor color;
   final bool isSelected;
-  const _SelectOptionColorItem({required this.option, required this.isSelected, Key? key}) : super(key: key);
+  const _SelectOptionColorItem({required this.color, required this.isSelected, Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
     Widget? checkmark;
     if (isSelected) {
-      checkmark = svg("grid/details", color: theme.iconColor);
+      checkmark = svg("grid/checkmark");
     }
 
-    final String hex = '#${option.color(context).value.toRadixString(16)}';
     final colorIcon = SizedBox.square(
       dimension: 16,
       child: Container(
         decoration: BoxDecoration(
-          color: option.color(context),
+          color: color.color(context),
           shape: BoxShape.circle,
         ),
       ),
@@ -176,75 +172,67 @@ class _SelectOptionColorItem extends StatelessWidget {
     return SizedBox(
       height: GridSize.typeOptionItemHeight,
       child: FlowyButton(
-        text: FlowyText.medium(option.name(), fontSize: 12),
+        text: FlowyText.medium(color.optionName(), fontSize: 12),
         hoverColor: theme.hover,
         leftIcon: colorIcon,
         rightIcon: checkmark,
         onTap: () {
-          context.read<EditOptionBloc>().add(EditOptionEvent.updateColor(hex));
+          context.read<EditOptionBloc>().add(EditOptionEvent.updateColor(color));
         },
       ),
     );
   }
 }
 
-enum SelectOptionColor {
-  purple,
-  pink,
-  lightPink,
-  orange,
-  yellow,
-  lime,
-  green,
-  aqua,
-  blue,
-}
-
 extension SelectOptionColorExtension on SelectOptionColor {
   Color color(BuildContext context) {
     final theme = context.watch<AppTheme>();
     switch (this) {
-      case SelectOptionColor.purple:
+      case SelectOptionColor.Purple:
         return theme.tint1;
-      case SelectOptionColor.pink:
+      case SelectOptionColor.Pink:
         return theme.tint2;
-      case SelectOptionColor.lightPink:
+      case SelectOptionColor.LightPink:
         return theme.tint3;
-      case SelectOptionColor.orange:
+      case SelectOptionColor.Orange:
         return theme.tint4;
-      case SelectOptionColor.yellow:
+      case SelectOptionColor.Yellow:
         return theme.tint5;
-      case SelectOptionColor.lime:
+      case SelectOptionColor.Lime:
         return theme.tint6;
-      case SelectOptionColor.green:
+      case SelectOptionColor.Green:
         return theme.tint7;
-      case SelectOptionColor.aqua:
+      case SelectOptionColor.Aqua:
         return theme.tint8;
-      case SelectOptionColor.blue:
+      case SelectOptionColor.Blue:
         return theme.tint9;
+      default:
+        throw ArgumentError;
     }
   }
 
-  String name() {
+  String optionName() {
     switch (this) {
-      case SelectOptionColor.purple:
+      case SelectOptionColor.Purple:
         return LocaleKeys.grid_selectOption_purpleColor.tr();
-      case SelectOptionColor.pink:
+      case SelectOptionColor.Pink:
         return LocaleKeys.grid_selectOption_pinkColor.tr();
-      case SelectOptionColor.lightPink:
+      case SelectOptionColor.LightPink:
         return LocaleKeys.grid_selectOption_lightPinkColor.tr();
-      case SelectOptionColor.orange:
+      case SelectOptionColor.Orange:
         return LocaleKeys.grid_selectOption_orangeColor.tr();
-      case SelectOptionColor.yellow:
+      case SelectOptionColor.Yellow:
         return LocaleKeys.grid_selectOption_yellowColor.tr();
-      case SelectOptionColor.lime:
+      case SelectOptionColor.Lime:
         return LocaleKeys.grid_selectOption_limeColor.tr();
-      case SelectOptionColor.green:
+      case SelectOptionColor.Green:
         return LocaleKeys.grid_selectOption_greenColor.tr();
-      case SelectOptionColor.aqua:
+      case SelectOptionColor.Aqua:
         return LocaleKeys.grid_selectOption_aquaColor.tr();
-      case SelectOptionColor.blue:
+      case SelectOptionColor.Blue:
         return LocaleKeys.grid_selectOption_blueColor.tr();
+      default:
+        throw ArgumentError;
     }
   }
 }

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart

@@ -130,7 +130,7 @@ class QuestionBubble extends StatelessWidget {
   }
 }
 
-class QuestionBubbleActionSheet with ActionList<BubbleActionWrapper> implements FlowyOverlayDelegate {
+class QuestionBubbleActionSheet with ActionList<BubbleActionWrapper>, FlowyOverlayDelegate {
   final Function(dartz.Option<BubbleAction>) onSelected;
   final _items = BubbleAction.values.map((action) => BubbleActionWrapper(action)).toList();
 

+ 39 - 11
frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_overlay.dart

@@ -1,10 +1,8 @@
 // ignore_for_file: unused_element
 
-import 'package:dartz/dartz.dart' show Tuple3;
+import 'dart:ui';
 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
@@ -75,6 +73,7 @@ TransitionBuilder overlayManagerBuilder() {
 }
 
 abstract class FlowyOverlayDelegate {
+  bool asBarrier() => false;
   void didRemove();
 }
 
@@ -110,8 +109,20 @@ class FlowyOverlay extends StatefulWidget {
   FlowyOverlayState createState() => FlowyOverlayState();
 }
 
+class OverlayItem {
+  Widget widget;
+  String identifier;
+  FlowyOverlayDelegate? delegate;
+
+  OverlayItem({
+    required this.widget,
+    required this.identifier,
+    this.delegate,
+  });
+}
+
 class FlowyOverlayState extends State<FlowyOverlay> {
-  List<Tuple3<Widget, String, FlowyOverlayDelegate?>> _overlayList = [];
+  List<OverlayItem> _overlayList = [];
   FlowyOverlayStyle style = FlowyOverlayStyle();
 
   /// Insert a overlay widget which frame is set by the widget, not the component.
@@ -181,19 +192,32 @@ class FlowyOverlayState extends State<FlowyOverlay> {
 
   void remove(String identifier) {
     setState(() {
-      final index = _overlayList.indexWhere((ele) => ele.value2 == identifier);
+      final index = _overlayList.indexWhere((item) => item.identifier == identifier);
       if (index != -1) {
-        _overlayList.removeAt(index).value3?.didRemove();
+        _overlayList.removeAt(index).delegate?.didRemove();
       }
     });
   }
 
   void removeAll() {
     setState(() {
-      for (var ele in _overlayList.reversed) {
-        ele.value3?.didRemove();
+      if (_overlayList.isEmpty) {
+        return;
+      }
+
+      final reveredList = _overlayList.reversed.toList();
+      final firstItem = reveredList.removeAt(0);
+      firstItem.delegate?.didRemove();
+      _overlayList.remove(firstItem);
+
+      for (final element in reveredList) {
+        if (element.delegate?.asBarrier() ?? false) {
+          return;
+        } else {
+          element.delegate?.didRemove();
+          _overlayList.remove(element);
+        }
       }
-      _overlayList = [];
     });
   }
 
@@ -252,13 +276,17 @@ class FlowyOverlayState extends State<FlowyOverlay> {
     }
 
     setState(() {
-      _overlayList.add(Tuple3(overlay, identifier, delegate));
+      _overlayList.add(OverlayItem(
+        widget: overlay,
+        identifier: identifier,
+        delegate: delegate,
+      ));
     });
   }
 
   @override
   Widget build(BuildContext context) {
-    final overlays = _overlayList.map((ele) => ele.value1);
+    final overlays = _overlayList.map((item) => item.widget);
     List<Widget> children = <Widget>[widget.child];
 
     Widget? child;

+ 8 - 4
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/selection_type_option.pb.dart

@@ -9,6 +9,10 @@ import 'dart:core' as $core;
 
 import 'package:protobuf/protobuf.dart' as $pb;
 
+import 'selection_type_option.pbenum.dart';
+
+export 'selection_type_option.pbenum.dart';
+
 class SingleSelectTypeOption extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SingleSelectTypeOption', createEmptyInstance: create)
     ..pc<SelectOption>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'options', $pb.PbFieldType.PM, subBuilder: SelectOption.create)
@@ -123,7 +127,7 @@ class SelectOption extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SelectOption', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
-    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'color')
+    ..e<SelectOptionColor>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'color', $pb.PbFieldType.OE, defaultOrMaker: SelectOptionColor.Purple, valueOf: SelectOptionColor.valueOf, enumValues: SelectOptionColor.values)
     ..hasRequiredFields = false
   ;
 
@@ -131,7 +135,7 @@ class SelectOption extends $pb.GeneratedMessage {
   factory SelectOption({
     $core.String? id,
     $core.String? name,
-    $core.String? color,
+    SelectOptionColor? color,
   }) {
     final _result = create();
     if (id != null) {
@@ -185,9 +189,9 @@ class SelectOption extends $pb.GeneratedMessage {
   void clearName() => clearField(2);
 
   @$pb.TagNumber(3)
-  $core.String get color => $_getSZ(2);
+  SelectOptionColor get color => $_getN(2);
   @$pb.TagNumber(3)
-  set color($core.String v) { $_setString(2, v); }
+  set color(SelectOptionColor v) { setField(3, v); }
   @$pb.TagNumber(3)
   $core.bool hasColor() => $_has(2);
   @$pb.TagNumber(3)

+ 33 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/selection_type_option.pbenum.dart

@@ -5,3 +5,36 @@
 // @dart = 2.12
 // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
 
+// ignore_for_file: UNDEFINED_SHOWN_NAME
+import 'dart:core' as $core;
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class SelectOptionColor extends $pb.ProtobufEnum {
+  static const SelectOptionColor Purple = SelectOptionColor._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Purple');
+  static const SelectOptionColor Pink = SelectOptionColor._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Pink');
+  static const SelectOptionColor LightPink = SelectOptionColor._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'LightPink');
+  static const SelectOptionColor Orange = SelectOptionColor._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Orange');
+  static const SelectOptionColor Yellow = SelectOptionColor._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Yellow');
+  static const SelectOptionColor Lime = SelectOptionColor._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Lime');
+  static const SelectOptionColor Green = SelectOptionColor._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Green');
+  static const SelectOptionColor Aqua = SelectOptionColor._(7, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Aqua');
+  static const SelectOptionColor Blue = SelectOptionColor._(8, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Blue');
+
+  static const $core.List<SelectOptionColor> values = <SelectOptionColor> [
+    Purple,
+    Pink,
+    LightPink,
+    Orange,
+    Yellow,
+    Lime,
+    Green,
+    Aqua,
+    Blue,
+  ];
+
+  static final $core.Map<$core.int, SelectOptionColor> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static SelectOptionColor? valueOf($core.int value) => _byValue[value];
+
+  const SelectOptionColor._($core.int v, $core.String n) : super(v, n);
+}
+

+ 20 - 2
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/selection_type_option.pbjson.dart

@@ -8,6 +8,24 @@
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
 import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use selectOptionColorDescriptor instead')
+const SelectOptionColor$json = const {
+  '1': 'SelectOptionColor',
+  '2': const [
+    const {'1': 'Purple', '2': 0},
+    const {'1': 'Pink', '2': 1},
+    const {'1': 'LightPink', '2': 2},
+    const {'1': 'Orange', '2': 3},
+    const {'1': 'Yellow', '2': 4},
+    const {'1': 'Lime', '2': 5},
+    const {'1': 'Green', '2': 6},
+    const {'1': 'Aqua', '2': 7},
+    const {'1': 'Blue', '2': 8},
+  ],
+};
+
+/// Descriptor for `SelectOptionColor`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List selectOptionColorDescriptor = $convert.base64Decode('ChFTZWxlY3RPcHRpb25Db2xvchIKCgZQdXJwbGUQABIICgRQaW5rEAESDQoJTGlnaHRQaW5rEAISCgoGT3JhbmdlEAMSCgoGWWVsbG93EAQSCAoETGltZRAFEgkKBUdyZWVuEAYSCAoEQXF1YRAHEggKBEJsdWUQCA==');
 @$core.Deprecated('Use singleSelectTypeOptionDescriptor instead')
 const SingleSelectTypeOption$json = const {
   '1': 'SingleSelectTypeOption',
@@ -36,9 +54,9 @@ const SelectOption$json = const {
   '2': const [
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
     const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
-    const {'1': 'color', '3': 3, '4': 1, '5': 9, '10': 'color'},
+    const {'1': 'color', '3': 3, '4': 1, '5': 14, '6': '.SelectOptionColor', '10': 'color'},
   ],
 };
 
 /// Descriptor for `SelectOption`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List selectOptionDescriptor = $convert.base64Decode('CgxTZWxlY3RPcHRpb24SDgoCaWQYASABKAlSAmlkEhIKBG5hbWUYAiABKAlSBG5hbWUSFAoFY29sb3IYAyABKAlSBWNvbG9y');
+final $typed_data.Uint8List selectOptionDescriptor = $convert.base64Decode('CgxTZWxlY3RPcHRpb24SDgoCaWQYASABKAlSAmlkEhIKBG5hbWUYAiABKAlSBG5hbWUSKAoFY29sb3IYAyABKA4yEi5TZWxlY3RPcHRpb25Db2xvclIFY29sb3I=');

+ 1 - 0
frontend/rust-lib/Cargo.lock

@@ -956,6 +956,7 @@ dependencies = [
  "rusty-money",
  "serde",
  "serde_json",
+ "serde_repr",
  "strum",
  "strum_macros",
  "tokio",

+ 1 - 0
frontend/rust-lib/flowy-grid/Cargo.toml

@@ -33,6 +33,7 @@ tokio = {version = "1", features = ["sync"]}
 rayon = "1.5"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = {version = "1.0"}
+serde_repr = "0.1"
 
 [dev-dependencies]
 flowy-test = { path = "../flowy-test" }

+ 92 - 28
frontend/rust-lib/flowy-grid/src/protobuf/model/selection_type_option.rs

@@ -430,7 +430,7 @@ pub struct SelectOption {
     // message fields
     pub id: ::std::string::String,
     pub name: ::std::string::String,
-    pub color: ::std::string::String,
+    pub color: SelectOptionColor,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -499,31 +499,20 @@ impl SelectOption {
         ::std::mem::replace(&mut self.name, ::std::string::String::new())
     }
 
-    // string color = 3;
+    // .SelectOptionColor color = 3;
 
 
-    pub fn get_color(&self) -> &str {
-        &self.color
+    pub fn get_color(&self) -> SelectOptionColor {
+        self.color
     }
     pub fn clear_color(&mut self) {
-        self.color.clear();
+        self.color = SelectOptionColor::Purple;
     }
 
     // Param is passed by value, moved
-    pub fn set_color(&mut self, v: ::std::string::String) {
+    pub fn set_color(&mut self, v: SelectOptionColor) {
         self.color = v;
     }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_color(&mut self) -> &mut ::std::string::String {
-        &mut self.color
-    }
-
-    // Take field
-    pub fn take_color(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.color, ::std::string::String::new())
-    }
 }
 
 impl ::protobuf::Message for SelectOption {
@@ -542,7 +531,7 @@ impl ::protobuf::Message for SelectOption {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
                 },
                 3 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.color)?;
+                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.color, 3, &mut self.unknown_fields)?
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -562,8 +551,8 @@ impl ::protobuf::Message for SelectOption {
         if !self.name.is_empty() {
             my_size += ::protobuf::rt::string_size(2, &self.name);
         }
-        if !self.color.is_empty() {
-            my_size += ::protobuf::rt::string_size(3, &self.color);
+        if self.color != SelectOptionColor::Purple {
+            my_size += ::protobuf::rt::enum_size(3, self.color);
         }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
@@ -577,8 +566,8 @@ impl ::protobuf::Message for SelectOption {
         if !self.name.is_empty() {
             os.write_string(2, &self.name)?;
         }
-        if !self.color.is_empty() {
-            os.write_string(3, &self.color)?;
+        if self.color != SelectOptionColor::Purple {
+            os.write_enum(3, ::protobuf::ProtobufEnum::value(&self.color))?;
         }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
@@ -628,7 +617,7 @@ impl ::protobuf::Message for SelectOption {
                 |m: &SelectOption| { &m.name },
                 |m: &mut SelectOption| { &mut m.name },
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<SelectOptionColor>>(
                 "color",
                 |m: &SelectOption| { &m.color },
                 |m: &mut SelectOption| { &mut m.color },
@@ -651,7 +640,7 @@ impl ::protobuf::Clear for SelectOption {
     fn clear(&mut self) {
         self.id.clear();
         self.name.clear();
-        self.color.clear();
+        self.color = SelectOptionColor::Purple;
         self.unknown_fields.clear();
     }
 }
@@ -668,15 +657,90 @@ impl ::protobuf::reflect::ProtobufValue for SelectOption {
     }
 }
 
+#[derive(Clone,PartialEq,Eq,Debug,Hash)]
+pub enum SelectOptionColor {
+    Purple = 0,
+    Pink = 1,
+    LightPink = 2,
+    Orange = 3,
+    Yellow = 4,
+    Lime = 5,
+    Green = 6,
+    Aqua = 7,
+    Blue = 8,
+}
+
+impl ::protobuf::ProtobufEnum for SelectOptionColor {
+    fn value(&self) -> i32 {
+        *self as i32
+    }
+
+    fn from_i32(value: i32) -> ::std::option::Option<SelectOptionColor> {
+        match value {
+            0 => ::std::option::Option::Some(SelectOptionColor::Purple),
+            1 => ::std::option::Option::Some(SelectOptionColor::Pink),
+            2 => ::std::option::Option::Some(SelectOptionColor::LightPink),
+            3 => ::std::option::Option::Some(SelectOptionColor::Orange),
+            4 => ::std::option::Option::Some(SelectOptionColor::Yellow),
+            5 => ::std::option::Option::Some(SelectOptionColor::Lime),
+            6 => ::std::option::Option::Some(SelectOptionColor::Green),
+            7 => ::std::option::Option::Some(SelectOptionColor::Aqua),
+            8 => ::std::option::Option::Some(SelectOptionColor::Blue),
+            _ => ::std::option::Option::None
+        }
+    }
+
+    fn values() -> &'static [Self] {
+        static values: &'static [SelectOptionColor] = &[
+            SelectOptionColor::Purple,
+            SelectOptionColor::Pink,
+            SelectOptionColor::LightPink,
+            SelectOptionColor::Orange,
+            SelectOptionColor::Yellow,
+            SelectOptionColor::Lime,
+            SelectOptionColor::Green,
+            SelectOptionColor::Aqua,
+            SelectOptionColor::Blue,
+        ];
+        values
+    }
+
+    fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<SelectOptionColor>("SelectOptionColor", file_descriptor_proto())
+        })
+    }
+}
+
+impl ::std::marker::Copy for SelectOptionColor {
+}
+
+impl ::std::default::Default for SelectOptionColor {
+    fn default() -> Self {
+        SelectOptionColor::Purple
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for SelectOptionColor {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
+    }
+}
+
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\x1bselection_type_option.proto\"f\n\x16SingleSelectTypeOption\x12'\n\
     \x07options\x18\x01\x20\x03(\x0b2\r.SelectOptionR\x07options\x12#\n\rdis\
     able_color\x18\x02\x20\x01(\x08R\x0cdisableColor\"e\n\x15MultiSelectType\
     Option\x12'\n\x07options\x18\x01\x20\x03(\x0b2\r.SelectOptionR\x07option\
-    s\x12#\n\rdisable_color\x18\x02\x20\x01(\x08R\x0cdisableColor\"H\n\x0cSe\
-    lectOption\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04name\
-    \x18\x02\x20\x01(\tR\x04name\x12\x14\n\x05color\x18\x03\x20\x01(\tR\x05c\
-    olorb\x06proto3\
+    s\x12#\n\rdisable_color\x18\x02\x20\x01(\x08R\x0cdisableColor\"\\\n\x0cS\
+    electOption\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04name\
+    \x18\x02\x20\x01(\tR\x04name\x12(\n\x05color\x18\x03\x20\x01(\x0e2\x12.S\
+    electOptionColorR\x05color*y\n\x11SelectOptionColor\x12\n\n\x06Purple\
+    \x10\0\x12\x08\n\x04Pink\x10\x01\x12\r\n\tLightPink\x10\x02\x12\n\n\x06O\
+    range\x10\x03\x12\n\n\x06Yellow\x10\x04\x12\x08\n\x04Lime\x10\x05\x12\t\
+    \n\x05Green\x10\x06\x12\x08\n\x04Aqua\x10\x07\x12\x08\n\x04Blue\x10\x08b\
+    \x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 12 - 1
frontend/rust-lib/flowy-grid/src/protobuf/proto/selection_type_option.proto

@@ -11,5 +11,16 @@ message MultiSelectTypeOption {
 message SelectOption {
     string id = 1;
     string name = 2;
-    string color = 3;
+    SelectOptionColor color = 3;
+}
+enum SelectOptionColor {
+    Purple = 0;
+    Pink = 1;
+    LightPink = 2;
+    Orange = 3;
+    Yellow = 4;
+    Lime = 5;
+    Green = 6;
+    Aqua = 7;
+    Blue = 8;
 }

+ 23 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs

@@ -3,7 +3,7 @@ use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use crate::services::row::CellDataSerde;
 use crate::services::util::*;
 use bytes::Bytes;
-use flowy_derive::ProtoBuf;
+use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::{FieldMeta, FieldType};
 use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
@@ -151,7 +151,7 @@ pub struct SelectOption {
     pub name: String,
 
     #[pb(index = 3)]
-    pub color: String,
+    pub color: SelectOptionColor,
 }
 
 impl SelectOption {
@@ -159,11 +159,31 @@ impl SelectOption {
         SelectOption {
             id: uuid(),
             name: name.to_owned(),
-            color: "".to_string(),
+            color: SelectOptionColor::default(),
         }
     }
 }
 
+#[derive(ProtoBuf_Enum, Serialize, Deserialize, Debug, Clone)]
+#[repr(u8)]
+pub enum SelectOptionColor {
+    Purple = 0,
+    Pink = 1,
+    LightPink = 2,
+    Orange = 3,
+    Yellow = 4,
+    Lime = 5,
+    Green = 6,
+    Aqua = 7,
+    Blue = 8,
+}
+
+impl std::default::Default for SelectOptionColor {
+    fn default() -> Self {
+        SelectOptionColor::Purple
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};