From 51e3609cd6449e29e2ff4fb4681708f1fe7156bc Mon Sep 17 00:00:00 2001 From: Major Date: Wed, 8 Sep 2021 08:09:08 +0200 Subject: [PATCH 01/19] ScrollHandler added more Smooth scroll support. --- .github/docs/JsInterop.md | 7 +++---- ...rsoft.Blazor.Components.Common.JsInterop.xml | 11 +++++++---- .../Scroll/IScrollHandler.cs | 11 +++++++---- .../Scroll/ScrollHandler.cs | 12 ++++++------ .../wwwroot/scroll.js | 17 +++++++++++------ .../wwwroot/scroll.min.js | 2 +- .../Shared/MainLayout.razor | 2 +- .../Shared/PageScroll.razor | 4 ++-- .../Shared/MainLayout.razor | 2 +- 9 files changed, 39 insertions(+), 29 deletions(-) diff --git a/.github/docs/JsInterop.md b/.github/docs/JsInterop.md index f677a47d..cc136c92 100644 --- a/.github/docs/JsInterop.md +++ b/.github/docs/JsInterop.md @@ -169,13 +169,12 @@ Exposes a Blazor `ElementReference` of the wrapped around HTML element. It can b - **`DisposeAsync()`: `ValueTask IAsyncDisposable()` interface**
Component implements `IAsyncDisposable` interface Blazor framework components also can `@implements IAsyncDisposable` where the injected service should be Disposed. - ### `IScrollHandler` Functions -- **`ScrollToElementAsync`**: **`Task ScrollToElementAsync(ElementReference elementReference)`**
+- **`ScrollToElementAsync`**: **`Task ScrollToElementAsync(ElementReference elementReference, bool smooth)`**
Scrolls the given element into the page view area. -- **`ScrollToElementByIdAsync`**: **`Task ScrollToElementByIdAsync(string id)`**
+- **`ScrollToElementByIdAsync`**: **`Task ScrollToElementByIdAsync(string id, bool smooth)`**
Finds element by Id and scrolls the given element into the page view area. -- **`ScrollToElementByNameAsync`**: **`Task ScrollToElementByNameAsync(string name)`**
+- **`ScrollToElementByNameAsync`**: **`Task ScrollToElementByNameAsync(string name, bool smooth)`**
Finds element by name and scrolls the given element into the page view area. - **`ScrollToPageEndAsync`**: **`Task ScrollToPageEndAsync(bool smooth)`**
Scrolls to end of the page (X bottom). diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml b/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml index dc12d0cd..955e1d42 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml @@ -732,25 +732,28 @@ Injectable service to handle JS document/window 'scroll' events for the whole document. - + Scrolls the given element into the page view area. Blazor reference to an HTML element + Scroll should jump or smoothly scroll Async Task - + Finds element by Id and scrolls the given element into the page view area. - DOM element id + DOM element id + Scroll should jump or smoothly scroll Async Task - + Finds element by name and scrolls the given element into the page view area. DOM element name + Scroll should jump or smoothly scroll Async Task diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs index a4526af3..7182c004 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs @@ -14,20 +14,23 @@ public interface IScrollHandler : IAsyncDisposable /// Scrolls the given element into the page view area. /// /// Blazor reference to an HTML element + /// Scroll should jump or smoothly scroll /// Async Task - Task ScrollToElementAsync(ElementReference elementReference); + Task ScrollToElementAsync(ElementReference elementReference, bool smooth = false); /// /// Finds element by Id and scrolls the given element into the page view area. /// - /// DOM element id + /// DOM element id + /// Scroll should jump or smoothly scroll /// Async Task - Task ScrollToElementByIdAsync(string id); + Task ScrollToElementByIdAsync(string id, bool smooth = false); /// /// Finds element by name and scrolls the given element into the page view area. /// /// DOM element name + /// Scroll should jump or smoothly scroll /// Async Task - Task ScrollToElementByNameAsync(string name); + Task ScrollToElementByNameAsync(string name, bool smooth = false); /// /// Scrolls to end of the page (X bottom). diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollHandler.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollHandler.cs index 5706773d..af51e72d 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollHandler.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollHandler.cs @@ -54,22 +54,22 @@ public async Task GetPageScrollSizeAsync() return await _scrollJs.InvokeAsync("getPageScrollSize"); } - public async Task ScrollToElementAsync(ElementReference elementReference) + public async Task ScrollToElementAsync(ElementReference elementReference, bool smooth) { await CheckJsObjectAsync(); - await _scrollJs.InvokeVoidAsync("scrollToElement", elementReference); + await _scrollJs.InvokeVoidAsync("scrollToElement", elementReference, smooth); } - public async Task ScrollToElementByIdAsync(string id) + public async Task ScrollToElementByIdAsync(string id, bool smooth) { await CheckJsObjectAsync(); - await _scrollJs.InvokeVoidAsync("scrollToElementById", id); + await _scrollJs.InvokeVoidAsync("scrollToElementById", id, smooth); } - public async Task ScrollToElementByNameAsync(string name) + public async Task ScrollToElementByNameAsync(string name, bool smooth) { await CheckJsObjectAsync(); - await _scrollJs.InvokeVoidAsync("scrollToElementByName", name); + await _scrollJs.InvokeVoidAsync("scrollToElementByName", name, smooth); } public async Task RegisterPageScrollAsync(Func scrollCallback) diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js index f412eff2..75a941da 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js @@ -1,20 +1,25 @@ /* Extensions */ //Element scrolled to page top -export function scrollToElement(element) { +export function scrollToElement(element, smooth) { if (element && typeof element.scrollIntoView === "function") { - element.scrollIntoView(); + if (smooth) { + element.scrollIntoView({ behavior: 'smooth' }); + } + else { + element.scrollIntoView(); + } } } -export function scrollToElementById(id) { +export function scrollToElementById(id, smooth) { if (id) { - scrollToElement(document.getElementById(id)); + scrollToElement(document.getElementById(id), smooth); } } -export function scrollToElementByName(name) { +export function scrollToElementByName(name, smooth) { if (name) { let elements = document.getElementsByName(name) if (elements && elements.length > 0) { - scrollToElement(elements[0]); + scrollToElement(elements[0], smooth); } } } diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.min.js b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.min.js index 2e9795e3..efc4a1dc 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.min.js +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.min.js @@ -1 +1 @@ -export function scrollToElement(n){n&&typeof n.scrollIntoView=="function"&&n.scrollIntoView()}export function scrollToElementById(n){n&&scrollToElement(document.getElementById(n))}export function scrollToElementByName(n){if(n){let t=document.getElementsByName(n);t&&t.length>0&&scrollToElement(t[0])}}export function scrollToElementInParent(t,i){t&&i&&typeof t.scrollTop!==n&&isElementHidden(i)&&(t.scrollTop=i.offsetHeight)}export function scrollInParentById(t,i){if(t&&i&&typeof t.scrollTop!==n){let n=document.getElementById(i);n&&isElementHidden(n)&&(t.scrollTop=n.offsetHeight)}}export function scrollInParentByClass(t,i){if(t&&i&&typeof t.scrollTop!==n){let n=document.getElementsByClassName(i);if(n&&n[0]){let i=n[0];isElementHiddenBelow(i)?(t.scrollTop+=n[0].offsetHeight,isElementHiddenBelow(i)&&(t.scrollTop=i.offsetTop-t.clientHeight+i.offsetHeight)):isElementHiddenAbove(i)&&(t.scrollTop-=n[0].offsetHeight,isElementHiddenAbove(i)&&(t.scrollTop=i.offsetTop))}}}export function isElementHidden(n){return isElementHiddenBelow(n)||isElementHiddenAbove(n)}export function isElementHiddenBelow(n){return!n||!n.offsetParent?!1:n.offsetHeight+n.offsetTop>n.offsetParent.scrollTop+n.offsetParent.clientHeight}export function isElementHiddenAbove(n){return!n||!n.offsetParent?!1:n.offsetTop=document.body.offsetHeight}}export function getPageScrollSize(){return{X:document.body.scrollWidth,Y:document.body.scrollHeight,IsPageTop:window.scrollY==0,IsPageBottom:window.innerHeight+window.scrollY>=document.body.offsetHeight}}function i(n){return function(){let t=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,r={X:i,Y:t};n.invokeMethodAsync("PageScroll",r)}}function r(n,t,i){let r=!1;for(let i=0;i0&&scrollToElement(i[0],t)}}export function scrollToElementInParent(t,i){t&&i&&typeof t.scrollTop!==n&&isElementHidden(i)&&(t.scrollTop=i.offsetHeight)}export function scrollInParentById(t,i){if(t&&i&&typeof t.scrollTop!==n){let n=document.getElementById(i);n&&isElementHidden(n)&&(t.scrollTop=n.offsetHeight)}}export function scrollInParentByClass(t,i){if(t&&i&&typeof t.scrollTop!==n){let n=document.getElementsByClassName(i);if(n&&n[0]){let i=n[0];isElementHiddenBelow(i)?(t.scrollTop+=n[0].offsetHeight,isElementHiddenBelow(i)&&(t.scrollTop=i.offsetTop-t.clientHeight+i.offsetHeight)):isElementHiddenAbove(i)&&(t.scrollTop-=n[0].offsetHeight,isElementHiddenAbove(i)&&(t.scrollTop=i.offsetTop))}}}export function isElementHidden(n){return isElementHiddenBelow(n)||isElementHiddenAbove(n)}export function isElementHiddenBelow(n){return!n||!n.offsetParent?!1:n.offsetHeight+n.offsetTop>n.offsetParent.scrollTop+n.offsetParent.clientHeight}export function isElementHiddenAbove(n){return!n||!n.offsetParent?!1:n.offsetTop=document.body.offsetHeight}}export function getPageScrollSize(){return{X:document.body.scrollWidth,Y:document.body.scrollHeight,IsPageTop:window.scrollY==0,IsPageBottom:window.innerHeight+window.scrollY>=document.body.offsetHeight}}function i(n){return function(){let t=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,r={X:i,Y:t};n.invokeMethodAsync("PageScroll",r)}}function r(n,t,i){let r=!1;for(let i=0;i + @* Google Analytics initialize*@ @using Majorsoft.Blazor.Extensions.Analytics.Google diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/PageScroll.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/PageScroll.razor index 64dd8bb4..a12bcc92 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/PageScroll.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/PageScroll.razor @@ -2,7 +2,7 @@ @if (ShowScrollToBottom) { - +
@@ -11,7 +11,7 @@ } - +
diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor index 62509ebb..ffdada27 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor @@ -20,7 +20,7 @@ @*Permalink initialize*@ @using Majorsoft.Blazor.Components.PermaLink - + @*Server hosted Blazor console log*@ @using Majorsoft.Blazor.Server.Logging.Console From 3f45335d050b588ddc9623d9757cb00e6c390382 Mon Sep 17 00:00:00 2001 From: Major Date: Wed, 8 Sep 2021 14:57:13 +0200 Subject: [PATCH 02/19] Added more smooth scroll, bugfixes and demo updates. --- .github/docs/JsInterop.md | 34 +++++++--- ...oft.Blazor.Components.Common.JsInterop.xml | 42 +++++++++--- .../ElementReferenceScrollExtensions.cs | 65 +++++++++++++++---- .../Scroll/IScrollHandler.cs | 6 +- .../wwwroot/scroll.js | 37 ++++++++--- .../wwwroot/scroll.min.js | 2 +- .../Shared/MainLayout.razor | 2 +- .../Components/JsDemo/BrowserDateJs.razor | 2 +- .../Components/JsDemo/ScrollJs.razor | 14 ++-- .../Shared/MainLayout.razor | 2 +- 10 files changed, 154 insertions(+), 52 deletions(-) diff --git a/.github/docs/JsInterop.md b/.github/docs/JsInterop.md index cc136c92..7801a18e 100644 --- a/.github/docs/JsInterop.md +++ b/.github/docs/JsInterop.md @@ -171,11 +171,11 @@ Component implements `IAsyncDisposable` interface Blazor framework components al ### `IScrollHandler` Functions - **`ScrollToElementAsync`**: **`Task ScrollToElementAsync(ElementReference elementReference, bool smooth)`**
-Scrolls the given element into the page view area. +Scrolls the given element into the page view area. **Note: smooth scroll on element level might not supported by all browsers.** - **`ScrollToElementByIdAsync`**: **`Task ScrollToElementByIdAsync(string id, bool smooth)`**
-Finds element by Id and scrolls the given element into the page view area. +Finds element by Id and scrolls the given element into the page view area. **Note: smooth scroll on element level might not supported by all browsers.** - **`ScrollToElementByNameAsync`**: **`Task ScrollToElementByNameAsync(string name, bool smooth)`**
-Finds element by name and scrolls the given element into the page view area. +Finds element by name and scrolls the given element into the page view area. **Note: smooth scroll on element level might not supported by all browsers.** - **`ScrollToPageEndAsync`**: **`Task ScrollToPageEndAsync(bool smooth)`**
Scrolls to end of the page (X bottom). - **`ScrollToPageTopAsync`**: **`Task ScrollToPageTopAsync(bool smooth)`**
@@ -196,18 +196,34 @@ Removes event listener for 'scroll' HTML event for the whole document/window by Implements `IAsyncDisposable` interface the injected service should be Disposed. ### `ElementReference` extensions -- **`ScrollToElementAsync`**: **`Task ScrollToElementAsync(this ElementReference elementReference)`**
-- **`ScrollToEndAsync`**: **`Task ScrollToEndAsync(this ElementReference elementReference)`**
-- **`ScrollToTopAsync`**: **`Task ScrollToTopAsync(this ElementReference elementReference)`**
-- **`ScrollToXAsync`**: **`Task ScrollToXAsync(this ElementReference elementReference, double xPos)`**
-- **`ScrollToYAsync`**: **`Task ScrollToYAsync(this ElementReference elementReference, double yPos)`**
-- **`GetScrollPositionAsync`**: **`Task GetScrollPositionAsync(this ElementReference elementReference)`**
+- **`ScrollToElementAsync`**: **`Task ScrollToElementAsync(this ElementReference elementReference, bool smooth = false)`**
+ Scrolls HTML page to given element. **Note: smooth scroll on element level might not supported by all browsers.** +- **`ScrollToEndAsync`**: **`Task ScrollToEndAsync(this ElementReference elementReference, bool smooth = false)`**
+Scrolls inside the given element to the bottom (end). **Note: smooth scroll on element level might not supported by all browsers.** +- **`ScrollToTopAsync`**: **`Task ScrollToTopAsync(this ElementReference elementReference, bool smooth = false)`**
+Scrolls inside the given element to the beginning (top). **Note: smooth scroll on element level might not supported by all browsers.** +- **`ScrollToXAsync`**: **`Task ScrollToXAsync(this ElementReference elementReference, double xPos, bool smooth = false)`**
+Scrolls inside the given element to the given X position. **Note: smooth scroll on element level might not supported by all browsers.** +- **`ScrollToYAsync`**: **`Task ScrollToYAsync(this ElementReference elementReference, double yPos, bool smooth = false)`**
+Scrolls inside the given element to the given Y position. **Note: smooth scroll on element level might not supported by all browsers.** +- **`ScrollToAsync`**: **`Task ScrollToYAsync(this ElementReference elementReference, double xPos, double yPos, bool smooth = false)`**
+Scrolls inside the given element to the given X and Y positions. **Note: smooth scroll on element level might not supported by all browsers.** +- **`GetScrollXPositionAsync`**: **`Task GetScrollXPositionAsync(this ElementReference elementReference)`**
+Returns given element scroll X (left) position. +- **`GetScrollYPositionAsync`**: **`Task GetScrollYPositionAsync(this ElementReference elementReference)`**
+Returns given element scroll Y (top) position. - **`IsElementHiddenAsync`**: **`Task IsElementHiddenAsync(this ElementReference elementReference)`**
+Returns given element is visible on HTML document or not. - **`IsElementHiddenBelowAsync`**: **`Task IsElementHiddenBelowAsync(this ElementReference elementReference)`**
+Returns given element is below of the view port. - **`IsElementHiddenAboveAsync`**: **`Task IsElementHiddenAboveAsync(this ElementReference elementReference)`**
+Returns given element is above of the view port. - **`ScrollToElementInParentAsync`**: **`Task ScrollToElementInParentAsync(this ElementReference parent, ElementReference innerElement)`**
+Scrolls inside the given parent element to the given inner element. - **`ScrollInParentByIdAsync`**: **`Task ScrollInParentByIdAsync(this ElementReference parent, string id)`**
+Scrolls inside the given parent element to the given inner element by Id. - **`ScrollInParentByClassAsync`**: **`Task ScrollInParentByClassAsync(this ElementReference parent, string className)`**
+Scrolls inside the given parent element to the given first found inner element by class name. ## Resize JS (See: [demo app](https://blazorextensions.z6.web.core.windows.net/jsinterop#resize-js)) **Resize JS** is an **injectable `IResizeHandler` service** for Window (global) and HTML Elements resize event callback handlers. diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml b/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml index 955e1d42..8407ba14 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml @@ -638,50 +638,72 @@ Extensions for HTML elements.
- + - Scrolls HTML page to given element + Scrolls HTML page to given element. Blazor reference to an HTML element + Scroll should jump or smoothly scroll Note: might not all browsers support it Async Task - + Scrolls inside the given element to the bottom (end). Blazor reference to an HTML element + Scroll should jump or smoothly scroll Note: might not all browsers support it Async Task - + Scrolls inside the given element to the beginning (top). Blazor reference to an HTML element + Scroll should jump or smoothly scroll Note: might not all browsers support it Async Task - + Scrolls inside the given element to the given X position. Blazor reference to an HTML element Scroll X position + Scroll should jump or smoothly scroll Note: might not all browsers support it Async Task - + Scrolls inside the given element to the given Y position. Blazor reference to an HTML element Scroll Y position + Scroll should jump or smoothly scroll Note: might not all browsers support it + Async Task + + + + Scrolls inside the given element to the given X and Y positions. + + Blazor reference to an HTML element + Scroll X position + Scroll Y position + Scroll should jump or smoothly scroll Note: might not all browsers support it Async Task - Returns given element scroll X position. + Returns given element scroll X (left) position. Blazor reference to an HTML element Async Task with X pos + + + Returns given element scroll Y (top) position. + + Blazor reference to an HTML element + Async Task with Y pos + Returns given element is visible on HTML document or not. @@ -737,7 +759,7 @@ Scrolls the given element into the page view area. Blazor reference to an HTML element - Scroll should jump or smoothly scroll + Scroll should jump or smoothly scroll Note: might not all browsers support it Async Task @@ -745,7 +767,7 @@ Finds element by Id and scrolls the given element into the page view area. DOM element id - Scroll should jump or smoothly scroll + Scroll should jump or smoothly scroll Note: might not all browsers support it Async Task @@ -753,7 +775,7 @@ Finds element by name and scrolls the given element into the page view area. DOM element name - Scroll should jump or smoothly scroll + Scroll should jump or smoothly scroll Note: might not all browsers support it Async Task diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ElementReferenceScrollExtensions.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ElementReferenceScrollExtensions.cs index 4531d286..addbfe7f 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ElementReferenceScrollExtensions.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ElementReferenceScrollExtensions.cs @@ -12,17 +12,18 @@ namespace Majorsoft.Blazor.Components.Common.JsInterop.Scroll public static class ElementReferenceScrollExtensions { /// - /// Scrolls HTML page to given element + /// Scrolls HTML page to given element. /// /// Blazor reference to an HTML element + /// Scroll should jump or smoothly scroll Note: might not all browsers support it /// Async Task - public static async Task ScrollToElementAsync(this ElementReference elementReference) + public static async Task ScrollToElementAsync(this ElementReference elementReference, bool smooth = false) { await using (var module = await elementReference.GetJsObject()) { if (module is not null) { - await module.InvokeVoidAsync("scrollToElement", elementReference); + await module.InvokeVoidAsync("scrollToElement", elementReference, smooth); } } } @@ -31,14 +32,15 @@ public static async Task ScrollToElementAsync(this ElementReference elementRefer /// Scrolls inside the given element to the bottom (end). /// /// Blazor reference to an HTML element + /// Scroll should jump or smoothly scroll Note: might not all browsers support it /// Async Task - public static async Task ScrollToEndAsync(this ElementReference elementReference) + public static async Task ScrollToEndAsync(this ElementReference elementReference, bool smooth = false) { await using (var module = await elementReference.GetJsObject()) { if (module is not null) { - await module.InvokeVoidAsync("scrollToEnd", elementReference); + await module.InvokeVoidAsync("scrollToEnd", elementReference, smooth); } } } @@ -47,14 +49,15 @@ public static async Task ScrollToEndAsync(this ElementReference elementReference /// Scrolls inside the given element to the beginning (top). /// /// Blazor reference to an HTML element + /// Scroll should jump or smoothly scroll Note: might not all browsers support it /// Async Task - public static async Task ScrollToTopAsync(this ElementReference elementReference) + public static async Task ScrollToTopAsync(this ElementReference elementReference, bool smooth = false) { await using (var module = await elementReference.GetJsObject()) { if (module is not null) { - await module.InvokeVoidAsync("scrollToTop", elementReference); + await module.InvokeVoidAsync("scrollToTop", elementReference, smooth); } } } @@ -64,14 +67,15 @@ public static async Task ScrollToTopAsync(this ElementReference elementReference /// /// Blazor reference to an HTML element /// Scroll X position + /// Scroll should jump or smoothly scroll Note: might not all browsers support it /// Async Task - public static async Task ScrollToXAsync(this ElementReference elementReference, double xPos) + public static async Task ScrollToXAsync(this ElementReference elementReference, double xPos, bool smooth = false) { await using (var module = await elementReference.GetJsObject()) { if (module is not null) { - await module.InvokeVoidAsync("scrollToX", elementReference, xPos); + await module.InvokeVoidAsync("scrollToX", elementReference, xPos, smooth); } } } @@ -81,20 +85,40 @@ public static async Task ScrollToXAsync(this ElementReference elementReference, /// /// Blazor reference to an HTML element /// Scroll Y position + /// Scroll should jump or smoothly scroll Note: might not all browsers support it /// Async Task - public static async Task ScrollToYAsync(this ElementReference elementReference, double yPos) + public static async Task ScrollToYAsync(this ElementReference elementReference, double yPos, bool smooth = false) { await using (var module = await elementReference.GetJsObject()) { if (module is not null) { - await module.InvokeVoidAsync("scrollToY", elementReference, yPos); + await module.InvokeVoidAsync("scrollToY", elementReference, yPos, smooth); } } } /// - /// Returns given element scroll X position. + /// Scrolls inside the given element to the given X and Y positions. + /// + /// Blazor reference to an HTML element + /// Scroll X position + /// Scroll Y position + /// Scroll should jump or smoothly scroll Note: might not all browsers support it + /// Async Task + public static async Task ScrollToAsync(this ElementReference elementReference, double xPos, double yPos, bool smooth = false) + { + await using (var module = await elementReference.GetJsObject()) + { + if (module is not null) + { + await module.InvokeVoidAsync("scrollTo", elementReference, xPos, yPos, smooth); + } + } + } + + /// + /// Returns given element scroll X (left) position. /// /// Blazor reference to an HTML element /// Async Task with X pos @@ -110,6 +134,23 @@ public static async Task GetScrollXPositionAsync(this ElementReference e return 0; } + /// + /// Returns given element scroll Y (top) position. + /// + /// Blazor reference to an HTML element + /// Async Task with Y pos + public static async Task GetScrollYPositionAsync(this ElementReference elementReference) + { + await using (var module = await elementReference.GetJsObject()) + { + if (module is not null) + { + return await module.InvokeAsync("getScrollYPosition", elementReference); + } + } + + return 0; + } /// /// Returns given element is visible on HTML document or not. diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs index 7182c004..41ad231a 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs @@ -14,21 +14,21 @@ public interface IScrollHandler : IAsyncDisposable /// Scrolls the given element into the page view area. /// /// Blazor reference to an HTML element - /// Scroll should jump or smoothly scroll + /// Scroll should jump or smoothly scroll Note: might not all browsers support it /// Async Task Task ScrollToElementAsync(ElementReference elementReference, bool smooth = false); /// /// Finds element by Id and scrolls the given element into the page view area. /// /// DOM element id - /// Scroll should jump or smoothly scroll + /// Scroll should jump or smoothly scroll Note: might not all browsers support it /// Async Task Task ScrollToElementByIdAsync(string id, bool smooth = false); /// /// Finds element by name and scrolls the given element into the page view area. /// /// DOM element name - /// Scroll should jump or smoothly scroll + /// Scroll should jump or smoothly scroll Note: might not all browsers support it /// Async Task Task ScrollToElementByNameAsync(string name, bool smooth = false); diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js index 75a941da..089b04de 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js @@ -80,27 +80,46 @@ export function isElementHiddenAbove(element) { } //Scrolling inside an element use it for e.g. Textarea -export function scrollToEnd(element) { +export function scrollToEnd(element, smooth) { if (element && typeof element.scrollTop !== undef && typeof element.scrollHeight !== undef) { - element.scrollTop = element.scrollHeight; + scrollTo(element, element.scrollLeft, element.scrollHeight, smooth); } } -export function scrollToTop(element) { +export function scrollToTop(element, smooth) { if (element && typeof element.scrollTop !== undef) { - element.scrollTop = 0; + scrollTo(element, element.scrollLeft, 0, smooth); } } -export function scrollToX(element, x) { +export function scrollToX(element, x, smooth) { if (element && typeof element.scrollTop !== undef) { - element.scrollTop = x; + scrollTo(element, x, element.scrollTop, smooth); } } -export function scrollToY(element, y) { +export function scrollToY(element, y, smooth) { if (element && typeof element.scrollLeft !== undef) { - element.scrollLeft = y; + scrollTo(element, element.scrollLeft, y, smooth); + } +} +export function scrollTo(element, x, y, smooth) { + if (element) { + if (smooth) { + element.scroll({ + top: y, + left: x, + behavior: 'smooth'}); + } + else { + element.scrollTop = y; + element.scrollLeft = x; + } } } export function getScrollXPosition(element) { + if (element && typeof element.scrollLeft !== undef) { + return element.scrollLeft; + } +} +export function getScrollYPosition(element) { if (element && typeof element.scrollTop !== undef) { return element.scrollTop; } @@ -250,4 +269,4 @@ export function dispose(eventIdArray) { removeScrollEvent(eventIdArray[i]); } } -} +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.min.js b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.min.js index efc4a1dc..2fd4b7c2 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.min.js +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.min.js @@ -1 +1 @@ -export function scrollToElement(n,t){n&&typeof n.scrollIntoView=="function"&&(t?n.scrollIntoView({behavior:"smooth"}):n.scrollIntoView())}export function scrollToElementById(n,t){n&&scrollToElement(document.getElementById(n),t)}export function scrollToElementByName(n,t){if(n){let i=document.getElementsByName(n);i&&i.length>0&&scrollToElement(i[0],t)}}export function scrollToElementInParent(t,i){t&&i&&typeof t.scrollTop!==n&&isElementHidden(i)&&(t.scrollTop=i.offsetHeight)}export function scrollInParentById(t,i){if(t&&i&&typeof t.scrollTop!==n){let n=document.getElementById(i);n&&isElementHidden(n)&&(t.scrollTop=n.offsetHeight)}}export function scrollInParentByClass(t,i){if(t&&i&&typeof t.scrollTop!==n){let n=document.getElementsByClassName(i);if(n&&n[0]){let i=n[0];isElementHiddenBelow(i)?(t.scrollTop+=n[0].offsetHeight,isElementHiddenBelow(i)&&(t.scrollTop=i.offsetTop-t.clientHeight+i.offsetHeight)):isElementHiddenAbove(i)&&(t.scrollTop-=n[0].offsetHeight,isElementHiddenAbove(i)&&(t.scrollTop=i.offsetTop))}}}export function isElementHidden(n){return isElementHiddenBelow(n)||isElementHiddenAbove(n)}export function isElementHiddenBelow(n){return!n||!n.offsetParent?!1:n.offsetHeight+n.offsetTop>n.offsetParent.scrollTop+n.offsetParent.clientHeight}export function isElementHiddenAbove(n){return!n||!n.offsetParent?!1:n.offsetTop=document.body.offsetHeight}}export function getPageScrollSize(){return{X:document.body.scrollWidth,Y:document.body.scrollHeight,IsPageTop:window.scrollY==0,IsPageBottom:window.innerHeight+window.scrollY>=document.body.offsetHeight}}function i(n){return function(){let t=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,r={X:i,Y:t};n.invokeMethodAsync("PageScroll",r)}}function r(n,t,i){let r=!1;for(let i=0;i0&&scrollToElement(i[0],t)}}export function scrollToElementInParent(t,i){t&&i&&typeof t.scrollTop!==n&&isElementHidden(i)&&(t.scrollTop=i.offsetHeight)}export function scrollInParentById(t,i){if(t&&i&&typeof t.scrollTop!==n){let n=document.getElementById(i);n&&isElementHidden(n)&&(t.scrollTop=n.offsetHeight)}}export function scrollInParentByClass(t,i){if(t&&i&&typeof t.scrollTop!==n){let n=document.getElementsByClassName(i);if(n&&n[0]){let i=n[0];isElementHiddenBelow(i)?(t.scrollTop+=n[0].offsetHeight,isElementHiddenBelow(i)&&(t.scrollTop=i.offsetTop-t.clientHeight+i.offsetHeight)):isElementHiddenAbove(i)&&(t.scrollTop-=n[0].offsetHeight,isElementHiddenAbove(i)&&(t.scrollTop=i.offsetTop))}}}export function isElementHidden(n){return isElementHiddenBelow(n)||isElementHiddenAbove(n)}export function isElementHiddenBelow(n){return!n||!n.offsetParent?!1:n.offsetHeight+n.offsetTop>n.offsetParent.scrollTop+n.offsetParent.clientHeight}export function isElementHiddenAbove(n){return!n||!n.offsetParent?!1:n.offsetTop=document.body.offsetHeight}}export function getPageScrollSize(){return{X:document.body.scrollWidth,Y:document.body.scrollHeight,IsPageTop:window.scrollY==0,IsPageBottom:window.innerHeight+window.scrollY>=document.body.offsetHeight}}function i(n){return function(){let t=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,r={X:i,Y:t};n.invokeMethodAsync("PageScroll",r)}}function r(n,t,i){let r=!1;for(let i=0;i + @* Google Analytics initialize*@ @using Majorsoft.Blazor.Extensions.Analytics.Google diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/BrowserDateJs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/BrowserDateJs.razor index 038dc059..2e51f130 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/BrowserDateJs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/BrowserDateJs.razor @@ -5,7 +5,7 @@ Browser Date JS is an injectable IBrowserDateService service simple JS call to new Date(); to retrieve client machine date and time. - +

Browser date time: @_date

diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor index 2eaf9dc5..94301dfc 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor @@ -40,6 +40,7 @@
+ Smooth scroll to View (might not supported by this Browser): @@ -103,11 +104,11 @@

- - + Smooth scroll in Textbox: + +
-
@@ -125,11 +126,14 @@ await ScrollEventHandler(); } + private bool _smoothScrollInTextbox = true; + //Scroll examples private ElementReference _scrollToView; + private bool _smoothScrollToView; private async Task ScrollToView() { - await _scrollToView.ScrollToElementAsync(); + await _scrollToView.ScrollToElementAsync(_smoothScrollToView); } private ElementReference _log; @@ -139,7 +143,7 @@ _logMessage += $"{DateTime.Now.TimeOfDay}: Page scrolled: X pos: {args.X}, Y pos: {args.Y}{Environment.NewLine}"; StateHasChanged(); - await _log.ScrollToEndAsync(); + await _log.ScrollToEndAsync(smooth: true); } private bool _scrollSubscribed = false; diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor index ffdada27..ddd63464 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor @@ -20,7 +20,7 @@ @*Permalink initialize*@ @using Majorsoft.Blazor.Components.PermaLink - + @*Server hosted Blazor console log*@ @using Majorsoft.Blazor.Server.Logging.Console From 1dd25734a9a4cea6f5996370b59cfe3be26b0c69 Mon Sep 17 00:00:00 2001 From: Major Date: Wed, 8 Sep 2021 17:22:04 +0200 Subject: [PATCH 03/19] Permalink service updates added event for perma value detection and update link. --- .github/docs/PermaLink.md | 18 ++++- .../IPermaLinkWatcherService.cs | 18 +++++ .../Majorsoft.Blazor.Components.PermaLink.xml | 52 ++++++++++++++ .../PermaLinkBlazorServerInitializer.razor | 10 +++ .../PermaLinkWatcherService.cs | 72 +++++++++++++++++-- .../PermalinkBlazorWasmInitializer.razor | 11 +++ .../PermalinkDetectedEventArgs.cs | 33 +++++++++ .../Components/Permalink.razor | 5 +- 8 files changed, 210 insertions(+), 9 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.PermaLink/PermalinkDetectedEventArgs.cs diff --git a/.github/docs/PermaLink.md b/.github/docs/PermaLink.md index 5e94be02..81ef2520 100644 --- a/.github/docs/PermaLink.md +++ b/.github/docs/PermaLink.md @@ -32,9 +32,15 @@ For more details see Usage section. ### Functions - **`WatchPermaLinks()`**: **`void WatchPermaLinks()`**
Starts a navigation watcher which will check for Permalinks in the URLs. +- **`ChangePermalink()`**: **`void ChangePermalink(string? newPermalink, bool doNotNavigate)`**
+Modify the current URL with given new peralink value and trigger navigation or just update browser History. - **`Dispose()`: `@implements IDisposable` interface**
Component implements `IDisposable` interface Blazor framework will call it when parent removed from render tree. +### Events +- **`PermalinkDetected`: `event EventHandler**
+Event handler for parmalinks detected on navigation. + ## `PermaLinkElement` component ### Properties @@ -68,9 +74,17 @@ Callback function called when Permalink icon clicked and **`PermaLinkIconActions ## PermaLinkBlazorServerInitializer Available from v1.4.0 It is convenient wrapper component to initialize navigation watcher in your Blazor Server App `MainLayout.razor` page. **Only one Initializer component allowed per Application.** +### Properties +- **`SmootScroll`: `bool { get; set; }` (default: false)**
+Scroll should be jump or smoothly scroll. **Note: smooth scroll on element level might not supported by all browsers.** + ## PermalinkBlazorWasmInitializer Available from v1.4.0 It is convenient wrapper component to initialize navigation watcher in your Blazor WebAssembly App `MainLayout.razor` page. **Only one Initializer component allowed per Application.** +### Properties +- **`SmootScroll`: `bool { get; set; }` (default: false)**
+Scroll should be jump or smoothly scroll. **Note: smooth scroll on element level might not supported by all browsers.** + # Configuration ## Installation @@ -141,7 +155,7 @@ Also instance should be disposed. ``` @*Permalink initialize*@ @using Majorsoft.Blazor.Components.PermaLink - + ``` #### Server hosted projects @@ -208,7 +222,7 @@ It has to be instantiated manually by using the following code. Also instance sh ``` @*Permalink initialize*@ @using Majorsoft.Blazor.Components.PermaLink - + ``` #### Creating permalink (#) navigation points inside a Blazor page diff --git a/src/Majorsoft.Blazor.Components.PermaLink/IPermaLinkWatcherService.cs b/src/Majorsoft.Blazor.Components.PermaLink/IPermaLinkWatcherService.cs index 918100aa..b5fb1f1a 100644 --- a/src/Majorsoft.Blazor.Components.PermaLink/IPermaLinkWatcherService.cs +++ b/src/Majorsoft.Blazor.Components.PermaLink/IPermaLinkWatcherService.cs @@ -8,6 +8,24 @@ namespace Majorsoft.Blazor.Components.PermaLink /// public interface IPermaLinkWatcherService : IDisposable { + /// + /// Should scroll smoothly to the found permalink element or jump. + /// Note: smooth scroll on element level might not supported by all Browsers. + /// + bool SmoothScroll { get; set; } + + /// + /// Event handler for parmalinks detected on navigation. + /// + public event EventHandler PermalinkDetected; + + /// + /// Modify the current URL with given new peralink value and trigger navigation or just update browser History. + /// + /// New peramlink value, NULL will remove permalink part from URL + /// False, will trigger a navigation. When true, just add new URL to the History + void ChangePermalink(string? newPermalink, bool doNotNavigate); + /// /// Starts a navigation watcher which will check for Permalinks in the URLs. /// diff --git a/src/Majorsoft.Blazor.Components.PermaLink/Majorsoft.Blazor.Components.PermaLink.xml b/src/Majorsoft.Blazor.Components.PermaLink/Majorsoft.Blazor.Components.PermaLink.xml index bba85b73..d2e0f8fa 100644 --- a/src/Majorsoft.Blazor.Components.PermaLink/Majorsoft.Blazor.Components.PermaLink.xml +++ b/src/Majorsoft.Blazor.Components.PermaLink/Majorsoft.Blazor.Components.PermaLink.xml @@ -10,11 +10,51 @@ and should be injected only once for the whole application. Best way to use MainLayout.razor.
+ + + Should scroll smoothly to the found permalink element or jump. + Note: smooth scroll on element level might not supported by all Browsers. + + + + + Event handler for parmalinks detected on navigation. + + + + + Modify the current URL with given new peralink value and trigger navigation or just update browser History. + + New peramlink value, NULL will remove permalink part from URL + False, will trigger a navigation. When true, just add new URL to the History + Starts a navigation watcher which will check for Permalinks in the URLs. + + + Event arguments for PermalinkDetected event. + + + + + System.EventArgs for Microsoft.AspNetCore.Components.NavigationManager.LocationChanged. + + + + + Detected permalink value. + + + + + Default constructor + + NavigationManager event + Detected permalink value + Extension methods to register required Permalink services into IServiceCollection @@ -38,6 +78,18 @@ Implementation of + + + Gets or sets if peramlink should be scrolled smoothly into view or not. + Note: smooth scroll on element level might not supported by all Browsers. + + + + + Gets or sets if peramlink should be scrolled smoothly into view or not. + Note: smooth scroll on element level might not supported by all Browsers. + + Map HTML container Id. It can be used when multiple Permalinks added to one page. diff --git a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor index 6ae21bee..d2c0bf84 100644 --- a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor +++ b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor @@ -31,6 +31,16 @@ } } + /// + /// Gets or sets if peramlink should be scrolled smoothly into view or not. + /// Note: smooth scroll on element level might not supported by all Browsers. + /// + [Parameter] public bool SmoothScroll + { + get => _permalinkWatcher.SmoothScroll; + set { _permalinkWatcher.SmoothScroll = value; } + } + public async ValueTask DisposeAsync() { if (_scrollHandler is not null) diff --git a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs index b11fdeb8..929cdf7c 100644 --- a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs +++ b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs @@ -1,4 +1,5 @@ -using System.Text.RegularExpressions; +using System; +using System.Text.RegularExpressions; using Majorsoft.Blazor.Components.Common.JsInterop.Scroll; @@ -19,11 +20,17 @@ public class PermaLinkWatcherService : IPermaLinkWatcherService private readonly NavigationManager _navigationManager; private readonly ILogger _logger; - public PermaLinkWatcherService(IScrollHandler scrollHandler, NavigationManager navigationManager, ILogger logger) + public event EventHandler PermalinkDetected; + + public bool SmoothScroll { get; set; } + + public PermaLinkWatcherService(IScrollHandler scrollHandler, NavigationManager navigationManager, ILogger logger, bool smoothScroll = false) { _scrollHandler = scrollHandler; _navigationManager = navigationManager; _logger = logger; + + SmoothScroll = smoothScroll; } public void WatchPermaLinks() @@ -39,14 +46,67 @@ public void WatchPermaLinks() private void HandleLocationChanged(object sender, LocationChangedEventArgs e) { _logger.LogDebug($"{nameof(PermaLinkWatcherService)} - {nameof(HandleLocationChanged)}: navigation happened new URL: {e.Location}"); - var match = _poundRegex.Match(e.Location); + var perma = DetectPermalink(e.Location); - if(match.Success && match.Groups.Count == 2) + if(!string.IsNullOrWhiteSpace(perma)) { - var perma = match.Groups[1].Value; _logger.LogDebug($"{nameof(PermaLinkWatcherService)} - {nameof(HandleLocationChanged)}: PermaLink found: {perma}"); - _scrollHandler.ScrollToElementByNameAsync(perma); + + if(PermalinkDetected is not null) + { + PermalinkDetected.Invoke(this, new PermalinkDetectedEventArgs(e, perma)); + } + + _scrollHandler.ScrollToElementByNameAsync(perma, SmoothScroll); + } + } + + public void ChangePermalink(string? newPermalink, bool doNotNavigate) + { + var perma = DetectPermalink(_navigationManager.Uri); + if (!string.IsNullOrWhiteSpace(perma)) + { + if (newPermalink?.ToLower() == perma?.ToLower()) //same + { + return; + } + + var newUri = _navigationManager.Uri.Replace($"#{perma}", ""); + if (string.IsNullOrWhiteSpace(newPermalink)) //remove + { + _navigationManager.NavigateTo(newUri); + return; + } + + SetBrowserUrl($"{newUri}#{newPermalink}", doNotNavigate); + } + else + { + SetBrowserUrl($"{_navigationManager.Uri}#{newPermalink}", doNotNavigate); + } + } + + private void SetBrowserUrl(string uri, bool doNotNavigate) + { + if(doNotNavigate) + { + //TODO: history.pushState(null, '', url); + return; + } + + _navigationManager.NavigateTo(uri, false); + } + + private string DetectPermalink(string uri) + { + var match = _poundRegex.Match(uri); + if (match.Success && match.Groups.Count == 2) + { + var perma = match.Groups[1].Value; + return perma; } + + return null; } public void Dispose() diff --git a/src/Majorsoft.Blazor.Components.PermaLink/PermalinkBlazorWasmInitializer.razor b/src/Majorsoft.Blazor.Components.PermaLink/PermalinkBlazorWasmInitializer.razor index 14accfb3..9f60da0d 100644 --- a/src/Majorsoft.Blazor.Components.PermaLink/PermalinkBlazorWasmInitializer.razor +++ b/src/Majorsoft.Blazor.Components.PermaLink/PermalinkBlazorWasmInitializer.razor @@ -17,6 +17,17 @@ _permalinkWatcher.WatchPermaLinks(); } + /// + /// Gets or sets if peramlink should be scrolled smoothly into view or not. + /// Note: smooth scroll on element level might not supported by all Browsers. + /// + [Parameter] + public bool SmoothScroll + { + get => _permalinkWatcher.SmoothScroll; + set { _permalinkWatcher.SmoothScroll = value; } + } + public void Dispose() { _permalinkWatcher?.Dispose(); diff --git a/src/Majorsoft.Blazor.Components.PermaLink/PermalinkDetectedEventArgs.cs b/src/Majorsoft.Blazor.Components.PermaLink/PermalinkDetectedEventArgs.cs new file mode 100644 index 00000000..6142708f --- /dev/null +++ b/src/Majorsoft.Blazor.Components.PermaLink/PermalinkDetectedEventArgs.cs @@ -0,0 +1,33 @@ +using System; + +using Microsoft.AspNetCore.Components.Routing; + +namespace Majorsoft.Blazor.Components.PermaLink +{ + /// + /// Event arguments for PermalinkDetected event. + /// + public class PermalinkDetectedEventArgs : EventArgs + { + /// + /// System.EventArgs for Microsoft.AspNetCore.Components.NavigationManager.LocationChanged. + /// + public LocationChangedEventArgs LocationChangedEventArgs { get; } + + /// + /// Detected permalink value. + /// + public string Permalink { get; } = ""; + + /// + /// Default constructor + /// + /// NavigationManager event + /// Detected permalink value + public PermalinkDetectedEventArgs(LocationChangedEventArgs locationChangedEventArgs, string permalink) + { + LocationChangedEventArgs = locationChangedEventArgs; + Permalink = permalink; + } + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor index 854f979a..1260f076 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor @@ -49,6 +49,7 @@ textarea {

