feat: support customizing tooltip style (#856)
This commit is contained in:
parent
c064543e0e
commit
4c4cc415d5
18 changed files with 195 additions and 162 deletions
|
|
@ -74,6 +74,13 @@ class _DesktopEditorState extends State<DesktopEditor> {
|
||||||
...textDirectionItems,
|
...textDirectionItems,
|
||||||
...alignmentItems,
|
...alignmentItems,
|
||||||
],
|
],
|
||||||
|
tooltipBuilder: (context, message, child) {
|
||||||
|
return Tooltip(
|
||||||
|
message: message,
|
||||||
|
preferBelow: false,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
editorState: editorState,
|
editorState: editorState,
|
||||||
textDirection: widget.textDirection,
|
textDirection: widget.textDirection,
|
||||||
editorScrollController: editorScrollController,
|
editorScrollController: editorScrollController,
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ class FloatingToolbar extends StatefulWidget {
|
||||||
required this.textDirection,
|
required this.textDirection,
|
||||||
required this.child,
|
required this.child,
|
||||||
this.style = const FloatingToolbarStyle(),
|
this.style = const FloatingToolbarStyle(),
|
||||||
|
this.tooltipBuilder,
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<ToolbarItem> items;
|
final List<ToolbarItem> items;
|
||||||
|
|
@ -39,6 +40,7 @@ class FloatingToolbar extends StatefulWidget {
|
||||||
final TextDirection? textDirection;
|
final TextDirection? textDirection;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final FloatingToolbarStyle style;
|
final FloatingToolbarStyle style;
|
||||||
|
final ToolbarTooltipBuilder? tooltipBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<FloatingToolbar> createState() => _FloatingToolbarState();
|
State<FloatingToolbar> createState() => _FloatingToolbarState();
|
||||||
|
|
@ -192,6 +194,7 @@ class _FloatingToolbarState extends State<FloatingToolbar>
|
||||||
toolbarElevation: widget.style.toolbarElevation,
|
toolbarElevation: widget.style.toolbarElevation,
|
||||||
toolbarShadowColor: widget.style.toolbarShadowColor,
|
toolbarShadowColor: widget.style.toolbarShadowColor,
|
||||||
textDirection: widget.textDirection ?? Directionality.of(context),
|
textDirection: widget.textDirection ?? Directionality.of(context),
|
||||||
|
tooltipBuilder: widget.tooltipBuilder,
|
||||||
);
|
);
|
||||||
return _toolbarWidget!;
|
return _toolbarWidget!;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@ const floatingToolbarContainerKey =
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
const floatingToolbarItemPrefixKey = 'appflowy_editor_floating_toolbar_item';
|
const floatingToolbarItemPrefixKey = 'appflowy_editor_floating_toolbar_item';
|
||||||
|
|
||||||
|
typedef ToolbarTooltipBuilder = Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
String message,
|
||||||
|
Widget child,
|
||||||
|
);
|
||||||
|
|
||||||
class FloatingToolbarWidget extends StatefulWidget {
|
class FloatingToolbarWidget extends StatefulWidget {
|
||||||
const FloatingToolbarWidget({
|
const FloatingToolbarWidget({
|
||||||
super.key,
|
super.key,
|
||||||
|
|
@ -21,6 +27,7 @@ class FloatingToolbarWidget extends StatefulWidget {
|
||||||
required this.items,
|
required this.items,
|
||||||
required this.editorState,
|
required this.editorState,
|
||||||
required this.textDirection,
|
required this.textDirection,
|
||||||
|
this.tooltipBuilder,
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<ToolbarItem> items;
|
final List<ToolbarItem> items;
|
||||||
|
|
@ -31,6 +38,7 @@ class FloatingToolbarWidget extends StatefulWidget {
|
||||||
final Color? toolbarShadowColor;
|
final Color? toolbarShadowColor;
|
||||||
final EditorState editorState;
|
final EditorState editorState;
|
||||||
final TextDirection textDirection;
|
final TextDirection textDirection;
|
||||||
|
final ToolbarTooltipBuilder? tooltipBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<FloatingToolbarWidget> createState() => _FloatingToolbarWidgetState();
|
State<FloatingToolbarWidget> createState() => _FloatingToolbarWidgetState();
|
||||||
|
|
@ -57,21 +65,22 @@ class _FloatingToolbarWidgetState extends State<FloatingToolbarWidget> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
textDirection: widget.textDirection,
|
textDirection: widget.textDirection,
|
||||||
children: activeItems
|
children: activeItems.mapIndexed(
|
||||||
.mapIndexed(
|
(index, item) {
|
||||||
(index, item) => Center(
|
return Center(
|
||||||
key: Key(
|
key: Key(
|
||||||
'${floatingToolbarItemPrefixKey}_${item.id}_$index',
|
'${floatingToolbarItemPrefixKey}_${item.id}_$index',
|
||||||
),
|
|
||||||
child: item.builder!(
|
|
||||||
context,
|
|
||||||
widget.editorState,
|
|
||||||
widget.toolbarActiveColor,
|
|
||||||
widget.toolbarIconColor,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
child: item.builder!(
|
||||||
.toList(growable: false),
|
context,
|
||||||
|
widget.editorState,
|
||||||
|
widget.toolbarActiveColor,
|
||||||
|
widget.toolbarIconColor,
|
||||||
|
widget.tooltipBuilder,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).toList(growable: false),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -28,19 +28,24 @@ class _AlignmentToolbarItem extends ToolbarItem {
|
||||||
id: 'editor.$id',
|
id: 'editor.$id',
|
||||||
group: 6,
|
group: 6,
|
||||||
isActive: onlyShowInTextType,
|
isActive: onlyShowInTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (
|
||||||
|
context,
|
||||||
|
editorState,
|
||||||
|
highlightColor,
|
||||||
|
iconColor,
|
||||||
|
tooltipBuilder,
|
||||||
|
) {
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
final nodes = editorState.getNodesInSelection(selection);
|
final nodes = editorState.getNodesInSelection(selection);
|
||||||
final isHighlight = nodes.every(
|
final isHighlight = nodes.every(
|
||||||
(n) => n.attributes[blockComponentAlign] == align,
|
(n) => n.attributes[blockComponentAlign] == align,
|
||||||
);
|
);
|
||||||
|
|
||||||
return SVGIconItemWidget(
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/$name',
|
iconName: 'toolbar/$name',
|
||||||
isHighlight: isHighlight,
|
isHighlight: isHighlight,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: getTooltipText(id),
|
|
||||||
onPressed: () => editorState.updateNode(
|
onPressed: () => editorState.updateNode(
|
||||||
selection,
|
selection,
|
||||||
(node) => node.copyWith(
|
(node) => node.copyWith(
|
||||||
|
|
@ -51,6 +56,16 @@ class _AlignmentToolbarItem extends ToolbarItem {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
getTooltipText(id),
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,15 @@ final ToolbarItem bulletedListItem = ToolbarItem(
|
||||||
id: 'editor.bulleted_list',
|
id: 'editor.bulleted_list',
|
||||||
group: 3,
|
group: 3,
|
||||||
isActive: onlyShowInSingleSelectionAndTextType,
|
isActive: onlyShowInSingleSelectionAndTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (context, editorState, highlightColor, iconColor, tooltipBuilder) {
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
final node = editorState.getNodeAtPath(selection.start.path)!;
|
final node = editorState.getNodeAtPath(selection.start.path)!;
|
||||||
final isHighlight = node.type == 'bulleted_list';
|
final isHighlight = node.type == 'bulleted_list';
|
||||||
return SVGIconItemWidget(
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/bulleted_list',
|
iconName: 'toolbar/bulleted_list',
|
||||||
isHighlight: isHighlight,
|
isHighlight: isHighlight,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: AppFlowyEditorL10n.current.bulletedList,
|
|
||||||
onPressed: () => editorState.formatNode(
|
onPressed: () => editorState.formatNode(
|
||||||
selection,
|
selection,
|
||||||
(node) => node.copyWith(
|
(node) => node.copyWith(
|
||||||
|
|
@ -21,5 +20,15 @@ final ToolbarItem bulletedListItem = ToolbarItem(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
AppFlowyEditorL10n.current.bulletedList,
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ ToolbarItem buildHighlightColorItem({List<ColorOption>? colorOptions}) {
|
||||||
id: 'editor.highlightColor',
|
id: 'editor.highlightColor',
|
||||||
group: 4,
|
group: 4,
|
||||||
isActive: onlyShowInTextType,
|
isActive: onlyShowInTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (context, editorState, highlightColor, iconColor, tooltipBuilder) {
|
||||||
String? highlightColorHex;
|
String? highlightColorHex;
|
||||||
|
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
|
|
@ -16,12 +16,12 @@ ToolbarItem buildHighlightColorItem({List<ColorOption>? colorOptions}) {
|
||||||
return highlightColorHex != null;
|
return highlightColorHex != null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return SVGIconItemWidget(
|
|
||||||
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/highlight_color',
|
iconName: 'toolbar/highlight_color',
|
||||||
isHighlight: isHighlight,
|
isHighlight: isHighlight,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: AppFlowyEditorL10n.current.highlightColor,
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
bool showClearButton = false;
|
bool showClearButton = false;
|
||||||
nodes.allSatisfyInSelection(selection, (delta) {
|
nodes.allSatisfyInSelection(selection, (delta) {
|
||||||
|
|
@ -47,6 +47,16 @@ ToolbarItem buildHighlightColorItem({List<ColorOption>? colorOptions}) {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
AppFlowyEditorL10n.current.highlightColor,
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ ToolbarItem buildTextColorItem({
|
||||||
id: 'editor.textColor',
|
id: 'editor.textColor',
|
||||||
group: 4,
|
group: 4,
|
||||||
isActive: onlyShowInTextType,
|
isActive: onlyShowInTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (context, editorState, highlightColor, iconColor, tooltipBuilder) {
|
||||||
String? textColorHex;
|
String? textColorHex;
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
final nodes = editorState.getNodesInSelection(selection);
|
final nodes = editorState.getNodesInSelection(selection);
|
||||||
|
|
@ -17,12 +17,12 @@ ToolbarItem buildTextColorItem({
|
||||||
return (textColorHex != null);
|
return (textColorHex != null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return SVGIconItemWidget(
|
|
||||||
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/text_color',
|
iconName: 'toolbar/text_color',
|
||||||
isHighlight: isHighlight,
|
isHighlight: isHighlight,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: AppFlowyEditorL10n.current.textColor,
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
bool showClearButton = false;
|
bool showClearButton = false;
|
||||||
nodes.allSatisfyInSelection(
|
nodes.allSatisfyInSelection(
|
||||||
|
|
@ -51,6 +51,16 @@ ToolbarItem buildTextColorItem({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
AppFlowyEditorL10n.current.textColor,
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,13 @@ class _FormatToolbarItem extends ToolbarItem {
|
||||||
id: 'editor.$id',
|
id: 'editor.$id',
|
||||||
group: 2,
|
group: 2,
|
||||||
isActive: onlyShowInTextType,
|
isActive: onlyShowInTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (
|
||||||
|
context,
|
||||||
|
editorState,
|
||||||
|
highlightColor,
|
||||||
|
iconColor,
|
||||||
|
tooltipBuilder,
|
||||||
|
) {
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
final nodes = editorState.getNodesInSelection(selection);
|
final nodes = editorState.getNodesInSelection(selection);
|
||||||
final isHighlight = nodes.allSatisfyInSelection(selection, (delta) {
|
final isHighlight = nodes.allSatisfyInSelection(selection, (delta) {
|
||||||
|
|
@ -40,14 +46,24 @@ class _FormatToolbarItem extends ToolbarItem {
|
||||||
(attributes) => attributes[name] == true,
|
(attributes) => attributes[name] == true,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return SVGIconItemWidget(
|
|
||||||
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/$name',
|
iconName: 'toolbar/$name',
|
||||||
isHighlight: isHighlight,
|
isHighlight: isHighlight,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: getTooltipText(id),
|
|
||||||
onPressed: () => editorState.toggleAttribute(name),
|
onPressed: () => editorState.toggleAttribute(name),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
getTooltipText(id),
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,18 +12,23 @@ class _HeadingToolbarItem extends ToolbarItem {
|
||||||
id: 'editor.h$level',
|
id: 'editor.h$level',
|
||||||
group: 1,
|
group: 1,
|
||||||
isActive: onlyShowInSingleSelectionAndTextType,
|
isActive: onlyShowInSingleSelectionAndTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (
|
||||||
|
context,
|
||||||
|
editorState,
|
||||||
|
highlightColor,
|
||||||
|
iconColor,
|
||||||
|
tooltipBuilder,
|
||||||
|
) {
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
final node = editorState.getNodeAtPath(selection.start.path)!;
|
final node = editorState.getNodeAtPath(selection.start.path)!;
|
||||||
final isHighlight =
|
final isHighlight =
|
||||||
node.type == 'heading' && node.attributes['level'] == level;
|
node.type == 'heading' && node.attributes['level'] == level;
|
||||||
final delta = (node.delta ?? Delta()).toJson();
|
final delta = (node.delta ?? Delta()).toJson();
|
||||||
return SVGIconItemWidget(
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/h$level',
|
iconName: 'toolbar/h$level',
|
||||||
isHighlight: isHighlight,
|
isHighlight: isHighlight,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: levelToTooltips(level),
|
|
||||||
onPressed: () => editorState.formatNode(
|
onPressed: () => editorState.formatNode(
|
||||||
selection,
|
selection,
|
||||||
(node) => node.copyWith(
|
(node) => node.copyWith(
|
||||||
|
|
@ -41,6 +46,16 @@ class _HeadingToolbarItem extends ToolbarItem {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
levelToTooltips(level),
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ class SVGIconItemWidget extends StatelessWidget {
|
||||||
required this.isHighlight,
|
required this.isHighlight,
|
||||||
required this.highlightColor,
|
required this.highlightColor,
|
||||||
this.iconColor,
|
this.iconColor,
|
||||||
this.tooltip,
|
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -22,7 +21,6 @@ class SVGIconItemWidget extends StatelessWidget {
|
||||||
final bool isHighlight;
|
final bool isHighlight;
|
||||||
final Color highlightColor;
|
final Color highlightColor;
|
||||||
final Color? iconColor;
|
final Color? iconColor;
|
||||||
final String? tooltip;
|
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -49,15 +47,7 @@ class SVGIconItemWidget extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (tooltip != null) {
|
|
||||||
child = Tooltip(
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
preferBelow: false,
|
|
||||||
message: tooltip,
|
|
||||||
waitDuration: const Duration(milliseconds: 500),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: size.width,
|
width: size.width,
|
||||||
height: size.height,
|
height: size.height,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ final linkItem = ToolbarItem(
|
||||||
id: 'editor.link',
|
id: 'editor.link',
|
||||||
group: 4,
|
group: 4,
|
||||||
isActive: onlyShowInSingleSelectionAndTextType,
|
isActive: onlyShowInSingleSelectionAndTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (context, editorState, highlightColor, iconColor, tooltipBuilder) {
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
final nodes = editorState.getNodesInSelection(selection);
|
final nodes = editorState.getNodesInSelection(selection);
|
||||||
final isHref = nodes.allSatisfyInSelection(selection, (delta) {
|
final isHref = nodes.allSatisfyInSelection(selection, (delta) {
|
||||||
|
|
@ -20,16 +20,25 @@ final linkItem = ToolbarItem(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return SVGIconItemWidget(
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/link',
|
iconName: 'toolbar/link',
|
||||||
isHighlight: isHref,
|
isHighlight: isHref,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: AppFlowyEditorL10n.current.link,
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showLinkMenu(context, editorState, selection, isHref);
|
showLinkMenu(context, editorState, selection, isHref);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
AppFlowyEditorL10n.current.link,
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,15 @@ final ToolbarItem numberedListItem = ToolbarItem(
|
||||||
id: 'editor.numbered_list',
|
id: 'editor.numbered_list',
|
||||||
group: 3,
|
group: 3,
|
||||||
isActive: onlyShowInSingleSelectionAndTextType,
|
isActive: onlyShowInSingleSelectionAndTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (context, editorState, highlightColor, iconColor, tooltipBuilder) {
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
final node = editorState.getNodeAtPath(selection.start.path)!;
|
final node = editorState.getNodeAtPath(selection.start.path)!;
|
||||||
final isHighlight = node.type == 'numbered_list';
|
final isHighlight = node.type == 'numbered_list';
|
||||||
return SVGIconItemWidget(
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/numbered_list',
|
iconName: 'toolbar/numbered_list',
|
||||||
isHighlight: isHighlight,
|
isHighlight: isHighlight,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: AppFlowyEditorL10n.current.numberedList,
|
|
||||||
onPressed: () => editorState.formatNode(
|
onPressed: () => editorState.formatNode(
|
||||||
selection,
|
selection,
|
||||||
(node) => node.copyWith(
|
(node) => node.copyWith(
|
||||||
|
|
@ -21,5 +20,15 @@ final ToolbarItem numberedListItem = ToolbarItem(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
AppFlowyEditorL10n.current.numberedList,
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,16 @@ final ToolbarItem paragraphItem = ToolbarItem(
|
||||||
id: 'editor.paragraph',
|
id: 'editor.paragraph',
|
||||||
group: 1,
|
group: 1,
|
||||||
isActive: onlyShowInSingleSelectionAndTextType,
|
isActive: onlyShowInSingleSelectionAndTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (context, editorState, highlightColor, iconColor, tooltipBuilder) {
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
final node = editorState.getNodeAtPath(selection.start.path)!;
|
final node = editorState.getNodeAtPath(selection.start.path)!;
|
||||||
final isHighlight = node.type == 'paragraph';
|
final isHighlight = node.type == 'paragraph';
|
||||||
final delta = (node.delta ?? Delta()).toJson();
|
final delta = (node.delta ?? Delta()).toJson();
|
||||||
return SVGIconItemWidget(
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/text',
|
iconName: 'toolbar/text',
|
||||||
isHighlight: isHighlight,
|
isHighlight: isHighlight,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: AppFlowyEditorL10n.current.text,
|
|
||||||
onPressed: () => editorState.formatNode(
|
onPressed: () => editorState.formatNode(
|
||||||
selection,
|
selection,
|
||||||
(node) => node.copyWith(
|
(node) => node.copyWith(
|
||||||
|
|
@ -29,5 +28,15 @@ final ToolbarItem paragraphItem = ToolbarItem(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
AppFlowyEditorL10n.current.text,
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ final ToolbarItem placeholderItem = ToolbarItem(
|
||||||
id: placeholderItemId,
|
id: placeholderItemId,
|
||||||
group: -1,
|
group: -1,
|
||||||
isActive: (editorState) => true,
|
isActive: (editorState) => true,
|
||||||
builder: (_, __, ___, ____) {
|
builder: (_, __, ___, ____, _____) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 5),
|
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 5),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,15 @@ final ToolbarItem quoteItem = ToolbarItem(
|
||||||
id: 'editor.quote',
|
id: 'editor.quote',
|
||||||
group: 3,
|
group: 3,
|
||||||
isActive: onlyShowInSingleSelectionAndTextType,
|
isActive: onlyShowInSingleSelectionAndTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (context, editorState, highlightColor, iconColor, tooltipBuilder) {
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
final node = editorState.getNodeAtPath(selection.start.path)!;
|
final node = editorState.getNodeAtPath(selection.start.path)!;
|
||||||
final isHighlight = node.type == 'quote';
|
final isHighlight = node.type == 'quote';
|
||||||
return SVGIconItemWidget(
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/quote',
|
iconName: 'toolbar/quote',
|
||||||
isHighlight: isHighlight,
|
isHighlight: isHighlight,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: AppFlowyEditorL10n.current.quote,
|
|
||||||
onPressed: () => editorState.formatNode(
|
onPressed: () => editorState.formatNode(
|
||||||
selection,
|
selection,
|
||||||
(node) => node.copyWith(
|
(node) => node.copyWith(
|
||||||
|
|
@ -21,5 +20,15 @@ final ToolbarItem quoteItem = ToolbarItem(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
AppFlowyEditorL10n.current.quote,
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,23 @@ class _TextDirectionToolbarItem extends ToolbarItem {
|
||||||
id: 'editor.$id',
|
id: 'editor.$id',
|
||||||
group: 7,
|
group: 7,
|
||||||
isActive: onlyShowInTextType,
|
isActive: onlyShowInTextType,
|
||||||
builder: (context, editorState, highlightColor, iconColor) {
|
builder: (
|
||||||
|
context,
|
||||||
|
editorState,
|
||||||
|
highlightColor,
|
||||||
|
iconColor,
|
||||||
|
tooltipBuilder,
|
||||||
|
) {
|
||||||
final selection = editorState.selection!;
|
final selection = editorState.selection!;
|
||||||
final nodes = editorState.getNodesInSelection(selection);
|
final nodes = editorState.getNodesInSelection(selection);
|
||||||
final isHighlight = nodes.every(
|
final isHighlight = nodes.every(
|
||||||
(n) => n.attributes[blockComponentTextDirection] == name,
|
(n) => n.attributes[blockComponentTextDirection] == name,
|
||||||
);
|
);
|
||||||
return SVGIconItemWidget(
|
final child = SVGIconItemWidget(
|
||||||
iconName: 'toolbar/$iconName',
|
iconName: 'toolbar/$iconName',
|
||||||
isHighlight: isHighlight,
|
isHighlight: isHighlight,
|
||||||
highlightColor: highlightColor,
|
highlightColor: highlightColor,
|
||||||
iconColor: iconColor,
|
iconColor: iconColor,
|
||||||
tooltip: getTooltipText(id),
|
|
||||||
onPressed: () => editorState.updateNode(
|
onPressed: () => editorState.updateNode(
|
||||||
selection,
|
selection,
|
||||||
(node) => node.copyWith(
|
(node) => node.copyWith(
|
||||||
|
|
@ -50,6 +55,16 @@ class _TextDirectionToolbarItem extends ToolbarItem {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tooltipBuilder != null) {
|
||||||
|
return tooltipBuilder(
|
||||||
|
context,
|
||||||
|
getTooltipText(id),
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ class ToolbarItem {
|
||||||
EditorState editorState,
|
EditorState editorState,
|
||||||
Color highlightColor,
|
Color highlightColor,
|
||||||
Color? iconColor,
|
Color? iconColor,
|
||||||
|
ToolbarTooltipBuilder? tooltipBuilder,
|
||||||
)? builder;
|
)? builder;
|
||||||
|
|
||||||
// deprecated
|
// deprecated
|
||||||
|
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
|
||||||
import 'package:appflowy_editor/src/flutter/overlay.dart';
|
|
||||||
import 'package:appflowy_editor/src/render/toolbar/toolbar_item.dart';
|
|
||||||
import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart';
|
|
||||||
import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
|
|
||||||
|
|
||||||
mixin ToolbarMixin<T extends StatefulWidget> on State<T> {
|
|
||||||
void hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
class ToolbarWidget extends StatefulWidget {
|
|
||||||
const ToolbarWidget({
|
|
||||||
super.key,
|
|
||||||
required this.editorState,
|
|
||||||
required this.layerLink,
|
|
||||||
required this.offset,
|
|
||||||
required this.highlightColor,
|
|
||||||
this.iconColor,
|
|
||||||
required this.items,
|
|
||||||
this.alignment = Alignment.topLeft,
|
|
||||||
});
|
|
||||||
|
|
||||||
final EditorState editorState;
|
|
||||||
final LayerLink layerLink;
|
|
||||||
final Offset offset;
|
|
||||||
final Color highlightColor;
|
|
||||||
final Color? iconColor;
|
|
||||||
|
|
||||||
final List<ToolbarItem> items;
|
|
||||||
|
|
||||||
final Alignment alignment;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ToolbarWidget> createState() => _ToolbarWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ToolbarWidgetState extends State<ToolbarWidget> with ToolbarMixin {
|
|
||||||
OverlayEntry? _listToolbarOverlay;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Positioned(
|
|
||||||
top: widget.offset.dx,
|
|
||||||
left: widget.offset.dy,
|
|
||||||
child: CompositedTransformFollower(
|
|
||||||
link: widget.layerLink,
|
|
||||||
showWhenUnlinked: true,
|
|
||||||
offset: widget.offset,
|
|
||||||
followerAnchor: widget.alignment,
|
|
||||||
child: _buildToolbar(context, widget.highlightColor, widget.iconColor),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void hide() {
|
|
||||||
_listToolbarOverlay?.remove();
|
|
||||||
_listToolbarOverlay = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildToolbar(
|
|
||||||
BuildContext context,
|
|
||||||
Color highlightColor,
|
|
||||||
Color? iconColor,
|
|
||||||
) {
|
|
||||||
return Material(
|
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0, right: 8.0),
|
|
||||||
child: SizedBox(
|
|
||||||
height: 32.0,
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: widget.items
|
|
||||||
.map(
|
|
||||||
(item) => Center(
|
|
||||||
child: item.builder?.call(
|
|
||||||
context,
|
|
||||||
widget.editorState,
|
|
||||||
highlightColor,
|
|
||||||
iconColor,
|
|
||||||
) ??
|
|
||||||
item.itemBuilder?.call(context, widget.editorState) ??
|
|
||||||
ToolbarItemWidget(
|
|
||||||
item: item,
|
|
||||||
isHighlight: item.highlightCallback
|
|
||||||
?.call(widget.editorState) ??
|
|
||||||
false,
|
|
||||||
onPressed: () {
|
|
||||||
item.handler?.call(widget.editorState, context);
|
|
||||||
widget.editorState.service.keyboardService
|
|
||||||
?.enable();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(growable: false),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue