diff --git a/.github/docs/Collapse.md b/.github/docs/Collapse.md
index 29f86945..50c95713 100644
--- a/.github/docs/Collapse.md
+++ b/.github/docs/Collapse.md
@@ -42,10 +42,13 @@ Sets the `style` of the `background-color` when tab is not the Active tab. Use H
- **`HoverColor`: `string { get; set; }` (default: "WhiteSmoke") - Required**
Sets the `style` of the `background-color` when button is hovered over with mouse. Use HTML specified: **Color Names**, **RGB**, **HEX** or with **HSL** values.
- **`ContentHeight`: `int { get; set; }` (default: 200) - Required**
-Sets the height of Content panel in `px`. 0 is auto.
+Sets the height (in reality sets max-height because of CSS transition issues) of Content panel in `px`. 0 is auto.
+- **`MaxAllowedContentHeight`: `int { get; set; }` (default: 200) - Required**
+Sets the max-height if Content panel `ContentHeight` is set to 0 (auto).
- **`Animate`: `bool { get; set; }` (default: true)**
Determines to apply CSS animation and transition on Collapse state changes or not.
-**Note: in case of `auto` height some animation won't work.**
+**Note: in case of Content panel `ContentHeight` is set to 0 (auto), then use `MaxAllowedContentHeight` to set max-height CSS property which will be animated.
+Also important based on max-height value transition speed for expand/collapse might differ!.**
- **`Disabled`: `bool { get; set; }` (default: false)**
Determines whether the rendered HTML `` element should be disabled or not.
- **`InnerElementReference`: `ElementReference { get; }`**
diff --git a/.github/docs/JsInterop.md b/.github/docs/JsInterop.md
index f677a47d..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`.
@@ -169,14 +169,13 @@ 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)`**
-Scrolls the given element into the page view area.
-- **`ScrollToElementByIdAsync`**: **`Task ScrollToElementByIdAsync(string id)`**
-Finds element by Id and scrolls the given element into the page view area.
-- **`ScrollToElementByNameAsync`**: **`Task ScrollToElementByNameAsync(string name)`**
-Finds element by name and scrolls the given element into the page view area.
+- **`ScrollToElementAsync`**: **`Task ScrollToElementAsync(ElementReference elementReference, bool smooth)`**
+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. **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. **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)`**
@@ -197,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.
@@ -275,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.
@@ -313,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
@@ -351,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/.github/docs/Maps.md b/.github/docs/Maps.md
index c3c7d24c..e6f50038 100644
--- a/.github/docs/Maps.md
+++ b/.github/docs/Maps.md
@@ -19,16 +19,22 @@ It is available in the service provider (Google, Microsoft, etc.) developer site
**NOTE: None of the Majorsoft Maps component tracking or exposing you _Token_ or _API Key_!
Injecting and protecting this _Token_ or _API Key_ in your Blazor application is YOUR responsibility!**
-# Components
+# Components and Services
+
+#### Google:
- **`GoogleStaticMap`**: component is wrapping **Google Static Maps services** into Blazor components.
- **`GoogleMap`**: component is wrapping **Google JavaScript Maps services** into Blazor components.
-- **`BindMap`**: _Planned in release v1.4.0_
+- **`IGoogleMapService`**: Injectable service to handle Google JavaScript Maps functionalities. Available on the instance of `GoogleMap` object ref as well.
+
+#### Bing:
+- **`BindMap`**: _Planned in release v1.6.0_
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.**
@@ -93,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.**
@@ -111,6 +116,8 @@ You can learn about Google JavaScript Maps features and usage [here](https://dev
Exposes a Blazor `ElementReference` of the wrapped around HTML element. It can be used e.g. for JS interop, etc.
- **`MapId`: `string { get; }`**
Map HTML container Id. It can be used when multiple Maps added to one page.
+- **`GoogleMapService`: `string { get; }`**
+Exposes `IGeolocationService` which is handling JsInterop. This instance can be used for access more GoogleMap features.
- **`Width`: `int { get; set; }` (default: 400)**
Maps image Width in px.
- **`Height`: `int { get; set; }` (default: 300)**
diff --git a/.github/docs/PermaLink.md b/.github/docs/PermaLink.md
index 5e94be02..045bd811 100644
--- a/.github/docs/PermaLink.md
+++ b/.github/docs/PermaLink.md
@@ -32,9 +32,17 @@ 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.
+- **`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.
+### Events
+- **`PermalinkDetected`: `event EventHandler**
+Event handler for parmalinks detected on navigation.
+
## `PermaLinkElement` component
### Properties
@@ -68,9 +76,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 +157,7 @@ Also instance should be disposed.
```
@*Permalink initialize*@
@using Majorsoft.Blazor.Components.PermaLink
-
+
```
#### Server hosted projects
@@ -208,7 +224,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/.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*@
-
+ Tab 1
The first tab
-
+ Tab 2
The second tab
-
+ Can disable
This tab can be disabled
And also any TabItem can be disabled by using Disabled property.
-
+ Header icon
Tab with icon in header
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**
diff --git a/src/Majorsoft.Blazor.Components.Collapse.Tests/CollapsePanelTest.cs b/src/Majorsoft.Blazor.Components.Collapse.Tests/CollapsePanelTest.cs
index 09053a9b..7a52062b 100644
--- a/src/Majorsoft.Blazor.Components.Collapse.Tests/CollapsePanelTest.cs
+++ b/src/Majorsoft.Blazor.Components.Collapse.Tests/CollapsePanelTest.cs
@@ -27,7 +27,7 @@ public void CollapsePanel_should_rendered_correctly_html_attributes()
var id = div.FirstElementChild.GetAttribute("id");
div.MarkupMatches(@$"
-
+
");
}
@@ -43,7 +43,7 @@ public void CollapsePanel_should_rendered_correctly_Disabled()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
-
+
");
}
@@ -61,13 +61,13 @@ public void CollapsePanel_should_rendered_correctly_CommonHeader()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
Common header
-
+
");
div.Click();
rendered.MarkupMatches(@$"
Common header
-
+
");
}
@@ -83,13 +83,13 @@ public void CollapsePanel_should_rendered_correctly_ExpandedHeaderContent()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
Expanded header
-
+
");
div.Click();
rendered.MarkupMatches(@$"
-
+
");
}
@@ -105,13 +105,13 @@ public void CollapsePanel_should_rendered_correctly_CollapsedHeaderContent()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
-
+
");
div.Click();
rendered.MarkupMatches(@$"
Collapsed header
-
+
");
}
@@ -128,13 +128,13 @@ public void CollapsePanel_should_rendered_correctly_ExpandedColor()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
Expanded header
-
+
");
div.Click();
rendered.MarkupMatches(@$"
-
+
");
}
@@ -151,13 +151,13 @@ public void CollapsePanel_should_rendered_correctly_CollapsedColor()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
-
+
");
div.Click();
rendered.MarkupMatches(@$"
Collapsed header
-
+
");
}
@@ -173,13 +173,13 @@ public async Task CollapsePanel_should_rendered_correctly_HoverColor()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
-
+
");
await div.TriggerEventAsync("onmouseenter", new MouseEventArgs());
rendered.MarkupMatches(@$"
-
+
");
await div.TriggerEventAsync("onmouseleave", new MouseEventArgs());
@@ -187,13 +187,13 @@ public async Task CollapsePanel_should_rendered_correctly_HoverColor()
div.Click();
rendered.MarkupMatches(@$"
-
+
");
await div.TriggerEventAsync("onmouseenter", new MouseEventArgs());
rendered.MarkupMatches(@$"
-
+
");
}
@@ -211,14 +211,14 @@ public void CollapsePanel_should_rendered_correctly_Content()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
");
}
@@ -234,7 +234,7 @@ public void CollapsePanel_should_rendered_correctly_ContentHeight()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
-
+
");
}
@@ -250,7 +250,7 @@ public void CollapsePanel_should_rendered_correctly_Animate()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
-
+
");
}
@@ -267,7 +267,7 @@ public void CollapsePanel_should_rendered_correctly_ShowContentOverflow()
var id = div.GetAttribute("id");
rendered.MarkupMatches(@$"
@@ -83,10 +83,16 @@
}
///
- /// Sets the height of Content panel in `px`. 0 is auto.
+ /// Sets the height (in reality sets max-height because of CSS transition issues) of Content panel in `px`. 0 is auto.
///
[Parameter] public int ContentHeight { get; set; } = 200;
+ ///
+ /// Sets the max-height if Content panel is set to 0 (auto).
+ /// https://css-tricks.com/using-css-transitions-auto-dimensions/
+ ///
+ [Parameter] public int MaxAllowedContentHeight { get; set; } = 200;
+
///
/// Determines whether all the rendered HTML elements should be disabled or not.
///
@@ -114,7 +120,8 @@
///
/// Determines to apply CSS animation and transition on Collapse state changes or not.
- /// Note: in case of `auto` height some animation won't work.
+ /// Note: in case of Content panel is set to 0 (auto), then use to set max-height CSS property which will be animated.
+ /// Also important based on max-height value transition speed for expand/collapse might differ!
///
[Parameter] public bool Animate { get; set; } = true;
@@ -158,7 +165,7 @@
return "0px;";
}
- return ContentHeight <= 0 ? "auto;" : $"{ContentHeight}px;";
+ return ContentHeight <= 0 ? $"{MaxAllowedContentHeight}px;" : $"{ContentHeight}px;";
}
private string GetStyle()
{
diff --git a/src/Majorsoft.Blazor.Components.Collapse/CollapsePanel.razor.css b/src/Majorsoft.Blazor.Components.Collapse/CollapsePanel.razor.css
index 789ea5e4..1f205317 100644
--- a/src/Majorsoft.Blazor.Components.Collapse/CollapsePanel.razor.css
+++ b/src/Majorsoft.Blazor.Components.Collapse/CollapsePanel.razor.css
@@ -22,10 +22,11 @@
}
.collapseContent {
+ height: auto;
outline: none;
display: block;
}
.collapseContent.animate {
- -webkit-transition: opacity 1s, height 0.4s; /* For Safari 3.1 to 6.0 */
- transition: opacity 1s, height 0.4s;
+ -webkit-transition: opacity 1s, max-height 0.8s; /* For Safari 3.1 to 6.0 */
+ transition: opacity 1s, max-height 0.8s;
}
\ No newline at end of file
diff --git a/src/Majorsoft.Blazor.Components.Collapse/CollapsePanel.razor.min.css b/src/Majorsoft.Blazor.Components.Collapse/CollapsePanel.razor.min.css
index a3c9e27d..0ea8c57c 100644
--- a/src/Majorsoft.Blazor.Components.Collapse/CollapsePanel.razor.min.css
+++ b/src/Majorsoft.Blazor.Components.Collapse/CollapsePanel.razor.min.css
@@ -1 +1 @@
-.collapsePanel{outline:none}.collapsePanel[disabled]{opacity:.7;pointer-events:none}.collapseHeader{outline:none;width:100%;height:auto;cursor:pointer;border:1px solid transparent;padding:3px;display:flex;box-shadow:0 1px 2px #aaa}.collapseHeader.animate{-webkit-transition:background-color .5s;transition:background-color .5s}.collapseContent{outline:none;display:block}.collapseContent.animate{-webkit-transition:opacity 1s,height .4s;transition:opacity 1s,height .4s}
\ No newline at end of file
+.collapsePanel{outline:none}.collapsePanel[disabled]{opacity:.7;pointer-events:none}.collapseHeader{outline:none;width:100%;height:auto;cursor:pointer;border:1px solid transparent;padding:3px;display:flex;box-shadow:0 1px 2px #aaa}.collapseHeader.animate{-webkit-transition:background-color .5s;transition:background-color .5s}.collapseContent{height:auto;outline:none;display:block}.collapseContent.animate{-webkit-transition:opacity 1s,max-height .8s;transition:opacity 1s,max-height .8s}
\ No newline at end of file
diff --git a/src/Majorsoft.Blazor.Components.Collapse/Majorsoft.Blazor.Components.Collapse.xml b/src/Majorsoft.Blazor.Components.Collapse/Majorsoft.Blazor.Components.Collapse.xml
index 08fa754b..d7b9d5e2 100644
--- a/src/Majorsoft.Blazor.Components.Collapse/Majorsoft.Blazor.Components.Collapse.xml
+++ b/src/Majorsoft.Blazor.Components.Collapse/Majorsoft.Blazor.Components.Collapse.xml
@@ -91,7 +91,13 @@
- Sets the height of Content panel in `px`. 0 is auto.
+ Sets the height (in reality sets max-height because of CSS transition issues) of Content panel in `px`. 0 is auto.
+
+
+
+
+ Sets the max-height if Content panel is set to 0 (auto).
+ https://css-tricks.com/using-css-transitions-auto-dimensions/
@@ -107,7 +113,8 @@
Determines to apply CSS animation and transition on Collapse state changes or not.
- Note: in case of `auto` height some animation won't work.
+ Note: in case of Content panel is set to 0 (auto), then use to set max-height CSS property which will be animated.
+ Also important based on max-height value transition speed for expand/collapse might differ!
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.csproj b/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.csproj
index 3b323545..15d65d2a 100644
--- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.csproj
+++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.csproj
@@ -17,7 +17,7 @@
GitBlazor component that provides useful functionality and event notifications which can be achieved only with JS Interop e.g. scroll, clipboard, focus, resize, language detection, GEO location, etc. Part of Majorsoft Blazor library.See Releases here: https://github.com/majorimi/blazor-components/releases
- .Net5 Blazor Js Interop Click Scroll Focus BoundRect
+ .Net5 Blazor Js Interop Click Scroll Resize Focus BoundRect Browser Geolocation Head History ColorThemeLicense.txtBlazor Components JsInterop
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..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.
@@ -638,50 +698,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.
@@ -732,25 +814,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 Note: might not all browsers support it
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 Note: might not all browsers support it
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 Note: might not all browsers support it
Async Task
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.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 a4526af3..41ad231a 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 Note: might not all browsers support it
/// 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 Note: might not all browsers support it
/// 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 Note: might not all browsers support it
/// 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..089b04de 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);
}
}
}
@@ -75,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;
}
@@ -245,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 2e9795e3..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){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
+ /// 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; } = "black";
+
+ ///
+ /// The stroke opacity between 0.0 and 1.0.
+ ///
+ public double StrokeOpacity { get; set; } = 1.0;
+
+ ///
+ /// The stroke width in pixels.
+ ///
+ public double StrokeWeight { get; set; } = 2;
+
+ ///
+ /// Whether this polyline is visible on the map. Defaults to true.
+ ///
+ public bool Visible { get; set; } = true;
+
+ ///
+ /// 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/GoogleMap.razor b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor
index bb5be0df..9321c715 100644
--- a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor
+++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor
@@ -23,6 +23,11 @@
///
public ElementReference InnerElementReference => _jsMap;
+ ///
+ /// Exposes which is handling JsInterop. This instance can be used for access more GoogleMap features.
+ ///
+ public IGoogleMapService GoogleMapService => _mapService;
+
private bool _mapInitialized = false;
private bool _isDragging = false;
protected override async Task OnInitializedAsync()
@@ -32,6 +37,7 @@
mapContainerId: _mapContainerId,
backgroundColor: BackgroundColor,
controlSize: ControlSize,
+ restriction: Restriction,
mapInitializedCallback: async (mapId) =>
{
WriteDiag($"Google JavaScript API Map initialzied with DIV Id: '{_mapContainerId}'.");
@@ -131,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;
@@ -287,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;
///
@@ -325,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/GoogleMapLatLng.cs b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLng.cs
new file mode 100644
index 00000000..b6e9cc70
--- /dev/null
+++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLng.cs
@@ -0,0 +1,24 @@
+using System.Text.Json.Serialization;
+
+namespace Majorsoft.Blazor.Components.Maps.Google
+{
+ ///
+ /// 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.
+ ///
+ [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/GoogleMapLatLngBounds.cs b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLngBounds.cs
new file mode 100644
index 00000000..b814ee54
--- /dev/null
+++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapLatLngBounds.cs
@@ -0,0 +1,56 @@
+namespace Majorsoft.Blazor.Components.Maps.Google
+{
+ ///
+ /// A LatLngBounds instance represents a rectangle in geographical coordinates, including one that crosses the 180 degrees longitudinal meridian.
+ ///
+ 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.
+ ///
+ public GoogleMapLatLng Center { get; set; }
+
+ ///
+ /// Returns the north-east corner of this bounds.
+ /// Equivalent with `getNorthEast()` method call but it would require JsInterop.
+ ///
+ public GoogleMapLatLng NorthEast { get; set; }
+
+ ///
+ /// Returns the south-west corner of this bounds.
+ /// Equivalent with `getSouthWest()` method call but it would require JsInterop.
+ ///
+ public GoogleMapLatLng SouthWest { get; set; }
+
+ ///
+ /// Converts the given map bounds to a lat/lng span.
+ /// Equivalent with `toSpan()` method call but it would require JsInterop.
+ ///
+ public GoogleMapLatLng Span { get; set; }
+
+ ///
+ /// Returns if the bounds are empty.
+ /// Equivalent with `isEmpty()` method call but it would require JsInterop.
+ ///
+ public bool IsEmpty { get; set; }
+ }
+}
\ No newline at end of file
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..20e48380
--- /dev/null
+++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMapRestriction.cs
@@ -0,0 +1,20 @@
+namespace Majorsoft.Blazor.Components.Maps.Google
+{
+ ///
+ /// 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
+ {
+ ///
+ /// 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 5706cb95..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;
@@ -51,7 +52,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 +91,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)
@@ -236,6 +238,31 @@ await _mapsJs.InvokeVoidAsync("removeMarkers", MapContainerId,
}
}
+ public async ValueTask GetBoundsAsync()
+ {
+ await CheckJsObjectAsync();
+ return await _mapsJs.InvokeAsync("getBounds", MapContainerId);
+ }
+
+ public async ValueTask GetCenterAsync()
+ {
+ await CheckJsObjectAsync();
+ return await _mapsJs.InvokeAsync("getCenter", MapContainerId);
+ }
+
+ public async ValueTask GetDivAsync()
+ {
+ await CheckJsObjectAsync();
+ return await _mapsJs.InvokeAsync("getDiv", MapContainerId);
+ }
+
+ public async Task AddPolyline(params GoogleMapPolylineOptions[] googleMapPolylineOptions)
+ {
+ await CheckJsObjectAsync();
+ await _mapsJs.InvokeVoidAsync("polylineSetMap", MapContainerId, googleMapPolylineOptions);
+ }
+
+
private async Task CheckJsObjectAsync()
{
if (_mapsJs is null)
diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs b/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs
index 94b82721..6a0e70f7 100644
--- a/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs
+++ b/src/Majorsoft.Blazor.Components.Maps/Google/IGoogleMapService.cs
@@ -1,13 +1,14 @@
-
-using System;
+using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Threading.Tasks;
+using Microsoft.JSInterop;
+
namespace Majorsoft.Blazor.Components.Maps.Google
{
///
- /// 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.
///
public interface IGoogleMapService : IAsyncDisposable
{
@@ -75,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.
@@ -165,11 +167,40 @@ 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
- ///
+ /// 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();
+
+ ///
+ /// Creates and removes Polyline on the Map with given values and event callbacks.
+ ///
+ ///
+ ///
+ Task AddPolyline(params GoogleMapPolylineOptions[] googleMapPolylineOptions);
}
}
\ No newline at end of file
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 0b48d11a..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,6 +47,164 @@
Maps specific geo coordinates or address
+
+
+ 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.
@@ -233,603 +391,713 @@
Maximum allowed with of the Info Window.
-
+
- MarkerOptions object used to define the properties that can be set on a Marker with event callbacks.
+ A LatLng is a point in geographical coordinates: latitude and longitude.
-
+
- Callback function called when Marker was clicked.
+ 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.
-
+
- Callback function called when Marker dragging.
+ 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.
-
+
- Callback function called when Marker drag ended.
+ A LatLngBounds instance represents a rectangle in geographical coordinates, including one that crosses the 180 degrees longitudinal meridian.
-
+
- Callback function called when Marker drag started.
+ Default constructor
-
+
- Default constructor.
+ Initialize object for Map restrictions
- Marker position on the Map
+ The south-west corner of this bounds.
+ The north-east corner of this bounds.
-
+
- Animations that can be played on a marker.
+ Computes the center of this LatLngBounds
+ Equivalent with `getCenter()` method call but it would require JsInterop.
-
+
- No animation.
+ Returns the north-east corner of this bounds.
+ Equivalent with `getNorthEast()` method call but it would require JsInterop.
-
+
- Marker bounces until animation is stopped.
+ Returns the south-west corner of this bounds.
+ Equivalent with `getSouthWest()` method call but it would require JsInterop.
-
+
- Marker falls from the top of the map ending with a small bounce.
+ Converts the given map bounds to a lat/lng span.
+ Equivalent with `toSpan()` method call but it would require JsInterop.
-
+
- MarkerOptions object used to define the properties that can be set on a Marker.
+ Returns if the bounds are empty.
+ Equivalent with `isEmpty()` method call but it would require JsInterop.
-
+
- Id of the Custom control.
+ 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"`
-
+
- The offset from the marker's position to the tip of an InfoWindow that has been opened with the marker as anchor.
+ Latitude and Longitude SW/NE corners of the bound.
-
+
- Which animation to play when marker is added to a map.
+ Is restriction strict.
-
+
- If true, the marker receives mouse and touch events. Default value is true.
+ Default implementation of
-
+
- If false, disables cross that appears beneath the marker when dragging. This option is true by default.
+ Options for the rendering of the map type control.
-
+
- Mouse cursor to show on hover.
+ IDs of map types to show in the control.
-
+
- If true, the marker can be dragged. Default value is false.
+ Position id. Used to specify the position of the control on the map. The default position is TOP_RIGHT.
-
+
- Adds a label to the marker. The label can either be a string, or a MarkerLabel object.
+ Style id. Used to select what style of map type control to display.
-
+
- Icon for the foreground. If a string is provided, it is treated as though it were an Icon with the string as url.
+ Default constructor
-
+
- The marker's opacity between 0.0 and 1.0.
+ Clear list.
-
+
- 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).
+ Resets the list of Map types with the given params array.
+ Required map types to use
-
+
- Marker position. Required in order to display the marker.
+ Identifiers for common MapTypesControls.
-
+
- Image map region definition used for drag/click.
+ 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.
-
+
- 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 standard horizontal radio buttons bar.
-
+
- If true, the marker is visible.
+ A dropdown menu for the screen realestate conscious.
-
+
- All markers are displayed on the map in order of their zIndex, with higher values displaying in front of markers with lower values.
+ 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.
-
+
- Info Window to show for Marker on click.
+ 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.
-
+
- Default constructor.
+ 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'.
- Marker position on the Map
-
+
- Rather than use Google's marker icons, you are free to use your own custom icons instead.
+ 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'.
-
+
- Custom icon URL.
+ The Maps Static API creates maps in several formats
-
+
- Icon is position in relation to the specified markers locations.
+ (default) specifies a standard roadmap image, as is normally shown on the Google Maps website.
-
+
- The anchor point sets how the icon is placed in relation to the specified markers locations.
+ Specifies satellite image.
-
+
- 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.
+ Specifies a physical relief map image, showing terrain and vegetation.
-
+
- The text to be displayed in the label.
+ Specifies a hybrid of the satellite and roadmap image, showing a transparent layer of major streets and place names on the satellite image.
-
+
- The className property of the label's element (equivalent to the element's class attribute).
- Multiple space-separated CSS classes can be added.
+ Google maps images may be returned in several common web graphics formats
-
+
- The color of the label text. Default color is black.
+ The markers parameter defines a set of one or more markers (map pins) at a set of locations.
-
+
- The font family of the label text (equivalent to the CSS font-family property).
+ Set of marker style descriptors.
-
+
- The font size of the label text (equivalent to the CSS font-size property). Default size is 14px.
+ Override default markers and with your own custom icons instead.
-
+
- The font weight of the label text (equivalent to the CSS font-weight property).
+ Each marker descriptor must contain a set of one or more locations defining where to place the marker on the map.
-
+
- Default constructor.
+ Checks if any marker style was defined in or properties.
- Label text
-
+
- A structure representing a Marker icon image.
+ Default constructor
-
+
- The URL of the image or sprite sheet.
+ Injectable service to handle Google JavaScript Maps functionalities. Available on the instance of object ref as well.
-
+
- 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.
+ HTML Div Id which was set when Maps initialized with method.
-
+
- 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.
+ 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 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).
+ Sets the center point as coordinates of the Map.
+ Latitude component
+ Longitude component
+ Async task
-
+
- The size of the entire image after scaling, if any. Use this property to stretch/shrink an image or a sprite.
+ Sets the center point as coordinates after Address was resolved to coords of the Map.
+ Address as required center point
+ 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.
+ Sets the center point as coordinates of the Map with smooth slide animation.
+ Latitude component
+ Longitude component
+ Async task
-
+
- Default constructor.
+ Sets the center point as coordinates after Address was resolved to coords of the Map smooth slide animation.
- Icon Url
+ Address as required center point
+ Async task
-
+
- This object defines the clickable region of a marker image.
+ Sets map Zoom level.
+ Required Zoom level should be 0-22
+ 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.
+ Sets the map type.
+ Required
+ Async task
-
+
- Describes the shape's type and can be circle, poly or rect.
+ Sets the compass heading for aerial imagery measured in degrees from cardinal direction North.
+ Required heading 0-360
+ Async task
-
+
- Describes the shape's type and can be circle, poly or rect.
+ 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
-
+
- Specifies the size of marker
+ Notify Maps JS about container DIV resized.
+ Async task
-
+
- The set of marker style descriptors is a series of value assignments separated by the pipe (|) character.
+ 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
-
+
- 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.
+ Sets given options to Map.
+ Google JavaScript Maps options
+ Async task
-
+
- 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}.
+ Creates Custom Controls on the Map on the given position with event callbacks.
+ Enumerable CustomControl elements
+ Async task
-
+
- Specifies a single uppercase alphanumeric character from the set {A-Z, 0-9}.
+ 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
+ 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.
+ 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 implementation of
+ 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
-
+
- Options for the rendering of the map type control.
+ Returns HTMLElement The mapDiv of the map.
+ Async task
-
+
- IDs of map types to show in the control.
+ Creates and removes Polyline on the Map with given values and event callbacks.
+
+
-
+
- Position id. Used to specify the position of the control on the map. The default position is TOP_RIGHT.
+ MarkerOptions object used to define the properties that can be set on a Marker with event callbacks.
-
+
- Style id. Used to select what style of map type control to display.
+ Callback function called when Marker was clicked.
-
+
- Default constructor
+ Callback function called when Marker dragging.
-
+
- Clear list.
+ Callback function called when Marker drag ended.
-
+
- Resets the list of Map types with the given params array.
+ Callback function called when Marker drag started.
- Required map types to use
-
+
- Identifiers for common MapTypesControls.
+ Default constructor.
+ Marker position on the Map
-
+
- 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.
+ Animations that can be played on a marker.
-
+
- The standard horizontal radio buttons bar.
+ No animation.
-
+
- A dropdown menu for the screen realestate conscious.
+ Marker bounces until animation is stopped.
-
+
- 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.
+ Marker falls from the top of the map ending with a small bounce.
-
+
- 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.
+ MarkerOptions object used to define the properties that can be set on a Marker.
-
+
- 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'.
+ Id of the Custom control.
-
+
- 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 offset from the marker's position to the tip of an InfoWindow that has been opened with the marker as anchor.
-
+
- The Maps Static API creates maps in several formats
+ Which animation to play when marker is added to a map.
-
+
- (default) specifies a standard roadmap image, as is normally shown on the Google Maps website.
+ If true, the marker receives mouse and touch events. Default value is true.
-
+
- Specifies satellite image.
+ If false, disables cross that appears beneath the marker when dragging. This option is true by default.
-
+
- Specifies a physical relief map image, showing terrain and vegetation.
+ Mouse cursor to show on hover.
-
+
- Specifies a hybrid of the satellite and roadmap image, showing a transparent layer of major streets and place names on the satellite image.
+ If true, the marker can be dragged. Default value is false.
-
+
- Google maps images may be returned in several common web graphics formats
+ Adds a label to the marker. The label can either be a string, or a MarkerLabel object.
-
+
- The markers parameter defines a set of one or more markers (map pins) at a set of locations.
+ Icon for the foreground. If a string is provided, it is treated as though it were an Icon with the string as url.
-
+
- Set of marker style descriptors.
+ The marker's opacity between 0.0 and 1.0.
-
+
- Override default markers and with your own custom icons instead.
+ 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).
-
+
- Each marker descriptor must contain a set of one or more locations defining where to place the marker on the map.
+ Marker position. Required in order to display the marker.
-
+
- Checks if any marker style was defined in or properties.
+ Image map region definition used for drag/click.
-
+
- Default constructor
+ Rollover text. If provided, an accessibility text (e.g. for use with screen readers)
+ will be added to the marker with the provided value.
-
+
- Injectable service to handle Google JavaScript Maps functionalities.
+ If true, the marker is visible.
-
+
- HTML Div Id which was set when Maps initialized with method.
+ All markers are displayed on the map in order of their zIndex, with higher values displaying in front of markers with lower values.
-
+
- This function must be called only once to initialize Google JavaScript Maps with ApiKey and event callbacks.
+ Info Window to show for Marker on click.
- 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.
+ Default constructor.
- Latitude component
- Longitude component
- Async task
+ Marker position on the Map
-
+
- Sets the center point as coordinates after Address was resolved to coords of the Map.
+ Rather than use Google's marker icons, you are free to use your own custom icons instead.
- Address as required center point
- Async task
-
+
- Sets the center point as coordinates of the Map with smooth slide animation.
+ Custom icon URL.
- Latitude component
- Longitude component
- Async task
-
+
- Sets the center point as coordinates after Address was resolved to coords of the Map smooth slide animation.
+ Icon is position in relation to the specified markers locations.
- Address as required center point
- Async task
-
+
- Sets map Zoom level.
+ The anchor point sets how the icon is placed in relation to the specified markers locations.
- Required Zoom level should be 0-22
- Async task
-
+
- Sets the map type.
+ 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.
- Required
- Async task
-
+
- Sets the compass heading for aerial imagery measured in degrees from cardinal direction North.
+ The text to be displayed in the label.
- Required heading 0-360
- Async task
-
+
- 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).
+ The className property of the label's element (equivalent to the element's class attribute).
+ Multiple space-separated CSS classes can be added.
- Required tilt 0 or 45
- Async task
-
+
- Notify Maps JS about container DIV resized.
+ The color of the label text. Default color is black.
- 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.
+ The font family of the label text (equivalent to the CSS font-family property).
- Icons are clickable or not
- Async task
-
+
- Sets given options to Map.
+ The font size of the label text (equivalent to the CSS font-size property). Default size is 14px.
- Google JavaScript Maps options
- Async task
-
+
- Creates Custom Controls on the Map on the given position with event callbacks.
+ The font weight of the label text (equivalent to the CSS font-weight property).
- Enumerable CustomControl elements
- Async task
-
+
- Creates markers on the Map with InfoWindows on the given position with event callbacks.
+ Default constructor.
+
+ Label text
+
+
+
+ A structure representing a Marker icon image.
+
+
+
+
+ The URL of the image or sprite sheet.
+
+
+
+
+ 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 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 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).
+
+
+
+
+ The size of the entire image after scaling, if any. Use this property to stretch/shrink an image or a sprite.
+
+
+
+
+ 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.
+
+
+
+
+ Default constructor.
+
+ Icon Url
+
+
+
+ This object defines the clickable region of a marker image.
+
+
+
+
+ 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.
+
+
+
+
+ Describes the shape's type and can be circle, poly or rect.
+
+
+
+
+ Specifies the size of marker
+
+
+
+
+ The set of marker style descriptors is a series of value assignments separated by the pipe (|) character.
+
+
+
+
+ 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.
+
+
+
+
+ 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}.
+
+
+
+
+ Specifies a single uppercase alphanumeric character from the set {A-Z, 0-9}.
+
+
+
+
+ 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.
- Enumerable new markers to add
- Enumerable markers removed or replaced
-
@@ -841,6 +1109,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.
@@ -858,6 +1131,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 76db8114..e77824cc 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.lat,
+ west: mapInfo.restriction.latLngBounds.southWest.lng,
+ north: mapInfo.restriction.latLngBounds.northEast.lat,
+ east: mapInfo.restriction.latLngBounds.northEast.lng
+ },
+ 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 }
});
}
}
@@ -342,6 +358,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 = { lat: latLngObject.lat(), lng: latLngObject.lng() };
+ return ret;
+}
+
//set methods
export function setZoom(elementId, zoom) {
if (elementId) {
@@ -554,6 +613,22 @@ 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 options = polylineOptions[i];
+
+ let polyline = new google.maps.Polyline(options);
+ polyline.setMap(mapWithDotnetRef.map);
+ }
+ }
+ }
+}
+
//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 68bbd396..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,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,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.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/IPermaLinkWatcherService.cs b/src/Majorsoft.Blazor.Components.PermaLink/IPermaLinkWatcherService.cs
index 918100aa..fe3feb7d 100644
--- a/src/Majorsoft.Blazor.Components.PermaLink/IPermaLinkWatcherService.cs
+++ b/src/Majorsoft.Blazor.Components.PermaLink/IPermaLinkWatcherService.cs
@@ -8,6 +8,31 @@ 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);
+
+ ///
+ /// Checks the current URL for permalink again and re-triggers `PermalinkDetected` event if requested.
+ ///
+ /// PermalinkDetected should be re-triggered or not
+ /// Found permalink value or NULL
+ string? CheckPermalink(bool triggerEvent = false);
+
///
/// 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..e6d2b20f 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,58 @@
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
+
+
+
+ Checks the current URL for permalink again and re-triggers `PermalinkDetected` event if requested.
+
+ PermalinkDetected should be re-triggered or not
+ Found permalink value or NULL
+
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 +85,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..3ca45972 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,11 +27,27 @@
if (firstRender)
{
//setup permalink
- _permalinkWatcher = new PermaLinkWatcherService(_scrollHandler, _navigationManager, _logger);
+ _permalinkWatcher = new PermaLinkWatcherService(_scrollHandler, _navigationManager, _logger, _navigationHistoryService);
_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
+ {
+ if(_permalinkWatcher is not null)
+ {
+ _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..3549c601 100644
--- a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs
+++ b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs
@@ -1,5 +1,7 @@
-using System.Text.RegularExpressions;
+using System;
+using System.Text.RegularExpressions;
+using Majorsoft.Blazor.Components.Common.JsInterop.Navigation;
using Majorsoft.Blazor.Components.Common.JsInterop.Scroll;
using Microsoft.AspNetCore.Components;
@@ -18,12 +20,23 @@ public class PermaLinkWatcherService : IPermaLinkWatcherService
private readonly IScrollHandler _scrollHandler;
private readonly NavigationManager _navigationManager;
private readonly ILogger _logger;
+ private readonly INavigationHistoryService _navigationHistoryService;
- 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,
+ INavigationHistoryService navigationHistoryService,
+ bool smoothScroll = false)
{
_scrollHandler = scrollHandler;
_navigationManager = navigationManager;
_logger = logger;
+ _navigationHistoryService = navigationHistoryService;
+ SmoothScroll = smoothScroll;
}
public void WatchPermaLinks()
@@ -38,15 +51,81 @@ 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);
+ _logger.LogDebug($"{nameof(PermaLinkWatcherService)} - {nameof(HandleLocationChanged)}: navigation happened new URL: '{e.Location}'");
+ var perma = DetectPermalink(e.Location);
+
+ if(!string.IsNullOrWhiteSpace(perma))
+ {
+ if(PermalinkDetected is not null)
+ {
+ PermalinkDetected.Invoke(this, new PermalinkDetectedEventArgs(e, perma));
+ }
+
+ _scrollHandler.ScrollToElementByNameAsync(perma, SmoothScroll);
+ }
+ }
+
+ public void ChangePermalink(string? newPermalink, bool doNotNavigate)
+ {
+ _logger.LogDebug($"{nameof(PermaLinkWatcherService)} - {nameof(ChangePermalink)}: current URL: '{_navigationManager.Uri}', new URL Permalink: '{newPermalink}'");
+
+ var perma = DetectPermalink(_navigationManager.Uri);
+ if (!string.IsNullOrWhiteSpace(perma))
+ {
+ var newUri = _navigationManager.Uri.Replace($"#{perma}", "");
+ if (string.IsNullOrWhiteSpace(newPermalink)) //remove
+ {
+ _navigationManager.NavigateTo(newUri);
+ return;
+ }
- if(match.Success && match.Groups.Count == 2)
+ SetBrowserUrl($"{newUri}#{newPermalink}", doNotNavigate);
+ }
+ else
+ {
+ SetBrowserUrl($"{_navigationManager.Uri}#{newPermalink}", doNotNavigate);
+ }
+ }
+
+ private void SetBrowserUrl(string uri, bool doNotNavigate)
+ {
+ _logger.LogDebug($"{nameof(PermaLinkWatcherService)} - {nameof(SetBrowserUrl)}: new URL: '{uri}', doNotNavigate: '{doNotNavigate}'");
+
+ if (doNotNavigate)
+ {
+ _navigationHistoryService?.PushStateAsync(null, "", uri);
+ return;
+ }
+
+ _navigationManager.NavigateTo(uri, false);
+ }
+ public string? CheckPermalink(bool triggerEvent)
+ {
+ var perma = DetectPermalink(_navigationManager.Uri);
+ if (!string.IsNullOrWhiteSpace(perma))
+ {
+ if (PermalinkDetected is not null)
+ {
+ PermalinkDetected.Invoke(this, new PermalinkDetectedEventArgs(
+ new LocationChangedEventArgs(_navigationManager.Uri, false), perma));
+ }
+ }
+
+ return perma;
+ }
+
+ private string? DetectPermalink(string uri)
+ {
+ var match = _poundRegex.Match(uri);
+ if (match.Success && match.Groups.Count == 2)
{
var perma = match.Groups[1].Value;
- _logger.LogDebug($"{nameof(PermaLinkWatcherService)} - {nameof(HandleLocationChanged)}: PermaLink found: {perma}");
- _scrollHandler.ScrollToElementByNameAsync(perma);
+ _logger.LogDebug($"{nameof(PermaLinkWatcherService)} - {nameof(DetectPermalink)}: PermaLink found: '{perma}'");
+
+ 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.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.Tabs.Tests/TabsPanelTest.cs b/src/Majorsoft.Blazor.Components.Tabs.Tests/TabsPanelTest.cs
index bacdabbb..2c9f19da 100644
--- a/src/Majorsoft.Blazor.Components.Tabs.Tests/TabsPanelTest.cs
+++ b/src/Majorsoft.Blazor.Components.Tabs.Tests/TabsPanelTest.cs
@@ -4,6 +4,7 @@
using Bunit;
using Majorsoft.Blazor.Components.CommonTestsBase;
+using Majorsoft.Blazor.Components.PermaLink;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.DependencyInjection;
@@ -17,11 +18,16 @@ namespace Majorsoft.Blazor.Components.Tabs.Tests
[TestClass]
public class TabsPanelTest : ComponentsTestBase
{
+ Mock _peramalinkMock;
+
[TestInitialize]
public void Init()
{
var logger = new Mock>();
+ _peramalinkMock = new Mock();
+
_testContext.Services.Add(new ServiceDescriptor(typeof(ILogger), logger.Object));
+ _testContext.Services.Add(new ServiceDescriptor(typeof(IPermaLinkWatcherService), _peramalinkMock.Object));
}
[TestMethod]
@@ -86,6 +92,9 @@ public void TabsPanel_should_render_correct_html()
var tab2 = _testContext.RenderComponent(parameters => parameters
.Add(p => p.Parent, rendered.Instance));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -113,6 +122,9 @@ public void TabsPanel_should_render_correct_TabItems_Header_and_Content()
.Add(p => p.Header, "Tab header 2")
.Add(p => p.Content, "tab content 2"));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
Assert.AreEqual(rendered.Instance.ActiveTab, tab1.Instance);
@@ -151,6 +163,9 @@ public void TabsPanel_should_render_correct_TabItem_Disabled()
.Add(p => p.Content, "tab content 2")
.Add(p => p.Disabled, true));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
Assert.AreEqual(rendered.Instance.ActiveTab, tab1.Instance);
@@ -183,6 +198,9 @@ public void TabsPanel_should_render_correct_TabItem_Hidden()
.Add(p => p.Header, "Tab header 2")
.Add(p => p.Content, "tab content 2")
.Add(p => p.Hidden, true));
+
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -211,6 +229,9 @@ public void TabsPanel_should_render_correct_ActiveColor()
var tab2 = _testContext.RenderComponent(parameters => parameters
.Add(p => p.Parent, rendered.Instance));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -234,6 +255,9 @@ public void TabsPanel_should_render_correct_InactiveColor()
var tab2 = _testContext.RenderComponent(parameters => parameters
.Add(p => p.Parent, rendered.Instance));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -257,6 +281,9 @@ public async Task TabsPanel_should_render_correct_HoverColor_on_active_Tab()
var tab2 = _testContext.RenderComponent(parameters => parameters
.Add(p => p.Parent, rendered.Instance));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -293,6 +320,9 @@ public async Task TabsPanel_should_render_correct_HoverColor_on_inactive_Tab()
var tab2 = _testContext.RenderComponent(parameters => parameters
.Add(p => p.Parent, rendered.Instance));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -329,6 +359,9 @@ public void TabsPanel_should_render_non_Animate()
var tab2 = _testContext.RenderComponent(parameters => parameters
.Add(p => p.Parent, rendered.Instance));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -352,6 +385,9 @@ public void TabsPanel_should_render_correct_TabItemsHeight()
var tab2 = _testContext.RenderComponent(parameters => parameters
.Add(p => p.Parent, rendered.Instance));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -375,6 +411,9 @@ public void TabsPanel_should_render_correct_TabItemsWidth()
var tab2 = _testContext.RenderComponent(parameters => parameters
.Add(p => p.Parent, rendered.Instance));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -390,6 +429,8 @@ public void TabsPanel_should_render_correct_TabItemsWidth()
[TestMethod]
public async Task TabsPanel_should_render_correct_active_Tab_on_click()
{
+ _peramalinkMock.Setup(s => s.ChangePermalink(It.IsAny(), It.IsAny()));
+
var rendered = _testContext.RenderComponent();
var tab1 = _testContext.RenderComponent(parameters => parameters
@@ -397,6 +438,9 @@ public async Task TabsPanel_should_render_correct_active_Tab_on_click()
var tab2 = _testContext.RenderComponent(parameters => parameters
.Add(p => p.Parent, rendered.Instance));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -419,6 +463,8 @@ public async Task TabsPanel_should_render_correct_active_Tab_on_click()
");
});
+
+ _peramalinkMock.Verify(v => v.ChangePermalink(It.IsAny(), It.IsAny()), Times.Never);
}
[TestMethod]
@@ -432,6 +478,9 @@ public void TabsPanel_should_render_correct_TabPositon()
var tab2 = _testContext.RenderComponent(parameters => parameters
.Add(p => p.Parent, rendered.Instance));
+ rendered.SetParametersAndRender(parameters => parameters
+ .Add(p => p.ActiveTab, tab1.Instance)); //This works automatically but Unit tests not render components at once
+
var div = rendered.Find("div");
Assert.IsNotNull(div);
@@ -449,5 +498,73 @@ public void TabsPanel_should_render_correct_TabPositon()
");
}
}
+
+ [TestMethod]
+ public void TabsPanel_should_render_correct_TabActivation()
+ {
+ _peramalinkMock.Setup(s => s.ChangePermalink(It.IsAny(), It.IsAny()));
+ _peramalinkMock.SetupAdd(s => s.PermalinkDetected += It.IsAny>());
+ _peramalinkMock.SetupRemove(s => s.PermalinkDetected -= It.IsAny>());
+
+ var rendered = _testContext.RenderComponent(parameters => parameters
+ .Add(p => p.AllowTabActivationByPermalink, true));
+
+ var tab1 = _testContext.RenderComponent(parameters => parameters
+ .Add(p => p.Parent, rendered.Instance)
+ .Add(p => p.Permalink, "tab1"));
+ var tab2 = _testContext.RenderComponent(parameters => parameters
+ .Add(p => p.Parent, rendered.Instance)
+ .Add(p => p.Permalink, "tab2"));
+
+ _peramalinkMock.Raise(e => e.PermalinkDetected += null, new PermalinkDetectedEventArgs(null, "tab2"));
+ rendered.Render();
+
+ var div = rendered.Find("div");
+ Assert.IsNotNull(div);
+
+ var id = div.GetAttribute("id");
+ div.MarkupMatches(@$"
Example usage of multiple independent CollapsePanels with default settings.
-
- Note: content height: auto prevents some CSS animation to work.
+
+
+ Note: in case of Content panel ContentHeight is set to 0 (auto), then use MaxAllowedContentHeight to set max-height CSS property which will be animated.
+ Also important based on max-height value transition speed for expand/collapse might differ!
+
@@ -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/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/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
+ _goDelta = int.Parse(e.Value?.ToString()))" />
+
+
+
+