Skip to content

Commit

Permalink
feat: apply format and restore cursor after component results
Browse files Browse the repository at this point in the history
  • Loading branch information
LeadcodeDev committed Jun 14, 2024
1 parent f99ac65 commit 49b2161
Show file tree
Hide file tree
Showing 11 changed files with 74 additions and 41 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ jobs:
- uses: dart-lang/setup-dart@v1
- name: Install dependencies
run: dart pub get
- name: Lint
run: dart format .
- name: Publish
run: dart pub publish --force
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.1.0

- Implement password secure
- Restore cursor after component result

## 1.0.0

- Initial version.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ user input.
To use Commander in your Dart project, add this to your `pubspec.yaml` file :
```yaml
dependencies:
cli_commander: ^1.0.0
commander_ui: ^1.1.0
```
Then run `pub get` to install the dependencies.
Expand Down
22 changes: 12 additions & 10 deletions example/input.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:io';

import 'package:commander_ui/commander_ui.dart';

final class Item {
Expand All @@ -13,20 +15,20 @@ Future<void> main() async {
final input = Input(
answer: 'Please give us your name',
placeholder: 'firstname lastname',
validate: (value) =>
switch(value) {
String value when value
.trim()
.isNotEmpty => Ok(null),
_ => Err('Please provide a valid name')
}
);

final value = switch(await input.handle()) {
secure:
true, // 👈 Optional, ou can hide the input like html input with password type
validate: (value) => switch (value) {
String value when value.trim().isNotEmpty => Ok(null),
_ => Err('Please provide a valid name')
});

final value = switch (await input.handle()) {
Ok(:final value) => 'My value is $value',
Err(:final error) => Exception('Error: $error'),
_ => 'Unknown',
};

print(value);

exit(0);
}
19 changes: 14 additions & 5 deletions example/select.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:io';

import 'package:commander_ui/commander_ui.dart';

final class Item {
Expand All @@ -10,20 +12,27 @@ final class Item {
Future<void> main() async {
StdinBuffer.initialize();

final List<Item> items = List.generate(
20, (index) => Item('${index + 1}. Hello World', index + 1));

String formatSelectedLine(String line) =>
'${AsciiColors.green('❯')} ${AsciiColors.lightCyan(line)}';

final select = Select(
answer: "Please select your best hello",
options: List.generate(20, (index) => Item('${index + 1}. Hello World', index + 1)),
options: items,
placeholder: 'Type to filter',
selectedLineStyle: (line) => '${AsciiColors.green('❯')} ${AsciiColors.lightCyan(line)}',
selectedLineStyle: formatSelectedLine,
unselectedLineStyle: (line) => ' $line',
onDisplay: (item) => item.name
);
onDisplay: (item) => item.name);

final selected = switch(await select.handle()) {
final selected = switch (await select.handle()) {
Ok(:final value) => 'My value is ${value.value}',
Err(:final error) => Exception('Error: $error'),
_ => 'Unknown',
};

print(selected);

exit(0);
}
2 changes: 0 additions & 2 deletions lib/commander_ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ export '../src/commons/cli.dart';
export '../src/commons/color.dart';

export '../src/application/stdin_buffer.dart';


8 changes: 4 additions & 4 deletions lib/src/commons/cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ mixin Tools {
throw Exception('Could not parse cursor position');
}

return (
int.parse(match.group(1)!),
int.parse(match.group(2)!)
);
final line = int.parse(match.group(1)!);
final column = int.parse(match.group(2)!);

return (line, column);
}

Future<int> getAvailableLinesBelowCursor() async {
Expand Down
35 changes: 23 additions & 12 deletions lib/src/components/select.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import 'package:commander_ui/src/component.dart';
import 'package:commander_ui/src/key_down_event_listener.dart';
import 'package:commander_ui/src/result.dart';

final class Select<T, R extends dynamic> with Tools implements Component<Result<T>> {
final class Select<T, R extends dynamic>
with Tools
implements Component<Result<T>> {
String filter = '';
int currentIndex = 0;
bool isRendering = false;
Expand All @@ -35,10 +37,14 @@ final class Select<T, R extends dynamic> with Tools implements Component<Result<
String Function(String)? selectedLineStyle,
String Function(String)? unselectedLineStyle,
}) {
this.noResultFoundMessage = noResultFoundMessage ?? AsciiColors.dim('No result found');
this.exitMessage = exitMessage ?? '${AsciiColors.red('✘')} Operation canceled by user';
this.selectedLineStyle = selectedLineStyle ?? (line) => '${AsciiColors.green('❯')} $selectedLineStyle(line)';
this.unselectedLineStyle = unselectedLineStyle ?? (line) => ' $unselectedLineStyle(line)';
this.noResultFoundMessage =
noResultFoundMessage ?? AsciiColors.dim('No result found');
this.exitMessage =
exitMessage ?? '${AsciiColors.red('✘')} Operation canceled by user';
this.selectedLineStyle = selectedLineStyle ??
(line) => '${AsciiColors.green('❯')} $selectedLineStyle(line)';
this.unselectedLineStyle =
unselectedLineStyle ?? (line) => ' $unselectedLineStyle(line)';
}

@override
Expand Down Expand Up @@ -95,9 +101,11 @@ final class Select<T, R extends dynamic> with Tools implements Component<Result<
return;
}

final value = onDisplay?.call(options[currentIndex]) ?? options[currentIndex].toString();
final value = onDisplay?.call(options[currentIndex]) ??
options[currentIndex].toString();

stdout.writeln('${AsciiColors.green('✔')} $answer · ${AsciiColors.lightGreen(value)}');
stdout.writeln(
'${AsciiColors.green('✔')} $answer · ${AsciiColors.lightGreen(value)}');
saveCursorPosition();
showCursor();
_completer.complete(Ok(options[currentIndex]));
Expand Down Expand Up @@ -139,11 +147,12 @@ final class Select<T, R extends dynamic> with Tools implements Component<Result<
List<T> filteredArr = options.where((item) {
final value = onDisplay?.call(item) ?? item.toString();
return filter.isNotEmpty
? value.toLowerCase().contains(filter.toLowerCase())
: true;
? value.toLowerCase().contains(filter.toLowerCase())
: true;
}).toList();

buffer.writeln('${AsciiColors.yellow('?')} $answer : ${filter.isEmpty ? AsciiColors.dim(placeholder ?? '') : filter}');
buffer.writeln(
'${AsciiColors.yellow('?')} $answer : ${filter.isEmpty ? AsciiColors.dim(placeholder ?? '') : filter}');

if (filteredArr.isEmpty) {
buffer.writeln(noResultFoundMessage);
Expand All @@ -152,10 +161,12 @@ final class Select<T, R extends dynamic> with Tools implements Component<Result<
if (currentIndex >= filteredArr.length - 2) {
start = filteredArr.length - 5;
}
int end = start + 5 <= filteredArr.length ? start + 5 : filteredArr.length;
int end =
start + 5 <= filteredArr.length ? start + 5 : filteredArr.length;

for (int i = start; i < end; i++) {
final value = onDisplay?.call(filteredArr[i]) ?? filteredArr[i].toString();
final value =
onDisplay?.call(filteredArr[i]) ?? filteredArr[i].toString();
if (i == currentIndex) {
copy.add(selectedLineStyle(value));
} else {
Expand Down
10 changes: 6 additions & 4 deletions lib/src/key_down_event_listener.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class KeyDownEventListener {

KeyDownEventListener() {
subscription = StdinBuffer.stream.transform(utf8.decoder).listen((data) {
final listener = listeners.firstWhereOrNull((listener) => listener.key.value == data);
final listener =
listeners.firstWhereOrNull((listener) => listener.key.value == data);
if (listener case KeyDownListener listener) {
listener.callback(data, dispose);
return;
Expand All @@ -26,11 +27,13 @@ class KeyDownEventListener {
});
}

void match(AnsiCharacter key, void Function(String, void Function() dispose) callback) {
void match(AnsiCharacter key,
void Function(String, void Function() dispose) callback) {
listeners.add(KeyDownListener(key, callback));
}

void catchAll(FutureOr<void> Function(String, void Function() dispose) callback) {
void catchAll(
FutureOr<void> Function(String, void Function() dispose) callback) {
fallback = callback;
}

Expand All @@ -49,7 +52,6 @@ class KeyDownEventListener {
}
}


final class KeyDownListener {
final AnsiCharacter key;
final void Function(String, void Function()) callback;
Expand Down
8 changes: 6 additions & 2 deletions lib/src/result.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
abstract class Result<T> {
const Result();

R when<R>({required R Function(T value) ok, required String Function(Object error) err});
R when<R>(
{required R Function(T value) ok,
required String Function(Object error) err});
T unwrap();
}

Expand All @@ -19,7 +21,9 @@ class Err<T> extends Result<T> with ResultWhen {

mixin ResultWhen<T> on Result<T> {
@override
R when<R>({required R Function(T value) ok, required String Function(Object error) err}) {
R when<R>(
{required R Function(T value) ok,
required String Function(Object error) err}) {
if (this is Ok<T>) {
return ok((this as Ok<T>).value);
} else {
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: commander_ui
description: Commander is a Dart library for creating user interfaces within the terminal.
version: 1.0.0
version: 1.1.0
repository: https://github.com/LeadcodeDev/commander

topics:
Expand Down

0 comments on commit 49b2161

Please sign in to comment.