From 6a9ba765a07d9cacf345f7583fda2082632aabf8 Mon Sep 17 00:00:00 2001 From: Jcparkyn <51850908+Jcparkyn@users.noreply.github.com> Date: Tue, 21 Jul 2020 17:05:35 +1000 Subject: [PATCH 1/7] Add ContentEditableDiv --- .../Components/ContentEditableDiv.razor | 42 ++++++++++++ Nodexr/wwwroot/index.html | 1 + Nodexr/wwwroot/js/ContentEditable.js | 66 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 Nodexr/Shared/Components/ContentEditableDiv.razor create mode 100644 Nodexr/wwwroot/js/ContentEditable.js diff --git a/Nodexr/Shared/Components/ContentEditableDiv.razor b/Nodexr/Shared/Components/ContentEditableDiv.razor new file mode 100644 index 00000000..ba531023 --- /dev/null +++ b/Nodexr/Shared/Components/ContentEditableDiv.razor @@ -0,0 +1,42 @@ +@inject IJSRuntime JS; + +
+ +@code { + + [Parameter] + public string Text { get; set; } + + [Parameter] + public string CssClass { get; set; } + + [Parameter] + public EventCallback TextChanged { get; set; } + + ElementReference divElement; + + protected string textToDisplay; + + protected override void OnInitialized() + { + //Text = Text.Replace(Environment.NewLine, "
"); + } + + //send initial text (if any) to javascript to place in the div + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JS.InvokeVoidAsync("contentEditable.initContentEditable", divElement, DotNetObjectReference.Create(this), Text); + } + } + + //receive input text from javascript and invoke callback to parent component + [JSInvokable] + public async Task GetUpdatedTextFromJavascript(string textFromJavascript) + { + Text = textFromJavascript; + await TextChanged.InvokeAsync(textFromJavascript); + } +} diff --git a/Nodexr/wwwroot/index.html b/Nodexr/wwwroot/index.html index c5bad6b6..84af50f1 100644 --- a/Nodexr/wwwroot/index.html +++ b/Nodexr/wwwroot/index.html @@ -99,6 +99,7 @@ + diff --git a/Nodexr/wwwroot/js/ContentEditable.js b/Nodexr/wwwroot/js/ContentEditable.js new file mode 100644 index 00000000..a3b9468d --- /dev/null +++ b/Nodexr/wwwroot/js/ContentEditable.js @@ -0,0 +1,66 @@ + +window.contentEditable = { + getInnerHtml: function (element) { + return element.innerHTML; + }, + + initContentEditable: function (div, instance, textToDisplay) { + div.innerText = textToDisplay; + div.addEventListener("input", function () { + let text = div.innerText;//contentEditable.strip(div.innerText); + instance.invokeMethodAsync("GetUpdatedTextFromJavascript", text); + }); + + try { + div.contentEditable = "plaintext-only"; + } + catch (e) { + contentEditable.setupFallbackPlaintextOnly(div); + } + }, + + setupFallbackPlaintextOnly: function (elem) { + elem.contentEditable = "true"; + contentEditable.forcePlaintextPaste(elem); + + elem.addEventListener("drop", function (e) { + e.preventDefault(); + return false; + }); + }, + + forcePlaintextPaste: function (elem) { + elem.addEventListener("paste", function (e) { + e.preventDefault(); + if (e.clipboardData && e.clipboardData.getData) { + var text = e.clipboardData.getData("text/plain"); + document.execCommand("insertHTML", false, text); + } else if (window.clipboardData && window.clipboardData.getData) { + var text = window.clipboardData.getData("Text"); + contentEditable.insertTextAtCursor(text); + } + }); + }, + + insertTextAtCursor: function(text) { + var sel, range; + if (window.getSelection) { + sel = window.getSelection(); + if (sel.getRangeAt && sel.rangeCount) { + range = sel.getRangeAt(0); + range.deleteContents(); + range.insertNode(document.createTextNode(text)); + } + } else if (document.selection && document.selection.createRange) { + document.selection.createRange().text = text; + } + }, + + strip: function(html) { + let tempDiv = document.createElement("div"); + tempDiv.innerText = html; + let text = tempDiv.innerText; + tempDiv.remove(); + return text; + } +} \ No newline at end of file From 683f6719fa2aba6bfeed87ce8f37cb634cdefd1b Mon Sep 17 00:00:00 2001 From: Jcparkyn <51850908+Jcparkyn@users.noreply.github.com> Date: Tue, 21 Jul 2020 17:12:26 +1000 Subject: [PATCH 2/7] Allow inline editing using ContentEditableDiv. --- Nodexr/Shared/Components/OutputDisplay.razor | 29 ++++++++++++++++--- .../Components/OutputDisplaySegment.razor | 2 +- Nodexr/Shared/Components/OutputEditor.razor | 25 ++++++++++++++++ Nodexr/wwwroot/css/site.css | 23 ++++++++++++--- 4 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 Nodexr/Shared/Components/OutputEditor.razor diff --git a/Nodexr/Shared/Components/OutputDisplay.razor b/Nodexr/Shared/Components/OutputDisplay.razor index c0a5de81..fd48b656 100644 --- a/Nodexr/Shared/Components/OutputDisplay.razor +++ b/Nodexr/Shared/Components/OutputDisplay.razor @@ -9,8 +9,15 @@

Output:

-
@foreach (var segment in @NodeHandler.CachedOutput.Contents) - {@*This must not be surrounded by whitespace*@}
+
@if (isEditing) + {} + else + { + @foreach (var segment in @NodeHandler.CachedOutput.Contents) + {@*This must not be surrounded by whitespace*@} + } +
@@ -20,12 +27,26 @@ @functions{ + bool isEditing = false; + protected override void OnInitialized() { NodeHandler.OutputChanged += (sender, e) => StateHasChanged(); } - private async Task OnEditButtonClick() + private void OnEditButtonClick() + { + isEditing = !isEditing; + } + + private void OnEditSubmitted(string newExpression) + { + isEditing = false; + Console.WriteLine("Submitted: " + newExpression); + NodeHandler.TryCreateTreeFromRegex(newExpression); + } + + @*private async Task OnEditButtonClick() { var modalParameters = new ModalParameters(); modalParameters.Add(nameof(EditRegexDialog.previousRegex), NodeHandler.CachedOutput.Expression); @@ -41,7 +62,7 @@ Console.WriteLine("Custom Regex: " + customRegex); NodeHandler.TryCreateTreeFromRegex(customRegex); } - } + }*@ private async Task OnCreateLinkButtonClick() { diff --git a/Nodexr/Shared/Components/OutputDisplaySegment.razor b/Nodexr/Shared/Components/OutputDisplaySegment.razor index fdf3858b..abd2d36c 100644 --- a/Nodexr/Shared/Components/OutputDisplaySegment.razor +++ b/Nodexr/Shared/Components/OutputDisplaySegment.razor @@ -1,7 +1,7 @@ @implements IDisposable @inject INodeHandler NodeHandler - + +
+ +@code { + + string expression; + + [Parameter] public string StartExpression + { + set => expression = value; + } + + [Parameter] public EventCallback OnSubmitted { get; set; } + + protected async Task OnKeyPress(KeyboardEventArgs e) + { + if(e.Key == "Enter") + { + await OnSubmitted.InvokeAsync(expression); + } + } +} diff --git a/Nodexr/wwwroot/css/site.css b/Nodexr/wwwroot/css/site.css index b60282ae..c80ab0cd 100644 --- a/Nodexr/wwwroot/css/site.css +++ b/Nodexr/wwwroot/css/site.css @@ -266,17 +266,20 @@ input { } .output-regex { + border: none; background-color: var(--col-field); color: var(--col-text-medium); - border: none; + min-width: 250px; + padding: 0px 5px; + height: auto; +} + +.output-regex-text { font-size: 30px; font-family: Consolas, monospace; line-height: 34px; - min-width: 250px; - padding: 0px 5px; white-space: pre-wrap; word-break: break-all; - height: auto; } .output-segment { @@ -302,6 +305,18 @@ input { background-color: hsl(222, 14%, 22%); } +.output-edit-textarea { + position: absolute; + resize: none; + background-color: transparent; + color: inherit; + overflow: hidden; + border: none; + padding: 0; + width: 100%; +/* height: 100%;*/ +} + .search-text-highlight, .search-textarea { padding: 3px; /*letter-spacing: 1px;*/ From b4ed24d570a03e55e4884852aa5e48a2bfd154d6 Mon Sep 17 00:00:00 2001 From: Jcparkyn <51850908+Jcparkyn@users.noreply.github.com> Date: Wed, 22 Jul 2020 13:33:47 +1000 Subject: [PATCH 3/7] Add dialog to inline editor with cancel and confirm buttons. --- Nodexr/Shared/Components/OutputDisplay.razor | 51 +++++++++++--------- Nodexr/Shared/Components/OutputEditor.razor | 31 ++++++++++-- Nodexr/wwwroot/css/node.css | 11 ++--- Nodexr/wwwroot/css/site.css | 32 +++++++++++- 4 files changed, 90 insertions(+), 35 deletions(-) diff --git a/Nodexr/Shared/Components/OutputDisplay.razor b/Nodexr/Shared/Components/OutputDisplay.razor index fd48b656..bf77fe9a 100644 --- a/Nodexr/Shared/Components/OutputDisplay.razor +++ b/Nodexr/Shared/Components/OutputDisplay.razor @@ -11,7 +11,8 @@
@if (isEditing) {} + OnSubmitted="@OnEditSubmitted" + OnCanceled="OnEditCancelled"/>} else { @foreach (var segment in @NodeHandler.CachedOutput.Contents) @@ -22,6 +23,7 @@ +
@@ -42,10 +44,15 @@ private void OnEditSubmitted(string newExpression) { isEditing = false; - Console.WriteLine("Submitted: " + newExpression); NodeHandler.TryCreateTreeFromRegex(newExpression); } + private void OnEditCancelled() + { + isEditing = false; + StateHasChanged(); + } + @*private async Task OnEditButtonClick() { var modalParameters = new ModalParameters(); @@ -64,32 +71,32 @@ } }*@ - private async Task OnCreateLinkButtonClick() - { - var urlParams = new Dictionary +private async Task OnCreateLinkButtonClick() +{ + var urlParams = new Dictionary { { "parse", NodeHandler.CachedOutput.Expression } }; - if (RegexReplaceHandler.SearchText != RegexReplaceHandler.DefaultSearchText) - { - urlParams.Add("search", RegexReplaceHandler.SearchText); - } - - if (RegexReplaceHandler.ReplacementRegex != RegexReplaceHandler.DefaultReplacementRegex) - { - urlParams.Add("replace", RegexReplaceHandler.ReplacementRegex); - } - - string url = QueryHelpers.AddQueryString(NavManager.BaseUri, urlParams); - await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", url, ""); - ToastService.ShowInfo("", "Link copied to clipboard"); + if (RegexReplaceHandler.SearchText != RegexReplaceHandler.DefaultSearchText) + { + urlParams.Add("search", RegexReplaceHandler.SearchText); } - private async Task CopyTextToClipboard() + if (RegexReplaceHandler.ReplacementRegex != RegexReplaceHandler.DefaultReplacementRegex) { - string regex = NodeHandler.CachedOutput.Expression; - await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", regex, ""); - ToastService.ShowInfo(regex, "Copied to clipboard"); + urlParams.Add("replace", RegexReplaceHandler.ReplacementRegex); } + + string url = QueryHelpers.AddQueryString(NavManager.BaseUri, urlParams); + await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", url, ""); + ToastService.ShowInfo("", "Link copied to clipboard"); +} + +private async Task CopyTextToClipboard() +{ + string regex = NodeHandler.CachedOutput.Expression; + await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", regex, ""); + ToastService.ShowInfo(regex, "Copied to clipboard"); +} } diff --git a/Nodexr/Shared/Components/OutputEditor.razor b/Nodexr/Shared/Components/OutputEditor.razor index 6a4f4c25..e2274e36 100644 --- a/Nodexr/Shared/Components/OutputEditor.razor +++ b/Nodexr/Shared/Components/OutputEditor.razor @@ -1,7 +1,13 @@  -
+
+
+ Input your own regular expression here to convert it to a node tree.
+ + +
@code { @@ -14,12 +20,31 @@ } [Parameter] public EventCallback OnSubmitted { get; set; } + [Parameter] public EventCallback OnCanceled { get; set; } protected async Task OnKeyPress(KeyboardEventArgs e) { - if(e.Key == "Enter") + if(e.Key == "Enter" && !e.ShiftKey) { - await OnSubmitted.InvokeAsync(expression); + await Submit(); } } + + protected async Task OnKeyUp(KeyboardEventArgs e) + { + if (e.Key == "Escape") + { + await Cancel(); + } + } + + protected async Task Submit() + { + await OnSubmitted.InvokeAsync(expression); + } + + protected async Task Cancel() + { + await OnCanceled.InvokeAsync(null); + } } diff --git a/Nodexr/wwwroot/css/node.css b/Nodexr/wwwroot/css/node.css index c08e2dd1..e7899997 100644 --- a/Nodexr/wwwroot/css/node.css +++ b/Nodexr/wwwroot/css/node.css @@ -18,6 +18,7 @@ padding: 2px 4px 0px 4px; /*margin-bottom: 0px;*/ border-radius: 8px 8px 0px 0px; + color: var(--col-text-invert); } .node .node-title.collapsed { border-radius: 8px @@ -34,13 +35,14 @@ border: none; font-size: 0.8rem; padding: 0 0 0 7px; + color: inherit; } .node .icon-button { position: relative; background: none; border: none; - color: var(--col-text-strong2); + color: inherit; font-size: 0.9rem; padding: 1px 0 0 0; outline: 0 !important; @@ -206,16 +208,9 @@ padding-left: 7px; } - - - .add-button { position: relative; margin: 5px 0px 0px 0px; - background-color: rgba(200,200,256,0.09); - border: none; - border-radius: 6px; - color: var(--col-text-medium); font-size: 0.9rem; } diff --git a/Nodexr/wwwroot/css/site.css b/Nodexr/wwwroot/css/site.css index c80ab0cd..52fc59a8 100644 --- a/Nodexr/wwwroot/css/site.css +++ b/Nodexr/wwwroot/css/site.css @@ -36,6 +36,7 @@ --col-text-strong2: #eaeaea; --col-text-medium: #d0d0d0; --col-text-subtle: #acacac; + --col-text-invert: #262626; --col-field: hsl(218, 12%, 28%); --col-field-editable: hsl(218, 10%, 28%); /**/ @@ -257,11 +258,11 @@ input { margin: 5px; } - .output-regex-container :first-child { + .output-regex-container > *:first-child { border-radius: 5px 0 0 5px; } - .output-regex-container :last-child { + .output-regex-container > *:last-child { border-radius: 0 5px 5px 0; } @@ -299,6 +300,7 @@ input { min-width: unset; background-color: hsl(223, 13%, 44%); font-size: 24px; + border-radius: 0; } .output-regex-button:hover { @@ -317,6 +319,25 @@ input { /* height: 100%;*/ } +.output-edit-prompt { + font-size: 1.1em; + position: absolute; + width: 20em; + background-color: rgba(0, 0, 0, 0.8); + border-radius: 5px; + padding: 0.2em 0.2em 0.2em 0.5em; + margin-top: 6px; + margin-left: -5px; +} + + .output-edit-prompt button { + margin: 2px; + float: right; + } + .output-edit-prompt button:hover { + background-color: #c0c0ff60; + } + .search-text-highlight, .search-textarea { padding: 3px; /*letter-spacing: 1px;*/ @@ -378,6 +399,13 @@ mark { padding: 0; } +button { + background-color: rgba(200,200,256,0.2); + border: none; + border-radius: 6px; + color: var(--col-text-medium); +} + button:focus { outline: none; } From 87dc5d07a630a1dc8070627ebf0bd4e63b1d7bf1 Mon Sep 17 00:00:00 2001 From: Jcparkyn <51850908+Jcparkyn@users.noreply.github.com> Date: Wed, 22 Jul 2020 13:58:15 +1000 Subject: [PATCH 4/7] Add 'Revert' button after parsing --- Nodexr/Shared/Components/RevertParseDialog.razor | 11 +++++++++++ Nodexr/Shared/Services/NodeHandler.cs | 14 ++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 Nodexr/Shared/Components/RevertParseDialog.razor diff --git a/Nodexr/Shared/Components/RevertParseDialog.razor b/Nodexr/Shared/Components/RevertParseDialog.razor new file mode 100644 index 00000000..113dfb4c --- /dev/null +++ b/Nodexr/Shared/Components/RevertParseDialog.razor @@ -0,0 +1,11 @@ +@inject INodeHandler NodeHandler + +

