diff --git a/CHANGELOG.md b/CHANGELOG.md index add2d3e..860be78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,12 @@ -## [1.0.1] - 2020/06/18 +## [1.0.2] - 2021/02/11 + +* Reposition cursor if style applied while no text were selected + +## [1.0.1] - 2020/08/13 * Add web support -## [1.0.0] - 2020/06/18 +## [1.0.0] - 2020/07/07 * Add RTL support @@ -11,6 +15,6 @@ * Remove unused dependency * Add more docs -## [0.0.1] - 2020/01/31 +## [0.0.1] - 2020/02/18 * Initialize project diff --git a/README.md b/README.md index e5cd1d1..034f8ce 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ MarkdownEditableTextInput is a TextField Widget that allow you to convert easily ## Features - [x] Convert to Bold, Italic, Title (h1,h2,h3), List and Link - [x] Support text direction -- [ ] Customize textInput colors ## Demo ![](pictures/test_edition.gif) diff --git a/example/android/app/src/main/kotlin/epsi/example/MainActivity.kt b/example/android/app/src/main/kotlin/epsi/example/MainActivity.kt new file mode 100644 index 0000000..862c9eb --- /dev/null +++ b/example/android/app/src/main/kotlin/epsi/example/MainActivity.kt @@ -0,0 +1,6 @@ +package epsi.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..449a9f9 --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id new file mode 100644 index 0000000..2ac0bdb --- /dev/null +++ b/example/ios/Flutter/.last_build_id @@ -0,0 +1 @@ +2ba6af71af99db6e035eee54ae4f354f \ No newline at end of file diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/lib/main.dart b/example/lib/main.dart index ff05dd4..c18e76a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,6 +4,7 @@ import 'package:markdown_editable_textinput/markdown_text_input.dart'; void main() => runApp(MyApp()); +// ignore: public_member_api_docs class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); @@ -17,10 +18,10 @@ class _MyAppState extends State { return MaterialApp( home: Theme( data: ThemeData( - primaryColor: const Color(0xFF2B3409), - accentColor: const Color(0xFF71881B), - cardColor: const Color(0xFFF7FBEA), - textTheme: const TextTheme(body1: TextStyle(fontSize: 20)), + primaryColor: const Color(0xFF2C1C6B), + accentColor: const Color(0xFF200681), + cardColor: const Color(0xFFF8F9FC), + textTheme: const TextTheme(bodyText2: TextStyle(fontSize: 20)), ), child: Scaffold( appBar: AppBar( @@ -41,7 +42,7 @@ class _MyAppState extends State { (String value) => setState(() => description = value), description, label: 'Description', - maxLines: 3, + maxLines: 2, ), Padding( padding: const EdgeInsets.only(top: 10), diff --git a/example/pubspec.lock b/example/pubspec.lock index 5f5125b..1a93713 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,13 +1,6 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - archive: - dependency: transitive - description: - name: archive - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.13" args: dependency: transitive description: @@ -21,42 +14,42 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.1" + version: "2.5.0-nullsafety.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" - charcode: + version: "2.1.0-nullsafety.1" + characters: dependency: transitive description: - name: charcode + name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" - collection: + version: "1.1.0-nullsafety.3" + charcode: dependency: transitive description: - name: collection + name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.14.12" - convert: + version: "1.2.0-nullsafety.1" + clock: dependency: transitive description: - name: convert + name: clock url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" - crypto: + version: "1.1.0-nullsafety.1" + collection: dependency: transitive description: - name: crypto + name: collection url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "1.15.0-nullsafety.3" cupertino_icons: dependency: "direct main" description: @@ -70,7 +63,14 @@ packages: name: effective_dart url: "https://pub.dartlang.org" source: hosted - version: "1.2.1" + version: "1.3.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" flutter: dependency: "direct main" description: flutter @@ -82,68 +82,47 @@ packages: name: flutter_markdown url: "https://pub.dartlang.org" source: hosted - version: "0.3.3" + version: "0.3.5" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" - image: - dependency: transitive - description: - name: image - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.12" markdown: dependency: transitive description: name: markdown url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.8" markdown_editable_textinput: dependency: "direct main" description: path: ".." relative: true source: path - version: "1.0.1" + version: "1.0.2" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.6" + version: "0.12.10-nullsafety.1" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.8" + version: "1.3.0-nullsafety.3" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.4" - petitparser: - dependency: transitive - description: - name: petitparser - url: "https://pub.dartlang.org" - source: hosted - version: "2.4.0" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.3" + version: "1.8.0-nullsafety.1" sky_engine: dependency: transitive description: flutter @@ -155,63 +134,56 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0-nullsafety.2" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "1.10.0-nullsafety.1" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety.1" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.1.0-nullsafety.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0-nullsafety.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.15" + version: "0.2.19-nullsafety.2" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.3.0-nullsafety.3" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" - xml: - dependency: transitive - description: - name: xml - url: "https://pub.dartlang.org" - source: hosted - version: "3.6.1" + version: "2.1.0-nullsafety.3" sdks: - dart: ">=2.6.0 <3.0.0" + dart: ">=2.10.0-110 <2.11.0" flutter: ">=1.10.7 <2.0.0" diff --git a/example/web/favicon.png b/example/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/example/web/favicon.png differ diff --git a/example/web/icons/Icon-192.png b/example/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/example/web/icons/Icon-192.png differ diff --git a/example/web/icons/Icon-512.png b/example/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/example/web/icons/Icon-512.png differ diff --git a/example/web/index.html b/example/web/index.html new file mode 100644 index 0000000..1460b5e --- /dev/null +++ b/example/web/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/example/web/manifest.json b/example/web/manifest.json new file mode 100644 index 0000000..8c01291 --- /dev/null +++ b/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/lib/format_markdown.dart b/lib/format_markdown.dart index d36eebc..ee8e943 100644 --- a/lib/format_markdown.dart +++ b/lib/format_markdown.dart @@ -6,19 +6,24 @@ class FormatMarkdown { static ResultMarkdown convertToMarkdown(MarkdownType type, String data, int fromIndex, int toIndex, {int titleSize = 1}) { String changedData; + int replaceCursorIndex; switch (type) { case MarkdownType.bold: changedData = '**${data.substring(fromIndex, toIndex)}**'; + replaceCursorIndex = 2; break; case MarkdownType.italic: changedData = '_${data.substring(fromIndex, toIndex)}_'; + replaceCursorIndex = 1; break; case MarkdownType.link: changedData = '[${data.substring(fromIndex, toIndex)}](${data.substring(fromIndex, toIndex)})'; + replaceCursorIndex = 3; break; case MarkdownType.title: changedData = "${"#" * titleSize} ${data.substring(fromIndex, toIndex)}"; + replaceCursorIndex = 0; break; case MarkdownType.list: var index = 0; @@ -27,12 +32,14 @@ class FormatMarkdown { index++; return index == splitedData.length ? '* $value' : '* $value\n'; }).join(); + replaceCursorIndex = 0; break; } + final cursorIndex = changedData.length; - return ResultMarkdown( - data.substring(0, fromIndex) + changedData + data.substring(toIndex, data.length), cursorIndex); + return ResultMarkdown(data.substring(0, fromIndex) + changedData + data.substring(toIndex, data.length), + cursorIndex, replaceCursorIndex); } } @@ -44,8 +51,11 @@ class ResultMarkdown { /// cursor index just after the converted part in markdown int cursorIndex; + /// index at which cursor need to be replaced if no text selected + int replaceCursorIndex; + /// Return [ResultMarkdown] - ResultMarkdown(this.data, this.cursorIndex); + ResultMarkdown(this.data, this.cursorIndex, this.replaceCursorIndex); } /// Represent markdown possible type to convert diff --git a/lib/markdown_text_input.dart b/lib/markdown_text_input.dart index 009a677..51049f1 100644 --- a/lib/markdown_text_input.dart +++ b/lib/markdown_text_input.dart @@ -10,7 +10,7 @@ class MarkdownTextInput extends StatefulWidget { final String initialValue; /// Validator for the TextFormField - final Function validators; + final String Function(String value) validators; /// String displayed at hintText in TextFormField final String label; @@ -23,7 +23,7 @@ class MarkdownTextInput extends StatefulWidget { /// Constructor for [MarkdownTextInput] MarkdownTextInput(this.onTextChanged, this.initialValue, - {this.label, this.validators, this.textDirection, this.maxLines}); + {this.label = '', this.validators, this.textDirection = TextDirection.ltr, this.maxLines = 10}); @override _MarkdownTextInputState createState() => _MarkdownTextInputState(); @@ -35,6 +35,7 @@ class _MarkdownTextInputState extends State { void onTap(MarkdownType type, {int titleSize = 1}) { final basePosition = textSelection.baseOffset; + var noTextSelected = (textSelection.baseOffset - textSelection.extentOffset) == 0; final result = FormatMarkdown.convertToMarkdown( type, _controller.text, textSelection.baseOffset, textSelection.extentOffset, @@ -42,6 +43,10 @@ class _MarkdownTextInputState extends State { _controller.value = _controller.value .copyWith(text: result.data, selection: TextSelection.collapsed(offset: basePosition + result.cursorIndex)); + + if (noTextSelected) { + _controller.selection = TextSelection.collapsed(offset: _controller.selection.end - result.replaceCursorIndex); + } } @override @@ -75,7 +80,7 @@ class _MarkdownTextInputState extends State { maxLines: widget.maxLines, controller: _controller, textCapitalization: TextCapitalization.sentences, - validator: widget.validators != null ? (value) => widget.validators(value) as String : null, + validator: (value) => widget.validators(value), cursorColor: Theme.of(context).primaryColor, textDirection: widget.textDirection ?? TextDirection.ltr, decoration: InputDecoration( diff --git a/pictures/test_edition.gif b/pictures/test_edition.gif index 30b8771..5782354 100644 Binary files a/pictures/test_edition.gif and b/pictures/test_edition.gif differ diff --git a/pubspec.lock b/pubspec.lock index 10ac605..5b0798c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,69 +1,62 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - archive: - dependency: transitive - description: - name: archive - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.13" - args: - dependency: transitive - description: - name: args - url: "https://pub.dartlang.org" - source: hosted - version: "1.6.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.1" + version: "2.5.0-nullsafety.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" - charcode: + version: "2.1.0-nullsafety.1" + characters: dependency: transitive description: - name: charcode + name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" - collection: + version: "1.1.0-nullsafety.3" + charcode: dependency: transitive description: - name: collection + name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.14.12" - convert: + version: "1.2.0-nullsafety.1" + clock: dependency: transitive description: - name: convert + name: clock url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" - crypto: + version: "1.1.0-nullsafety.1" + collection: dependency: transitive description: - name: crypto + name: collection url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "1.15.0-nullsafety.3" effective_dart: dependency: "direct main" description: name: effective_dart url: "https://pub.dartlang.org" source: hosted - version: "1.2.3" + version: "1.3.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" flutter: dependency: "direct main" description: flutter @@ -74,48 +67,27 @@ packages: description: flutter source: sdk version: "0.0.0" - image: - dependency: transitive - description: - name: image - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.12" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.6" + version: "0.12.10-nullsafety.1" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.8" + version: "1.3.0-nullsafety.3" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.4" - petitparser: - dependency: transitive - description: - name: petitparser - url: "https://pub.dartlang.org" - source: hosted - version: "2.4.0" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.3" + version: "1.8.0-nullsafety.1" sky_engine: dependency: transitive description: flutter @@ -127,62 +99,55 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0-nullsafety.2" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "1.10.0-nullsafety.1" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety.1" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.1.0-nullsafety.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0-nullsafety.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.15" + version: "0.2.19-nullsafety.2" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.3.0-nullsafety.3" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" - xml: - dependency: transitive - description: - name: xml - url: "https://pub.dartlang.org" - source: hosted - version: "3.6.1" + version: "2.1.0-nullsafety.3" sdks: - dart: ">=2.6.0 <3.0.0" + dart: ">=2.10.0-110 <2.11.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0da79dc..df45755 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: markdown_editable_textinput description: A TextField Widget that allow you to convert easily what's in the TextField to Markdown. -version: 1.0.1 +version: 1.0.2 homepage: https://github.com/playmoweb/markdown-editable-textinput repository: https://github.com/playmoweb/markdown-editable-textinput