Internal page Permalink examles:

+ Smooth scroll:
  • No permalink
  • Non existing permalink
  • @@ -148,7 +149,9 @@ textarea {
-@code{ +@inject IPermaLinkWatcherService _permalinkWatcher + +@code { private ShowPermaLinkIcon _showIcon = ShowPermaLinkIcon.OnHover; private PermaLinkIconPosition _iconPosition = PermaLinkIconPosition.Right; private PermaLinkStyle _iconStyle; From 7ff462f9271f15ba0889aa3373cd7c44c6a77eb1 Mon Sep 17 00:00:00 2001 From: Major Date: Wed, 8 Sep 2021 18:44:34 +0200 Subject: [PATCH 04/19] Implemented INavigationHistoryService --- .github/docs/JsInterop.md | 27 +++++++- .../JsInteropExtension.cs | 2 + ...oft.Blazor.Components.Common.JsInterop.xml | 60 ++++++++++++++++ .../Navigation/INavigationHistoryService.cs | 61 ++++++++++++++++ .../Navigation/NavigationHistoryService.cs | 38 ++++++++++ .../PermaLinkBlazorServerInitializerTest.cs | 4 ++ .../PermaLinkBlazorServerInitializer.razor | 3 +- .../PermaLinkWatcherService.cs | 14 ++-- .../_Imports.razor | 3 +- .../Components/Index.razor | 1 + .../Components/JSInterop.razor | 3 + .../Components/JsDemo/BrowserHistory.razor | 69 +++++++++++++++++++ .../_Imports.razor | 1 + 13 files changed, 277 insertions(+), 9 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.Common.JsInterop/Navigation/INavigationHistoryService.cs create mode 100644 src/Majorsoft.Blazor.Components.Common.JsInterop/Navigation/NavigationHistoryService.cs create mode 100644 src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/BrowserHistory.razor diff --git a/.github/docs/JsInterop.md b/.github/docs/JsInterop.md index 7801a18e..80fc42b9 100644 --- a/.github/docs/JsInterop.md +++ b/.github/docs/JsInterop.md @@ -15,7 +15,6 @@ For code examples [see usage](https://github.com/majorimi/blazor-components/blob You can try it out by using the [demo app](https://blazorextensions.z6.web.core.windows.net/jsinterop). # Features - - **Click JS**: - `ClickBoundariesElement` is a component which wraps the given content to a DIV and subscribes to all click events: `OnOutsideClick`, `OnInsideClick`. - Also an **injectable `IClickBoundariesHandler` service** for callback event handlers. @@ -31,8 +30,9 @@ You can try it out by using the [demo app](https://blazorextensions.z6.web.core. - **Language JS**: is an **injectable `ILanguageService` service** for detect the browser language preference. - **Browser Date JS**: is an **injectable `IBrowserDateService` service** is a simple JS call to `new Date();` to retrieve client machine date and time. - **Browser Theme JS**: is an **injectable `IBrowserThemeService` service** to handle Browser color scheme queries and changes. -- **Geo JS**: is an **injectable `IGeolocationService` service** for detect the device Geolocation (GPS position, speed, heading, etc.). +- **Geo JS**: is an **injectable `IGeolocationService` service** for detect the device [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API) (GPS position, speed, heading, etc.). - **Head JS**: is an **injectable `IHtmlHeadService` service** for accessing and setting HTML document `Head tags`. +- **Browser History JS**: is an **injectable `INavigationHistoryService` service** to access [HTML History API](https://developer.mozilla.org/en-US/docs/Web/API/History) functionality. ## Click JS (See: [demo app](https://blazorextensions.z6.web.core.windows.net/jsinterop#click-js)) **NOTE: Blazor supports `@onclick` event which is equivalent with `OnInsideClick`. @@ -290,7 +290,6 @@ Removes event listener for `prefers-color-scheme` HTML event for the Browser. - **`DisposeAsync`: `ValueTask IAsyncDisposable()` interface**
Implements `IAsyncDisposable` interface the injected service should be Disposed. - ## Geolocation JS (See: [demo app](https://blazorextensions.z6.web.core.windows.net/jsinterop#geo-js)) **Geolocation JS** is an injectable `IGeolocationService` service for **detect the device Geolocation (GPS position, speed, heading, etc.)**. It is using the Geolocation API which allows users to provide their location to web applications if they desire. @@ -328,6 +327,26 @@ If you have multiple fav icon tags set in the Head first call `GetHtmlFavIconsAs - **`DisposeAsync`: `ValueTask IAsyncDisposable()` interface**
Implements `IAsyncDisposable` interface the injected service should be Disposed. +## Browser History JS (See: [demo app](https://blazorextensions.z6.web.core.windows.net/jsinterop#history-js)) +**Browser History JS** is an injectable `INavigationHistoryService` service** to access HTML History API functionality. +It is useful when don't want to rely on Blazor `NavigationManager` which does not have access to full History list and when it navigates trigger a page load/update. + +### Functions +- **`GetLengthAsync`**: **`ValueTask GetLengthAsync()`**
+Returns an Integer representing the number of elements in the session history, including the currently loaded page. +- **`GetScrollRestorationAsync`**: **`ValueTask GetScrollRestorationAsync()`**
+Allows web applications to explicitly set default scroll restoration behavior on history navigation. This property can be either `auto` or `manual`. +- **`BackAsync`**: **`ValueTask BackAsync()`**
+This asynchronous method goes to the previous page in session history, the same action as when the user clicks the browser's Back button. Equivalent to history.go(-1). +- **`ForwardAsync`**: **`ValueTask ForwardAsync()`**
+This asynchronous method goes to the next page in session history, the same action as when the user clicks the browser's Forward button; this is equivalent to history.go(1). +- **`GoAsync`**: **`ValueTask GoAsync(int delta)`**
+Asynchronously loads a page from the session history, identified by its relative location to the current page, for example -1 for the previous page or 1 for the next page. +- **`ReplaceStateAsync`**: **`ValueTask ReplaceStateAsync(ExpandoObject? state, string title, string url)`**
+Updates the most recent entry on the history stack to have the specified data, title, and, if provided, URL. +- **`PushStateAsync`**: **`ValueTask PushStateAsync(ExpandoObject? state, string title, string url)`**
+Pushes the given data onto the session history stack with the specified title (and, if provided, URL). + # Configuration @@ -366,6 +385,8 @@ Add using statement to your Blazor .razor file. Or globally refe @using Majorsoft.Blazor.Components.Common.JsInterop.BrowserDate @*Only if you want to use Browser ColorTheme*@ @using Majorsoft.Blazor.Components.Common.JsInterop.BrowserColorTheme +@*Only if you want to use Browser History*@ +@using Majorsoft.Blazor.Components.Common.JsInterop.History ``` diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/JsInteropExtension.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/JsInteropExtension.cs index 3940404e..0fa686e4 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/JsInteropExtension.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/JsInteropExtension.cs @@ -9,6 +9,7 @@ using Majorsoft.Blazor.Components.Common.JsInterop.GlobalMouseEvents; using Majorsoft.Blazor.Components.Common.JsInterop.Head; using Majorsoft.Blazor.Components.Common.JsInterop.Language; +using Majorsoft.Blazor.Components.Common.JsInterop.Navigation; using Majorsoft.Blazor.Components.Common.JsInterop.Resize; using Majorsoft.Blazor.Components.Common.JsInterop.Scroll; @@ -44,6 +45,7 @@ public static IServiceCollection AddJsInteropExtensions(this IServiceCollection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); return services; } diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml b/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml index 8407ba14..4d236f0f 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml @@ -561,6 +561,66 @@ Implementation of
+ + + Injectable service to handle Browser history JS Interops. + https://developer.mozilla.org/en-US/docs/Web/API/History + + + + + Returns an Integer representing the number of elements in the session history, including the currently loaded page. For example, for a page loaded in a new tab this property returns 1. + + ValueTask + + + + Allows web applications to explicitly set default scroll restoration behavior on history navigation. This property can be either `auto` or `manual`. + + ValueTask + + + + This asynchronous method goes to the previous page in session history, the same action as when the user clicks the browser's Back button. Equivalent to history.go(-1). + + ValueTask + + + + This asynchronous method goes to the next page in session history, the same action as when the user clicks the browser's Forward button; this is equivalent to history.go(1). + + ValueTask + + + + Asynchronously loads a page from the session history, identified by its relative location to the current page, for example -1 for the previous page or 1 for the next page. + + The position in the history to which you want to move, relative to the current page. A negative value moves backwards, a positive value moves forwards. + ValueTask + + + + Updates the most recent entry on the history stack to have the specified data, title, and, if provided, URL. + + Arbitrary object of the page + Page tile + New URL to show and add to history + ValueTask + + + + Pushes the given data onto the session history stack with the specified title (and, if provided, URL). + + Arbitrary object of the page + Page tile + New URL to show and add to history + ValueTask + + + + Implementation of + + Injectable service to handle JS 'resize' events for HTML element or the whole document. diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Navigation/INavigationHistoryService.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Navigation/INavigationHistoryService.cs new file mode 100644 index 00000000..57102997 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Navigation/INavigationHistoryService.cs @@ -0,0 +1,61 @@ +using System.Dynamic; +using System.Threading.Tasks; + +namespace Majorsoft.Blazor.Components.Common.JsInterop.Navigation +{ + /// + /// Injectable service to handle Browser history JS Interops. + /// https://developer.mozilla.org/en-US/docs/Web/API/History + /// + public interface INavigationHistoryService + { + /// + /// Returns an Integer representing the number of elements in the session history, including the currently loaded page. For example, for a page loaded in a new tab this property returns 1. + /// + /// ValueTask + ValueTask GetLengthAsync(); + + /// + /// Allows web applications to explicitly set default scroll restoration behavior on history navigation. This property can be either `auto` or `manual`. + /// + /// ValueTask + ValueTask GetScrollRestorationAsync(); + + /// + /// This asynchronous method goes to the previous page in session history, the same action as when the user clicks the browser's Back button. Equivalent to history.go(-1). + /// + /// ValueTask + ValueTask BackAsync(); + + /// + /// This asynchronous method goes to the next page in session history, the same action as when the user clicks the browser's Forward button; this is equivalent to history.go(1). + /// + /// ValueTask + ValueTask ForwardAsync(); + + /// + /// Asynchronously loads a page from the session history, identified by its relative location to the current page, for example -1 for the previous page or 1 for the next page. + /// + /// The position in the history to which you want to move, relative to the current page. A negative value moves backwards, a positive value moves forwards. + /// ValueTask + ValueTask GoAsync(int delta); + + /// + /// Updates the most recent entry on the history stack to have the specified data, title, and, if provided, URL. + /// + /// Arbitrary object of the page + /// Page tile + /// New URL to show and add to history + /// ValueTask + ValueTask ReplaceStateAsync(ExpandoObject? state, string title, string url); + + /// + /// Pushes the given data onto the session history stack with the specified title (and, if provided, URL). + /// + /// Arbitrary object of the page + /// Page tile + /// New URL to show and add to history + /// ValueTask + ValueTask PushStateAsync(ExpandoObject? state, string title, string url); + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Navigation/NavigationHistoryService.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Navigation/NavigationHistoryService.cs new file mode 100644 index 00000000..43f0ccf6 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Navigation/NavigationHistoryService.cs @@ -0,0 +1,38 @@ +using System.Dynamic; +using System.Threading.Tasks; + +using Microsoft.JSInterop; + +namespace Majorsoft.Blazor.Components.Common.JsInterop.Navigation +{ + /// + /// Implementation of + /// + public class NavigationHistoryService : INavigationHistoryService + { + private readonly IJSRuntime _jSRuntime; + + public NavigationHistoryService(IJSRuntime jSRuntime) + { + _jSRuntime = jSRuntime; + } + + public async ValueTask GetLengthAsync() => await _jSRuntime.InvokeAsync("eval", "history.length"); + public async ValueTask GetScrollRestorationAsync() => await _jSRuntime.InvokeAsync("eval", "history.scrollRestoration"); + + public async ValueTask BackAsync() + => await _jSRuntime.InvokeVoidAsync("history.back"); + + public async ValueTask ForwardAsync() + => await _jSRuntime.InvokeVoidAsync("history.forward"); + + public async ValueTask GoAsync(int delta) + => await _jSRuntime.InvokeVoidAsync("history.go", delta); + + public async ValueTask PushStateAsync(ExpandoObject? state, string title, string url) + => await _jSRuntime.InvokeVoidAsync("history.pushState", state, title, url); + + public async ValueTask ReplaceStateAsync(ExpandoObject? state, string title, string url) + => await _jSRuntime.InvokeVoidAsync("history.replaceState", state, title, url); + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.PermaLink.Tests/PermaLinkBlazorServerInitializerTest.cs b/src/Majorsoft.Blazor.Components.PermaLink.Tests/PermaLinkBlazorServerInitializerTest.cs index a0ad3a90..915431d2 100644 --- a/src/Majorsoft.Blazor.Components.PermaLink.Tests/PermaLinkBlazorServerInitializerTest.cs +++ b/src/Majorsoft.Blazor.Components.PermaLink.Tests/PermaLinkBlazorServerInitializerTest.cs @@ -2,6 +2,7 @@ using Bunit; +using Majorsoft.Blazor.Components.Common.JsInterop.Navigation; using Majorsoft.Blazor.Components.Common.JsInterop.Scroll; using Majorsoft.Blazor.Components.CommonTestsBase; @@ -18,6 +19,7 @@ public class PermaLinkBlazorServerInitializerTest : ComponentsTestBase _permaLinkWatcherServiceMock; private Mock _scrollHandlerMock; + private Mock _navigationHistoryServiceMock; [TestInitialize] public void Init() @@ -25,10 +27,12 @@ public void Init() var logger = new Mock>(); _permaLinkWatcherServiceMock = new Mock(); _scrollHandlerMock = new Mock(); + _navigationHistoryServiceMock = new Mock(); _testContext.Services.Add(new ServiceDescriptor(typeof(ILogger), logger.Object)); _testContext.Services.Add(new ServiceDescriptor(typeof(IPermaLinkWatcherService), _permaLinkWatcherServiceMock.Object)); _testContext.Services.Add(new ServiceDescriptor(typeof(IScrollHandler), _scrollHandlerMock.Object)); + _testContext.Services.Add(new ServiceDescriptor(typeof(INavigationHistoryService), _navigationHistoryServiceMock.Object)); _testContext.Services.Add(new ServiceDescriptor(typeof(SingletonComponentService), new SingletonComponentService())); _testContext.Services.Add(new ServiceDescriptor(typeof(SingletonComponentService), new SingletonComponentService())); } diff --git a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor index d2c0bf84..bb583c82 100644 --- a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor +++ b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor @@ -3,6 +3,7 @@ @inject IScrollHandler _scrollHandler @inject NavigationManager _navigationManager +@inject INavigationHistoryService _navigationHistoryService @inject ILogger _logger @inject SingletonComponentService _singleton @@ -26,7 +27,7 @@ if (firstRender) { //setup permalink - _permalinkWatcher = new PermaLinkWatcherService(_scrollHandler, _navigationManager, _logger); + _permalinkWatcher = new PermaLinkWatcherService(_scrollHandler, _navigationManager, _logger, _navigationHistoryService); _permalinkWatcher.WatchPermaLinks(); } } diff --git a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs index 929cdf7c..bfb67a17 100644 --- a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs +++ b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs @@ -1,6 +1,7 @@ using System; using System.Text.RegularExpressions; +using Majorsoft.Blazor.Components.Common.JsInterop.Navigation; using Majorsoft.Blazor.Components.Common.JsInterop.Scroll; using Microsoft.AspNetCore.Components; @@ -19,17 +20,22 @@ public class PermaLinkWatcherService : IPermaLinkWatcherService private readonly IScrollHandler _scrollHandler; private readonly NavigationManager _navigationManager; private readonly ILogger _logger; + private readonly INavigationHistoryService _navigationHistoryService; public event EventHandler PermalinkDetected; public bool SmoothScroll { get; set; } - public PermaLinkWatcherService(IScrollHandler scrollHandler, NavigationManager navigationManager, ILogger logger, bool smoothScroll = false) + public PermaLinkWatcherService(IScrollHandler scrollHandler, + NavigationManager navigationManager, + ILogger logger, + INavigationHistoryService navigationHistoryService, + bool smoothScroll = false) { _scrollHandler = scrollHandler; _navigationManager = navigationManager; _logger = logger; - + _navigationHistoryService = navigationHistoryService; SmoothScroll = smoothScroll; } @@ -55,7 +61,7 @@ private void HandleLocationChanged(object sender, LocationChangedEventArgs e) if(PermalinkDetected is not null) { PermalinkDetected.Invoke(this, new PermalinkDetectedEventArgs(e, perma)); - } + } _scrollHandler.ScrollToElementByNameAsync(perma, SmoothScroll); } @@ -90,7 +96,7 @@ private void SetBrowserUrl(string uri, bool doNotNavigate) { if(doNotNavigate) { - //TODO: history.pushState(null, '', url); + _navigationHistoryService?.PushStateAsync(null, "", uri); return; } diff --git a/src/Majorsoft.Blazor.Components.PermaLink/_Imports.razor b/src/Majorsoft.Blazor.Components.PermaLink/_Imports.razor index b3acd5c8..400301eb 100644 --- a/src/Majorsoft.Blazor.Components.PermaLink/_Imports.razor +++ b/src/Majorsoft.Blazor.Components.PermaLink/_Imports.razor @@ -5,4 +5,5 @@ @using Majorsoft.Blazor.Components.Common.JsInterop.Scroll @using Majorsoft.Blazor.Components.Common.JsInterop.Clipboard -@using Majorsoft.Blazor.Components.Core.Events \ No newline at end of file +@using Majorsoft.Blazor.Components.Core.Events +@using Majorsoft.Blazor.Components.Common.JsInterop.Navigation \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Index.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Index.razor index 480e6cd9..ff6530e2 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Index.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Index.razor @@ -50,6 +50,7 @@
  • Browser Theme Js
  • Geo Js
  • Head Js
  • +
  • Browser History Js
  • diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor index ddabac18..40a64d26 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor @@ -35,6 +35,7 @@ Smooth scroll:
  • Browser Theme Js
  • Geolocation Js
  • Head Js
  • +
  • Browser History Js
  • @@ -62,6 +63,8 @@ Smooth scroll: + +
    @*Scroll to page end with ScrollHandler for custom floating component see ScrollToPageTop*@ diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/BrowserHistory.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/BrowserHistory.razor new file mode 100644 index 00000000..03d40ecf --- /dev/null +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/BrowserHistory.razor @@ -0,0 +1,69 @@ +
    + +

    Browser History JS

    +
    + +

    + Browser History JS is an injectable INavigationHistoryService to access + HTML History API functionality. + It is useful when don't want to rely on Blazor NavigationManager which does not have access to full History list and when it navigates trigger a page load/update. +

    + +
    +
    +

    + Number of History items: @_historyCount +
    + Scroll restoration behavior: @_scrollRestore +

    +
    +
    + +
    +
    +
    History manipulation:
    + + History Title: + History URL: + + + +
    +
    + +
    +
    +
    Navigations:
    + Go with Delta: @_goDelta + + + + +
    +
    +
    + +@inject INavigationHistoryService _navigationHistoryService +@inject NavigationManager _navigationManager + +@code { + private int _historyCount = 0; + private string _scrollRestore = ""; + private int _goDelta = 0; + private string _title = ""; + private string _url = ""; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if(firstRender) + { + _historyCount = await _navigationHistoryService.GetLengthAsync(); + _scrollRestore = await _navigationHistoryService.GetScrollRestorationAsync(); + + _url = _navigationManager.Uri; + + StateHasChanged(); + } + } + +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor index 0f127b7d..aa435835 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor @@ -40,6 +40,7 @@ @using Majorsoft.Blazor.Components.Common.JsInterop.Head @using Majorsoft.Blazor.Components.Common.JsInterop.BrowserDate @using Majorsoft.Blazor.Components.Common.JsInterop.BrowserColorTheme +@using Majorsoft.Blazor.Components.Common.JsInterop.Navigation @using Majorsoft.Blazor.Components.PermaLink From 0325ae6d43fd9a9a81735e45f34fc79a990a822d Mon Sep 17 00:00:00 2001 From: Major Date: Fri, 10 Sep 2021 14:19:49 +0200 Subject: [PATCH 05/19] Implemented TabActivation with permalinks --- .github/docs/PermaLink.md | 2 + .github/docs/Tabs.md | 45 ++++++- ....Blazor.Components.Common.JsInterop.csproj | 2 +- .../IPermaLinkWatcherService.cs | 7 ++ .../Majorsoft.Blazor.Components.PermaLink.xml | 7 ++ .../PermaLinkWatcherService.cs | 33 +++-- .../TabsPanelTest.cs | 117 ++++++++++++++++++ .../Majorsoft.Blazor.Components.Tabs.csproj | 1 + .../Majorsoft.Blazor.Components.Tabs.xml | 12 ++ .../TabItem.razor | 6 + .../TabsPanel.razor | 70 +++++++++-- .../_Imports.razor | 4 +- .../Components/Tabs.razor | 12 +- src/Majorsoft.Blazor.Components.sln | 2 +- 14 files changed, 291 insertions(+), 29 deletions(-) diff --git a/.github/docs/PermaLink.md b/.github/docs/PermaLink.md index 81ef2520..045bd811 100644 --- a/.github/docs/PermaLink.md +++ b/.github/docs/PermaLink.md @@ -34,6 +34,8 @@ For more details see Usage section. Starts a navigation watcher which will check for Permalinks in the URLs. - **`ChangePermalink()`**: **`void ChangePermalink(string? newPermalink, bool doNotNavigate)`**
    Modify the current URL with given new peralink value and trigger navigation or just update browser History. +- **`CheckPermalink()`**: **`string? CheckPermalink(bool triggerEvent = false)`**
    +Checks the current URL for permalink again and re-triggers `PermalinkDetected` event if requested. - **`Dispose()`: `@implements IDisposable` interface**
    Component implements `IDisposable` interface Blazor framework will call it when parent removed from render tree. diff --git a/.github/docs/Tabs.md b/.github/docs/Tabs.md index 74ae8d83..45b21969 100644 --- a/.github/docs/Tabs.md +++ b/.github/docs/Tabs.md @@ -37,6 +37,9 @@ Sets all `TabItem` elements height in `px`. Sets all `TabItem` elements element width in `px`. - **`Disabled`: `bool { get; set; }` (default: false)**
    Determines whether all the rendered HTML elements should be disabled or not. +- **`AllowTabActivationByPermalink`: `bool { get; set; }` (default: true)**
    +Enables or disables `TabItem` activation with URL Permalink fragment. +**NOTE: in order to make TabActivation work `Majorsoft.Blazor.Components.PermaLink` component is used and it MUST [set up correctly](https://github.com/majorimi/blazor-components/blob/master/.github/docs/PermaLink.md#configuration)!** - **`Animate`: `bool { get; set; }` (default: true)**
    Determines to apply CSS animation and transion on Tab changes or not. - **`TabPositon`: `TabPositons { get; set; }` (default: TabPositons.Left)**
    @@ -67,6 +70,9 @@ Required HTML content to show content of current TabItem. Determines whether the current rendered TabItem should be disabled or not. - **`Hidden`: `bool { get; set; }` (default: false)**
    Determines whether the current rendered TabItem should be hidden or not. +- **`Permalink`: `string { get; set; }` (default: "")**
    +Permalink value to append to the URL and activate the `TabItem` based on matching value. +**NOTE: in order to make TabActivation work `Majorsoft.Blazor.Components.PermaLink` component is used and it MUST [set up correctly](https://github.com/majorimi/blazor-components/blob/master/.github/docs/PermaLink.md#configuration)!** **Arbitrary HTML attributes e.g.: `tabindex="1"` will be passed to the corresponding rendered HTML element ``**. @@ -88,11 +94,42 @@ Add using statement to your Blazor `.razor` file. Or globally re @using Majorsoft.Blazor.Components.Tabs ``` +### Dependences +**Majorsoft.Blazor.Components.Tabs** package "partially" depends on other Majorsoft Nuget packages: +- [Majorsoft.Blazor.Components.Common.JsInterop](https://www.nuget.org/packages/Majorsoft.Blazor.Components.Common.JsInterop) +which handles JS Interop for many features e.g. scrolling, etc. +- [Majorsoft.Blazor.Components.Common.PermaLink](https://www.nuget.org/packages/Majorsoft.Blazor.Components.PermaLink) +which track navigations (URL changes) and identify permalink elements. + +**NOTE: only TabItem activation feature depend on Permalink. If you don't want to use that feature just leave `Permalink` parameters empty and do not setup PermalinkWatcher. +Also later this feature can be disabled by `AllowTabActivationByPermalink = false`.** + ### `TabsPanel` and `TabItem` usage Following code example shows how to use **`TabsPanel`** with **`TabItem`** component in your Blazor App. +**NOTE: to use TabActivation feature `Permalink="Tab1"` must be set and Permalink services must be [configured correctly](https://github.com/majorimi/blazor-components/blob/master/.github/docs/PermaLink.md#configuration)!** + ``` +@*Simple tab usage*@ + + + +
    Tab1
    + Tab1 +
    + +
    Tab2
    + Tab2 +
    + +
    Tab3
    + Tab3 +
    +
    +
    + +@*Advanced tab usage*@ - + - + -
    /// Enumerable new markers to add /// Enumerable markers removed or replaced - /// + /// Async task Task CreateMarkersAsync(IEnumerable? newMarkers, IEnumerable? markers); + + /// + /// Returns the lat/lng bounds of the current viewport. If more than one copy of the world is visible, + /// the bounds range in longitude from -180 to 180 degrees inclusive. If the map is not yet initialized or + /// center and zoom have not been set then the result is undefined. For vector maps with non-zero tilt or heading, + /// the returned lat/lng bounds represents the smallest bounding box that includes the visible region of the map's viewport. + /// + /// Async task + ValueTask GetBoundsAsync(); + + /// + /// Returns the position displayed at the center of the map. Note that this LatLng object is not wrapped. See LatLng for more information. + /// If the center or bounds have not been set then the result is undefined. + /// + /// Async task + ValueTask GetCenterAsync(); + + /// + /// Returns HTMLElement The mapDiv of the map. + /// + /// Async task + ValueTask GetDivAsync(); } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Maps/GoogleMapLatLng.cs b/src/Majorsoft.Blazor.Components.Maps/GoogleMapLatLng.cs new file mode 100644 index 00000000..18fc0c02 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Maps/GoogleMapLatLng.cs @@ -0,0 +1,20 @@ +namespace Majorsoft.Blazor.Components.Maps +{ + /// + /// A LatLng is a point in geographical coordinates: latitude and longitude. + /// + public class GoogleMapLatLng + { + /// + /// Latitude ranges between -90 and 90 degrees, inclusive. Values above or below this range will be clamped to the range [-90, 90]. + /// This means that if the value specified is less than -90, it will be set to -90. And if the value is greater than 90, it will be set to 90. + /// + public double Latitude { get; set; } + + /// + /// Longitude ranges between -180 and 180 degrees, inclusive. Values above or below this range will be wrapped so that they fall within the range. + /// For example, a value of -190 will be converted to 170. A value of 190 will be converted to -170. This reflects the fact that longitudes wrap around the globe. + /// + public double Longitude { get; set; } + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml index 0b48d11a..fc5e16c5 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml +++ b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml @@ -47,6 +47,23 @@ Maps specific geo coordinates or address
    + + + A LatLng is a point in geographical coordinates: latitude and longitude. + + + + + Latitude ranges between -90 and 90 degrees, inclusive. Values above or below this range will be clamped to the range [-90, 90]. + This means that if the value specified is less than -90, it will be set to -90. And if the value is greater than 90, it will be set to 90. + + + + + Longitude ranges between -180 and 180 degrees, inclusive. Values above or below this range will be wrapped so that they fall within the range. + For example, a value of -190 will be converted to 170. A value of 190 will be converted to -170. This reflects the fact that longitudes wrap around the globe. + + Identifiers used to specify the placement of controls on the map. @@ -233,6 +250,41 @@ Maximum allowed with of the Info Window. + + + A LatLngBounds instance represents a rectangle in geographical coordinates, including one that crosses the 180 degrees longitudinal meridian. + + + + + Computes the center of this LatLngBounds + Equivalent with `getCenter()` method call but it would require JsInterop. + + + + + Returns the north-east corner of this bounds. + Equivalent with `getNorthEast()` method call but it would require JsInterop. + + + + + Returns the south-west corner of this bounds. + Equivalent with `getSouthWest()` method call but it would require JsInterop. + + + + + Converts the given map bounds to a lat/lng span. + Equivalent with `toSpan()` method call but it would require JsInterop. + + + + + Returns if the bounds are empty. + Equivalent with `isEmpty()` method call but it would require JsInterop. + + MarkerOptions object used to define the properties that can be set on a Marker with event callbacks. @@ -694,7 +746,7 @@ - Injectable service to handle Google JavaScript Maps functionalities. + Injectable service to handle Google JavaScript Maps functionalities. Available on the instance of object ref as well. @@ -829,7 +881,29 @@ Enumerable new markers to add Enumerable markers removed or replaced - + Async task + + + + Returns the lat/lng bounds of the current viewport. If more than one copy of the world is visible, + the bounds range in longitude from -180 to 180 degrees inclusive. If the map is not yet initialized or + center and zoom have not been set then the result is undefined. For vector maps with non-zero tilt or heading, + the returned lat/lng bounds represents the smallest bounding box that includes the visible region of the map's viewport. + + Async task + + + + Returns the position displayed at the center of the map. Note that this LatLng object is not wrapped. See LatLng for more information. + If the center or bounds have not been set then the result is undefined. + + Async task + + + + Returns HTMLElement The mapDiv of the map. + + Async task @@ -841,6 +915,11 @@ Exposes a Blazor of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + + + Exposes which is handling JsInterop. This instance can be used for access more GoogleMap features. + + Color used for the background of the Map div. This color will be visible when tiles have not yet loaded as the user pans. diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js index 76db8114..76d88e56 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js @@ -342,6 +342,49 @@ export function panToAddress(elementId, address) { } } } +//get methods +export function getBounds(elementId) { + if (elementId) { + let mapWithDotnetRef = getElementIdWithDotnetRef(_mapsElementDict, elementId); + if (mapWithDotnetRef && mapWithDotnetRef.map) { + let bounds = mapWithDotnetRef.map.getBounds(); + + let ret = { + Center: convertToLatLng(bounds.getCenter()), + NorthEast: convertToLatLng(bounds.getNorthEast()), + SouthWest: convertToLatLng(bounds.getSouthWest()), + Span: convertToLatLng(bounds.toSpan()), + IsEmpty: bounds.isEmpty(), + }; + return ret; + } + } +} +export function getCenter(elementId) { + if (elementId) { + let mapWithDotnetRef = getElementIdWithDotnetRef(_mapsElementDict, elementId); + if (mapWithDotnetRef && mapWithDotnetRef.map) { + let center = mapWithDotnetRef.map.getCenter(); + + let ret = convertToLatLng(center); + return ret; + } + } +} +export function getDiv(elementId) { + if (elementId) { + let mapWithDotnetRef = getElementIdWithDotnetRef(_mapsElementDict, elementId); + if (mapWithDotnetRef && mapWithDotnetRef.map) { + var ret = mapWithDotnetRef.map.getDiv(); + return ret; + } + } +} +function convertToLatLng(latLngObject) { + let ret = { Latitude: latLngObject.lat(), Longitude: latLngObject.lng() }; + return ret; +} + //set methods export function setZoom(elementId, zoom) { if (elementId) { diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js index 68bbd396..c6203724 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js @@ -1 +1 @@ -export function init(t,i,r,f,e){if(t&&i&&r){u(n,i,r,f,e);let o="https://maps.googleapis.com/maps/api/js?key=",h=!1,l=document.querySelectorAll("head > script");if(l.forEach(n=>{if(n){let t=n.getAttribute("src");if(t&&t.startsWith(o)){h=!0;return}}}),h){window.google&&window.initGoogleMaps();return}let c=document.createElement("script");c.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(c);o=o+t+"&callback=initGoogleMaps&libraries=&v=weekly";let s=document.createElement("script");s.src=o;s.defer=!0;document.head.appendChild(s)}}function u(n,t,i,r,u){for(let i=0;i{e.invokeMethodAsync("CustomControlClicked",i)})}}}export function createMarkers(r,u){if(r&&u&&u.length){let o=t(n,r);if(o&&o.map)for(var f=0;f{o.ref.invokeMethodAsync("MarkerClicked",n.id),i&&i.open(o.map,t)})}if(n.draggable){t.addListener("drag",()=>{i("MarkerDrag",n.id,t.getPosition().toJSON())});t.addListener("dragend",()=>{i("MarkerDragEnd",n.id,t.getPosition().toJSON())});t.addListener("dragstart",()=>{i("MarkerDragStart",n.id,t.getPosition().toJSON())});function i(n,t,i){let r={Latitude:i.lat,Longitude:i.lng};o.ref.invokeMethodAsync(n,t,r)}}}}}export function removeMarkers(r,u){if(r&&u&&u.length){let e=t(n,r);if(e&&e.map)for(var f=0;f{if(n.id==t.id){t.setMap(null);i.splice(r,1);return}})}}}function e(n,t){t&&n&&(t.setPosition({lat:n.position.latitude,lng:n.position.longitude}),t.anchorPoint=n.anchorPoint?{x:n.anchorPoint.x,y:n.anchorPoint.y}:null,t.setAnimation(n.animation),t.setClickable(n.clickable),t.crossOnDrag=n.crossOnDrag,t.setCursor(n.cursor),t.setDraggable(n.draggable),t.setIcon(n.icon),t.setLabel(n.label),t.setOpacity(n.opacity),t.optimized=n.optimized,t.setShape(n.shape),t.setTitle(n.title),t.setVisible(n.visible),t.setZIndex(n.zIndex))}export function getAddressCoordinates(i,u){r(u,function(r){if(r){let u=t(n,i);u&&u.map&&u.ref.invokeMethodAsync("AddressSearch",r)}})}function r(n,t){let i=new google.maps.Geocoder;i.geocode({address:n},function(n,i){i==google.maps.GeocoderStatus.OK&&t(n)})}export function dispose(i){if(i){let r=t(n,i);r.map=null;r.ref=null;f(n,i)}}window.initGoogleMaps=()=>{for(let i=0;i{u(n,"MapClicked")});r.addListener("dblclick",n=>{u(n,"MapDoubleClicked")});r.addListener("contextmenu",n=>{u(n,"MapContextMenu")});r.addListener("mouseup",n=>{u(n,"MapMouseUp")});r.addListener("mousedown",n=>{u(n,"MapMouseDown")});r.addListener("mousemove",n=>{u(n,"MapMouseMove")});r.addListener("mouseover",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOver")}});r.addListener("mouseout",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOut")}});r.addListener("center_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapCenterChanged",t)}}});r.addListener("zoom_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapZoomChanged",r.getZoom())}});r.addListener("maptypeid_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTypeIdChanged",r.getMapTypeId())}});r.addListener("heading_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapHeadingChanged",r.getHeading())}});r.addListener("tilt_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTiltChanged",r.getTilt())}});r.addListener("bounds_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapBoundsChanged")}});r.addListener("projection_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapProjectionChanged")}});r.addListener("draggable_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapDraggableChanged")}});r.addListener("streetview_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapStreetviewChanged")}});r.addListener("drag",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDrag",t)}}});r.addListener("dragend",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragEnd",t)}}});r.addListener("dragstart",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragStart",t)}}});r.addListener("resize",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i){let n={Width:r.getDiv().offsetWidth,Height:r.getDiv().offsetHeight};i.ref.invokeMethodAsync("MapResized",n)}}});r.addListener("tilesloaded",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTilesLoaded")}});r.addListener("idle",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapIdle")}});n[i].value.ref.invokeMethodAsync("MapInitialized",f)}}};let n=[],i=[]; \ No newline at end of file +export function init(t,i,r,u,e){if(t&&i&&r){f(n,i,r,u,e);let o="https://maps.googleapis.com/maps/api/js?key=",h=!1,l=document.querySelectorAll("head > script");if(l.forEach(n=>{if(n){let t=n.getAttribute("src");if(t&&t.startsWith(o)){h=!0;return}}}),h){window.google&&window.initGoogleMaps();return}let c=document.createElement("script");c.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(c);o=o+t+"&callback=initGoogleMaps&libraries=&v=weekly";let s=document.createElement("script");s.src=o;s.defer=!0;document.head.appendChild(s)}}function f(n,t,i,r,u){for(let i=0;i{e.invokeMethodAsync("CustomControlClicked",i)})}}}export function createMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{e.ref.invokeMethodAsync("MarkerClicked",n.id),i&&i.open(e.map,t)})}if(n.draggable){t.addListener("drag",()=>{i("MarkerDrag",n.id,t.getPosition().toJSON())});t.addListener("dragend",()=>{i("MarkerDragEnd",n.id,t.getPosition().toJSON())});t.addListener("dragstart",()=>{i("MarkerDragStart",n.id,t.getPosition().toJSON())});function i(n,t,i){let r={Latitude:i.lat,Longitude:i.lng};e.ref.invokeMethodAsync(n,t,r)}}}}}export function removeMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{if(n.id==t.id){t.setMap(null);r.splice(i,1);return}})}}}function o(n,t){t&&n&&(t.setPosition({lat:n.position.latitude,lng:n.position.longitude}),t.anchorPoint=n.anchorPoint?{x:n.anchorPoint.x,y:n.anchorPoint.y}:null,t.setAnimation(n.animation),t.setClickable(n.clickable),t.crossOnDrag=n.crossOnDrag,t.setCursor(n.cursor),t.setDraggable(n.draggable),t.setIcon(n.icon),t.setLabel(n.label),t.setOpacity(n.opacity),t.optimized=n.optimized,t.setShape(n.shape),t.setTitle(n.title),t.setVisible(n.visible),t.setZIndex(n.zIndex))}export function getAddressCoordinates(i,r){u(r,function(r){if(r){let u=t(n,i);u&&u.map&&u.ref.invokeMethodAsync("AddressSearch",r)}})}function u(n,t){let i=new google.maps.Geocoder;i.geocode({address:n},function(n,i){i==google.maps.GeocoderStatus.OK&&t(n)})}export function dispose(i){if(i){let r=t(n,i);r.map=null;r.ref=null;e(n,i)}}window.initGoogleMaps=()=>{for(let i=0;i{u(n,"MapClicked")});r.addListener("dblclick",n=>{u(n,"MapDoubleClicked")});r.addListener("contextmenu",n=>{u(n,"MapContextMenu")});r.addListener("mouseup",n=>{u(n,"MapMouseUp")});r.addListener("mousedown",n=>{u(n,"MapMouseDown")});r.addListener("mousemove",n=>{u(n,"MapMouseMove")});r.addListener("mouseover",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOver")}});r.addListener("mouseout",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOut")}});r.addListener("center_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapCenterChanged",t)}}});r.addListener("zoom_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapZoomChanged",r.getZoom())}});r.addListener("maptypeid_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTypeIdChanged",r.getMapTypeId())}});r.addListener("heading_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapHeadingChanged",r.getHeading())}});r.addListener("tilt_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTiltChanged",r.getTilt())}});r.addListener("bounds_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapBoundsChanged")}});r.addListener("projection_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapProjectionChanged")}});r.addListener("draggable_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapDraggableChanged")}});r.addListener("streetview_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapStreetviewChanged")}});r.addListener("drag",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDrag",t)}}});r.addListener("dragend",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragEnd",t)}}});r.addListener("dragstart",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragStart",t)}}});r.addListener("resize",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i){let n={Width:r.getDiv().offsetWidth,Height:r.getDiv().offsetHeight};i.ref.invokeMethodAsync("MapResized",n)}}});r.addListener("tilesloaded",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTilesLoaded")}});r.addListener("idle",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapIdle")}});n[i].value.ref.invokeMethodAsync("MapInitialized",f)}}};let n=[],r=[]; \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor index 1f97aa32..011256e3 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor @@ -444,19 +444,19 @@ private bool _staticMapShowPath = false; private IEnumerable _staticMapPaths = new List() -{ + { { new GeolocationData("Budapest")}, { new GeolocationData("London")} }; private bool _staticMapShowVisiblePoints = false; private IEnumerable _staticMapVisiblePoints = new List() -{ + { { new GeolocationData("Budapest" )}, { new GeolocationData("London")} }; private bool _staticMapShowMarkers = false; private IEnumerable _staticMapMarkers = new List() -{ + { { new GoogleStaticMapMarker() }, { new GoogleStaticMapMarker() { @@ -590,7 +590,9 @@ //Events private async Task OnMapInitialized(string elementId) { - _mapsLog = await WriteLog(_mapsLog, $"Map was initialized container DIV Id: '{elementId}'"); + _mapsLog = await WriteLog(_mapsLog, $"Map was initialized container DIV Id: '{elementId}'. Map DIV can be accessed by: .GoogleMapService.GetDivAsync()"); + + var div = await _googleMap.GoogleMapService.GetDivAsync(); } private async Task OnMapClicked(GeolocationCoordinate coordinate) { @@ -695,6 +697,9 @@ _mapsLog = await WriteLog(_mapsLog, $"Map Center location changed to: {coordinate.ToString()}"); } _jsMapCenter = new GeolocationData(coordinate.Latitude, coordinate.Longitude); //Two-way binding with event value + + //Center point can be accessed as well with method call + var center = await _googleMap.GoogleMapService.GetCenterAsync(); } private async Task OnMapZoomLevelChanged(byte zoom) { @@ -737,8 +742,11 @@ { if (_logOtherEvents) { - _mapsLog = await WriteLog(_mapsLog, $"Map boundaries changed"); + _mapsLog = await WriteLog(_mapsLog, $"Map boundaries changed. Bond information can be called: '.GoogleMapService.GetBoundsAsync()'"); } + + //Bond information can be called: + var bounds = await _googleMap.GoogleMapService.GetBoundsAsync(); } private async Task OnMapProjectionChanged() { From 275eeb7b78bf06c528fee0b1318a06e03a3468bf Mon Sep 17 00:00:00 2001 From: Major Date: Mon, 20 Dec 2021 17:33:40 +0100 Subject: [PATCH 13/19] Implemented GoogleMap restriction boundaries in Init --- .../Google/GoogleMap.razor | 6 +++ .../Google/GoogleMapLatLngBounds.cs | 18 +++++++++ .../Google/GoogleMapRestriction.cs | 18 +++++++++ .../Google/GoogleMapService.cs | 5 ++- .../Google/IGoogleMapService.cs | 3 +- .../Majorsoft.Blazor.Components.Maps.xml | 37 ++++++++++++++++++- .../wwwroot/googleMaps.js | 24 ++++++++++-- .../wwwroot/googleMaps.min.js | 2 +- .../Components/MapsGoogle.razor | 10 ++++- 9 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapRestriction.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor index 52576427..b4f75ebd 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor @@ -37,6 +37,7 @@ mapContainerId: _mapContainerId, backgroundColor: BackgroundColor, controlSize: ControlSize, + restriction: Restriction, mapInitializedCallback: async (mapId) => { WriteDiag($"Google JavaScript API Map initialzied with DIV Id: '{_mapContainerId}'."); @@ -292,6 +293,11 @@ /// This option can only be set when the map is initialized. Use method to set it up. /// [Parameter] public IEnumerable? CustomControls { get; set; } + /// + /// Restrictions for Maps by coordinates SW/NE. + /// This option can only be set when the map is initialized. Use method to set it up. + /// + [Parameter] public GoogleMapRestriction? Restriction { get; set; } private ObservableRangeCollection? _markers; /// diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLngBounds.cs b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLngBounds.cs index 5a71af64..b814ee54 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLngBounds.cs +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLngBounds.cs @@ -5,6 +5,24 @@ /// public class GoogleMapLatLngBounds { + /// + /// Default constructor + /// + public GoogleMapLatLngBounds() + { + } + + /// + /// Initialize object for Map restrictions + /// + /// The south-west corner of this bounds. + /// The north-east corner of this bounds. + public GoogleMapLatLngBounds(GoogleMapLatLng southWest, GoogleMapLatLng northEast) + { + SouthWest = southWest; + NorthEast = northEast; + } + /// /// Computes the center of this LatLngBounds /// Equivalent with `getCenter()` method call but it would require JsInterop. diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapRestriction.cs b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapRestriction.cs new file mode 100644 index 00000000..09a67545 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapRestriction.cs @@ -0,0 +1,18 @@ +namespace Majorsoft.Blazor.Components.Maps.Google +{ + /// + /// Restrictions coordinates for + /// + public class GoogleMapRestriction + { + /// + /// Latitude and Longitude SW/NE corners of the bound. + /// + public GoogleMapLatLngBounds LatLngBounds { get; set; } + + /// + /// Is restriction strict. + /// + public bool StrictBounds { get; set; } + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs index 579965ed..2f65b01d 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs @@ -51,7 +51,8 @@ public async Task InitMapAsync(string apiKey, Func? mapDragStartCallback = null, Func? mapResizedCallback = null, Func? mapTilesLoadedCallback = null, - Func? mapIdleCallback = null) + Func? mapIdleCallback = null, + GoogleMapRestriction restriction = null) { if(MapContainerId == mapContainerId) { @@ -89,7 +90,7 @@ public async Task InitMapAsync(string apiKey, _dotNetObjectReference = DotNetObjectReference.Create(info); - await _mapsJs.InvokeVoidAsync("init", apiKey, mapContainerId, _dotNetObjectReference, backgroundColor, controlSize); + await _mapsJs.InvokeVoidAsync("init", apiKey, mapContainerId, _dotNetObjectReference, backgroundColor, controlSize, restriction); } public async Task SetCenterAsync(double latitude, double longitude) diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs b/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs index 1d69b226..dd58a954 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs +++ b/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs @@ -76,7 +76,8 @@ Task InitMapAsync(string apiKey, Func mapDragStartCallback = null, Func mapResizedCallback = null, Func mapTilesLoadedCallback = null, - Func mapIdleCallback = null); + Func mapIdleCallback = null, + GoogleMapRestriction restriction = null); /// /// Sets the center point as coordinates of the Map. diff --git a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml index fc5e16c5..f78d003f 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml +++ b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml @@ -255,6 +255,18 @@ A LatLngBounds instance represents a rectangle in geographical coordinates, including one that crosses the 180 degrees longitudinal meridian. + + + Default constructor + + + + + Initialize object for Map restrictions + + The south-west corner of this bounds. + The north-east corner of this bounds. + Computes the center of this LatLngBounds @@ -596,6 +608,21 @@ Default scale value is 1; accepted values are 1, 2, and 4. + + + Restrictions coordinates for + + + + + Latitude and Longitude SW/NE corners of the bound. + + + + + Is restriction strict. + + Default implementation of @@ -751,10 +778,10 @@ - HTML Div Id which was set when Maps initialized with method. + HTML Div Id which was set when Maps initialized with method. - + This function must be called only once to initialize Google JavaScript Maps with ApiKey and event callbacks. @@ -937,6 +964,12 @@ This option can only be set when the map is initialized. Use method to set it up. + + + Restrictions for Maps by coordinates SW/NE. + This option can only be set when the map is initialized. Use method to set it up. + + MarkerOptions object used to define the properties that can be set on a Marker. diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js index 76d88e56..afddecb1 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js @@ -1,9 +1,9 @@ -export function init(key, elementId, dotnetRef, backgroundColor, controlSize) { +export function init(key, elementId, dotnetRef, backgroundColor, controlSize, restriction) { if (!key || !elementId || !dotnetRef) { return; } - storeElementIdWithDotnetRef(_mapsElementDict, elementId, dotnetRef, backgroundColor, controlSize); //Store map info + storeElementIdWithDotnetRef(_mapsElementDict, elementId, dotnetRef, backgroundColor, controlSize, restriction); //Store map info let src = "https://maps.googleapis.com/maps/api/js?key="; let scriptsIncluded = false; @@ -50,9 +50,25 @@ window.initGoogleMaps = () => { } //Create Map + let restrict = null; + if (mapInfo.restriction && mapInfo.restriction.latLngBounds + && mapInfo.restriction.latLngBounds.northEast && mapInfo.restriction.latLngBounds.southWest) { + restrict = + { + latLngBounds: { + south: mapInfo.restriction.latLngBounds.southWest.latitude, + west: mapInfo.restriction.latLngBounds.southWest.longitude, + north: mapInfo.restriction.latLngBounds.northEast.latitude, + east: mapInfo.restriction.latLngBounds.northEast.longitude + }, + strictBounds: mapInfo.restriction.strictBounds, + }; + } + let map = new google.maps.Map(document.getElementById(elementId), { backgroundColor: mapInfo.bgColor, controlSize: mapInfo.ctrSize, + restriction: restrict, }); map.elementId = elementId; _mapsElementDict[i].value.map = map; @@ -265,7 +281,7 @@ window.initGoogleMaps = () => { }; //Store elementId with .NET Ref -function storeElementIdWithDotnetRef(dict, elementId, dotnetRef, backgroundColor, controlSize) { +function storeElementIdWithDotnetRef(dict, elementId, dotnetRef, backgroundColor, controlSize, restriction) { let elementFound = false; for (let i = 0; i < dict.length; i++) { if (dict[i].key === elementId) { @@ -276,7 +292,7 @@ function storeElementIdWithDotnetRef(dict, elementId, dotnetRef, backgroundColor if (!elementFound) { dict.push({ key: elementId, - value: { ref: dotnetRef, map: null, bgColor: backgroundColor, ctrSize: controlSize } + value: { ref: dotnetRef, map: null, bgColor: backgroundColor, ctrSize: controlSize, restriction: restriction } }); } } diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js index c6203724..87e87a56 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js @@ -1 +1 @@ -export function init(t,i,r,u,e){if(t&&i&&r){f(n,i,r,u,e);let o="https://maps.googleapis.com/maps/api/js?key=",h=!1,l=document.querySelectorAll("head > script");if(l.forEach(n=>{if(n){let t=n.getAttribute("src");if(t&&t.startsWith(o)){h=!0;return}}}),h){window.google&&window.initGoogleMaps();return}let c=document.createElement("script");c.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(c);o=o+t+"&callback=initGoogleMaps&libraries=&v=weekly";let s=document.createElement("script");s.src=o;s.defer=!0;document.head.appendChild(s)}}function f(n,t,i,r,u){for(let i=0;i{e.invokeMethodAsync("CustomControlClicked",i)})}}}export function createMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{e.ref.invokeMethodAsync("MarkerClicked",n.id),i&&i.open(e.map,t)})}if(n.draggable){t.addListener("drag",()=>{i("MarkerDrag",n.id,t.getPosition().toJSON())});t.addListener("dragend",()=>{i("MarkerDragEnd",n.id,t.getPosition().toJSON())});t.addListener("dragstart",()=>{i("MarkerDragStart",n.id,t.getPosition().toJSON())});function i(n,t,i){let r={Latitude:i.lat,Longitude:i.lng};e.ref.invokeMethodAsync(n,t,r)}}}}}export function removeMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{if(n.id==t.id){t.setMap(null);r.splice(i,1);return}})}}}function o(n,t){t&&n&&(t.setPosition({lat:n.position.latitude,lng:n.position.longitude}),t.anchorPoint=n.anchorPoint?{x:n.anchorPoint.x,y:n.anchorPoint.y}:null,t.setAnimation(n.animation),t.setClickable(n.clickable),t.crossOnDrag=n.crossOnDrag,t.setCursor(n.cursor),t.setDraggable(n.draggable),t.setIcon(n.icon),t.setLabel(n.label),t.setOpacity(n.opacity),t.optimized=n.optimized,t.setShape(n.shape),t.setTitle(n.title),t.setVisible(n.visible),t.setZIndex(n.zIndex))}export function getAddressCoordinates(i,r){u(r,function(r){if(r){let u=t(n,i);u&&u.map&&u.ref.invokeMethodAsync("AddressSearch",r)}})}function u(n,t){let i=new google.maps.Geocoder;i.geocode({address:n},function(n,i){i==google.maps.GeocoderStatus.OK&&t(n)})}export function dispose(i){if(i){let r=t(n,i);r.map=null;r.ref=null;e(n,i)}}window.initGoogleMaps=()=>{for(let i=0;i{u(n,"MapClicked")});r.addListener("dblclick",n=>{u(n,"MapDoubleClicked")});r.addListener("contextmenu",n=>{u(n,"MapContextMenu")});r.addListener("mouseup",n=>{u(n,"MapMouseUp")});r.addListener("mousedown",n=>{u(n,"MapMouseDown")});r.addListener("mousemove",n=>{u(n,"MapMouseMove")});r.addListener("mouseover",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOver")}});r.addListener("mouseout",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOut")}});r.addListener("center_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapCenterChanged",t)}}});r.addListener("zoom_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapZoomChanged",r.getZoom())}});r.addListener("maptypeid_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTypeIdChanged",r.getMapTypeId())}});r.addListener("heading_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapHeadingChanged",r.getHeading())}});r.addListener("tilt_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTiltChanged",r.getTilt())}});r.addListener("bounds_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapBoundsChanged")}});r.addListener("projection_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapProjectionChanged")}});r.addListener("draggable_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapDraggableChanged")}});r.addListener("streetview_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapStreetviewChanged")}});r.addListener("drag",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDrag",t)}}});r.addListener("dragend",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragEnd",t)}}});r.addListener("dragstart",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragStart",t)}}});r.addListener("resize",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i){let n={Width:r.getDiv().offsetWidth,Height:r.getDiv().offsetHeight};i.ref.invokeMethodAsync("MapResized",n)}}});r.addListener("tilesloaded",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTilesLoaded")}});r.addListener("idle",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapIdle")}});n[i].value.ref.invokeMethodAsync("MapInitialized",f)}}};let n=[],r=[]; \ No newline at end of file +export function init(t,i,r,u,e,o){if(t&&i&&r){f(n,i,r,u,e,o);let s="https://maps.googleapis.com/maps/api/js?key=",c=!1,a=document.querySelectorAll("head > script");if(a.forEach(n=>{if(n){let t=n.getAttribute("src");if(t&&t.startsWith(s)){c=!0;return}}}),c){window.google&&window.initGoogleMaps();return}let l=document.createElement("script");l.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(l);s=s+t+"&callback=initGoogleMaps&libraries=&v=weekly";let h=document.createElement("script");h.src=s;h.defer=!0;document.head.appendChild(h)}}function f(n,t,i,r,u,f){for(let i=0;i{e.invokeMethodAsync("CustomControlClicked",i)})}}}export function createMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{e.ref.invokeMethodAsync("MarkerClicked",n.id),i&&i.open(e.map,t)})}if(n.draggable){t.addListener("drag",()=>{i("MarkerDrag",n.id,t.getPosition().toJSON())});t.addListener("dragend",()=>{i("MarkerDragEnd",n.id,t.getPosition().toJSON())});t.addListener("dragstart",()=>{i("MarkerDragStart",n.id,t.getPosition().toJSON())});function i(n,t,i){let r={Latitude:i.lat,Longitude:i.lng};e.ref.invokeMethodAsync(n,t,r)}}}}}export function removeMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{if(n.id==t.id){t.setMap(null);r.splice(i,1);return}})}}}function o(n,t){t&&n&&(t.setPosition({lat:n.position.latitude,lng:n.position.longitude}),t.anchorPoint=n.anchorPoint?{x:n.anchorPoint.x,y:n.anchorPoint.y}:null,t.setAnimation(n.animation),t.setClickable(n.clickable),t.crossOnDrag=n.crossOnDrag,t.setCursor(n.cursor),t.setDraggable(n.draggable),t.setIcon(n.icon),t.setLabel(n.label),t.setOpacity(n.opacity),t.optimized=n.optimized,t.setShape(n.shape),t.setTitle(n.title),t.setVisible(n.visible),t.setZIndex(n.zIndex))}export function getAddressCoordinates(i,r){u(r,function(r){if(r){let u=t(n,i);u&&u.map&&u.ref.invokeMethodAsync("AddressSearch",r)}})}function u(n,t){let i=new google.maps.Geocoder;i.geocode({address:n},function(n,i){i==google.maps.GeocoderStatus.OK&&t(n)})}export function dispose(i){if(i){let r=t(n,i);r.map=null;r.ref=null;e(n,i)}}window.initGoogleMaps=()=>{for(let i=0;i{f(n,"MapClicked")});r.addListener("dblclick",n=>{f(n,"MapDoubleClicked")});r.addListener("contextmenu",n=>{f(n,"MapContextMenu")});r.addListener("mouseup",n=>{f(n,"MapMouseUp")});r.addListener("mousedown",n=>{f(n,"MapMouseDown")});r.addListener("mousemove",n=>{f(n,"MapMouseMove")});r.addListener("mouseover",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOver")}});r.addListener("mouseout",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOut")}});r.addListener("center_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapCenterChanged",t)}}});r.addListener("zoom_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapZoomChanged",r.getZoom())}});r.addListener("maptypeid_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTypeIdChanged",r.getMapTypeId())}});r.addListener("heading_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapHeadingChanged",r.getHeading())}});r.addListener("tilt_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTiltChanged",r.getTilt())}});r.addListener("bounds_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapBoundsChanged")}});r.addListener("projection_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapProjectionChanged")}});r.addListener("draggable_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapDraggableChanged")}});r.addListener("streetview_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapStreetviewChanged")}});r.addListener("drag",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDrag",t)}}});r.addListener("dragend",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragEnd",t)}}});r.addListener("dragstart",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragStart",t)}}});r.addListener("resize",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i){let n={Width:r.getDiv().offsetWidth,Height:r.getDiv().offsetHeight};i.ref.invokeMethodAsync("MapResized",n)}}});r.addListener("tilesloaded",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTilesLoaded")}});r.addListener("idle",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapIdle")}});n[i].value.ref.invokeMethodAsync("MapInitialized",e)}}};let n=[],r=[]; \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor index 011256e3..f3eeb50e 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor @@ -242,7 +242,8 @@

    Maps Pre-render settings: Background color: @_jsMapBackgroundColor - Control size: @(_jsMapControlSize)px + Control size: @(_jsMapControlSize)px
    + Restrict map to New Zealand:

    @@ -255,6 +256,7 @@ Height="@_jsMapHeight" Width="@_jsMapWidth" BackgroundColor="@_jsMapBackgroundColor" + Restriction="@(_jsRestrictMap ? _restriction : null)" ControlSize="@_jsMapControlSize" Center="@_jsMapCenter" AnimateCenterChange="@_jsMapAnimateCenterChange" @@ -501,6 +503,11 @@ private GoogleMap _googleMap; private GeolocationData _jsMapCenter = new GeolocationData("Times Square New York"); private string _jsMapBackgroundColor = "lightblue"; + private GoogleMapRestriction _restriction = new GoogleMapRestriction() + { + LatLngBounds = new GoogleMapLatLngBounds(new GoogleMapLatLng() { Latitude = -47.35, Longitude = 166.28 }, + new GoogleMapLatLng() { Latitude = -34.36, Longitude = -175.81 }) + }; private int _jsMapControlSize = 38; private byte _jsMapZoomLevel = 10; private int _jsMapWidth = 450; @@ -526,6 +533,7 @@ private bool _jsScaleControl = true; private bool _jsStreetViewControl = true; private bool _jsZoomControl = true; + private bool _jsRestrictMap = false; private List _jsCustomControls = new List(); private ObservableRangeCollection _jsMarkers = new ObservableRangeCollection(); From a75b7b01b0e904940978f0cb8a5afaf2bed4f5ba Mon Sep 17 00:00:00 2001 From: Major Date: Sat, 1 Jan 2022 18:00:22 +0100 Subject: [PATCH 14/19] Added comments for maps restrictions. --- .github/docs/Maps.md | 2 +- .../Google/GoogleMap.razor | 4 +++- .../Google/GoogleMapRestriction.cs | 2 ++ .../Majorsoft.Blazor.Components.Maps.xml | 2 ++ .../Components/MapsGoogle.razor | 23 +++++++------------ 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.github/docs/Maps.md b/.github/docs/Maps.md index 08877166..e6f50038 100644 --- a/.github/docs/Maps.md +++ b/.github/docs/Maps.md @@ -34,6 +34,7 @@ Maps using `IGeolocationService` (see "Dependences") to center current position. It can be omitted and injected separately to your components as well to get or track device location. To see how it works please check **Geo JS** [documentation](https://github.com/majorimi/blazor-components/blob/master/.github/docs/JsInterop.md#geolocation-js-see-demo-app) and [demo](https://blazorextensions.z6.web.core.windows.net/jsinterop#geo-js). + ## `GoogleStaticMap` component (See: [demo app](https://blazorextensions.z6.web.core.windows.net/maps#google-static-maps)) :warning: **To use Google Maps Platform, you must have a billing account. The billing account is used to track costs associated with your projects.** @@ -98,7 +99,6 @@ Once operation has finished successfully `OnLocationDetected` event will be fire - **`DisposeAsync()`: `Task DisposeAsync()`**
    Component implements `IAsyncDisposable` interface Blazor framework will call it when parent removed from render tree. - ## `GoogleMap` component (See: [demo app](https://blazorextensions.z6.web.core.windows.net/maps#google-js-maps)) :warning: **To use Google Maps Platform, you must have a billing account. The billing account is used to track costs associated with your projects.** diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor index b4f75ebd..9321c715 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor @@ -137,7 +137,7 @@ }, mapZoomChangedCallback: async (zoom) => { - WriteDiag($"Map Zoom level changed to: '{zoom}'."); + WriteDiag($"Map Zoom level callback changed to: '{zoom}'."); if (_zoomLevel == zoom) return; @@ -336,6 +336,8 @@ if (_mapInitialized && value != _zoomLevel) { _zoomLevel = value; + + WriteDiag($"Map Zoom level property Set to: '{_zoomLevel}'."); InvokeAsync(async () => await _mapService.SetZoomAsync(_zoomLevel)); } } diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapRestriction.cs b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapRestriction.cs index 09a67545..20e48380 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapRestriction.cs +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapRestriction.cs @@ -2,6 +2,8 @@ { /// /// Restrictions coordinates for + /// NOTE: Google Maps restriction is basically a MAX Zoom level. So it does not allow users to zoom out (zoom level value forced). + /// In order to notify Blazor about the maximum Zoom level two-way binding MUST be used: `@bind-Zoom="_jsMapZoomLevel" @bind-Zoom:event="OnMapZoomLevelChanged"` /// public class GoogleMapRestriction { diff --git a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml index f78d003f..125773b8 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml +++ b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml @@ -611,6 +611,8 @@ Restrictions coordinates for + NOTE: Google Maps restriction is basically a MAX Zoom level. So it does not allow users to zoom out (zoom level value forced). + In order to notify Blazor about the maximum Zoom level two-way binding MUST be used: `@bind-Zoom="_jsMapZoomLevel" @bind-Zoom:event="OnMapZoomLevelChanged"` diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor index f3eeb50e..ae61e9ba 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor @@ -241,9 +241,12 @@

    - Maps Pre-render settings: Background color: @_jsMapBackgroundColor - Control size: @(_jsMapControlSize)px
    - Restrict map to New Zealand: +

    Maps Pre-render settings:
    +
      +
    • Background color: @_jsMapBackgroundColor
    • +
    • Control size: @(_jsMapControlSize)px
    • +
    • Restrict map to New Zealand:
    • +

    @@ -260,7 +263,7 @@ ControlSize="@_jsMapControlSize" Center="@_jsMapCenter" AnimateCenterChange="@_jsMapAnimateCenterChange" - Zoom="@_jsMapZoomLevel" + @bind-Zoom="_jsMapZoomLevel" @bind-Zoom:event="OnMapZoomLevelChanged" ZoomControl="@_jsZoomControl" ZoomControlOptionsPosition="GoogleMapControlPositions.RIGHT_BOTTOM" MaxZoom="null" @@ -298,7 +301,6 @@ OnMapMouseOver="@OnMapMouseOver" OnMapMouseOut="@OnMapMouseOut" OnMapCenterChanged="@OnMapCenterChanged" - OnMapZoomLevelChanged="@OnMapZoomLevelChanged" OnMapTypeChanged="@OnMapTypeChanged" OnMapHeadingChanged="@OnMapHeadingChanged" OnMapTiltChanged="@OnMapTiltChanged" @@ -533,7 +535,7 @@ private bool _jsScaleControl = true; private bool _jsStreetViewControl = true; private bool _jsZoomControl = true; - private bool _jsRestrictMap = false; + private bool _jsRestrictMap = false; //must be set before Init... private List _jsCustomControls = new List(); private ObservableRangeCollection _jsMarkers = new ObservableRangeCollection(); @@ -709,15 +711,6 @@ //Center point can be accessed as well with method call var center = await _googleMap.GoogleMapService.GetCenterAsync(); } - private async Task OnMapZoomLevelChanged(byte zoom) - { - //Can be used with Binding and custom event: @bind-Zoom="_jsMapZoomLevel" @bind-Zoom:event="OnMapZoomLevelChanged" - if (_logOtherEvents) - { - _mapsLog = await WriteLog(_mapsLog, $"Map Zoom level changed to: {zoom}"); - } - _jsMapZoomLevel = zoom; //Two-way binding with event value - } private async Task OnMapTypeChanged(GoogleMapTypes googleMapTypes) { //Can be used with Binding and custom event: @bind-MapType="_jsMapType" @bind-MapType:event="OnMapTypeChanged" From 41bc0957d48835867fd84f4dea52dea418597b6b Mon Sep 17 00:00:00 2001 From: Major Date: Sat, 22 Jan 2022 22:08:32 +0100 Subject: [PATCH 15/19] Added GoogleMapPolylineOptions --- .../Google/GoogleMapPolylineOptions.cs | 184 ++++++++++++++++++ .../Google/GoogleMapService.cs | 6 + .../Google/IGoogleMapService.cs | 7 + .../Majorsoft.Blazor.Components.Maps.xml | 165 ++++++++++++++++ .../wwwroot/googleMaps.js | 21 ++ .../wwwroot/googleMaps.min.js | 2 +- 6 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapPolylineOptions.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapPolylineOptions.cs b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapPolylineOptions.cs new file mode 100644 index 00000000..3af23eb6 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapPolylineOptions.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Majorsoft.Blazor.Components.Maps.Google +{ + /// + /// PolylineOptions object used to define the properties that can be set on a Polyline. + /// https://developers.google.com/maps/documentation/javascript/reference/polygon#Polyline + /// + public class GoogleMapPolylineOptions + { + public Guid Id { get; } + + /// + /// Indicates whether this Polyline handles mouse events. Defaults to true. + /// + public bool Clickable { get; set; } + + /// + /// If set to true, the user can drag this shape over the map. The geodesic property defines the mode of dragging. Defaults to false. + /// + public bool Draggable { get; set; } + + /// + /// If set to true, the user can edit this shape by dragging the control points shown at the vertices and on each segment. Defaults to false. + /// + public bool Editable { get; set; } + + /// + /// When true, edges of the polygon are interpreted as geodesic and will follow the curvature of the Earth. When false, edges of the polygon are rendered as + /// straight lines in screen space. Note that the shape of a geodesic polygon may appear to change when dragged, as the dimensions are maintained relative to + /// the surface of the earth. Defaults to false. + /// + public bool Geodesic { get; set; } + + /// + /// The icons to be rendered along the polyline. + /// + public GoogleMapIconSequence[] Icons { get; set; } + + /// + /// The ordered sequence of coordinates of the Polyline. + /// + public GoogleMapLatLng Path { get; set; } + + /// + /// The stroke color. All CSS3 colors are supported except for extended named colors. + /// + public string StrokeColor { get; set; } + + /// + /// The stroke opacity between 0.0 and 1.0. + /// + public double StrokeOpacity { get; set; } + + /// + /// The stroke width in pixels. + /// + public double StrokeWeight { get; set; } + + /// + /// Whether this polyline is visible on the map. Defaults to true. + /// + public bool Visible { get; set; } + + /// + /// The zIndex compared to other polys. + /// + public int ZIndex { get; set; } + + public GoogleMapPolylineOptions() + { + Id = Guid.NewGuid(); + } + } + + /// + /// Describes how icons are to be rendered on a line. + /// + public class GoogleMapIconSequence + { + /// + /// If true, each icon in the sequence has the same fixed rotation regardless of the angle of the edge on which it lies. Defaults to false, + /// in which case each icon in the sequence is rotated to align with its edge. + /// + public bool FixedRotation { get; set; } + + /// + /// The icon to render on the line. + /// + public GoogleMapIconSequenceSymbol Icon { get; set; } + + /// + /// The distance from the start of the line at which an icon is to be rendered. This distance may be expressed as a percentage of line's length + /// (e.g. '50%') or in pixels (e.g. '50px'). Defaults to '100%'. + /// + public string Offset { get; set; } + + /// + /// The distance between consecutive icons on the line. This distance may be expressed as a percentage of the line's length (e.g. '50%') or in pixels + /// (e.g. '50px'). To disable repeating of the icon, specify '0'. Defaults to '0'. + /// + public string Repeat { get; set; } + } + + /// + /// Describes a symbol, which consists of a vector path with styling. A symbol can be used as the icon of a marker, or placed on a polyline. + /// + public class GoogleMapIconSequenceSymbol + { + /// + /// The symbol's path, which is a built-in symbol path, or a custom path expressed using SVG path notation. Required. + /// + public string Path { get; set; } + + /// + /// The position at which to anchor an image in correspondence to the location of the marker on the map. + /// By default, the anchor is located along the center point of the bottom of the image. + /// + public Point? Anchor { get; set; } + + /// + /// The symbol's fill color. All CSS3 colors are supported except for extended named colors. For symbol markers, this defaults to 'black'. + /// For symbols on polylines, this defaults to the stroke color of the corresponding polyline. + /// + public string FillColor { get; set; } + + /// + /// The symbol's fill opacity. Defaults to 0. + /// + public double FillOpacity { get; set; } + + /// + /// The origin of the label relative to the top-left corner of the icon image, if a label is supplied by the marker. + /// By default, the origin is located in the center point of the image. + /// + public Point? LabelOrigin { get; set; } + + /// + /// The angle by which to rotate the symbol, expressed clockwise in degrees. Defaults to 0. A symbol in an IconSequence where fixedRotation is false + /// is rotated relative to the angle of the edge on which it lies. + /// + public double Rotation { get; set; } + + /// + /// The amount by which the symbol is scaled in size. For symbol markers, this defaults to 1; after scaling, the symbol may be of any size. For symbols on a polyline, + /// this defaults to the stroke weight of the polyline; after scaling, the symbol must lie inside a square 22 pixels in size centered at the symbol's anchor. + /// + public double Scale { get; set; } + + /// + /// The symbol's stroke color. All CSS3 colors are supported except for extended named colors. For symbol markers, this defaults to 'black'. + /// For symbols on a polyline, this defaults to the stroke color of the polyline. + /// + public string StrokeColor { get; set; } + + /// + /// The symbol's stroke opacity. For symbol markers, this defaults to 1. For symbols on a polyline, this defaults to the stroke opacity of the polyline. + /// + public double StrokeOpacity { get; set; } + + /// + /// The symbol's stroke weight. Defaults to the scale of the symbol. + /// + public double StrokeWeight { get; set; } + + /// + /// Default constructor. + /// + /// The symbol's path + public GoogleMapIconSequenceSymbol(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException($"'{nameof(path)}' cannot be null or whitespace", nameof(path)); + } + + Path = path; + } + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs index 2f65b01d..43bd33c2 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs @@ -255,6 +255,12 @@ public async ValueTask GetDivAsync() return await _mapsJs.InvokeAsync("getDiv", MapContainerId); } + public async Task AddPolyline(params GoogleMapPolylineOptions[] googleMapPolylineOptions) + { + await CheckJsObjectAsync(); + await _mapsJs.InvokeAsync("polylineSetMap", MapContainerId, googleMapPolylineOptions); + } + private async Task CheckJsObjectAsync() { diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs b/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs index dd58a954..b8926a9f 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs +++ b/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs @@ -195,5 +195,12 @@ Task InitMapAsync(string apiKey, ///
    /// Async task ValueTask GetDivAsync(); + + /// + /// + /// + /// + /// + Task AddPolyline(params GoogleMapPolylineOptions[] googleMapPolylineOptions); } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml index 125773b8..b5aac774 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml +++ b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml @@ -608,6 +608,164 @@ Default scale value is 1; accepted values are 1, 2, and 4.
    + + + PolylineOptions object used to define the properties that can be set on a Polyline. + https://developers.google.com/maps/documentation/javascript/reference/polygon#Polyline + + + + + Indicates whether this Polyline handles mouse events. Defaults to true. + + + + + If set to true, the user can drag this shape over the map. The geodesic property defines the mode of dragging. Defaults to false. + + + + + If set to true, the user can edit this shape by dragging the control points shown at the vertices and on each segment. Defaults to false. + + + + + When true, edges of the polygon are interpreted as geodesic and will follow the curvature of the Earth. When false, edges of the polygon are rendered as + straight lines in screen space. Note that the shape of a geodesic polygon may appear to change when dragged, as the dimensions are maintained relative to + the surface of the earth. Defaults to false. + + + + + The icons to be rendered along the polyline. + + + + + The ordered sequence of coordinates of the Polyline. + + + + + The stroke color. All CSS3 colors are supported except for extended named colors. + + + + + The stroke opacity between 0.0 and 1.0. + + + + + The stroke width in pixels. + + + + + Whether this polyline is visible on the map. Defaults to true. + + + + + The zIndex compared to other polys. + + + + + Describes how icons are to be rendered on a line. + + + + + If true, each icon in the sequence has the same fixed rotation regardless of the angle of the edge on which it lies. Defaults to false, + in which case each icon in the sequence is rotated to align with its edge. + + + + + The icon to render on the line. + + + + + The distance from the start of the line at which an icon is to be rendered. This distance may be expressed as a percentage of line's length + (e.g. '50%') or in pixels (e.g. '50px'). Defaults to '100%'. + + + + + The distance between consecutive icons on the line. This distance may be expressed as a percentage of the line's length (e.g. '50%') or in pixels + (e.g. '50px'). To disable repeating of the icon, specify '0'. Defaults to '0'. + + + + + Describes a symbol, which consists of a vector path with styling. A symbol can be used as the icon of a marker, or placed on a polyline. + + + + + The symbol's path, which is a built-in symbol path, or a custom path expressed using SVG path notation. Required. + + + + + The position at which to anchor an image in correspondence to the location of the marker on the map. + By default, the anchor is located along the center point of the bottom of the image. + + + + + The symbol's fill color. All CSS3 colors are supported except for extended named colors. For symbol markers, this defaults to 'black'. + For symbols on polylines, this defaults to the stroke color of the corresponding polyline. + + + + + The symbol's fill opacity. Defaults to 0. + + + + + The origin of the label relative to the top-left corner of the icon image, if a label is supplied by the marker. + By default, the origin is located in the center point of the image. + + + + + The angle by which to rotate the symbol, expressed clockwise in degrees. Defaults to 0. A symbol in an IconSequence where fixedRotation is false + is rotated relative to the angle of the edge on which it lies. + + + + + The amount by which the symbol is scaled in size. For symbol markers, this defaults to 1; after scaling, the symbol may be of any size. For symbols on a polyline, + this defaults to the stroke weight of the polyline; after scaling, the symbol must lie inside a square 22 pixels in size centered at the symbol's anchor. + + + + + The symbol's stroke color. All CSS3 colors are supported except for extended named colors. For symbol markers, this defaults to 'black'. + For symbols on a polyline, this defaults to the stroke color of the polyline. + + + + + The symbol's stroke opacity. For symbol markers, this defaults to 1. For symbols on a polyline, this defaults to the stroke opacity of the polyline. + + + + + The symbol's stroke weight. Defaults to the scale of the symbol. + + + + + Default constructor. + + The symbol's path + Restrictions coordinates for @@ -934,6 +1092,13 @@ Async task + + + + + + + Map HTML container Id. It can be used when multiple Maps added to one page. diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js index afddecb1..f7718763 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js @@ -613,6 +613,27 @@ function setMarkerData(markerData, marker) { marker.setZIndex(markerData.zIndex); } +//Drawing +export function polylineSetMap(elementId, polylineOptions) { + if (elementId && polylineOptions && polylineOptions.length) { + let mapWithDotnetRef = getElementIdWithDotnetRef(_mapsElementDict, elementId); + if (mapWithDotnetRef && mapWithDotnetRef.map) { + + for (var i = 0; i < polylineOptions.length; i++) { + let markerData = polylineOptions[i]; + + //_mapsMarkers.forEach((element, index) => { + // if (markerData.id == element.id) { + // element.setMap(null); + // _mapsMarkers.splice(index, 1); + // return; + // } + //}); + } + } + } +} + //Google GeoCoder export function getAddressCoordinates(elementId, address) { geocodeAddress(address, function (results) { diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js index 87e87a56..9a6eddf7 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js @@ -1 +1 @@ -export function init(t,i,r,u,e,o){if(t&&i&&r){f(n,i,r,u,e,o);let s="https://maps.googleapis.com/maps/api/js?key=",c=!1,a=document.querySelectorAll("head > script");if(a.forEach(n=>{if(n){let t=n.getAttribute("src");if(t&&t.startsWith(s)){c=!0;return}}}),c){window.google&&window.initGoogleMaps();return}let l=document.createElement("script");l.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(l);s=s+t+"&callback=initGoogleMaps&libraries=&v=weekly";let h=document.createElement("script");h.src=s;h.defer=!0;document.head.appendChild(h)}}function f(n,t,i,r,u,f){for(let i=0;i{e.invokeMethodAsync("CustomControlClicked",i)})}}}export function createMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{e.ref.invokeMethodAsync("MarkerClicked",n.id),i&&i.open(e.map,t)})}if(n.draggable){t.addListener("drag",()=>{i("MarkerDrag",n.id,t.getPosition().toJSON())});t.addListener("dragend",()=>{i("MarkerDragEnd",n.id,t.getPosition().toJSON())});t.addListener("dragstart",()=>{i("MarkerDragStart",n.id,t.getPosition().toJSON())});function i(n,t,i){let r={Latitude:i.lat,Longitude:i.lng};e.ref.invokeMethodAsync(n,t,r)}}}}}export function removeMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{if(n.id==t.id){t.setMap(null);r.splice(i,1);return}})}}}function o(n,t){t&&n&&(t.setPosition({lat:n.position.latitude,lng:n.position.longitude}),t.anchorPoint=n.anchorPoint?{x:n.anchorPoint.x,y:n.anchorPoint.y}:null,t.setAnimation(n.animation),t.setClickable(n.clickable),t.crossOnDrag=n.crossOnDrag,t.setCursor(n.cursor),t.setDraggable(n.draggable),t.setIcon(n.icon),t.setLabel(n.label),t.setOpacity(n.opacity),t.optimized=n.optimized,t.setShape(n.shape),t.setTitle(n.title),t.setVisible(n.visible),t.setZIndex(n.zIndex))}export function getAddressCoordinates(i,r){u(r,function(r){if(r){let u=t(n,i);u&&u.map&&u.ref.invokeMethodAsync("AddressSearch",r)}})}function u(n,t){let i=new google.maps.Geocoder;i.geocode({address:n},function(n,i){i==google.maps.GeocoderStatus.OK&&t(n)})}export function dispose(i){if(i){let r=t(n,i);r.map=null;r.ref=null;e(n,i)}}window.initGoogleMaps=()=>{for(let i=0;i{f(n,"MapClicked")});r.addListener("dblclick",n=>{f(n,"MapDoubleClicked")});r.addListener("contextmenu",n=>{f(n,"MapContextMenu")});r.addListener("mouseup",n=>{f(n,"MapMouseUp")});r.addListener("mousedown",n=>{f(n,"MapMouseDown")});r.addListener("mousemove",n=>{f(n,"MapMouseMove")});r.addListener("mouseover",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOver")}});r.addListener("mouseout",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOut")}});r.addListener("center_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapCenterChanged",t)}}});r.addListener("zoom_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapZoomChanged",r.getZoom())}});r.addListener("maptypeid_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTypeIdChanged",r.getMapTypeId())}});r.addListener("heading_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapHeadingChanged",r.getHeading())}});r.addListener("tilt_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTiltChanged",r.getTilt())}});r.addListener("bounds_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapBoundsChanged")}});r.addListener("projection_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapProjectionChanged")}});r.addListener("draggable_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapDraggableChanged")}});r.addListener("streetview_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapStreetviewChanged")}});r.addListener("drag",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDrag",t)}}});r.addListener("dragend",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragEnd",t)}}});r.addListener("dragstart",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragStart",t)}}});r.addListener("resize",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i){let n={Width:r.getDiv().offsetWidth,Height:r.getDiv().offsetHeight};i.ref.invokeMethodAsync("MapResized",n)}}});r.addListener("tilesloaded",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTilesLoaded")}});r.addListener("idle",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapIdle")}});n[i].value.ref.invokeMethodAsync("MapInitialized",e)}}};let n=[],r=[]; \ No newline at end of file +export function init(t,i,r,u,e,o){if(t&&i&&r){f(n,i,r,u,e,o);let s="https://maps.googleapis.com/maps/api/js?key=",c=!1,a=document.querySelectorAll("head > script");if(a.forEach(n=>{if(n){let t=n.getAttribute("src");if(t&&t.startsWith(s)){c=!0;return}}}),c){window.google&&window.initGoogleMaps();return}let l=document.createElement("script");l.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(l);s=s+t+"&callback=initGoogleMaps&libraries=&v=weekly";let h=document.createElement("script");h.src=s;h.defer=!0;document.head.appendChild(h)}}function f(n,t,i,r,u,f){for(let i=0;i{e.invokeMethodAsync("CustomControlClicked",i)})}}}export function createMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{e.ref.invokeMethodAsync("MarkerClicked",n.id),i&&i.open(e.map,t)})}if(n.draggable){t.addListener("drag",()=>{i("MarkerDrag",n.id,t.getPosition().toJSON())});t.addListener("dragend",()=>{i("MarkerDragEnd",n.id,t.getPosition().toJSON())});t.addListener("dragstart",()=>{i("MarkerDragStart",n.id,t.getPosition().toJSON())});function i(n,t,i){let r={Latitude:i.lat,Longitude:i.lng};e.ref.invokeMethodAsync(n,t,r)}}}}}export function removeMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{if(n.id==t.id){t.setMap(null);r.splice(i,1);return}})}}}function o(n,t){t&&n&&(t.setPosition({lat:n.position.latitude,lng:n.position.longitude}),t.anchorPoint=n.anchorPoint?{x:n.anchorPoint.x,y:n.anchorPoint.y}:null,t.setAnimation(n.animation),t.setClickable(n.clickable),t.crossOnDrag=n.crossOnDrag,t.setCursor(n.cursor),t.setDraggable(n.draggable),t.setIcon(n.icon),t.setLabel(n.label),t.setOpacity(n.opacity),t.optimized=n.optimized,t.setShape(n.shape),t.setTitle(n.title),t.setVisible(n.visible),t.setZIndex(n.zIndex))}export function polylineSetMap(i,r){if(i&&r&&r.length){let f=t(n,i);if(f&&f.map)for(var u=0;u{for(let i=0;i{f(n,"MapClicked")});r.addListener("dblclick",n=>{f(n,"MapDoubleClicked")});r.addListener("contextmenu",n=>{f(n,"MapContextMenu")});r.addListener("mouseup",n=>{f(n,"MapMouseUp")});r.addListener("mousedown",n=>{f(n,"MapMouseDown")});r.addListener("mousemove",n=>{f(n,"MapMouseMove")});r.addListener("mouseover",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOver")}});r.addListener("mouseout",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOut")}});r.addListener("center_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapCenterChanged",t)}}});r.addListener("zoom_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapZoomChanged",r.getZoom())}});r.addListener("maptypeid_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTypeIdChanged",r.getMapTypeId())}});r.addListener("heading_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapHeadingChanged",r.getHeading())}});r.addListener("tilt_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTiltChanged",r.getTilt())}});r.addListener("bounds_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapBoundsChanged")}});r.addListener("projection_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapProjectionChanged")}});r.addListener("draggable_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapDraggableChanged")}});r.addListener("streetview_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapStreetviewChanged")}});r.addListener("drag",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDrag",t)}}});r.addListener("dragend",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragEnd",t)}}});r.addListener("dragstart",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragStart",t)}}});r.addListener("resize",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i){let n={Width:r.getDiv().offsetWidth,Height:r.getDiv().offsetHeight};i.ref.invokeMethodAsync("MapResized",n)}}});r.addListener("tilesloaded",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTilesLoaded")}});r.addListener("idle",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapIdle")}});n[i].value.ref.invokeMethodAsync("MapInitialized",e)}}};let n=[],r=[]; \ No newline at end of file From 47ce2dd3b4f03894adb1f16bdb9a9ae4f027628a Mon Sep 17 00:00:00 2001 From: Major Date: Sun, 23 Jan 2022 18:06:02 +0100 Subject: [PATCH 16/19] Code subfoldering --- .../GoogleMapPolylineOptions.cs | 0 .../Google/{ => Markers}/GoogleMapMarker.cs | 0 .../GoogleMapMarkerAnimationTypes.cs | 0 .../{ => Markers}/GoogleMapMarkerBase.cs | 0 .../GoogleMapMarkerCustomIcon.cs | 0 .../GoogleMapMarkerCustomIconAnchors.cs | 0 .../{ => Markers}/GoogleMapMarkerLabel.cs | 0 .../{ => Markers}/GoogleMapMarkerLabelIcon.cs | 0 .../{ => Markers}/GoogleMapMarkerShape.cs | 0 .../GoogleMapMarkerShapeTypes.cs | 0 .../{ => Markers}/GoogleMapMarkerSizes.cs | 0 .../{ => Markers}/GoogleMapMarkerStyle.cs | 0 .../Majorsoft.Blazor.Components.Maps.xml | 942 +++++++++--------- 13 files changed, 471 insertions(+), 471 deletions(-) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Drawings}/GoogleMapPolylineOptions.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarker.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarkerAnimationTypes.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarkerBase.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarkerCustomIcon.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarkerCustomIconAnchors.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarkerLabel.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarkerLabelIcon.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarkerShape.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarkerShapeTypes.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarkerSizes.cs (100%) rename src/Majorsoft.Blazor.Components.Maps/Google/{ => Markers}/GoogleMapMarkerStyle.cs (100%) diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapPolylineOptions.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Drawings/GoogleMapPolylineOptions.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapPolylineOptions.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Drawings/GoogleMapPolylineOptions.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarker.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarker.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarker.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarker.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerAnimationTypes.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerAnimationTypes.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerAnimationTypes.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerAnimationTypes.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerBase.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerBase.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerBase.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerBase.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerCustomIcon.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerCustomIcon.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerCustomIcon.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerCustomIcon.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerCustomIconAnchors.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerCustomIconAnchors.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerCustomIconAnchors.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerCustomIconAnchors.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerLabel.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerLabel.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerLabel.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerLabel.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerLabelIcon.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerLabelIcon.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerLabelIcon.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerLabelIcon.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerShape.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerShape.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerShape.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerShape.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerShapeTypes.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerShapeTypes.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerShapeTypes.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerShapeTypes.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerSizes.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerSizes.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerSizes.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerSizes.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerStyle.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerStyle.cs similarity index 100% rename from src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapMarkerStyle.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/Markers/GoogleMapMarkerStyle.cs diff --git a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml index b5aac774..443e445e 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml +++ b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml @@ -64,6 +64,164 @@ For example, a value of -190 will be converted to 170. A value of 190 will be converted to -170. This reflects the fact that longitudes wrap around the globe. + + + PolylineOptions object used to define the properties that can be set on a Polyline. + https://developers.google.com/maps/documentation/javascript/reference/polygon#Polyline + + + + + Indicates whether this Polyline handles mouse events. Defaults to true. + + + + + If set to true, the user can drag this shape over the map. The geodesic property defines the mode of dragging. Defaults to false. + + + + + If set to true, the user can edit this shape by dragging the control points shown at the vertices and on each segment. Defaults to false. + + + + + When true, edges of the polygon are interpreted as geodesic and will follow the curvature of the Earth. When false, edges of the polygon are rendered as + straight lines in screen space. Note that the shape of a geodesic polygon may appear to change when dragged, as the dimensions are maintained relative to + the surface of the earth. Defaults to false. + + + + + The icons to be rendered along the polyline. + + + + + The ordered sequence of coordinates of the Polyline. + + + + + The stroke color. All CSS3 colors are supported except for extended named colors. + + + + + The stroke opacity between 0.0 and 1.0. + + + + + The stroke width in pixels. + + + + + Whether this polyline is visible on the map. Defaults to true. + + + + + The zIndex compared to other polys. + + + + + Describes how icons are to be rendered on a line. + + + + + If true, each icon in the sequence has the same fixed rotation regardless of the angle of the edge on which it lies. Defaults to false, + in which case each icon in the sequence is rotated to align with its edge. + + + + + The icon to render on the line. + + + + + The distance from the start of the line at which an icon is to be rendered. This distance may be expressed as a percentage of line's length + (e.g. '50%') or in pixels (e.g. '50px'). Defaults to '100%'. + + + + + The distance between consecutive icons on the line. This distance may be expressed as a percentage of the line's length (e.g. '50%') or in pixels + (e.g. '50px'). To disable repeating of the icon, specify '0'. Defaults to '0'. + + + + + Describes a symbol, which consists of a vector path with styling. A symbol can be used as the icon of a marker, or placed on a polyline. + + + + + The symbol's path, which is a built-in symbol path, or a custom path expressed using SVG path notation. Required. + + + + + The position at which to anchor an image in correspondence to the location of the marker on the map. + By default, the anchor is located along the center point of the bottom of the image. + + + + + The symbol's fill color. All CSS3 colors are supported except for extended named colors. For symbol markers, this defaults to 'black'. + For symbols on polylines, this defaults to the stroke color of the corresponding polyline. + + + + + The symbol's fill opacity. Defaults to 0. + + + + + The origin of the label relative to the top-left corner of the icon image, if a label is supplied by the marker. + By default, the origin is located in the center point of the image. + + + + + The angle by which to rotate the symbol, expressed clockwise in degrees. Defaults to 0. A symbol in an IconSequence where fixedRotation is false + is rotated relative to the angle of the edge on which it lies. + + + + + The amount by which the symbol is scaled in size. For symbol markers, this defaults to 1; after scaling, the symbol may be of any size. For symbols on a polyline, + this defaults to the stroke weight of the polyline; after scaling, the symbol must lie inside a square 22 pixels in size centered at the symbol's anchor. + + + + + The symbol's stroke color. All CSS3 colors are supported except for extended named colors. For symbol markers, this defaults to 'black'. + For symbols on a polyline, this defaults to the stroke color of the polyline. + + + + + The symbol's stroke opacity. For symbol markers, this defaults to 1. For symbols on a polyline, this defaults to the stroke opacity of the polyline. + + + + + The symbol's stroke weight. Defaults to the scale of the symbol. + + + + + Default constructor. + + The symbol's path + Identifiers used to specify the placement of controls on the map. @@ -297,807 +455,649 @@ Equivalent with `isEmpty()` method call but it would require JsInterop. - + - MarkerOptions object used to define the properties that can be set on a Marker with event callbacks. + Restrictions coordinates for + NOTE: Google Maps restriction is basically a MAX Zoom level. So it does not allow users to zoom out (zoom level value forced). + In order to notify Blazor about the maximum Zoom level two-way binding MUST be used: `@bind-Zoom="_jsMapZoomLevel" @bind-Zoom:event="OnMapZoomLevelChanged"` - + - Callback function called when Marker was clicked. + Latitude and Longitude SW/NE corners of the bound. - + - Callback function called when Marker dragging. + Is restriction strict. - + - Callback function called when Marker drag ended. + Default implementation of - + - Callback function called when Marker drag started. + Options for the rendering of the map type control. - + - Default constructor. + IDs of map types to show in the control. - Marker position on the Map - + - Animations that can be played on a marker. + Position id. Used to specify the position of the control on the map. The default position is TOP_RIGHT. - + - No animation. + Style id. Used to select what style of map type control to display. - + - Marker bounces until animation is stopped. + Default constructor - + - Marker falls from the top of the map ending with a small bounce. + Clear list. - + - MarkerOptions object used to define the properties that can be set on a Marker. + Resets the list of Map types with the given params array. + Required map types to use - + - Id of the Custom control. + Identifiers for common MapTypesControls. - + - The offset from the marker's position to the tip of an InfoWindow that has been opened with the marker as anchor. + When the DEFAULT control is shown, it will vary according to window size and other factors. + The DEFAULT control may change in future versions of the API. - + - Which animation to play when marker is added to a map. + The standard horizontal radio buttons bar. - + - If true, the marker receives mouse and touch events. Default value is true. + A dropdown menu for the screen realestate conscious. - + - If false, disables cross that appears beneath the marker when dragging. This option is true by default. + The MapTypeStyle is a collection of selectors and stylers that define how the map should be styled. + Selectors specify the map features and/or elements that should be affected, and stylers specify how those + features and elements should be modified. - + - Mouse cursor to show on hover. + The style rules to apply to the selected map features and elements. + The rules are applied in the order that you specify in this array. - + - If true, the marker can be dragged. Default value is false. + The element to which a styler should be applied. An element is a visual aspect of a feature on the map. + Example: a label, an icon, the stroke or fill applied to the geometry, and more. Optional. + If elementType is not specified, the value is assumed to be 'all'. - + - Adds a label to the marker. The label can either be a string, or a MarkerLabel object. + The feature, or group of features, to which a styler should be applied. Optional. + If featureType is not specified, the value is assumed to be 'all'. - + - Icon for the foreground. If a string is provided, it is treated as though it were an Icon with the string as url. + The Maps Static API creates maps in several formats - + - The marker's opacity between 0.0 and 1.0. + (default) specifies a standard roadmap image, as is normally shown on the Google Maps website. - + - Optimization renders many markers as a single static element. Optimized rendering is enabled by default. - Disable optimized rendering for animated GIFs or PNGs, or when each marker must be rendered as a separate DOM element (advanced usage only). + Specifies satellite image. - + - Marker position. Required in order to display the marker. + Specifies a physical relief map image, showing terrain and vegetation. - + - Image map region definition used for drag/click. + Specifies a hybrid of the satellite and roadmap image, showing a transparent layer of major streets and place names on the satellite image. - + - Rollover text. If provided, an accessibility text (e.g. for use with screen readers) - will be added to the marker with the provided value. + Google maps images may be returned in several common web graphics formats - + - If true, the marker is visible. + The markers parameter defines a set of one or more markers (map pins) at a set of locations. - + - All markers are displayed on the map in order of their zIndex, with higher values displaying in front of markers with lower values. + Set of marker style descriptors. - + - Info Window to show for Marker on click. + Override default markers and with your own custom icons instead. - + - Default constructor. + Each marker descriptor must contain a set of one or more locations defining where to place the marker on the map. - Marker position on the Map - + - Rather than use Google's marker icons, you are free to use your own custom icons instead. + Checks if any marker style was defined in or properties. - + - Custom icon URL. + Default constructor - + - Icon is position in relation to the specified markers locations. + Injectable service to handle Google JavaScript Maps functionalities. Available on the instance of object ref as well. - + - The anchor point sets how the icon is placed in relation to the specified markers locations. + HTML Div Id which was set when Maps initialized with method. - + - These options specify the appearance of a marker label. - A marker label is a string (often a single character) which will appear inside the marker. + This function must be called only once to initialize Google JavaScript Maps with ApiKey and event callbacks. + Google API Key which has permission for Google JavaScript Maps + HTML Div Id which will contain Google Map + Color used for the background of the Map div. This color will be visible when tiles have not yet loaded as the user pans. This option can only be set when the map is initialized. + Size in pixels of the controls appearing on the map. This value must be supplied directly when creating the Map. + Callback function for Map initialized event + Callback function for Map clicked event + Callback function for Map double clicked event + Callback function for Map content menu event + Callback function for Map mouse up event + Callback function for Map mouse down event + Callback function for Map mouse move event + Callback function for Map mouse enter event + Callback function for Map mouse leaving event + Callback function for Map center point changed event + Callback function for Map zoom level changed event + Callback function for Map type changed event + Callback function for Map heading changed event + Callback function for Map tilt position changed event + Callback function for Map boundaries changed event + Callback function for Map projection changed event + Callback function for Map draggable changed event + Callback function for Map street-view changed event + Callback function for Map dragging event + Callback function for Map drag ended event + Callback function for Map drag started event + Callback function for Map resized event + Callback function for Map tiles loaded event + Callback function for Map idle event + Async task - + - The text to be displayed in the label. + Sets the center point as coordinates of the Map. + Latitude component + Longitude component + Async task - + - The className property of the label's element (equivalent to the element's class attribute). - Multiple space-separated CSS classes can be added. + Sets the center point as coordinates after Address was resolved to coords of the Map. + Address as required center point + Async task - + - The color of the label text. Default color is black. + Sets the center point as coordinates of the Map with smooth slide animation. + Latitude component + Longitude component + Async task - + - The font family of the label text (equivalent to the CSS font-family property). + Sets the center point as coordinates after Address was resolved to coords of the Map smooth slide animation. + Address as required center point + Async task - + - The font size of the label text (equivalent to the CSS font-size property). Default size is 14px. + Sets map Zoom level. + Required Zoom level should be 0-22 + Async task - + - The font weight of the label text (equivalent to the CSS font-weight property). + Sets the map type. + Required + Async task - + - Default constructor. + Sets the compass heading for aerial imagery measured in degrees from cardinal direction North. - Label text + Required heading 0-360 + Async task - + - A structure representing a Marker icon image. + Controls the automatic switching behavior for the angle of incidence of the map. The only allowed values are 0 and 45. setTilt(0) causes the map to always use a 0° overhead view regardless of the zoom level and viewport. + setTilt(45) causes the tilt angle to automatically switch to 45 whenever 45° imagery is available for the current zoom level and viewport, and switch back to 0 whenever 45° imagery is not available (this is the default behavior). + Required tilt 0 or 45 + Async task - + - The URL of the image or sprite sheet. + Notify Maps JS about container DIV resized. + Async task - + - The position at which to anchor an image in correspondence to the location of the marker on the map. - By default, the anchor is located along the center point of the bottom of the image. + Controls whether the map icons are clickable or not. A map icon represents a point of interest, also known as a POI. + To disable the clickability of map icons, pass a value of false to this method. + Icons are clickable or not + Async task - + - The origin of the label relative to the top-left corner of the icon image, if a label is supplied by the marker. - By default, the origin is located in the center point of the image. + Sets given options to Map. + Google JavaScript Maps options + Async task - + - The position of the image within a sprite, if any. - By default, the origin is located at the top left corner of the image (0, 0). + Creates Custom Controls on the Map on the given position with event callbacks. + Enumerable CustomControl elements + Async task - + - The size of the entire image after scaling, if any. Use this property to stretch/shrink an image or a sprite. + Creates markers on the Map with InfoWindows on the given position with event callbacks. + Enumerable new markers to add + Enumerable markers removed or replaced + Async task - + - The display size of the sprite or image. When using sprites, you must specify the sprite size. - If the size is not provided, it will be set when the image loads. + Returns the lat/lng bounds of the current viewport. If more than one copy of the world is visible, + the bounds range in longitude from -180 to 180 degrees inclusive. If the map is not yet initialized or + center and zoom have not been set then the result is undefined. For vector maps with non-zero tilt or heading, + the returned lat/lng bounds represents the smallest bounding box that includes the visible region of the map's viewport. + Async task - + - Default constructor. + Returns the position displayed at the center of the map. Note that this LatLng object is not wrapped. See LatLng for more information. + If the center or bounds have not been set then the result is undefined. - Icon Url + Async task - + - This object defines the clickable region of a marker image. + Returns HTMLElement The mapDiv of the map. + Async task - + - The format of this attribute depends on the value of the type and follows the w3 AREA coords specification found - at http://www.w3.org/TR/REC-html40/struct/objects.html#adef-coords. + + + - + - Describes the shape's type and can be circle, poly or rect. + MarkerOptions object used to define the properties that can be set on a Marker with event callbacks. - + - Describes the shape's type and can be circle, poly or rect. + Callback function called when Marker was clicked. - + - Specifies the size of marker + Callback function called when Marker dragging. - + - The set of marker style descriptors is a series of value assignments separated by the pipe (|) character. + Callback function called when Marker drag ended. - + - Specifies the size of marker from the set {tiny, mid, small}. - If no size parameter is set, the marker will appear in its default (normal) size. + Callback function called when Marker drag started. - + - Specifies a 24-bit color (example: color=0xFFFFCC) or a predefined color from the set - {black, brown, green, purple, yellow, blue, gray, orange, red, white}. + Default constructor. + Marker position on the Map - + - Specifies a single uppercase alphanumeric character from the set {A-Z, 0-9}. + Animations that can be played on a marker. - + - Scale value is multiplied with the marker image size to produce the actual output size of the marker in pixels. - Default scale value is 1; accepted values are 1, 2, and 4. + No animation. - + - PolylineOptions object used to define the properties that can be set on a Polyline. - https://developers.google.com/maps/documentation/javascript/reference/polygon#Polyline + Marker bounces until animation is stopped. - + - Indicates whether this Polyline handles mouse events. Defaults to true. + Marker falls from the top of the map ending with a small bounce. - + - If set to true, the user can drag this shape over the map. The geodesic property defines the mode of dragging. Defaults to false. + MarkerOptions object used to define the properties that can be set on a Marker. - + - If set to true, the user can edit this shape by dragging the control points shown at the vertices and on each segment. Defaults to false. + Id of the Custom control. - + - When true, edges of the polygon are interpreted as geodesic and will follow the curvature of the Earth. When false, edges of the polygon are rendered as - straight lines in screen space. Note that the shape of a geodesic polygon may appear to change when dragged, as the dimensions are maintained relative to - the surface of the earth. Defaults to false. + The offset from the marker's position to the tip of an InfoWindow that has been opened with the marker as anchor. - - - The icons to be rendered along the polyline. - - - - - The ordered sequence of coordinates of the Polyline. - - - - - The stroke color. All CSS3 colors are supported except for extended named colors. - - - - - The stroke opacity between 0.0 and 1.0. - - - - - The stroke width in pixels. - - - - - Whether this polyline is visible on the map. Defaults to true. - - - - - The zIndex compared to other polys. - - - - - Describes how icons are to be rendered on a line. - - - + - If true, each icon in the sequence has the same fixed rotation regardless of the angle of the edge on which it lies. Defaults to false, - in which case each icon in the sequence is rotated to align with its edge. + Which animation to play when marker is added to a map. - + - The icon to render on the line. + If true, the marker receives mouse and touch events. Default value is true. - + - The distance from the start of the line at which an icon is to be rendered. This distance may be expressed as a percentage of line's length - (e.g. '50%') or in pixels (e.g. '50px'). Defaults to '100%'. + If false, disables cross that appears beneath the marker when dragging. This option is true by default. - + - The distance between consecutive icons on the line. This distance may be expressed as a percentage of the line's length (e.g. '50%') or in pixels - (e.g. '50px'). To disable repeating of the icon, specify '0'. Defaults to '0'. + Mouse cursor to show on hover. - + - Describes a symbol, which consists of a vector path with styling. A symbol can be used as the icon of a marker, or placed on a polyline. + If true, the marker can be dragged. Default value is false. - + - The symbol's path, which is a built-in symbol path, or a custom path expressed using SVG path notation. Required. + Adds a label to the marker. The label can either be a string, or a MarkerLabel object. - + - The position at which to anchor an image in correspondence to the location of the marker on the map. - By default, the anchor is located along the center point of the bottom of the image. + Icon for the foreground. If a string is provided, it is treated as though it were an Icon with the string as url. - + - The symbol's fill color. All CSS3 colors are supported except for extended named colors. For symbol markers, this defaults to 'black'. - For symbols on polylines, this defaults to the stroke color of the corresponding polyline. + The marker's opacity between 0.0 and 1.0. - + - The symbol's fill opacity. Defaults to 0. + Optimization renders many markers as a single static element. Optimized rendering is enabled by default. + Disable optimized rendering for animated GIFs or PNGs, or when each marker must be rendered as a separate DOM element (advanced usage only). - + - The origin of the label relative to the top-left corner of the icon image, if a label is supplied by the marker. - By default, the origin is located in the center point of the image. + Marker position. Required in order to display the marker. - + - The angle by which to rotate the symbol, expressed clockwise in degrees. Defaults to 0. A symbol in an IconSequence where fixedRotation is false - is rotated relative to the angle of the edge on which it lies. + Image map region definition used for drag/click. - + - The amount by which the symbol is scaled in size. For symbol markers, this defaults to 1; after scaling, the symbol may be of any size. For symbols on a polyline, - this defaults to the stroke weight of the polyline; after scaling, the symbol must lie inside a square 22 pixels in size centered at the symbol's anchor. + Rollover text. If provided, an accessibility text (e.g. for use with screen readers) + will be added to the marker with the provided value. - + - The symbol's stroke color. All CSS3 colors are supported except for extended named colors. For symbol markers, this defaults to 'black'. - For symbols on a polyline, this defaults to the stroke color of the polyline. + If true, the marker is visible. - + - The symbol's stroke opacity. For symbol markers, this defaults to 1. For symbols on a polyline, this defaults to the stroke opacity of the polyline. + All markers are displayed on the map in order of their zIndex, with higher values displaying in front of markers with lower values. - + - The symbol's stroke weight. Defaults to the scale of the symbol. + Info Window to show for Marker on click. - + Default constructor. - The symbol's path - - - - Restrictions coordinates for - NOTE: Google Maps restriction is basically a MAX Zoom level. So it does not allow users to zoom out (zoom level value forced). - In order to notify Blazor about the maximum Zoom level two-way binding MUST be used: `@bind-Zoom="_jsMapZoomLevel" @bind-Zoom:event="OnMapZoomLevelChanged"` - - - - - Latitude and Longitude SW/NE corners of the bound. - - - - - Is restriction strict. - - - - - Default implementation of - - - - - Options for the rendering of the map type control. - - - - - IDs of map types to show in the control. - - - - - Position id. Used to specify the position of the control on the map. The default position is TOP_RIGHT. - - - - - Style id. Used to select what style of map type control to display. - - - - - Default constructor - - - - - Clear list. - - - - - Resets the list of Map types with the given params array. - - Required map types to use - - - - Identifiers for common MapTypesControls. - - - - - When the DEFAULT control is shown, it will vary according to window size and other factors. - The DEFAULT control may change in future versions of the API. - - - - - The standard horizontal radio buttons bar. - - - - - A dropdown menu for the screen realestate conscious. - - - - - The MapTypeStyle is a collection of selectors and stylers that define how the map should be styled. - Selectors specify the map features and/or elements that should be affected, and stylers specify how those - features and elements should be modified. - - - - - The style rules to apply to the selected map features and elements. - The rules are applied in the order that you specify in this array. - - - - - The element to which a styler should be applied. An element is a visual aspect of a feature on the map. - Example: a label, an icon, the stroke or fill applied to the geometry, and more. Optional. - If elementType is not specified, the value is assumed to be 'all'. - - - - - The feature, or group of features, to which a styler should be applied. Optional. - If featureType is not specified, the value is assumed to be 'all'. - - - - - The Maps Static API creates maps in several formats - - - - - (default) specifies a standard roadmap image, as is normally shown on the Google Maps website. - + Marker position on the Map - + - Specifies satellite image. + Rather than use Google's marker icons, you are free to use your own custom icons instead. - + - Specifies a physical relief map image, showing terrain and vegetation. + Custom icon URL. - + - Specifies a hybrid of the satellite and roadmap image, showing a transparent layer of major streets and place names on the satellite image. + Icon is position in relation to the specified markers locations. - + - Google maps images may be returned in several common web graphics formats + The anchor point sets how the icon is placed in relation to the specified markers locations. - + - The markers parameter defines a set of one or more markers (map pins) at a set of locations. + These options specify the appearance of a marker label. + A marker label is a string (often a single character) which will appear inside the marker. - + - Set of marker style descriptors. + The text to be displayed in the label. - + - Override default markers and with your own custom icons instead. + The className property of the label's element (equivalent to the element's class attribute). + Multiple space-separated CSS classes can be added. - + - Each marker descriptor must contain a set of one or more locations defining where to place the marker on the map. + The color of the label text. Default color is black. - + - Checks if any marker style was defined in or properties. + The font family of the label text (equivalent to the CSS font-family property). - + - Default constructor + The font size of the label text (equivalent to the CSS font-size property). Default size is 14px. - + - Injectable service to handle Google JavaScript Maps functionalities. Available on the instance of object ref as well. + The font weight of the label text (equivalent to the CSS font-weight property). - + - HTML Div Id which was set when Maps initialized with method. + Default constructor. + Label text - + - This function must be called only once to initialize Google JavaScript Maps with ApiKey and event callbacks. + A structure representing a Marker icon image. - Google API Key which has permission for Google JavaScript Maps - HTML Div Id which will contain Google Map - Color used for the background of the Map div. This color will be visible when tiles have not yet loaded as the user pans. This option can only be set when the map is initialized. - Size in pixels of the controls appearing on the map. This value must be supplied directly when creating the Map. - Callback function for Map initialized event - Callback function for Map clicked event - Callback function for Map double clicked event - Callback function for Map content menu event - Callback function for Map mouse up event - Callback function for Map mouse down event - Callback function for Map mouse move event - Callback function for Map mouse enter event - Callback function for Map mouse leaving event - Callback function for Map center point changed event - Callback function for Map zoom level changed event - Callback function for Map type changed event - Callback function for Map heading changed event - Callback function for Map tilt position changed event - Callback function for Map boundaries changed event - Callback function for Map projection changed event - Callback function for Map draggable changed event - Callback function for Map street-view changed event - Callback function for Map dragging event - Callback function for Map drag ended event - Callback function for Map drag started event - Callback function for Map resized event - Callback function for Map tiles loaded event - Callback function for Map idle event - Async task - + - Sets the center point as coordinates of the Map. + The URL of the image or sprite sheet. - Latitude component - Longitude component - Async task - + - Sets the center point as coordinates after Address was resolved to coords of the Map. + The position at which to anchor an image in correspondence to the location of the marker on the map. + By default, the anchor is located along the center point of the bottom of the image. - Address as required center point - Async task - + - Sets the center point as coordinates of the Map with smooth slide animation. + The origin of the label relative to the top-left corner of the icon image, if a label is supplied by the marker. + By default, the origin is located in the center point of the image. - Latitude component - Longitude component - Async task - + - Sets the center point as coordinates after Address was resolved to coords of the Map smooth slide animation. + The position of the image within a sprite, if any. + By default, the origin is located at the top left corner of the image (0, 0). - Address as required center point - Async task - + - Sets map Zoom level. + The size of the entire image after scaling, if any. Use this property to stretch/shrink an image or a sprite. - Required Zoom level should be 0-22 - Async task - + - Sets the map type. + The display size of the sprite or image. When using sprites, you must specify the sprite size. + If the size is not provided, it will be set when the image loads. - Required - Async task - + - Sets the compass heading for aerial imagery measured in degrees from cardinal direction North. + Default constructor. - Required heading 0-360 - Async task + Icon Url - + - Controls the automatic switching behavior for the angle of incidence of the map. The only allowed values are 0 and 45. setTilt(0) causes the map to always use a 0° overhead view regardless of the zoom level and viewport. - setTilt(45) causes the tilt angle to automatically switch to 45 whenever 45° imagery is available for the current zoom level and viewport, and switch back to 0 whenever 45° imagery is not available (this is the default behavior). + This object defines the clickable region of a marker image. - Required tilt 0 or 45 - Async task - + - Notify Maps JS about container DIV resized. + The format of this attribute depends on the value of the type and follows the w3 AREA coords specification found + at http://www.w3.org/TR/REC-html40/struct/objects.html#adef-coords. - Async task - + - Controls whether the map icons are clickable or not. A map icon represents a point of interest, also known as a POI. - To disable the clickability of map icons, pass a value of false to this method. + Describes the shape's type and can be circle, poly or rect. - Icons are clickable or not - Async task - + - Sets given options to Map. + Describes the shape's type and can be circle, poly or rect. - Google JavaScript Maps options - Async task - + - Creates Custom Controls on the Map on the given position with event callbacks. + Specifies the size of marker - Enumerable CustomControl elements - Async task - + - Creates markers on the Map with InfoWindows on the given position with event callbacks. + The set of marker style descriptors is a series of value assignments separated by the pipe (|) character. - Enumerable new markers to add - Enumerable markers removed or replaced - Async task - + - Returns the lat/lng bounds of the current viewport. If more than one copy of the world is visible, - the bounds range in longitude from -180 to 180 degrees inclusive. If the map is not yet initialized or - center and zoom have not been set then the result is undefined. For vector maps with non-zero tilt or heading, - the returned lat/lng bounds represents the smallest bounding box that includes the visible region of the map's viewport. + Specifies the size of marker from the set {tiny, mid, small}. + If no size parameter is set, the marker will appear in its default (normal) size. - Async task - + - Returns the position displayed at the center of the map. Note that this LatLng object is not wrapped. See LatLng for more information. - If the center or bounds have not been set then the result is undefined. + Specifies a 24-bit color (example: color=0xFFFFCC) or a predefined color from the set + {black, brown, green, purple, yellow, blue, gray, orange, red, white}. - Async task - + - Returns HTMLElement The mapDiv of the map. + Specifies a single uppercase alphanumeric character from the set {A-Z, 0-9}. - Async task - + - + Scale value is multiplied with the marker image size to produce the actual output size of the marker in pixels. + Default scale value is 1; accepted values are 1, 2, and 4. - - From 6d062f7bb9712c16cd27c96641f297e6073d58ef Mon Sep 17 00:00:00 2001 From: Major Date: Thu, 17 Feb 2022 20:26:00 +0100 Subject: [PATCH 17/19] AddPolyline feature --- .../Drawings/GoogleMapPolylineOptions.cs | 14 +++---- .../{ => Google}/GoogleMapLatLng.cs | 6 ++- .../Google/GoogleMapService.cs | 5 ++- .../Google/IGoogleMapService.cs | 4 +- .../Majorsoft.Blazor.Components.Maps.xml | 40 +++++++++---------- .../wwwroot/googleMaps.js | 23 +++++------ .../wwwroot/googleMaps.min.js | 2 +- .../Components/MapsGoogle.razor | 40 ++++++++++++++++--- 8 files changed, 80 insertions(+), 54 deletions(-) rename src/Majorsoft.Blazor.Components.Maps/{ => Google}/GoogleMapLatLng.cs (85%) diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/Drawings/GoogleMapPolylineOptions.cs b/src/Majorsoft.Blazor.Components.Maps/Google/Drawings/GoogleMapPolylineOptions.cs index 3af23eb6..55481865 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/Drawings/GoogleMapPolylineOptions.cs +++ b/src/Majorsoft.Blazor.Components.Maps/Google/Drawings/GoogleMapPolylineOptions.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Majorsoft.Blazor.Components.Maps.Google { @@ -44,27 +40,27 @@ public class GoogleMapPolylineOptions /// /// The ordered sequence of coordinates of the Polyline. /// - public GoogleMapLatLng Path { get; set; } + public GoogleMapLatLng[] Path { get; set; } /// /// The stroke color. All CSS3 colors are supported except for extended named colors. /// - public string StrokeColor { get; set; } + public string StrokeColor { get; set; } = "black"; /// /// The stroke opacity between 0.0 and 1.0. /// - public double StrokeOpacity { get; set; } + public double StrokeOpacity { get; set; } = 1.0; /// /// The stroke width in pixels. /// - public double StrokeWeight { get; set; } + public double StrokeWeight { get; set; } = 2; /// /// Whether this polyline is visible on the map. Defaults to true. /// - public bool Visible { get; set; } + public bool Visible { get; set; } = true; /// /// The zIndex compared to other polys. diff --git a/src/Majorsoft.Blazor.Components.Maps/GoogleMapLatLng.cs b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLng.cs similarity index 85% rename from src/Majorsoft.Blazor.Components.Maps/GoogleMapLatLng.cs rename to src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLng.cs index 18fc0c02..b6e9cc70 100644 --- a/src/Majorsoft.Blazor.Components.Maps/GoogleMapLatLng.cs +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLng.cs @@ -1,4 +1,6 @@ -namespace Majorsoft.Blazor.Components.Maps +using System.Text.Json.Serialization; + +namespace Majorsoft.Blazor.Components.Maps.Google { /// /// A LatLng is a point in geographical coordinates: latitude and longitude. @@ -9,12 +11,14 @@ public class GoogleMapLatLng /// Latitude ranges between -90 and 90 degrees, inclusive. Values above or below this range will be clamped to the range [-90, 90]. /// This means that if the value specified is less than -90, it will be set to -90. And if the value is greater than 90, it will be set to 90. /// + [JsonPropertyName("lat")] public double Latitude { get; set; } /// /// Longitude ranges between -180 and 180 degrees, inclusive. Values above or below this range will be wrapped so that they fall within the range. /// For example, a value of -190 will be converted to 170. A value of 190 will be converted to -170. This reflects the fact that longitudes wrap around the globe. /// + [JsonPropertyName("lng")] public double Longitude { get; set; } } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs index 43bd33c2..d9ee5ba9 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapService.cs @@ -1,4 +1,5 @@ -using Microsoft.JSInterop; + +using Microsoft.JSInterop; using System; using System.Collections.Generic; @@ -258,7 +259,7 @@ public async ValueTask GetDivAsync() public async Task AddPolyline(params GoogleMapPolylineOptions[] googleMapPolylineOptions) { await CheckJsObjectAsync(); - await _mapsJs.InvokeAsync("polylineSetMap", MapContainerId, googleMapPolylineOptions); + await _mapsJs.InvokeVoidAsync("polylineSetMap", MapContainerId, googleMapPolylineOptions); } diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs b/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs index b8926a9f..6a0e70f7 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs +++ b/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs @@ -167,7 +167,7 @@ Task InitMapAsync(string apiKey, Task CreateCustomControlsAsync(IEnumerable mapCustomControls); /// - /// Creates markers on the Map with InfoWindows on the given position with event callbacks. + /// Creates and removes Markers on the Map with InfoWindows on the given position with event callbacks. /// /// Enumerable new markers to add /// Enumerable markers removed or replaced @@ -197,7 +197,7 @@ Task InitMapAsync(string apiKey, ValueTask GetDivAsync(); /// - /// + /// Creates and removes Polyline on the Map with given values and event callbacks. /// /// /// diff --git a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml index 443e445e..b45346f2 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml +++ b/src/Majorsoft.Blazor.Components.Maps/Majorsoft.Blazor.Components.Maps.xml @@ -47,23 +47,6 @@ Maps specific geo coordinates or address - - - A LatLng is a point in geographical coordinates: latitude and longitude. - - - - - Latitude ranges between -90 and 90 degrees, inclusive. Values above or below this range will be clamped to the range [-90, 90]. - This means that if the value specified is less than -90, it will be set to -90. And if the value is greater than 90, it will be set to 90. - - - - - Longitude ranges between -180 and 180 degrees, inclusive. Values above or below this range will be wrapped so that they fall within the range. - For example, a value of -190 will be converted to 170. A value of 190 will be converted to -170. This reflects the fact that longitudes wrap around the globe. - - PolylineOptions object used to define the properties that can be set on a Polyline. @@ -408,6 +391,23 @@ Maximum allowed with of the Info Window. + + + A LatLng is a point in geographical coordinates: latitude and longitude. + + + + + Latitude ranges between -90 and 90 degrees, inclusive. Values above or below this range will be clamped to the range [-90, 90]. + This means that if the value specified is less than -90, it will be set to -90. And if the value is greater than 90, it will be set to 90. + + + + + Longitude ranges between -180 and 180 degrees, inclusive. Values above or below this range will be wrapped so that they fall within the range. + For example, a value of -190 will be converted to 170. A value of 190 will be converted to -170. This reflects the fact that longitudes wrap around the globe. + + A LatLngBounds instance represents a rectangle in geographical coordinates, including one that crosses the 180 degrees longitudinal meridian. @@ -418,7 +418,7 @@ Default constructor - + Initialize object for Map restrictions @@ -753,7 +753,7 @@ - Creates markers on the Map with InfoWindows on the given position with event callbacks. + Creates and removes Markers on the Map with InfoWindows on the given position with event callbacks. Enumerable new markers to add Enumerable markers removed or replaced @@ -783,7 +783,7 @@ - + Creates and removes Polyline on the Map with given values and event callbacks. diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js index f7718763..e77824cc 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js @@ -56,10 +56,10 @@ window.initGoogleMaps = () => { restrict = { latLngBounds: { - south: mapInfo.restriction.latLngBounds.southWest.latitude, - west: mapInfo.restriction.latLngBounds.southWest.longitude, - north: mapInfo.restriction.latLngBounds.northEast.latitude, - east: mapInfo.restriction.latLngBounds.northEast.longitude + south: mapInfo.restriction.latLngBounds.southWest.lat, + west: mapInfo.restriction.latLngBounds.southWest.lng, + north: mapInfo.restriction.latLngBounds.northEast.lat, + east: mapInfo.restriction.latLngBounds.northEast.lng }, strictBounds: mapInfo.restriction.strictBounds, }; @@ -397,7 +397,7 @@ export function getDiv(elementId) { } } function convertToLatLng(latLngObject) { - let ret = { Latitude: latLngObject.lat(), Longitude: latLngObject.lng() }; + let ret = { lat: latLngObject.lat(), lng: latLngObject.lng() }; return ret; } @@ -620,15 +620,10 @@ export function polylineSetMap(elementId, polylineOptions) { if (mapWithDotnetRef && mapWithDotnetRef.map) { for (var i = 0; i < polylineOptions.length; i++) { - let markerData = polylineOptions[i]; - - //_mapsMarkers.forEach((element, index) => { - // if (markerData.id == element.id) { - // element.setMap(null); - // _mapsMarkers.splice(index, 1); - // return; - // } - //}); + let options = polylineOptions[i]; + + let polyline = new google.maps.Polyline(options); + polyline.setMap(mapWithDotnetRef.map); } } } diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js index 9a6eddf7..6de23b6d 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js @@ -1 +1 @@ -export function init(t,i,r,u,e,o){if(t&&i&&r){f(n,i,r,u,e,o);let s="https://maps.googleapis.com/maps/api/js?key=",c=!1,a=document.querySelectorAll("head > script");if(a.forEach(n=>{if(n){let t=n.getAttribute("src");if(t&&t.startsWith(s)){c=!0;return}}}),c){window.google&&window.initGoogleMaps();return}let l=document.createElement("script");l.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(l);s=s+t+"&callback=initGoogleMaps&libraries=&v=weekly";let h=document.createElement("script");h.src=s;h.defer=!0;document.head.appendChild(h)}}function f(n,t,i,r,u,f){for(let i=0;i{e.invokeMethodAsync("CustomControlClicked",i)})}}}export function createMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{e.ref.invokeMethodAsync("MarkerClicked",n.id),i&&i.open(e.map,t)})}if(n.draggable){t.addListener("drag",()=>{i("MarkerDrag",n.id,t.getPosition().toJSON())});t.addListener("dragend",()=>{i("MarkerDragEnd",n.id,t.getPosition().toJSON())});t.addListener("dragstart",()=>{i("MarkerDragStart",n.id,t.getPosition().toJSON())});function i(n,t,i){let r={Latitude:i.lat,Longitude:i.lng};e.ref.invokeMethodAsync(n,t,r)}}}}}export function removeMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{if(n.id==t.id){t.setMap(null);r.splice(i,1);return}})}}}function o(n,t){t&&n&&(t.setPosition({lat:n.position.latitude,lng:n.position.longitude}),t.anchorPoint=n.anchorPoint?{x:n.anchorPoint.x,y:n.anchorPoint.y}:null,t.setAnimation(n.animation),t.setClickable(n.clickable),t.crossOnDrag=n.crossOnDrag,t.setCursor(n.cursor),t.setDraggable(n.draggable),t.setIcon(n.icon),t.setLabel(n.label),t.setOpacity(n.opacity),t.optimized=n.optimized,t.setShape(n.shape),t.setTitle(n.title),t.setVisible(n.visible),t.setZIndex(n.zIndex))}export function polylineSetMap(i,r){if(i&&r&&r.length){let f=t(n,i);if(f&&f.map)for(var u=0;u{for(let i=0;i{f(n,"MapClicked")});r.addListener("dblclick",n=>{f(n,"MapDoubleClicked")});r.addListener("contextmenu",n=>{f(n,"MapContextMenu")});r.addListener("mouseup",n=>{f(n,"MapMouseUp")});r.addListener("mousedown",n=>{f(n,"MapMouseDown")});r.addListener("mousemove",n=>{f(n,"MapMouseMove")});r.addListener("mouseover",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOver")}});r.addListener("mouseout",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOut")}});r.addListener("center_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapCenterChanged",t)}}});r.addListener("zoom_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapZoomChanged",r.getZoom())}});r.addListener("maptypeid_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTypeIdChanged",r.getMapTypeId())}});r.addListener("heading_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapHeadingChanged",r.getHeading())}});r.addListener("tilt_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTiltChanged",r.getTilt())}});r.addListener("bounds_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapBoundsChanged")}});r.addListener("projection_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapProjectionChanged")}});r.addListener("draggable_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapDraggableChanged")}});r.addListener("streetview_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapStreetviewChanged")}});r.addListener("drag",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDrag",t)}}});r.addListener("dragend",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragEnd",t)}}});r.addListener("dragstart",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragStart",t)}}});r.addListener("resize",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i){let n={Width:r.getDiv().offsetWidth,Height:r.getDiv().offsetHeight};i.ref.invokeMethodAsync("MapResized",n)}}});r.addListener("tilesloaded",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTilesLoaded")}});r.addListener("idle",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapIdle")}});n[i].value.ref.invokeMethodAsync("MapInitialized",e)}}};let n=[],r=[]; \ No newline at end of file +export function init(t,i,r,u,e,o){if(t&&i&&r){f(n,i,r,u,e,o);let s="https://maps.googleapis.com/maps/api/js?key=",c=!1,a=document.querySelectorAll("head > script");if(a.forEach(n=>{if(n){let t=n.getAttribute("src");if(t&&t.startsWith(s)){c=!0;return}}}),c){window.google&&window.initGoogleMaps();return}let l=document.createElement("script");l.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(l);s=s+t+"&callback=initGoogleMaps&libraries=&v=weekly";let h=document.createElement("script");h.src=s;h.defer=!0;document.head.appendChild(h)}}function f(n,t,i,r,u,f){for(let i=0;i{e.invokeMethodAsync("CustomControlClicked",i)})}}}export function createMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{e.ref.invokeMethodAsync("MarkerClicked",n.id),i&&i.open(e.map,t)})}if(n.draggable){t.addListener("drag",()=>{i("MarkerDrag",n.id,t.getPosition().toJSON())});t.addListener("dragend",()=>{i("MarkerDragEnd",n.id,t.getPosition().toJSON())});t.addListener("dragstart",()=>{i("MarkerDragStart",n.id,t.getPosition().toJSON())});function i(n,t,i){let r={Latitude:i.lat,Longitude:i.lng};e.ref.invokeMethodAsync(n,t,r)}}}}}export function removeMarkers(i,u){if(i&&u&&u.length){let e=t(n,i);if(e&&e.map)for(var f=0;f{if(n.id==t.id){t.setMap(null);r.splice(i,1);return}})}}}function o(n,t){t&&n&&(t.setPosition({lat:n.position.latitude,lng:n.position.longitude}),t.anchorPoint=n.anchorPoint?{x:n.anchorPoint.x,y:n.anchorPoint.y}:null,t.setAnimation(n.animation),t.setClickable(n.clickable),t.crossOnDrag=n.crossOnDrag,t.setCursor(n.cursor),t.setDraggable(n.draggable),t.setIcon(n.icon),t.setLabel(n.label),t.setOpacity(n.opacity),t.optimized=n.optimized,t.setShape(n.shape),t.setTitle(n.title),t.setVisible(n.visible),t.setZIndex(n.zIndex))}export function polylineSetMap(i,r){if(i&&r&&r.length){let f=t(n,i);if(f&&f.map)for(var u=0;u{for(let i=0;i{f(n,"MapClicked")});r.addListener("dblclick",n=>{f(n,"MapDoubleClicked")});r.addListener("contextmenu",n=>{f(n,"MapContextMenu")});r.addListener("mouseup",n=>{f(n,"MapMouseUp")});r.addListener("mousedown",n=>{f(n,"MapMouseDown")});r.addListener("mousemove",n=>{f(n,"MapMouseMove")});r.addListener("mouseover",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOver")}});r.addListener("mouseout",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapMouseOut")}});r.addListener("center_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapCenterChanged",t)}}});r.addListener("zoom_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapZoomChanged",r.getZoom())}});r.addListener("maptypeid_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTypeIdChanged",r.getMapTypeId())}});r.addListener("heading_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapHeadingChanged",r.getHeading())}});r.addListener("tilt_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTiltChanged",r.getTilt())}});r.addListener("bounds_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapBoundsChanged")}});r.addListener("projection_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapProjectionChanged")}});r.addListener("draggable_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapDraggableChanged")}});r.addListener("streetview_changed",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapStreetviewChanged")}});r.addListener("drag",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDrag",t)}}});r.addListener("dragend",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragEnd",t)}}});r.addListener("dragstart",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i&&r.getCenter()){let n=r.getCenter().toJSON(),t={Latitude:n.lat,Longitude:n.lng};i.ref.invokeMethodAsync("MapDragStart",t)}}});r.addListener("resize",()=>{if(r&&r.elementId){let i=t(n,r.elementId);if(i){let n={Width:r.getDiv().offsetWidth,Height:r.getDiv().offsetHeight};i.ref.invokeMethodAsync("MapResized",n)}}});r.addListener("tilesloaded",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapTilesLoaded")}});r.addListener("idle",()=>{if(r&&r.elementId){let i=t(n,r.elementId);i&&i.ref.invokeMethodAsync("MapIdle")}});n[i].value.ref.invokeMethodAsync("MapInitialized",e)}}};let n=[],r=[]; \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor index ae61e9ba..da62c73a 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor @@ -241,7 +241,7 @@

    -

    Maps Pre-render settings:
    +
    Maps Pre-render settings:
    • Background color: @_jsMapBackgroundColor
    • Control size: @(_jsMapControlSize)px
    • @@ -317,9 +317,9 @@ ApiKey="@_googleMapsApiKey" /> @**@ + @bind-Zoom="_jsMapZoomLevel" @bind-Zoom:event="OnMapZoomLevelChanged" + OnMapInitialized="@(() => {})" + ApiKey="@_googleMapsApiKey" />*@
    @@ -329,6 +329,15 @@ + + + +
    +
    + +
    @@ -350,7 +359,6 @@ - @implements IAsyncDisposable @@ -597,6 +605,28 @@ }); } + public async Task AddMapDrawings() + { + var polylines = new GoogleMapPolylineOptions() + { + Path = new GoogleMapLatLng[] + { + new GoogleMapLatLng() { Latitude = 37.772, Longitude = -122.214 }, + new GoogleMapLatLng() { Latitude = 21.291, Longitude = -157.821 }, + new GoogleMapLatLng() { Latitude = -18.142, Longitude = 178.431 }, + new GoogleMapLatLng() { Latitude = -27.467, Longitude = 153.027 }, + }, + Geodesic = true, + Draggable = true, + Clickable = true, + StrokeColor = "#FF0000", + StrokeOpacity = 0.5, + StrokeWeight = 5 + }; + + await _googleMap.GoogleMapService.AddPolyline(polylines); + } + //Events private async Task OnMapInitialized(string elementId) { From 2101978676bc41d5a29efc98f52b44ea0315d2ea Mon Sep 17 00:00:00 2001 From: Major Date: Mon, 30 May 2022 21:53:18 +0200 Subject: [PATCH 18/19] Typeahead exposed OnFocus event. --- .../Components/Typeahead.razor | 3 ++- .../TypeaheadInput.razor | 2 +- .../TypeaheadInput.razor.cs | 11 +++++++++-- .../TypeaheadInputText.razor | 2 +- .../TypeaheadInputText.razor.cs | 11 +++++++++-- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Typeahead.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Typeahead.razor index 764c67de..ac044408 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Typeahead.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Typeahead.razor @@ -89,7 +89,8 @@ textarea { AccentColor="@_accentColor" OnSelectedItemChanged="@(text => SelectedItemChanged(text))" OnDropdownOpen="@(() => WriteEventLog("TypeaheadInput: dropdown opened"))" - OnDropdownClose="@(() => WriteEventLog("TypeaheadInput: dropdown closed"))"> + OnDropdownClose="@(() => WriteEventLog("TypeaheadInput: dropdown closed"))" + OnFocus="@(() => WriteEventLog("TypeaheadInput: textbox focused"))"> diff --git a/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor b/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor index 84976eb5..7e3a1c91 100644 --- a/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor +++ b/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor @@ -11,7 +11,7 @@ OnValueChanged="e => OnValueChanged(e)" ForceNotifyByEnter="false" ForceNotifyOnBlur="false" - @onkeydown="OnKeyDown" @onfocus="OnFocus" + @onkeydown="OnKeyDown" @onfocus="OnFocused" autocomplete="off" @attributes=AdditionalAttributes /> diff --git a/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor.cs b/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor.cs index e1af9a10..4aea606c 100644 --- a/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor.cs +++ b/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor.cs @@ -179,6 +179,7 @@ private string GetDropdownWidth() [Parameter] public EventCallback OnSelectedItemChanged { get; set; } [Parameter] public EventCallback OnDropdownOpen { get; set; } [Parameter] public EventCallback OnDropdownClose { get; set; } + [Parameter] public EventCallback OnFocus { get; set; } [Parameter(CaptureUnmatchedValues = true)] public Dictionary AdditionalAttributes { get; set; } @@ -305,14 +306,20 @@ private async Task OnKeyDown(KeyboardEventArgs e) } } } - private async Task OnFocus(FocusEventArgs e) + private async Task OnFocused(FocusEventArgs e) { - WriteDiag($"{nameof(OnFocus)} event: '{e.Type}'."); + WriteDiag($"{nameof(OnFocused)} event: '{e.Type}'."); _hasNoResult = false; await _clickHandler.RegisterClickBoundariesAsync(_typeahead.InnerElementReference, OnOutsideClick, OnInsideClick); await Activate(); + + if (OnFocus.HasDelegate) + { + WriteDiag($"{nameof(OnFocus)} delegate: '{e.Type}'."); + await OnFocus.InvokeAsync(e); + } } private async Task OnOutsideClick(MouseEventArgs e) { diff --git a/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInputText.razor b/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInputText.razor index 7d6297ff..9fd6fa72 100644 --- a/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInputText.razor +++ b/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInputText.razor @@ -11,7 +11,7 @@ OnValueChanged="e => OnValueChanged(e)" ForceNotifyByEnter="false" ForceNotifyOnBlur="false" - @onkeydown="OnKeyDown" @onfocus="OnFocus" + @onkeydown="OnKeyDown" @onfocus="OnFocused" autocomplete="off" @attributes=AdditionalAttributes /> diff --git a/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInputText.razor.cs b/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInputText.razor.cs index f5e0d662..26b6c7d1 100644 --- a/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInputText.razor.cs +++ b/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInputText.razor.cs @@ -180,6 +180,7 @@ private string GetDropdownWidth() [Parameter] public EventCallback OnSelectedItemChanged { get; set; } [Parameter] public EventCallback OnDropdownOpen { get; set; } [Parameter] public EventCallback OnDropdownClose { get; set; } + [Parameter] public EventCallback OnFocus { get; set; } [Parameter(CaptureUnmatchedValues = true)] public Dictionary AdditionalAttributes { get; set; } @@ -306,14 +307,20 @@ private async Task OnKeyDown(KeyboardEventArgs e) } } } - private async Task OnFocus(FocusEventArgs e) + private async Task OnFocused(FocusEventArgs e) { - WriteDiag($"{nameof(OnFocus)} event: '{e.Type}'."); + WriteDiag($"{nameof(OnFocused)} event: '{e.Type}'."); _hasNoResult = false; await _clickHandler.RegisterClickBoundariesAsync(_typeahead.InnerElementReference, OnOutsideClick, OnInsideClick); await Activate(); + + if (OnFocus.HasDelegate) + { + WriteDiag($"{nameof(OnFocus)} delegate: '{e.Type}'."); + await OnFocus.InvokeAsync(e); + } } private async Task OnOutsideClick(MouseEventArgs e) { From 8115276fdf5a646ca081b7bb0386c73ad1cb32c9 Mon Sep 17 00:00:00 2001 From: Imre Toth Date: Mon, 30 May 2022 21:57:09 +0200 Subject: [PATCH 19/19] Update Typeahead.md --- .github/docs/Typeahead.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/docs/Typeahead.md b/.github/docs/Typeahead.md index 3668a3c1..da7c0e5e 100644 --- a/.github/docs/Typeahead.md +++ b/.github/docs/Typeahead.md @@ -104,6 +104,8 @@ Optional HTML content to show when **Search** is in progress. Callback function called when typeahead dropdown panel opened. - **`OnDropdownClose`: `EventCallback` delegate**
    Callback function called when typeahead dropdown panel opened. +- **`OnFocus`: `EventCallback` delegate**
    + Callback function called when typeahead textbox got focus. ### Functions - **`DisposeAsync()`: `@implements IAsyncDisposable` interface**