RevertParseDialog

+ + +@code { + + + public static RenderFragment GetRenderFragment() => @; + +} diff --git a/Nodexr/Shared/Services/NodeHandler.cs b/Nodexr/Shared/Services/NodeHandler.cs index e98843ba..6f82eb63 100644 --- a/Nodexr/Shared/Services/NodeHandler.cs +++ b/Nodexr/Shared/Services/NodeHandler.cs @@ -30,6 +30,7 @@ public interface INodeHandler void DeselectAllNodes(); bool TryCreateTreeFromRegex(string regex); bool IsNodeSelected(INode node); + void RevertPreviousParse(); } public class NodeHandler : INodeHandler @@ -74,6 +75,9 @@ private set private readonly IToastService toastService; + //Stores the previous tree from before the most recent parse, so that the parse can be reverted. + private NodeTree treePrevious; + public NodeHandler(NavigationManager navManager, IToastService toastService) { this.toastService = toastService; @@ -104,9 +108,12 @@ public bool TryCreateTreeFromRegex(string regex) if (parseResult.Success) { + treePrevious = tree; Tree = parseResult.Value; ForceRefreshNodeGraph(); OnOutputChanged(this, EventArgs.Empty); + var fragment = Components.RevertParseDialog.GetRenderFragment(); + toastService.ShowSuccess(fragment, "Parsed successfully"); return true; } else @@ -117,6 +124,13 @@ public bool TryCreateTreeFromRegex(string regex) } } + public void RevertPreviousParse() + { + Tree = treePrevious; + ForceRefreshNodeGraph(); + OnOutputChanged(this, EventArgs.Empty); + } + public void ForceRefreshNodeGraph() { OnRequireNodeGraphRefresh?.Invoke(this, EventArgs.Empty); From 1f443706b23e06a9d57b05bd1cd8769fd6ea9c63 Mon Sep 17 00:00:00 2001 From: Jcparkyn <51850908+Jcparkyn@users.noreply.github.com> Date: Wed, 22 Jul 2020 16:02:50 +1000 Subject: [PATCH 5/7] Add option to revert after parsing, and a toast notification if the parse resulted in a different expression. --- Nodexr/Shared/Components/OutputDisplay.razor | 3 +-- .../Shared/Components/RevertParseDialog.razor | 11 --------- Nodexr/Shared/Components/ToastButton.razor | 22 ++++++++++++++++++ Nodexr/Shared/MainLayout.razor | 2 +- Nodexr/Shared/Services/NodeHandler.cs | 23 ++++++++++++++----- Nodexr/wwwroot/css/site.css | 15 ++++++++++++ 6 files changed, 56 insertions(+), 20 deletions(-) delete mode 100644 Nodexr/Shared/Components/RevertParseDialog.razor create mode 100644 Nodexr/Shared/Components/ToastButton.razor diff --git a/Nodexr/Shared/Components/OutputDisplay.razor b/Nodexr/Shared/Components/OutputDisplay.razor index bf77fe9a..80de477e 100644 --- a/Nodexr/Shared/Components/OutputDisplay.razor +++ b/Nodexr/Shared/Components/OutputDisplay.razor @@ -20,10 +20,9 @@ }
- + -
diff --git a/Nodexr/Shared/Components/RevertParseDialog.razor b/Nodexr/Shared/Components/RevertParseDialog.razor deleted file mode 100644 index 113dfb4c..00000000 --- a/Nodexr/Shared/Components/RevertParseDialog.razor +++ /dev/null @@ -1,11 +0,0 @@ -@inject INodeHandler NodeHandler - -

