From ed61091c0dc52f983112813bdbed14227d152316 Mon Sep 17 00:00:00 2001 From: birjuvachhani Date: Mon, 3 Jul 2023 18:39:56 +0530 Subject: [PATCH 1/4] Tab Bar #1 - Implement tab bar node, transformers, codegen, settings and library section. --- lib/src/api/models/paint.dart | 12 +- lib/src/api/node_json_converter.dart | 1 + lib/src/api/nodes/nodes.dart | 1 + lib/src/api/nodes/tab_bar_node.dart | 276 ++++++++++++++++++++++++++ lib/src/api/nodes/tab_bar_node.g.dart | 259 ++++++++++++++++++++++++ 5 files changed, 543 insertions(+), 6 deletions(-) create mode 100644 lib/src/api/nodes/tab_bar_node.dart create mode 100644 lib/src/api/nodes/tab_bar_node.g.dart diff --git a/lib/src/api/models/paint.dart b/lib/src/api/models/paint.dart index fc668d2..258c281 100644 --- a/lib/src/api/models/paint.dart +++ b/lib/src/api/models/paint.dart @@ -212,7 +212,7 @@ class PaintModel with EquatableMixin, SerializableMixin { ); /// Create a Solid Paint with only the required properties. - PaintModel.solid({ + const PaintModel.solid({ required this.id, this.visible = true, this.opacity = 1, @@ -238,7 +238,7 @@ class PaintModel with EquatableMixin, SerializableMixin { assetID = null; /// Create an Image Paint with only the required properties. - PaintModel.image({ + const PaintModel.image({ required this.id, required this.downloadUrl, required this.fit, @@ -266,7 +266,7 @@ class PaintModel with EquatableMixin, SerializableMixin { color = null; /// Creates [PaintModel] with linear gradient. - PaintModel.linearGradient({ + const PaintModel.linearGradient({ required this.id, this.visible = true, this.opacity = 1, @@ -292,7 +292,7 @@ class PaintModel with EquatableMixin, SerializableMixin { assetID = null; /// Creates [PaintModel] with radial gradient. - PaintModel.radialGradient({ + const PaintModel.radialGradient({ required this.id, this.visible = true, this.opacity = 1, @@ -318,7 +318,7 @@ class PaintModel with EquatableMixin, SerializableMixin { assetID = null; /// Creates [PaintModel] with angular gradient. - PaintModel.angularGradient({ + const PaintModel.angularGradient({ required this.id, this.visible = true, this.opacity = 1, @@ -344,7 +344,7 @@ class PaintModel with EquatableMixin, SerializableMixin { assetID = null; /// Creates [PaintModel] with given data. - PaintModel({ + const PaintModel({ required this.id, required this.type, this.visible = true, diff --git a/lib/src/api/node_json_converter.dart b/lib/src/api/node_json_converter.dart index bea8e93..a7abff3 100644 --- a/lib/src/api/node_json_converter.dart +++ b/lib/src/api/node_json_converter.dart @@ -72,6 +72,7 @@ class NodeJsonConverter implements JsonConverter { 'variance': VarianceNode.fromJson, 'listView': ListViewNode.fromJson, 'pageView': PageViewNode.fromJson, + 'tabBar': TabBarNode.fromJson, }; } diff --git a/lib/src/api/nodes/nodes.dart b/lib/src/api/nodes/nodes.dart index ef7fb3e..d261df4 100644 --- a/lib/src/api/nodes/nodes.dart +++ b/lib/src/api/nodes/nodes.dart @@ -39,3 +39,4 @@ export 'variance_node.dart'; export 'web_view_google_maps_properties.dart'; export 'web_view_node.dart'; export 'web_view_twitter_properties.dart'; +export 'tab_bar_node.dart'; diff --git a/lib/src/api/nodes/tab_bar_node.dart b/lib/src/api/nodes/tab_bar_node.dart new file mode 100644 index 0000000..f23c715 --- /dev/null +++ b/lib/src/api/nodes/tab_bar_node.dart @@ -0,0 +1,276 @@ +// Copyright (c) 2022, Codelessly. +// All rights reserved. Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import '../mixins.dart'; +import '../models/models.dart'; +import 'nodes.dart'; + +part 'tab_bar_node.g.dart'; + +/// Represents tabs in UI. +@JsonSerializable() +class TabBarNode extends SceneNode with CustomPropertiesMixin, ScrollableMixin { + @override + final String type = 'tabBar'; + + /// The properties of the TabBar]. + TabBarProperties properties; + + /// The index of the initially selected tab. + int initialIndex = 0; + + @override + bool get supportsPadding => true; + + /// Creates a [TabBarNode] with the given data. + TabBarNode({ + bool? value, + required super.id, + required super.name, + required super.basicBoxLocal, + super.outerBoxLocal, + super.retainedOuterBoxLocal, + super.visible, + super.rotationDegrees, + super.alignment, + super.margin, + super.padding, + super.horizontalFit, + super.verticalFit, + super.flex, + super.constraints, + super.edgePins, + super.aspectRatioLock, + super.positioningMode, + super.parentID, + super.reactions, + super.variables, + required this.properties, + bool isScrollable = false, + ScrollPhysicsC physics = ScrollPhysicsC.alwaysScrollableScrollPhysics, + this.initialIndex = 0, + }) { + setScrollableMixin( + isScrollable: isScrollable, + scrollDirection: AxisC.horizontal, + reverse: false, + physics: physics, + primary: false, + shrinkWrap: false, + keyboardDismissBehavior: ScrollViewKeyboardDismissBehaviorC.manual, + useFlutterListView: false, + ); + } + + /// Creates a [TabBarNode] from a JSON object. + factory TabBarNode.fromJson(Map json) => _$TabBarNodeFromJson(json); + + @override + Map toJson() => _$TabBarNodeToJson(this); +} + +/// Represents the size of the tab indicator. +enum TabBarIndicatorSizeEnum { + /// The tab indicator's bounds are as wide as the space occupied by the tab + /// in the tab bar: from the right edge of the previous tab to the left edge + /// of the next tab. + tab, + + /// The tab's bounds are only as wide as the (centered) tab widget itself. + /// + /// This value is used to align the tab's label, typically a [Tab] + /// widget's text or icon, with the selected tab indicator. + label, +} + +/// The properties of a [TabBarNode]. +@JsonSerializable() +class TabBarProperties with SerializableMixin, EquatableMixin { + /// The tabs of the [TabBarNode]. + List tabs; + + /// The color of the tab indicator. + ColorRGBA? indicatorColor; + + /// The thickness of the tab indicator. + double indicatorWeight; + + /// The size of the tab indicator. + TabBarIndicatorSizeEnum indicatorSize; + + /// The color of the tab label. + ColorRGBA? labelColor; + + /// The style of the tab label. + TextProp labelStyle; + + /// The color of the unselected tab label. + ColorRGBA? unselectedLabelColor; + + /// The style of the unselected tab label. + TextProp unselectedLabelStyle; + + /// The color of the tab overlay. + ColorRGBA? overlayColor; + + /// The padding of the tab indicator. + EdgeInsetsModel indicatorPadding; + + /// The padding of the tab label. + EdgeInsetsModel labelPadding; + + /// The color of the tab divider. + ColorRGBA? dividerColor; + + /// Creates a new [TabBarProperties] instance. + TabBarProperties({ + List tabs = const [], + this.indicatorColor = ColorRGBA.black, + this.indicatorWeight = 2.0, + this.indicatorSize = TabBarIndicatorSizeEnum.tab, + this.labelColor = ColorRGBA.black, + this.unselectedLabelColor = ColorRGBA.black, + TextProp? labelStyle, + TextProp? unselectedLabelStyle, + this.overlayColor = ColorRGBA.grey10, + this.indicatorPadding = EdgeInsetsModel.zero, + this.dividerColor = ColorRGBA.black, + this.labelPadding = EdgeInsetsModel.zero, + }) : tabs = [...tabs], + labelStyle = labelStyle ?? TextProp(), + unselectedLabelStyle = unselectedLabelStyle ?? TextProp(); + + /// Creates a copy of this [LTabBarProperties] instance with the given value + /// overrides. + TabBarProperties copyWith({ + List? tabs, + ColorRGBA? indicatorColor, + double? indicatorWeight, + TabBarIndicatorSizeEnum? indicatorSize, + ColorRGBA? labelColor, + TextProp? labelStyle, + ColorRGBA? unselectedLabelColor, + TextProp? unselectedLabelStyle, + ColorRGBA? overlayColor, + EdgeInsetsModel? indicatorPadding, + EdgeInsetsModel? labelPadding, + ColorRGBA? dividerColor, + }) { + return TabBarProperties( + tabs: tabs ?? this.tabs, + indicatorColor: indicatorColor ?? this.indicatorColor, + indicatorWeight: indicatorWeight ?? this.indicatorWeight, + indicatorSize: indicatorSize ?? this.indicatorSize, + labelColor: labelColor ?? this.labelColor, + labelStyle: labelStyle ?? this.labelStyle, + unselectedLabelColor: unselectedLabelColor ?? this.unselectedLabelColor, + unselectedLabelStyle: unselectedLabelStyle ?? this.unselectedLabelStyle, + overlayColor: overlayColor ?? this.overlayColor, + indicatorPadding: indicatorPadding ?? this.indicatorPadding, + labelPadding: labelPadding ?? this.labelPadding, + dividerColor: dividerColor ?? this.dividerColor, + ); + } + + @override + Map toJson() => _$TabBarPropertiesToJson(this); + + /// Creates a [TabBarProperties] from a JSON object. + factory TabBarProperties.fromJson(Map json) => + _$TabBarPropertiesFromJson(json); + + @override + List get props => [ + tabs, + indicatorColor, + indicatorWeight, + indicatorSize, + labelColor, + labelStyle, + unselectedLabelColor, + unselectedLabelStyle, + overlayColor, + indicatorPadding, + labelPadding, + dividerColor, + ]; +} + +/// Represents a tab in a [TabBarNode]. +@JsonSerializable() +class TabItem with EquatableMixin, SerializableMixin { + /// ID of the tab. + final String id; + + /// Label of the tab. + String label; + + /// Alignment of the label. + TextAlignHorizontalEnum labelAlignment; + + /// Style of the label. + TextProp labelStyle; + + /// Describes the position of the icon. + IconPlacementEnum placement; + + /// Space between the icon and the label. + double gap; + + /// Icon to display in the button. + MultiSourceIconModel icon; + + /// Creates a new [TabItem] instance. + TabItem({ + required this.id, + required this.label, + this.labelAlignment = TextAlignHorizontalEnum.center, + TextProp? labelStyle, + this.placement = IconPlacementEnum.start, + this.gap = 8, + this.icon = const MultiSourceIconModel(size: 20, color: null), + }) : labelStyle = + labelStyle ?? TextProp.general(fills: List.empty(growable: true)); + + /// Creates a copy of this [TabItem] instance with the given values. + TabItem copyWith({ + String? id, + String? label, + TextAlignHorizontalEnum? labelAlignment, + TextProp? labelStyle, + IconPlacementEnum? placement, + double? gap, + MultiSourceIconModel? icon, + }) { + return TabItem( + id: id ?? this.id, + label: label ?? this.label, + labelAlignment: labelAlignment ?? this.labelAlignment, + labelStyle: labelStyle ?? this.labelStyle, + placement: placement ?? this.placement, + gap: gap ?? this.gap, + icon: icon ?? this.icon, + ); + } + + /// Creates a [TabItem] from a JSON object. + factory TabItem.fromJson(Map json) => _$TabItemFromJson(json); + + @override + Map toJson() => _$TabItemToJson(this); + + @override + List get props => [ + id, + label, + labelAlignment, + labelStyle, + placement, + gap, + icon, + ]; +} diff --git a/lib/src/api/nodes/tab_bar_node.g.dart b/lib/src/api/nodes/tab_bar_node.g.dart new file mode 100644 index 0000000..41895f5 --- /dev/null +++ b/lib/src/api/nodes/tab_bar_node.g.dart @@ -0,0 +1,259 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'tab_bar_node.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +TabBarNode _$TabBarNodeFromJson(Map json) => TabBarNode( + id: json['id'] as String, + name: json['name'] as String, + basicBoxLocal: NodeBox.fromJson(json['basicBoxLocal'] as Map), + outerBoxLocal: json['outerBoxLocal'] == null + ? null + : OuterNodeBox.fromJson(json['outerBoxLocal'] as Map), + visible: json['visible'] as bool? ?? true, + rotationDegrees: + json['rotation'] == null ? 0 : castRotation(json['rotation']), + alignment: json['alignment'] == null + ? AlignmentModel.none + : AlignmentModel.fromJson(json['alignment'] as Map), + margin: json['margin'] == null + ? EdgeInsetsModel.zero + : EdgeInsetsModel.fromJson(json['margin'] as Map), + padding: json['padding'] == null + ? EdgeInsetsModel.zero + : EdgeInsetsModel.fromJson(json['padding'] as Map), + horizontalFit: + $enumDecodeNullable(_$SizeFitEnumMap, json['horizontalFit']) ?? + SizeFit.fixed, + verticalFit: $enumDecodeNullable(_$SizeFitEnumMap, json['verticalFit']) ?? + SizeFit.fixed, + flex: json['flex'] as int? ?? 1, + constraints: json['constraints'] == null + ? const BoxConstraintsModel() + : BoxConstraintsModel.fromJson(json['constraints'] as Map), + edgePins: json['edgePins'] == null + ? EdgePinsModel.standard + : EdgePinsModel.fromJson( + Map.from(json['edgePins'] as Map)), + aspectRatioLock: json['aspectRatioLock'] as bool? ?? false, + positioningMode: $enumDecodeNullable( + _$PositioningModeEnumMap, json['positioningMode']) ?? + PositioningMode.align, + reactions: (json['reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList() ?? + const [], + variables: (json['variables'] as Map?)?.map( + (k, e) => MapEntry(k as String, e as String), + ) ?? + {}, + properties: TabBarProperties.fromJson(json['properties'] as Map), + isScrollable: json['isScrollable'] as bool? ?? false, + physics: $enumDecodeNullable(_$ScrollPhysicsCEnumMap, json['physics']) ?? + ScrollPhysicsC.alwaysScrollableScrollPhysics, + initialIndex: json['initialIndex'] as int? ?? 0, + ) + ..multipleVariables = (json['multipleVariables'] as Map?)?.map( + (k, e) => MapEntry(k as String, + (e as List).map((e) => e as String).toList()), + ) ?? + {} + ..widthFactor = (json['widthFactor'] as num?)?.toDouble() + ..heightFactor = (json['heightFactor'] as num?)?.toDouble() + ..scrollDirection = $enumDecode(_$AxisCEnumMap, json['scrollDirection']) + ..reverse = json['reverse'] as bool + ..primary = json['primary'] as bool + ..keyboardDismissBehavior = $enumDecode( + _$ScrollViewKeyboardDismissBehaviorCEnumMap, + json['keyboardDismissBehavior']) + ..useFlutterListView = json['useFlutterListView'] as bool + ..type = json['type'] as String; + +Map _$TabBarNodeToJson(TabBarNode instance) { + final val = { + 'variables': instance.variables, + 'multipleVariables': instance.multipleVariables, + 'id': instance.id, + 'name': instance.name, + 'visible': instance.visible, + 'constraints': instance.constraints.toJson(), + 'edgePins': instance.edgePins.toJson(), + 'positioningMode': _$PositioningModeEnumMap[instance.positioningMode]!, + 'horizontalFit': _$SizeFitEnumMap[instance.horizontalFit]!, + 'verticalFit': _$SizeFitEnumMap[instance.verticalFit]!, + 'flex': instance.flex, + 'aspectRatioLock': instance.aspectRatioLock, + 'alignment': instance.alignment.toJson(), + 'reactions': instance.reactions.map((e) => e.toJson()).toList(), + 'outerBoxLocal': instance.outerBoxLocal.toJson(), + 'basicBoxLocal': instance.basicBoxLocal.toJson(), + 'margin': instance.margin.toJson(), + 'padding': instance.padding.toJson(), + 'rotation': instance.rotationDegrees, + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('widthFactor', instance.widthFactor); + writeNotNull('heightFactor', instance.heightFactor); + val['isScrollable'] = instance.isScrollable; + val['scrollDirection'] = _$AxisCEnumMap[instance.scrollDirection]!; + val['reverse'] = instance.reverse; + val['primary'] = instance.primary; + val['physics'] = _$ScrollPhysicsCEnumMap[instance.physics]!; + val['keyboardDismissBehavior'] = _$ScrollViewKeyboardDismissBehaviorCEnumMap[ + instance.keyboardDismissBehavior]!; + val['useFlutterListView'] = instance.useFlutterListView; + val['type'] = instance.type; + val['properties'] = instance.properties.toJson(); + val['initialIndex'] = instance.initialIndex; + return val; +} + +const _$SizeFitEnumMap = { + SizeFit.locked: 'locked', + SizeFit.fixed: 'fixed', + SizeFit.expanded: 'expanded', + SizeFit.flexible: 'flexible', + SizeFit.shrinkWrap: 'shrinkWrap', +}; + +const _$PositioningModeEnumMap = { + PositioningMode.align: 'align', + PositioningMode.pin: 'pin', +}; + +const _$ScrollPhysicsCEnumMap = { + ScrollPhysicsC.alwaysScrollableScrollPhysics: 'alwaysScrollableScrollPhysics', + ScrollPhysicsC.bouncingScrollPhysics: 'bouncingScrollPhysics', + ScrollPhysicsC.clampingScrollPhysics: 'clampingScrollPhysics', + ScrollPhysicsC.neverScrollableScrollPhysics: 'neverScrollableScrollPhysics', + ScrollPhysicsC.rangeMaintainingScrollPhysics: 'rangeMaintainingScrollPhysics', +}; + +const _$AxisCEnumMap = { + AxisC.horizontal: 'horizontal', + AxisC.vertical: 'vertical', +}; + +const _$ScrollViewKeyboardDismissBehaviorCEnumMap = { + ScrollViewKeyboardDismissBehaviorC.manual: 'manual', + ScrollViewKeyboardDismissBehaviorC.onDrag: 'onDrag', +}; + +TabBarProperties _$TabBarPropertiesFromJson(Map json) => TabBarProperties( + tabs: (json['tabs'] as List?) + ?.map((e) => TabItem.fromJson(e as Map)) + .toList() ?? + const [], + indicatorColor: json['indicatorColor'] == null + ? ColorRGBA.black + : ColorRGBA.fromJson(json['indicatorColor'] as Map), + indicatorWeight: (json['indicatorWeight'] as num?)?.toDouble() ?? 2.0, + indicatorSize: $enumDecodeNullable( + _$TabBarIndicatorSizeEnumEnumMap, json['indicatorSize']) ?? + TabBarIndicatorSizeEnum.tab, + labelColor: json['labelColor'] == null + ? ColorRGBA.black + : ColorRGBA.fromJson(json['labelColor'] as Map), + unselectedLabelColor: json['unselectedLabelColor'] == null + ? ColorRGBA.black + : ColorRGBA.fromJson(json['unselectedLabelColor'] as Map), + labelStyle: json['labelStyle'] == null + ? null + : TextProp.fromJson(json['labelStyle'] as Map), + unselectedLabelStyle: json['unselectedLabelStyle'] == null + ? null + : TextProp.fromJson(json['unselectedLabelStyle'] as Map), + overlayColor: json['overlayColor'] == null + ? ColorRGBA.grey10 + : ColorRGBA.fromJson(json['overlayColor'] as Map), + indicatorPadding: json['indicatorPadding'] == null + ? EdgeInsetsModel.zero + : EdgeInsetsModel.fromJson(json['indicatorPadding'] as Map), + dividerColor: json['dividerColor'] == null + ? ColorRGBA.black + : ColorRGBA.fromJson(json['dividerColor'] as Map), + labelPadding: json['labelPadding'] == null + ? EdgeInsetsModel.zero + : EdgeInsetsModel.fromJson(json['labelPadding'] as Map), + ); + +Map _$TabBarPropertiesToJson(TabBarProperties instance) { + final val = { + 'tabs': instance.tabs.map((e) => e.toJson()).toList(), + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('indicatorColor', instance.indicatorColor?.toJson()); + val['indicatorWeight'] = instance.indicatorWeight; + val['indicatorSize'] = + _$TabBarIndicatorSizeEnumEnumMap[instance.indicatorSize]!; + writeNotNull('labelColor', instance.labelColor?.toJson()); + val['labelStyle'] = instance.labelStyle.toJson(); + writeNotNull('unselectedLabelColor', instance.unselectedLabelColor?.toJson()); + val['unselectedLabelStyle'] = instance.unselectedLabelStyle.toJson(); + writeNotNull('overlayColor', instance.overlayColor?.toJson()); + val['indicatorPadding'] = instance.indicatorPadding.toJson(); + val['labelPadding'] = instance.labelPadding.toJson(); + writeNotNull('dividerColor', instance.dividerColor?.toJson()); + return val; +} + +const _$TabBarIndicatorSizeEnumEnumMap = { + TabBarIndicatorSizeEnum.tab: 'tab', + TabBarIndicatorSizeEnum.label: 'label', +}; + +TabItem _$TabItemFromJson(Map json) => TabItem( + id: json['id'] as String, + label: json['label'] as String, + labelAlignment: $enumDecodeNullable( + _$TextAlignHorizontalEnumEnumMap, json['labelAlignment']) ?? + TextAlignHorizontalEnum.center, + labelStyle: json['labelStyle'] == null + ? null + : TextProp.fromJson(json['labelStyle'] as Map), + placement: + $enumDecodeNullable(_$IconPlacementEnumEnumMap, json['placement']) ?? + IconPlacementEnum.start, + gap: (json['gap'] as num?)?.toDouble() ?? 8, + icon: json['icon'] == null + ? const MultiSourceIconModel(size: 20, color: null) + : MultiSourceIconModel.fromJson(json['icon'] as Map), + ); + +Map _$TabItemToJson(TabItem instance) => { + 'id': instance.id, + 'label': instance.label, + 'labelAlignment': + _$TextAlignHorizontalEnumEnumMap[instance.labelAlignment]!, + 'labelStyle': instance.labelStyle.toJson(), + 'placement': _$IconPlacementEnumEnumMap[instance.placement]!, + 'gap': instance.gap, + 'icon': instance.icon.toJson(), + }; + +const _$TextAlignHorizontalEnumEnumMap = { + TextAlignHorizontalEnum.left: 'left', + TextAlignHorizontalEnum.center: 'center', + TextAlignHorizontalEnum.right: 'right', + TextAlignHorizontalEnum.justified: 'justified', +}; + +const _$IconPlacementEnumEnumMap = { + IconPlacementEnum.start: 'start', + IconPlacementEnum.end: 'end', +}; From b27a1b76480a1989e1e768f65198a1e9950e7fca Mon Sep 17 00:00:00 2001 From: birjuvachhani Date: Tue, 4 Jul 2023 13:15:09 +0530 Subject: [PATCH 2/4] Tab Bar #2 - Fix and improve settings. - Fix and improve code-gen. --- lib/src/api/nodes/tab_bar_node.dart | 88 ++++++++++++++++++--------- lib/src/api/nodes/tab_bar_node.g.dart | 45 ++++++-------- 2 files changed, 76 insertions(+), 57 deletions(-) diff --git a/lib/src/api/nodes/tab_bar_node.dart b/lib/src/api/nodes/tab_bar_node.dart index f23c715..0dd4bcb 100644 --- a/lib/src/api/nodes/tab_bar_node.dart +++ b/lib/src/api/nodes/tab_bar_node.dart @@ -87,6 +87,34 @@ enum TabBarIndicatorSizeEnum { label, } +/// Represents a tab item content: text, icon, or both. +enum TabBarContentType { + /// Show only the text. + label('Label'), + + /// Show only the icon. + icon('Icon'), + + /// Show both the text and the icon. + labelAndIcon('Label & Icon'); + + const TabBarContentType(this.displayLabel); + + /// The display label of the tab item content. + final String displayLabel; + + /// Whether to show the icon. + bool get showIcon => + this == TabBarContentType.icon || this == TabBarContentType.labelAndIcon; + + /// Whether to show the text. + bool get showLabel => + this == TabBarContentType.label || this == TabBarContentType.labelAndIcon; + + /// Whether to show both the text and the icon. + bool get showBoth => this == TabBarContentType.labelAndIcon; +} + /// The properties of a [TabBarNode]. @JsonSerializable() class TabBarProperties with SerializableMixin, EquatableMixin { @@ -126,6 +154,18 @@ class TabBarProperties with SerializableMixin, EquatableMixin { /// The color of the tab divider. ColorRGBA? dividerColor; + /// Determines the direction of label and icon in a tab. + AxisC tabItemDirection; + + /// Spacing between icon and text of a tab. + double gap; + + /// Determines what to show for a tab: text, icon or both. + TabBarContentType contentType; + + /// Whether to show the divider. + bool showDivider; + /// Creates a new [TabBarProperties] instance. TabBarProperties({ List tabs = const [], @@ -140,6 +180,10 @@ class TabBarProperties with SerializableMixin, EquatableMixin { this.indicatorPadding = EdgeInsetsModel.zero, this.dividerColor = ColorRGBA.black, this.labelPadding = EdgeInsetsModel.zero, + this.tabItemDirection = AxisC.horizontal, + this.gap = 10, + this.contentType = TabBarContentType.labelAndIcon, + this.showDivider = true, }) : tabs = [...tabs], labelStyle = labelStyle ?? TextProp(), unselectedLabelStyle = unselectedLabelStyle ?? TextProp(); @@ -159,6 +203,10 @@ class TabBarProperties with SerializableMixin, EquatableMixin { EdgeInsetsModel? indicatorPadding, EdgeInsetsModel? labelPadding, ColorRGBA? dividerColor, + AxisC? tabItemDirection, + double? gap, + TabBarContentType? contentType, + bool? showDivider, }) { return TabBarProperties( tabs: tabs ?? this.tabs, @@ -173,6 +221,10 @@ class TabBarProperties with SerializableMixin, EquatableMixin { indicatorPadding: indicatorPadding ?? this.indicatorPadding, labelPadding: labelPadding ?? this.labelPadding, dividerColor: dividerColor ?? this.dividerColor, + tabItemDirection: tabItemDirection ?? this.tabItemDirection, + gap: gap ?? this.gap, + contentType: contentType ?? this.contentType, + showDivider: showDivider ?? this.showDivider, ); } @@ -197,6 +249,10 @@ class TabBarProperties with SerializableMixin, EquatableMixin { indicatorPadding, labelPadding, dividerColor, + tabItemDirection, + gap, + contentType, + showDivider, ]; } @@ -209,18 +265,6 @@ class TabItem with EquatableMixin, SerializableMixin { /// Label of the tab. String label; - /// Alignment of the label. - TextAlignHorizontalEnum labelAlignment; - - /// Style of the label. - TextProp labelStyle; - - /// Describes the position of the icon. - IconPlacementEnum placement; - - /// Space between the icon and the label. - double gap; - /// Icon to display in the button. MultiSourceIconModel icon; @@ -228,13 +272,9 @@ class TabItem with EquatableMixin, SerializableMixin { TabItem({ required this.id, required this.label, - this.labelAlignment = TextAlignHorizontalEnum.center, TextProp? labelStyle, - this.placement = IconPlacementEnum.start, - this.gap = 8, this.icon = const MultiSourceIconModel(size: 20, color: null), - }) : labelStyle = - labelStyle ?? TextProp.general(fills: List.empty(growable: true)); + }); /// Creates a copy of this [TabItem] instance with the given values. TabItem copyWith({ @@ -249,10 +289,6 @@ class TabItem with EquatableMixin, SerializableMixin { return TabItem( id: id ?? this.id, label: label ?? this.label, - labelAlignment: labelAlignment ?? this.labelAlignment, - labelStyle: labelStyle ?? this.labelStyle, - placement: placement ?? this.placement, - gap: gap ?? this.gap, icon: icon ?? this.icon, ); } @@ -264,13 +300,5 @@ class TabItem with EquatableMixin, SerializableMixin { Map toJson() => _$TabItemToJson(this); @override - List get props => [ - id, - label, - labelAlignment, - labelStyle, - placement, - gap, - icon, - ]; + List get props => [id, label, icon]; } diff --git a/lib/src/api/nodes/tab_bar_node.g.dart b/lib/src/api/nodes/tab_bar_node.g.dart index 41895f5..53c9f48 100644 --- a/lib/src/api/nodes/tab_bar_node.g.dart +++ b/lib/src/api/nodes/tab_bar_node.g.dart @@ -184,6 +184,14 @@ TabBarProperties _$TabBarPropertiesFromJson(Map json) => TabBarProperties( labelPadding: json['labelPadding'] == null ? EdgeInsetsModel.zero : EdgeInsetsModel.fromJson(json['labelPadding'] as Map), + tabItemDirection: + $enumDecodeNullable(_$AxisCEnumMap, json['tabItemDirection']) ?? + AxisC.horizontal, + gap: (json['gap'] as num?)?.toDouble() ?? 10, + contentType: $enumDecodeNullable( + _$TabBarContentTypeEnumMap, json['contentType']) ?? + TabBarContentType.labelAndIcon, + showDivider: json['showDivider'] as bool? ?? true, ); Map _$TabBarPropertiesToJson(TabBarProperties instance) { @@ -209,6 +217,10 @@ Map _$TabBarPropertiesToJson(TabBarProperties instance) { val['indicatorPadding'] = instance.indicatorPadding.toJson(); val['labelPadding'] = instance.labelPadding.toJson(); writeNotNull('dividerColor', instance.dividerColor?.toJson()); + val['tabItemDirection'] = _$AxisCEnumMap[instance.tabItemDirection]!; + val['gap'] = instance.gap; + val['contentType'] = _$TabBarContentTypeEnumMap[instance.contentType]!; + val['showDivider'] = instance.showDivider; return val; } @@ -217,19 +229,15 @@ const _$TabBarIndicatorSizeEnumEnumMap = { TabBarIndicatorSizeEnum.label: 'label', }; +const _$TabBarContentTypeEnumMap = { + TabBarContentType.label: 'label', + TabBarContentType.icon: 'icon', + TabBarContentType.labelAndIcon: 'labelAndIcon', +}; + TabItem _$TabItemFromJson(Map json) => TabItem( id: json['id'] as String, label: json['label'] as String, - labelAlignment: $enumDecodeNullable( - _$TextAlignHorizontalEnumEnumMap, json['labelAlignment']) ?? - TextAlignHorizontalEnum.center, - labelStyle: json['labelStyle'] == null - ? null - : TextProp.fromJson(json['labelStyle'] as Map), - placement: - $enumDecodeNullable(_$IconPlacementEnumEnumMap, json['placement']) ?? - IconPlacementEnum.start, - gap: (json['gap'] as num?)?.toDouble() ?? 8, icon: json['icon'] == null ? const MultiSourceIconModel(size: 20, color: null) : MultiSourceIconModel.fromJson(json['icon'] as Map), @@ -238,22 +246,5 @@ TabItem _$TabItemFromJson(Map json) => TabItem( Map _$TabItemToJson(TabItem instance) => { 'id': instance.id, 'label': instance.label, - 'labelAlignment': - _$TextAlignHorizontalEnumEnumMap[instance.labelAlignment]!, - 'labelStyle': instance.labelStyle.toJson(), - 'placement': _$IconPlacementEnumEnumMap[instance.placement]!, - 'gap': instance.gap, 'icon': instance.icon.toJson(), }; - -const _$TextAlignHorizontalEnumEnumMap = { - TextAlignHorizontalEnum.left: 'left', - TextAlignHorizontalEnum.center: 'center', - TextAlignHorizontalEnum.right: 'right', - TextAlignHorizontalEnum.justified: 'justified', -}; - -const _$IconPlacementEnumEnumMap = { - IconPlacementEnum.start: 'start', - IconPlacementEnum.end: 'end', -}; From a285ac570c1a3e73e9f21fb07ff78b3d0280501b Mon Sep 17 00:00:00 2001 From: birjuvachhani Date: Tue, 4 Jul 2023 15:37:47 +0530 Subject: [PATCH 3/4] Tab Bar #2 - Fix and improve settings. - Fix and improve code-gen. - Add more properties and controls. --- lib/src/api/models/shape_border.dart | 4 +++ lib/src/api/nodes/tab_bar_node.dart | 37 ++++++++++++++++++++++++++- lib/src/api/nodes/tab_bar_node.g.dart | 35 ++++++++++++++++++++++--- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/lib/src/api/models/shape_border.dart b/lib/src/api/models/shape_border.dart index 4364048..486608e 100644 --- a/lib/src/api/models/shape_border.dart +++ b/lib/src/api/models/shape_border.dart @@ -30,4 +30,8 @@ enum CShapeBorder { /// Displayable string representation of this [CShapeBorder]. final String label; + + /// Whether this [CShapeBorder] can have a radius. + bool get canHaveRadius => + this != rectangle && this != circle && this != stadium; } diff --git a/lib/src/api/nodes/tab_bar_node.dart b/lib/src/api/nodes/tab_bar_node.dart index 0dd4bcb..27f088a 100644 --- a/lib/src/api/nodes/tab_bar_node.dart +++ b/lib/src/api/nodes/tab_bar_node.dart @@ -115,6 +115,21 @@ enum TabBarContentType { bool get showBoth => this == TabBarContentType.labelAndIcon; } +/// Represents the style of the tab indicator. +enum TabIndicatorStyle { + /// no indicator. + none, + + /// Default style. + underline, + + /// A rectangle that stretches from one tab to another. + filled, + + /// A border around the tab. + border; +} + /// The properties of a [TabBarNode]. @JsonSerializable() class TabBarProperties with SerializableMixin, EquatableMixin { @@ -122,7 +137,7 @@ class TabBarProperties with SerializableMixin, EquatableMixin { List tabs; /// The color of the tab indicator. - ColorRGBA? indicatorColor; + ColorRGBA indicatorColor; /// The thickness of the tab indicator. double indicatorWeight; @@ -166,6 +181,15 @@ class TabBarProperties with SerializableMixin, EquatableMixin { /// Whether to show the divider. bool showDivider; + /// The style of the tab indicator. + TabIndicatorStyle indicatorStyle; + + /// The shape of the tab indicator. + CShapeBorder indicatorShape; + + /// The corner radius of the tab indicator. + CornerRadius indicatorCornerRadius; + /// Creates a new [TabBarProperties] instance. TabBarProperties({ List tabs = const [], @@ -184,6 +208,9 @@ class TabBarProperties with SerializableMixin, EquatableMixin { this.gap = 10, this.contentType = TabBarContentType.labelAndIcon, this.showDivider = true, + this.indicatorStyle = TabIndicatorStyle.underline, + this.indicatorShape = CShapeBorder.roundedRectangle, + this.indicatorCornerRadius = CornerRadius.zero, }) : tabs = [...tabs], labelStyle = labelStyle ?? TextProp(), unselectedLabelStyle = unselectedLabelStyle ?? TextProp(); @@ -207,6 +234,9 @@ class TabBarProperties with SerializableMixin, EquatableMixin { double? gap, TabBarContentType? contentType, bool? showDivider, + TabIndicatorStyle? indicatorStyle, + CShapeBorder? indicatorShape, + CornerRadius? indicatorCornerRadius, }) { return TabBarProperties( tabs: tabs ?? this.tabs, @@ -225,6 +255,10 @@ class TabBarProperties with SerializableMixin, EquatableMixin { gap: gap ?? this.gap, contentType: contentType ?? this.contentType, showDivider: showDivider ?? this.showDivider, + indicatorStyle: indicatorStyle ?? this.indicatorStyle, + indicatorShape: indicatorShape ?? this.indicatorShape, + indicatorCornerRadius: + indicatorCornerRadius ?? this.indicatorCornerRadius, ); } @@ -253,6 +287,7 @@ class TabBarProperties with SerializableMixin, EquatableMixin { gap, contentType, showDivider, + indicatorStyle, ]; } diff --git a/lib/src/api/nodes/tab_bar_node.g.dart b/lib/src/api/nodes/tab_bar_node.g.dart index 53c9f48..b27ae90 100644 --- a/lib/src/api/nodes/tab_bar_node.g.dart +++ b/lib/src/api/nodes/tab_bar_node.g.dart @@ -192,11 +192,23 @@ TabBarProperties _$TabBarPropertiesFromJson(Map json) => TabBarProperties( _$TabBarContentTypeEnumMap, json['contentType']) ?? TabBarContentType.labelAndIcon, showDivider: json['showDivider'] as bool? ?? true, + indicatorStyle: $enumDecodeNullable( + _$TabIndicatorStyleEnumMap, json['indicatorStyle']) ?? + TabIndicatorStyle.underline, + indicatorShape: + $enumDecodeNullable(_$CShapeBorderEnumMap, json['indicatorShape']) ?? + CShapeBorder.roundedRectangle, + indicatorCornerRadius: json['indicatorCornerRadius'] == null + ? CornerRadius.zero + : CornerRadius.fromJson(json['indicatorCornerRadius'] as Map), ); Map _$TabBarPropertiesToJson(TabBarProperties instance) { final val = { 'tabs': instance.tabs.map((e) => e.toJson()).toList(), + 'indicatorColor': instance.indicatorColor.toJson(), + 'indicatorWeight': instance.indicatorWeight, + 'indicatorSize': _$TabBarIndicatorSizeEnumEnumMap[instance.indicatorSize]!, }; void writeNotNull(String key, dynamic value) { @@ -205,10 +217,6 @@ Map _$TabBarPropertiesToJson(TabBarProperties instance) { } } - writeNotNull('indicatorColor', instance.indicatorColor?.toJson()); - val['indicatorWeight'] = instance.indicatorWeight; - val['indicatorSize'] = - _$TabBarIndicatorSizeEnumEnumMap[instance.indicatorSize]!; writeNotNull('labelColor', instance.labelColor?.toJson()); val['labelStyle'] = instance.labelStyle.toJson(); writeNotNull('unselectedLabelColor', instance.unselectedLabelColor?.toJson()); @@ -221,6 +229,9 @@ Map _$TabBarPropertiesToJson(TabBarProperties instance) { val['gap'] = instance.gap; val['contentType'] = _$TabBarContentTypeEnumMap[instance.contentType]!; val['showDivider'] = instance.showDivider; + val['indicatorStyle'] = _$TabIndicatorStyleEnumMap[instance.indicatorStyle]!; + val['indicatorShape'] = _$CShapeBorderEnumMap[instance.indicatorShape]!; + val['indicatorCornerRadius'] = instance.indicatorCornerRadius.toJson(); return val; } @@ -235,6 +246,22 @@ const _$TabBarContentTypeEnumMap = { TabBarContentType.labelAndIcon: 'labelAndIcon', }; +const _$TabIndicatorStyleEnumMap = { + TabIndicatorStyle.none: 'none', + TabIndicatorStyle.underline: 'underline', + TabIndicatorStyle.filled: 'filled', + TabIndicatorStyle.border: 'border', +}; + +const _$CShapeBorderEnumMap = { + CShapeBorder.rectangle: 'rectangle', + CShapeBorder.circle: 'circle', + CShapeBorder.stadium: 'stadium', + CShapeBorder.roundedRectangle: 'roundedRectangle', + CShapeBorder.continuousRectangle: 'continuousRectangle', + CShapeBorder.beveledRectangle: 'beveledRectangle', +}; + TabItem _$TabItemFromJson(Map json) => TabItem( id: json['id'] as String, label: json['label'] as String, From c49ed702fe2afff4ac44a7d9c5491c50482efde7 Mon Sep 17 00:00:00 2001 From: birjuvachhani Date: Tue, 4 Jul 2023 17:08:19 +0530 Subject: [PATCH 4/4] Tab Bar #4 - Implement actions for tab bar. --- lib/src/api/nodes/tab_bar_node.dart | 18 +++++++++++++++--- lib/src/api/nodes/tab_bar_node.g.dart | 4 ++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/src/api/nodes/tab_bar_node.dart b/lib/src/api/nodes/tab_bar_node.dart index 27f088a..e79f2fd 100644 --- a/lib/src/api/nodes/tab_bar_node.dart +++ b/lib/src/api/nodes/tab_bar_node.dart @@ -13,7 +13,8 @@ part 'tab_bar_node.g.dart'; /// Represents tabs in UI. @JsonSerializable() -class TabBarNode extends SceneNode with CustomPropertiesMixin, ScrollableMixin { +class TabBarNode extends SceneNode + with CustomPropertiesMixin, ScrollableMixin, ParentReactionMixin { @override final String type = 'tabBar'; @@ -66,6 +67,12 @@ class TabBarNode extends SceneNode with CustomPropertiesMixin, ScrollableMixin { ); } + @override + List get triggerTypes => [TriggerType.click]; + + @override + List get reactiveChildren => properties.tabs; + /// Creates a [TabBarNode] from a JSON object. factory TabBarNode.fromJson(Map json) => _$TabBarNodeFromJson(json); @@ -293,7 +300,7 @@ class TabBarProperties with SerializableMixin, EquatableMixin { /// Represents a tab in a [TabBarNode]. @JsonSerializable() -class TabItem with EquatableMixin, SerializableMixin { +class TabItem with EquatableMixin, SerializableMixin, ReactionMixin { /// ID of the tab. final String id; @@ -308,8 +315,11 @@ class TabItem with EquatableMixin, SerializableMixin { required this.id, required this.label, TextProp? labelStyle, + List? reactions, this.icon = const MultiSourceIconModel(size: 20, color: null), - }); + }) { + setReactionMixin(reactions ?? []); + } /// Creates a copy of this [TabItem] instance with the given values. TabItem copyWith({ @@ -320,11 +330,13 @@ class TabItem with EquatableMixin, SerializableMixin { IconPlacementEnum? placement, double? gap, MultiSourceIconModel? icon, + List? reactions, }) { return TabItem( id: id ?? this.id, label: label ?? this.label, icon: icon ?? this.icon, + reactions: reactions ?? this.reactions, ); } diff --git a/lib/src/api/nodes/tab_bar_node.g.dart b/lib/src/api/nodes/tab_bar_node.g.dart index b27ae90..13bba34 100644 --- a/lib/src/api/nodes/tab_bar_node.g.dart +++ b/lib/src/api/nodes/tab_bar_node.g.dart @@ -265,12 +265,16 @@ const _$CShapeBorderEnumMap = { TabItem _$TabItemFromJson(Map json) => TabItem( id: json['id'] as String, label: json['label'] as String, + reactions: (json['reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList(), icon: json['icon'] == null ? const MultiSourceIconModel(size: 20, color: null) : MultiSourceIconModel.fromJson(json['icon'] as Map), ); Map _$TabItemToJson(TabItem instance) => { + 'reactions': instance.reactions.map((e) => e.toJson()).toList(), 'id': instance.id, 'label': instance.label, 'icon': instance.icon.toJson(),