Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions super_editor/.run/Demo_ Components in Components.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo: Components in Components" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/example/lib/main_components_in_components.dart" />
<method v="2" />
</configuration>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ class HeaderWithHintComponentBuilder implements ComponentBuilder {
const HeaderWithHintComponentBuilder();

@override
SingleColumnLayoutComponentViewModel? createViewModel(Document document, DocumentNode node) {
SingleColumnLayoutComponentViewModel? createViewModel(
PresenterContext context,
Document document,
DocumentNode node,
) {
// This component builder can work with the standard paragraph view model.
// We'll defer to the standard paragraph component builder to create it.
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ class UnselectableHrComponentBuilder implements ComponentBuilder {
const UnselectableHrComponentBuilder();

@override
SingleColumnLayoutComponentViewModel? createViewModel(Document document, DocumentNode node) {
SingleColumnLayoutComponentViewModel? createViewModel(
PresenterContext context,
Document document,
DocumentNode node,
) {
// This builder can work with the standard horizontal rule view model, so
// we'll defer to the standard horizontal rule builder.
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ class AnimatedTaskComponentBuilder implements ComponentBuilder {
const AnimatedTaskComponentBuilder();

@override
SingleColumnLayoutComponentViewModel? createViewModel(Document document, DocumentNode node) {
SingleColumnLayoutComponentViewModel? createViewModel(
PresenterContext context,
Document document,
DocumentNode node,
) {
// This builder can work with the standard task view model, so
// we'll defer to the standard task builder.
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,13 @@ class SpellingErrorParagraphComponentBuilder implements ComponentBuilder {
final UnderlineStyle underlineStyle;

@override
SingleColumnLayoutComponentViewModel? createViewModel(Document document, DocumentNode node) {
final viewModel = ParagraphComponentBuilder().createViewModel(document, node) as ParagraphComponentViewModel?;
SingleColumnLayoutComponentViewModel? createViewModel(
PresenterContext context,
Document document,
DocumentNode node,
) {
final viewModel =
ParagraphComponentBuilder().createViewModel(context, document, node) as ParagraphComponentViewModel?;
if (viewModel == null) {
return null;
}
Expand Down
239 changes: 239 additions & 0 deletions super_editor/example/lib/main_components_in_components.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import 'package:flutter/material.dart';
import 'package:super_editor/super_editor.dart';

void main() {
runApp(
MaterialApp(
home: _ComponentsInComponentsDemoScreen(),
),
);
}

class _ComponentsInComponentsDemoScreen extends StatefulWidget {
const _ComponentsInComponentsDemoScreen();

@override
State<_ComponentsInComponentsDemoScreen> createState() => _ComponentsInComponentsDemoScreenState();
}

class _ComponentsInComponentsDemoScreenState extends State<_ComponentsInComponentsDemoScreen> {
late final Editor _editor;

@override
void initState() {
super.initState();

_editor = createDefaultDocumentEditor(
document: MutableDocument(
nodes: [
ParagraphNode(
id: "1",
text: AttributedText("This is a demo of a Banner component."),
metadata: {
NodeMetadata.blockType: header1Attribution,
},
),
_BannerNode(id: "2", children: [
ParagraphNode(
id: "3",
text: AttributedText("Hello, Banner!"),
metadata: {
NodeMetadata.blockType: header2Attribution,
},
),
HorizontalRuleNode(id: "7"),
ImageNode(
id: "4",
imageUrl:
"https://www.thedroidsonroids.com/wp-content/uploads/2023/08/flutter_blog_series_What-is-Flutter-app-development-.png",
),
HorizontalRuleNode(id: "8"),
ParagraphNode(
id: "5",
text: AttributedText("This is a banner, which can contain any other blocks you want"),
),
]),
ParagraphNode(
id: "6",
text: AttributedText("This is after the banner component."),
),
],
),
composer: MutableDocumentComposer(),
);
}

@override
void dispose() {
_editor.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: SuperEditor(
editor: _editor,
stylesheet: defaultStylesheet.copyWith(
addRulesAfter: [
StyleRule(
BlockSelector("banner"),
(doc, docNode) {
return {
Styles.padding: CascadingPadding.only(left: 0, right: 0, top: 24, bottom: 0),
};
},
),
StyleRule(
BlockSelector.all.childOf("banner"),
(doc, docNode) {
return {
Styles.padding: CascadingPadding.symmetric(vertical: 0, horizontal: 0),
Styles.textStyle: const TextStyle(
color: Colors.white,
),
};
},
),
StyleRule(
BlockSelector(horizontalRuleBlockType.name).childOf("banner"),
(doc, docNode) {
return {
Styles.backgroundColor: Colors.white.withValues(alpha: 0.25),
};
},
),
],
),
componentBuilders: [
_BannerComponentBuilder(),
...defaultComponentBuilders,
],
),
);
}
}

const bannerBlockType = NamedAttribution("banner");

class _BannerComponentBuilder implements ComponentBuilder {
@override
SingleColumnLayoutComponentViewModel? createViewModel(
PresenterContext presenterContext,
Document document,
DocumentNode node,
) {
if (node is! _BannerNode) {
return null;
}

return CompositeNodeViewModel(
nodeId: node.id,
children: node.children.map((childNode) => presenterContext.createViewModel(childNode)!).toList(),
);
}

@override
Widget? createComponent(
SingleColumnDocumentComponentContext componentContext,
SingleColumnLayoutComponentViewModel componentViewModel,
) {
if (componentViewModel is! CompositeNodeViewModel) {
return null;
}

final childrenAndKeys = componentViewModel.children
.map(
(childViewModel) => componentContext.buildChildComponent(childViewModel),
)
.toList(growable: false);

// print("Building a _BannerComponent - banner key: ${componentContext.componentKey}");
// print(" - child keys: ${childrenAndKeys.map((x) => x.$1)}");
return _BannerComponent(
key: componentContext.componentKey,
// childComponentIds: [],
childComponentKeys: childrenAndKeys.map((childAndKey) => childAndKey.$1).toList(growable: false),
children: [
for (final child in childrenAndKeys) //
child.$2,
],
);
}
}

class _BannerComponent extends StatefulWidget {
const _BannerComponent({
super.key,
// required this.childComponentIds,
required this.childComponentKeys,
required this.children,
});

// final List<String> childComponentIds;

final List<GlobalKey<DocumentComponent>> childComponentKeys;

final List<Widget> children;

@override
State<_BannerComponent> createState() => _BannerComponentState();
}

class _BannerComponentState extends State<_BannerComponent> with ProxyDocumentComponent<_BannerComponent> {
@override
final childDocumentComponentKey = GlobalKey(debugLabel: 'banner-internal-column');

@override
Widget build(BuildContext context) {
return IgnorePointer(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
),
child: ColumnDocumentComponent(
key: childDocumentComponentKey,
// childComponentIds: widget.childComponentIds,
childComponentKeys: widget.childComponentKeys,
children: widget.children,
),
),
);
}
}

class _BannerNode extends CompositeNode {
_BannerNode({
required super.id,
required this.children,
Map<String, dynamic>? metadata,
}) : super(
metadata: {
if (metadata != null) //
...metadata,
NodeMetadata.blockType: bannerBlockType,
},
);

final List<DocumentNode> children;

@override
DocumentNode copyWithAddedMetadata(Map<String, dynamic> newProperties) {
// TODO: implement copyWithAddedMetadata
throw UnimplementedError();
}

@override
DocumentNode copyAndReplaceMetadata(Map<String, dynamic> newMetadata) {
// TODO: implement copyAndReplaceMetadata
throw UnimplementedError();
}

@override
String? copyContent(NodeSelection selection) {
// TODO: implement copyContent
throw UnimplementedError();
}
}
6 changes: 5 additions & 1 deletion super_editor/example_perf/lib/demos/rebuild_demo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ class AnimatedTaskComponentBuilder implements ComponentBuilder {
const AnimatedTaskComponentBuilder();

@override
SingleColumnLayoutComponentViewModel? createViewModel(Document document, DocumentNode node) {
SingleColumnLayoutComponentViewModel? createViewModel(
PresenterContext context,
Document document,
DocumentNode node,
) {
// This builder can work with the standard task view model, so
// we'll defer to the standard task builder.
return null;
Expand Down
Loading