Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: support for dynamic placeholders #2238

Closed
wants to merge 118 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
c6636b7
Chore: added transparent_attribue
Sep 15, 2024
87cea7a
Partial improvements
Sep 17, 2024
d5e4f55
Feat: support for dynamic placeholders
Sep 18, 2024
8f4b0da
Fix: web cannot use dynamic placeholders
Sep 18, 2024
14fe2b3
Chore: removed isLink check since is unnecessary
Sep 18, 2024
811c9a0
Chore: renamed builder param to builders for make more sense to its f…
Sep 18, 2024
c9c5a3d
Chore: dart format .
Sep 18, 2024
98f817f
Chore: fix example in doc comment
Sep 18, 2024
d2a0a63
Chore: renamed PlaceholderConfiguration to PlaceholderArguments
Sep 18, 2024
dd81cc2
Feat: support custom attributes for placeholders
Sep 18, 2024
82b3780
Chore: dart format
Sep 18, 2024
5741e6b
Chore: added late keyword for blackList
Sep 18, 2024
d6edcd2
Feat: added support for cursor placeholder
Sep 18, 2024
8d274a0
Fix: make CursorParagraphPlaceholderConfiguration nullable
Sep 18, 2024
88e9cca
Chore: improve placeholders examples
Sep 18, 2024
1485ba6
Merge branch 'master' into improve_headers
CatHood0 Sep 18, 2024
7be52d5
Fix(merge): missing trailing comma on EditableTextLine constructor
Sep 18, 2024
abf8363
Chore: run before_push to fix formats
Sep 18, 2024
d7c5d14
Chore: make placeholderComponentsConfiguration fully optional
Sep 18, 2024
1eefa9d
Fix: add validation to avoid show component placeholder when found co…
Sep 19, 2024
385586e
Merge branch 'master' into improve_headers
CatHood0 Sep 19, 2024
cece358
Chore: dart format
Sep 19, 2024
aa131a4
Chore: revert change where the placeholder is hiden if node contains …
Sep 20, 2024
f2db22b
Fix: placeholder does not show the aligned applied in the line
Sep 20, 2024
b90f3af
Chore: removed unused imports
Sep 20, 2024
2fb3131
Chore: fix curly issue with flutter analysis
Sep 20, 2024
e412236
Chore: add internal annotations and make strutStyle optional in build…
Sep 20, 2024
3f2d4e4
Fix: widget span is generating unexpected behaviors with the caret
Sep 20, 2024
8556861
Fix: placeholder text span is not taking the style of the dev
Sep 20, 2024
612a36a
chore(github): update the question template to add the question label…
EchoEllet Sep 19, 2024
6411648
chore(github): remove 'Code sample' text area input from bug report t…
EchoEllet Sep 19, 2024
8e57431
chore: rename closeWebPasteEvent() to cancelWebPasteEvent(), improve …
EchoEllet Sep 20, 2024
8e6dd4f
Feat: Removing check not allowing spell check on web (#2252)
joeserhtf Sep 20, 2024
eed3599
chore(version): update to version 10.6.6
singerdmx Sep 20, 2024
8dfac75
Chore: deprecate embed table feature (#2254)
CatHood0 Sep 21, 2024
55fbb16
chore(version): update to version 10.7.0
singerdmx Sep 21, 2024
03fc81c
chore: deprecate markdown_quill export, ignore warnings (#2256)
EchoEllet Sep 21, 2024
708baf2
chore: deprecate spell checker service (#2255)
EchoEllet Sep 21, 2024
ec520c4
chore(version): update to version 10.7.1
singerdmx Sep 21, 2024
35546ca
chore: deprecate flutter_quill/extensions.dart (#2258)
EchoEllet Sep 21, 2024
e228f14
chore(version): update to version 10.7.2
EchoEllet Sep 21, 2024
4076c98
chore: update minimum version of flutter_quill and super_clipboard in…
EchoEllet Sep 21, 2024
503ad20
chore: deprecate FlutterQuillExtensions
EchoEllet Sep 21, 2024
d2606a4
chore(version): update to version 10.7.3
EchoEllet Sep 21, 2024
a1bfa2d
chore(example): update pub.dev version of flutter_quill packages
EchoEllet Sep 21, 2024
0b78052
chore: deprecate QuillImageUtilities
EchoEllet Sep 21, 2024
bb4e47d
chore: remove pubspec_overrides.yaml and pubspec_overrides.yaml.disab…
EchoEllet Sep 21, 2024
1e989d2
ci: remove quill_native_bridge from automated publishing workflow (#2…
EchoEllet Sep 21, 2024
73dc05f
chore: remove quill_native_bridge from rootPackages in scripts to ens…
EchoEllet Sep 21, 2024
0eb4987
chore(version): update to version 10.7.4
singerdmx Sep 21, 2024
4ce5bc8
fix(ci): add flutter pub get step for quill_native_bridge to fix fail…
EchoEllet Sep 21, 2024
40fd709
revert: "Resolved issue with broken IME composing rect in Windows des…
CatHood0 Sep 22, 2024
4c23d98
chore(version): update to version 10.7.5
singerdmx Sep 22, 2024
a4f69a7
Comment Typos fix (#2267)
Luismi74 Sep 22, 2024
0f03135
docs: add important note for contributors before introducing new feat…
EchoEllet Sep 22, 2024
1690bff
docs: remove table support note in README of flutter_quill_extensions…
EchoEllet Sep 22, 2024
9cf64d2
docs: add 'and' before videos in extensions package
EchoEllet Sep 22, 2024
c8bd92e
docs(readme): add 'Breaking Changes' section (#2275)
EchoEllet Sep 23, 2024
abbe9ce
chore(example): update macOS Podfile.lock
EchoEllet Sep 24, 2024
e7e6b3f
Fix: Resolved issue with broken IME composing rect in Windows desktop…
agata Sep 25, 2024
b589972
chore(version): update to version 10.7.6
singerdmx Sep 25, 2024
740c0cd
fix: update version range of quill_native_bridge to avoid using lates…
EchoEllet Sep 25, 2024
45b6618
chore(version): update to version 10.7.7
EchoEllet Sep 25, 2024
5d2b74c
fix(extensions)!: drop support for YouTube iframeView for non-web pla…
EchoEllet Sep 25, 2024
b8e6af1
chore(version): update to version 10.8.0
EchoEllet Sep 25, 2024
e34ae5f
chore(example): exclude flutter_inappwebview_web web support js file …
EchoEllet Sep 25, 2024
ffe66fb
chore(deps): update dev dependency flutter_lints to 5.0.0, solve warn…
EchoEllet Sep 27, 2024
60ff374
Add Hungarian (hu) localization (#2291)
G-Greg Sep 27, 2024
da1ed15
fix: replace flutter_keyboard_visibility with flutter_keyboard_visibi…
EchoEllet Sep 27, 2024
65eadbf
chore: remove dart_quill_delta directory to move it outside of the re…
EchoEllet Sep 27, 2024
5f6dff2
docs(readme): update flutter_keyboard_visibility to flutter_keyboard_…
EchoEllet Sep 27, 2024
bafa87d
chore: remove .pubignore
EchoEllet Sep 27, 2024
8d6e401
chore(version): update to version 10.8.1
EchoEllet Sep 27, 2024
c9cdadf
fix(ci): use fixed version of simple_spell_checker to fix CI warnings
EchoEllet Sep 28, 2024
59810e9
Fixed minor typo in Hungarian (hu) localization (#2307)
G-Greg Oct 1, 2024
37529b0
chore(version): update to version 10.8.2
singerdmx Oct 1, 2024
241c6fa
fix(example): build failure on Android with latest version of Android…
EchoEllet Oct 6, 2024
d09e479
feat: Use quill_native_bridge as default impl in DefaultClipboardServ…
EchoEllet Oct 16, 2024
48cdbb4
chore(version): update to version 10.8.3-dev.0
EchoEllet Oct 17, 2024
5761021
chore(deps): update minimum version of flutter_quill in flutter_quill…
EchoEllet Oct 17, 2024
a18ab28
chore(version): update to version 10.8.3
EchoEllet Oct 17, 2024
071edab
chore(deps): bump minimum flutter_quill to 10.8.3 in flutter_quill_ex…
EchoEllet Oct 17, 2024
f2220cc
chore(deps): update min version of quill_native_bridge
EchoEllet Oct 17, 2024
62d8212
fix: avoid using getClipboardFiles() if unsupported on the current pl…
EchoEllet Oct 17, 2024
5ecebd2
chore(version): update to version 10.8.4
EchoEllet Oct 17, 2024
002a92f
docs(readme): improve the note message of quill_super_clipboard and q…
EchoEllet Oct 17, 2024
3482130
fix: allow all correct URLs to be formatted (#2328)
orevial Oct 22, 2024
c6fb247
fix(macos): Implement actions for ExpandSelectionToDocumentBoundaryIn…
EchoEllet Oct 23, 2024
bbf1ccf
chore(version): update to version 10.8.5
EchoEllet Oct 23, 2024
1cc1b50
fix: link menu action Cupertino modal popup now does NOT use root nav…
Zambrella Oct 24, 2024
0bd7513
Partial improvements
Sep 17, 2024
4b36413
Fix(merge): missing trailing comma on EditableTextLine constructor
Sep 18, 2024
364cb19
Chore: textDirection in build method from PlaceholderBuilder was chan…
Oct 28, 2024
09fb1d6
Remove unnecessary manual positioning of placeholder
Oct 28, 2024
c408522
Chore: remove print call and unnecessary import
Oct 28, 2024
5b7b65a
Chore: dart format
Oct 28, 2024
c4ccec7
Merge branch 'master' into improve_headers
CatHood0 Oct 28, 2024
8304a19
Merge from master branch
Nov 28, 2024
4bbd064
Chore: dart formatting
Nov 28, 2024
3a37acd
Chore: rename class names to make more sense with their functions
Nov 28, 2024
33a8cdd
Chore: fix missed changes on cursor, text_block and text_line
Nov 28, 2024
ce4c09b
Chore: changed param names to have the same from their classes
Nov 28, 2024
47bfd2b
Chore: fixed placeholders text scale issue on mobile devices
Nov 28, 2024
172a85c
Chore: dart formatting
Nov 28, 2024
d022960
Chore: restore example app
Nov 28, 2024
ce51947
Chore: removed _internal part from placeholder_builder file
Nov 28, 2024
8429efa
Chore: improved description of the _blackList in placeholder_builder …
Nov 28, 2024
6bb3a13
Chore: convert _isNodeInline into a function in a extension for Node …
Nov 28, 2024
057b8bd
Chore: dart format and removed unnecessary validation of localOffset …
Nov 28, 2024
47540d4
Chore: dart formatting
Nov 28, 2024
a4177ca
Fix: import from the old path of placeholder_builder
Nov 28, 2024
fb5b11a
Chore: improved doc comments and renamed params in QuillEditorConfig
Nov 29, 2024
13f858b
Chore: dart format
Jan 19, 2025
dcbf188
Chore(doc): added placeholder_introduction documentation
Jan 20, 2025
d07a680
Merge branch 'master' into improve_headers
CatHood0 Jan 20, 2025
03bf936
Chore: update pubspec.lock
Jan 20, 2025
7500ddb
Chore: updated changelog to the current version in stable release
Jan 20, 2025
cf20894
Chore: added missed letter in changelog
Jan 20, 2025
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
77 changes: 77 additions & 0 deletions doc/placeholders_introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# ✏️ Placeholders

> A **placeholder** is visible text that serves as a guide or instruction for the user when a text field or editable area is empty.

In **Flutter Quill**, there are three scenarios where **placeholders** can appear:

- When the document is empty, a placeholder will appear at the beginning.
- When a dynamic placeholder is configured, it can display custom guide text anywhere in the document.
- When a cursor placeholder is set up, it appears whenever a line is completely empty.

### 💡 How to Display a Placeholder When the Document is Empty

To configure this, you need to use the `QuillEditorConfig` class:

```dart
final config = QuillEditorConfig(
placeholder: 'Start writing your notes...',
);
```

### 🔎 What Are Dynamic Placeholders?

**Dynamic placeholders** are not static or predetermined; they are **generated or adjusted automatically** based on the context of the content or the attributes applied to the text in the editor.

These **placeholders** appear only when specific conditions are met, such as:

- The block is empty.
- No additional text attributes (e.g., styles or links) are applied.

#### 🛠️ How to Create a Dynamic Placeholder

Here's a simple example:

If you want to display guide text only when a header is applied and the line with this attribute is empty, you can use the following code:

```dart
final config = QuillEditorConfig(
placeholderConfig: PlaceholderConfig(
builders: {
Attribute.header.key: (Attribute attr, TextStyle style) {
// In this case, we will only support header levels h1 to h3
final values = [30, 27, 22];
final level = attr.value as int?;
if (level == null) return null;
final fontSize = values[(level - 1 < 0 || level - 1 > 3 ? 0 : level - 1)];
return PlaceholderTextBuilder(
text: 'Header $level',
style: TextStyle(
fontSize: fontSize.toDouble())
.merge(style.copyWith(
color: Colors.grey)),
);
},
),
);
```

### 🔎 What Are Cursor Placeholders?

**Cursor placeholders** appear when a line is completely empty and has no attributes applied. These placeholders automatically appear at the same level as the cursor, though their position can be adjusted using the `offset` parameter in the `CursorPlaceholderConfig` class.

Here's a simple implementation example:

```dart
final config = QuillEditorConfig(
cursorPlaceholderConfig: CursorPlaceholderConfig(
text: 'Write something...',
textStyle: TextStyle(
color: Colors.grey,
fontStyle: FontStyle.italic,
),
show: true,
offset: Offset(3.5, 2),
),
);
```

2 changes: 2 additions & 0 deletions lib/flutter_quill.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ export 'src/document/style.dart';
export 'src/editor/editor.dart';
export 'src/editor/embed/embed_editor_builder.dart';
export 'src/editor/raw_editor/builders/leading_block_builder.dart';
export 'src/editor/raw_editor/builders/placeholder/placeholder_configuration.dart';
export 'src/editor/raw_editor/config/events/events.dart';
export 'src/editor/raw_editor/config/raw_editor_config.dart';
export 'src/editor/raw_editor/quill_single_child_scroll_view.dart';
export 'src/editor/raw_editor/raw_editor.dart';
export 'src/editor/raw_editor/raw_editor_state.dart';
export 'src/editor/style_widgets/style_widgets.dart';
export 'src/editor/widgets/cursor.dart';
export 'src/editor/widgets/cursor_configuration/cursor_configuration.dart';
export 'src/editor/widgets/default_styles.dart';
export 'src/editor/widgets/link.dart';
export 'src/editor/widgets/text/utils/text_block_utils.dart';
Expand Down
10 changes: 10 additions & 0 deletions lib/src/common/extensions/nodes_ext.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import '../../document/nodes/node.dart';

extension NodesCheckingExtension on Node {
bool isNodeInline() {
for (final attr in style.attributes.values) {
if (!attr.isInline) return false;
}
return true;
}
}
43 changes: 43 additions & 0 deletions lib/src/editor/config/editor_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import '../../document/nodes/node.dart';
import '../../toolbar/theme/quill_dialog_theme.dart';
import '../embed/embed_editor_builder.dart';
import '../raw_editor/builders/leading_block_builder.dart';
import '../raw_editor/builders/placeholder/placeholder_configuration.dart';
import '../raw_editor/config/events/events.dart';
import '../raw_editor/config/raw_editor_config.dart';
import '../raw_editor/raw_editor.dart';
import '../widgets/cursor_configuration/cursor_configuration.dart';
import '../widgets/default_styles.dart';
import '../widgets/delegate.dart';
import '../widgets/link.dart';
Expand All @@ -25,6 +27,8 @@ class QuillEditorConfig {
const QuillEditorConfig({
this.scrollable = true,
this.padding = EdgeInsets.zero,
@experimental this.placeholderConfig,
@experimental this.cursorPlaceholderConfig,
@experimental this.characterShortcutEvents = const [],
@experimental this.spaceShortcutEvents = const [],
this.autoFocus = false,
Expand Down Expand Up @@ -135,6 +139,40 @@ class QuillEditorConfig {
@experimental
final List<SpaceShortcutEvent> spaceShortcutEvents;

/// Configuration for displaying placeholders in empty lines or near the cursor.
///
/// ### Example
///
/// To show a placeholder text specifically for header items:
///
/// ```dart
/// final configuration = PlaceholderConfig(
/// builders: <String, PlaceholderComponentBuilder>{
/// Attribute.header.key: (Attribute attr, style) {
/// final values = [30, 27, 22];
/// final level = attr.value as int?;
/// if (level == null) return null;
/// final fontSize = values[(level - 1 < 0 || level - 1 > 3 ? 0 : level - 1)];
/// return PlaceholderTextBuilder(
/// text: 'Header $level',
/// style: TextStyle(fontSize: fontSize.toDouble()).merge(style),
/// );
/// },
/// },
/// // If using custom attributes, register their keys here.
/// customBlockAttributesKeys: null,
/// );
/// ```
@experimental
final PlaceholderConfig? placeholderConfig;

/// Configures how a placeholder is displayed relative to the cursor.
///
/// This argument specifies the appearance, style, and position of a placeholder
/// shown at the cursor's location in an empty line.
@experimental
final CursorPlaceholderConfig? cursorPlaceholderConfig;

/// A handler for keys that are pressed when the editor is focused.
///
/// This feature is supported on **desktop devices only**.
Expand Down Expand Up @@ -501,14 +539,19 @@ class QuillEditorConfig {
ContentInsertionConfiguration? contentInsertionConfiguration,
GlobalKey<EditorState>? editorKey,
TextSelectionThemeData? textSelectionThemeData,
PlaceholderConfig? placeholderConfig,
bool? requestKeyboardFocusOnCheckListChanged,
CursorPlaceholderConfig? cursorPlaceholderConfig,
TextInputAction? textInputAction,
bool? enableScribble,
void Function()? onScribbleActivated,
EdgeInsets? scribbleAreaInsets,
void Function(TextInputAction action)? onPerformAction,
}) {
return QuillEditorConfig(
cursorPlaceholderConfig:
cursorPlaceholderConfig ?? this.cursorPlaceholderConfig,
placeholderConfig: placeholderConfig ?? this.placeholderConfig,
customLeadingBlockBuilder:
customLeadingBlockBuilder ?? this.customLeadingBlockBuilder,
placeholder: placeholder ?? this.placeholder,
Expand Down
8 changes: 7 additions & 1 deletion lib/src/editor/editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import '../document/nodes/container.dart' as container_node;
import '../document/nodes/leaf.dart';
import 'config/editor_config.dart';
import 'embed/embed_editor_builder.dart';
import 'raw_editor/builders/placeholder/placeholder_builder.dart';
import 'raw_editor/config/raw_editor_config.dart';
import 'raw_editor/raw_editor.dart';
import 'widgets/box.dart';
Expand Down Expand Up @@ -253,13 +254,17 @@ class QuillEditorState extends State<QuillEditor>

final showSelectionToolbar = configurations.enableInteractiveSelection &&
configurations.enableSelectionToolbar;

final placeholderBuilder = widget.config.placeholderConfig == null
? null
: PlaceholderBuilder(configuration: widget.config.placeholderConfig!);
final child = QuillRawEditor(
key: _editorKey,
controller: controller,
config: QuillRawEditorConfig(
characterShortcutEvents: widget.config.characterShortcutEvents,
spaceShortcutEvents: widget.config.spaceShortcutEvents,
cursorPlaceholderConfig: widget.config.cursorPlaceholderConfig,
placeholderBuilder: placeholderBuilder,
onKeyPressed: widget.config.onKeyPressed,
customLeadingBuilder: widget.config.customLeadingBlockBuilder,
focusNode: widget.focusNode,
Expand Down Expand Up @@ -1377,6 +1382,7 @@ class RenderEditor extends RenderEditableContainerBox
child.getCaretPrototype(child.globalToLocalPosition(textPosition));
_floatingCursorRect =
sizeAdjustment.inflateRect(caretPrototype).shift(boundedOffset);

_cursorController
.setFloatingCursorTextPosition(_floatingCursorTextPosition);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
@internal
library;

import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import '../../../../document/attribute.dart' show Attribute, AttributeScope;
import '../../../../document/nodes/line.dart';
import 'placeholder_configuration.dart';

// The black list of the keys that can not be used or permitted by the builder.
final List<String> _blackList = List.unmodifiable(<String>[
Attribute.align.key,
Attribute.direction.key,
Attribute.lineHeight.key,
Attribute.indent.key,
...Attribute.inlineKeys,
...Attribute.ignoreKeys,
]);

/// A utility class for managing placeholder rendering logic in a document editor.
///
/// The `PlaceholderBuilder` is responsible for determining when a placeholder
/// should be displayed in an empty node and for constructing the corresponding
/// visual representation.
///
/// - [configuration]: An instance of [PlaceholderConfig] containing placeholder
/// rendering rules and attribute customizations.
@experimental
@immutable
class PlaceholderBuilder {
const PlaceholderBuilder({
required this.configuration,
});

final PlaceholderConfig configuration;

Map<String, PlaceholderComponentBuilder> get builders =>
configuration.builders;
Set<String>? get customBlockAttributesKeys =>
configuration.customBlockAttributesKeys;

/// Determines whether a given [Line] node should display a placeholder.
///
/// This method checks if the node is empty and contains a block-level attribute
/// matching a builder key or custom attribute, excluding keys in the blacklist.
///
/// Returns a tuple:
/// - [bool]: Whether a placeholder should be shown.
/// - [String]: The key of the matching attribute, if applicable.
@experimental
(bool, String) shouldShowPlaceholder(Line node) {
if (builders.isEmpty) return (false, '');
var shouldShow = false;
var key = '';
for (final exclusiveKey in <dynamic>{
...Attribute.exclusiveBlockKeys,
...?customBlockAttributesKeys
}) {
if (node.style.containsKey(exclusiveKey) &&
node.style.attributes[exclusiveKey]?.scope == AttributeScope.block &&
!_blackList.contains(exclusiveKey)) {
shouldShow = true;
key = exclusiveKey;
break;
}
}
// we return if should show placeholders and the key of the attr that matches to get it directly
// avoiding an unnecessary traverse into the attributes of the node
return (node.isEmpty && shouldShow, key);
}

/// Constructs a [WidgetSpan] for rendering a placeholder in an empty line.
///
/// This method creates a visual representation of the placeholder based on
/// the block attribute and style configurations provided. Use [shouldShowPlaceholder]
/// before invoking this method to ensure the placeholder is needed.
@experimental
WidgetSpan? build({
required Attribute blockAttribute,
required TextStyle lineStyle,
required TextAlign align,
TextDirection? textDirection,
StrutStyle? strutStyle,
TextScaler? textScaler,
}) {
if (builders.isEmpty) return null;
final configuration =
builders[blockAttribute.key]?.call(blockAttribute, lineStyle);
// we don't need to add a placeholder that is null or contains a empty text
if (configuration == null || configuration.text.trim().isEmpty) {
return null;
}
final textWidget = Text(
configuration.text,
style: configuration.style,
textDirection: textDirection,
softWrap: true,
strutStyle: strutStyle,
textAlign: align,
textScaler: textScaler,
textWidthBasis: TextWidthBasis.longestLine,
);

// Use a [Row] with [Expanded] for placeholders in lines without explicit alignment.
// This ensures the placeholder spans the full width, avoiding unexpected alignment issues.
return WidgetSpan(
style: lineStyle,
child: align == TextAlign.end || align == TextAlign.center
? textWidget
: Row(
children: [
Expanded(
child: textWidget,
),
],
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'package:flutter/material.dart' show TextStyle;
import 'package:meta/meta.dart' show experimental, immutable;
import '../../../../document/attribute.dart';

typedef PlaceholderComponentBuilder = PlaceholderTextBuilder? Function(
Attribute, TextStyle);

/// Configuration class for defining how placeholders are handled in the editor.
///
/// The `PlaceholderConfig` allows customization of placeholder behavior by
/// providing builders for rendering specific components and defining custom
/// attribute keys that should be recognized during the placeholder build process.
///
/// - [builders]: A map associating placeholder keys with their respective
/// component builders, allowing custom rendering logic.
/// - [customBlockAttributesKeys]: A set of additional attribute keys to include
/// in placeholder processing. By default, only predefined keys are considered.
@experimental
@immutable
class PlaceholderConfig {
const PlaceholderConfig({
required this.builders,
this.customBlockAttributesKeys,
});

factory PlaceholderConfig.base() {
return const PlaceholderConfig(builders: {});
}

/// Add custom keys here to include them in placeholder builds, as external keys are ignored by default.
final Set<String>? customBlockAttributesKeys;
final Map<String, PlaceholderComponentBuilder> builders;

PlaceholderConfig copyWith({
Map<String, PlaceholderComponentBuilder>? builders,
Set<String>? customBlockAttributesKeys,
}) {
return PlaceholderConfig(
builders: builders ?? this.builders,
customBlockAttributesKeys:
customBlockAttributesKeys ?? this.customBlockAttributesKeys,
);
}
}

/// Represents the text that will be displayed
@immutable
class PlaceholderTextBuilder {
const PlaceholderTextBuilder({
required this.text,
required this.style,
});

final String text;
final TextStyle style;
}
Loading
Loading