RevertParseDialog

- - -@code { - - - public static RenderFragment GetRenderFragment() => @; - -} diff --git a/Nodexr/Shared/Components/ToastButton.razor b/Nodexr/Shared/Components/ToastButton.razor new file mode 100644 index 00000000..daa9abe4 --- /dev/null +++ b/Nodexr/Shared/Components/ToastButton.razor @@ -0,0 +1,22 @@ +@if (!string.IsNullOrEmpty(Info)) +{ + @Info +} + + +@code { + [Parameter] public Action OnClick { get; set; } + [Parameter] public string ButtonText { get; set; } + [Parameter] public string Info { get; set; } + + private bool hasBeenPressed = false; + + private void OnButtonClicked() + { + hasBeenPressed = true; + OnClick?.Invoke(); + } + + public static RenderFragment GetRenderFragment(Action onClick, string buttonText, string info = null) + => @; +} \ No newline at end of file diff --git a/Nodexr/Shared/MainLayout.razor b/Nodexr/Shared/MainLayout.razor index 8c734fe1..95891918 100644 --- a/Nodexr/Shared/MainLayout.razor +++ b/Nodexr/Shared/MainLayout.razor @@ -11,7 +11,7 @@ *@ + Timeout="13"/>
@Body
diff --git a/Nodexr/Shared/Services/NodeHandler.cs b/Nodexr/Shared/Services/NodeHandler.cs index 6f82eb63..f6190477 100644 --- a/Nodexr/Shared/Services/NodeHandler.cs +++ b/Nodexr/Shared/Services/NodeHandler.cs @@ -28,7 +28,7 @@ public interface INodeHandler void ForceRefreshNoodles(); void SelectNode(INode node); void DeselectAllNodes(); - bool TryCreateTreeFromRegex(string regex); + void TryCreateTreeFromRegex(string regex); bool IsNodeSelected(INode node); void RevertPreviousParse(); } @@ -102,7 +102,7 @@ public NodeHandler(NavigationManager navManager, IToastService toastService) /// /// The regular expression to parse, in string format /// Whether or not the parse attempt succeeded - public bool TryCreateTreeFromRegex(string regex) + public void TryCreateTreeFromRegex(string regex) { var parseResult = RegexParser.Parse(regex); @@ -112,15 +112,26 @@ public bool TryCreateTreeFromRegex(string regex) Tree = parseResult.Value; ForceRefreshNodeGraph(); OnOutputChanged(this, EventArgs.Empty); - var fragment = Components.RevertParseDialog.GetRenderFragment(); - toastService.ShowSuccess(fragment, "Parsed successfully"); - return true; + + if(CachedOutput.Expression == regex) + { + var fragment = Components.ToastButton.GetRenderFragment(RevertPreviousParse, "Revert to previous"); + toastService.ShowSuccess(fragment, "Converted to node tree successfully"); + } + else + { + var fragment = Components.ToastButton.GetRenderFragment( + RevertPreviousParse, + "Revert to previous", + "Your expression was parsed, but the resulting output is slighty different to your input. " + + "This is most likely due to a simplification that has been performed automatically.\n"); + toastService.ShowInfo(fragment, "Converted to node tree"); + } } else { toastService.ShowError(parseResult.Error.ToString(), "Couldn't parse input"); Console.WriteLine("Couldn't parse input: " + parseResult.Error); - return false; } } diff --git a/Nodexr/wwwroot/css/site.css b/Nodexr/wwwroot/css/site.css index 52fc59a8..3cdb144e 100644 --- a/Nodexr/wwwroot/css/site.css +++ b/Nodexr/wwwroot/css/site.css @@ -399,6 +399,21 @@ mark { padding: 0; } +.toast-button { + background-color: transparent; + font-size: large; + border: 1px solid white; + color: white; +} + +.toast-button:hover:enabled { + background-color: #ffffff44; +} + +.toast-button[disabled] { + color: #ffffffAA; +} + button { background-color: rgba(200,200,256,0.2); border: none; From c92a3b48efbbafce240b3dc55e32eef24ec3469c Mon Sep 17 00:00:00 2001 From: Jcparkyn <51850908+Jcparkyn@users.noreply.github.com> Date: Wed, 22 Jul 2020 16:52:14 +1000 Subject: [PATCH 6/7] Improve :focus styling of inline editor. --- Nodexr/Shared/Components/OutputDisplay.razor | 58 +++++++------------- Nodexr/wwwroot/css/site.css | 5 ++ 2 files changed, 25 insertions(+), 38 deletions(-) diff --git a/Nodexr/Shared/Components/OutputDisplay.razor b/Nodexr/Shared/Components/OutputDisplay.razor index 80de477e..ade66b76 100644 --- a/Nodexr/Shared/Components/OutputDisplay.razor +++ b/Nodexr/Shared/Components/OutputDisplay.razor @@ -20,9 +20,9 @@ } - - - + + + @@ -52,50 +52,32 @@ StateHasChanged(); } - @*private async Task OnEditButtonClick() + private async Task OnCreateLinkButtonClick() { - var modalParameters = new ModalParameters(); - modalParameters.Add(nameof(EditRegexDialog.previousRegex), NodeHandler.CachedOutput.Expression); + var urlParams = new Dictionary + { + { "parse", NodeHandler.CachedOutput.Expression } + }; - var modal = ModalService.Show("Custom Expression", modalParameters); - var result = await modal.Result; - if (result.Cancelled) + if (RegexReplaceHandler.SearchText != RegexReplaceHandler.DefaultSearchText) { - Console.WriteLine("Modal was cancelled"); + urlParams.Add("search", RegexReplaceHandler.SearchText); } - else if (result.Data is string customRegex) - { - Console.WriteLine("Custom Regex: " + customRegex); - NodeHandler.TryCreateTreeFromRegex(customRegex); - } - }*@ -private async Task OnCreateLinkButtonClick() -{ - var urlParams = new Dictionary + if (RegexReplaceHandler.ReplacementRegex != RegexReplaceHandler.DefaultReplacementRegex) { - { "parse", NodeHandler.CachedOutput.Expression } - }; + urlParams.Add("replace", RegexReplaceHandler.ReplacementRegex); + } - if (RegexReplaceHandler.SearchText != RegexReplaceHandler.DefaultSearchText) - { - urlParams.Add("search", RegexReplaceHandler.SearchText); + string url = QueryHelpers.AddQueryString(NavManager.BaseUri, urlParams); + await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", url, ""); + ToastService.ShowInfo("", "Link copied to clipboard"); } - if (RegexReplaceHandler.ReplacementRegex != RegexReplaceHandler.DefaultReplacementRegex) + private async Task CopyTextToClipboard() { - urlParams.Add("replace", RegexReplaceHandler.ReplacementRegex); + string regex = NodeHandler.CachedOutput.Expression; + await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", regex, ""); + ToastService.ShowInfo(regex, "Copied to clipboard"); } - - string url = QueryHelpers.AddQueryString(NavManager.BaseUri, urlParams); - await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", url, ""); - ToastService.ShowInfo("", "Link copied to clipboard"); -} - -private async Task CopyTextToClipboard() -{ - string regex = NodeHandler.CachedOutput.Expression; - await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", regex, ""); - ToastService.ShowInfo(regex, "Copied to clipboard"); -} } diff --git a/Nodexr/wwwroot/css/site.css b/Nodexr/wwwroot/css/site.css index 3cdb144e..e35b947e 100644 --- a/Nodexr/wwwroot/css/site.css +++ b/Nodexr/wwwroot/css/site.css @@ -275,12 +275,17 @@ input { height: auto; } +.output-regex-container > .output-regex:focus-within { + box-shadow: inset 0 0 4px 1px #0078f5; +} + .output-regex-text { font-size: 30px; font-family: Consolas, monospace; line-height: 34px; white-space: pre-wrap; word-break: break-all; + outline: 0; } .output-segment { From b5798c8ba6210c9990b153cf4461d5a0d29086ea Mon Sep 17 00:00:00 2001 From: Jcparkyn <51850908+Jcparkyn@users.noreply.github.com> Date: Wed, 22 Jul 2020 17:31:28 +1000 Subject: [PATCH 7/7] Automatically focus output edit field --- Nodexr/wwwroot/js/ContentEditable.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Nodexr/wwwroot/js/ContentEditable.js b/Nodexr/wwwroot/js/ContentEditable.js index a3b9468d..5573048c 100644 --- a/Nodexr/wwwroot/js/ContentEditable.js +++ b/Nodexr/wwwroot/js/ContentEditable.js @@ -7,8 +7,7 @@ window.contentEditable = { initContentEditable: function (div, instance, textToDisplay) { div.innerText = textToDisplay; div.addEventListener("input", function () { - let text = div.innerText;//contentEditable.strip(div.innerText); - instance.invokeMethodAsync("GetUpdatedTextFromJavascript", text); + instance.invokeMethodAsync("GetUpdatedTextFromJavascript", div.innerText); }); try { @@ -17,6 +16,16 @@ window.contentEditable = { catch (e) { contentEditable.setupFallbackPlaintextOnly(div); } + contentEditable.moveCursorToEnd(div); + }, + + moveCursorToEnd: function (elem) { + let s = window.getSelection(); + let r = document.createRange(); + r.setStart(elem, 1); + r.setEnd(elem, 1); + s.removeAllRanges(); + s.addRange(r); }, setupFallbackPlaintextOnly: function (elem) { @@ -55,12 +64,4 @@ window.contentEditable = { document.selection.createRange().text = text; } }, - - strip: function(html) { - let tempDiv = document.createElement("div"); - tempDiv.innerText = html; - let text = tempDiv.innerText; - tempDiv.remove(); - return text; - } } \ No newline at end of file