diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/BrowserStorage.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/BrowserStorage.razor new file mode 100644 index 00000000..4e214371 --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/BrowserStorage.razor @@ -0,0 +1,302 @@ +
+

Browser Storage extensions

+

+ Enables Browser Local and Session storages and + Cookies store access for Blazor applications. For usage see source code and docs on + Github. +
Majorsoft.Blazor.Extensions.BrowserStorage package is available on Nuget +

+ +
+

Browser Storage features:

+ +
+ +
+ +

Local Storage

+
+

+ Browser Local Storage is an injectable ILocalStorageService service for accessing + Browser's Local Storage API. +

+ +
+
+ LocalStorage item Count: @_localStorageCount + + @if (_localStorageCount > 0) + { + + + + + + + + @foreach (var item in _localStorageItems) + { + + + + + + } +
KeyValue
@item.Key@item.Value
+ } + + + + +
+
+ +
+
+ +
+ Name: + + Age: + + + +
+
+
+ +
+ +

Session Storage

+
+

+ Browser Session Storage is an injectable ISessionStorageService service for accessing + Browser's Session Storage API. +

+ +
+
+ SessionStorage item Count: @_sessionStorageCount + + @if (_sessionStorageCount > 0) + { + + + + + + + + @foreach (var item in _sessionStorageItems) + { + + + + + + } +
KeyValue
@item.Key@item.Value
+ } + + + + +
+
+
+ +
+
+ +
+ Name: + + Age: + + + +
+
+ +
+ +

Cookie Store

+
+

+ Browser Cookie Store is an injectable ICookieStoreService service for accessing + Browser's Cookie Store API. +
+ Note: This feature is available only in secure contexts (HTTPS), in some or all supporting browsers. +

+ + LocalStorage item Count: @_allCookies?.Count() + + @if (_allCookies?.Count() > 0) + { + + + + + + + + + + + + + + @foreach (var item in _allCookies) + { + + + + + + + + + + + } +
NameValueDomainPortPathExpiresHttpOnlySecure
@item.Name@item.Value@item.Domain@item.Port@item.Path@item.ExpiresAt (@item.Expires)@item.HttpOnly@item.Secure
+ + + } +
+
+
+ +
+ Value: + + + +
+
+
+ +@inject ILocalStorageService _localStorageService +@inject ISessionStorageService _sessionStorageService +@inject ICookieStoreService _cookieStoreService + +@code { + public class UserInfo + { + public string Name { get; set; } + public int Age { get; set; } + } + + protected override async Task OnInitializedAsync() + { + //LocalStorage + await InsertLocalStorageItems(); + _localStorageCount = await _localStorageService.CountAsync(); + await RefreshLocalStorageItems(); + + //SessionStorage + await InsertSessionStorageItems(); + _sessionStorageCount = await _sessionStorageService.CountAsync(); + await RefreshSessionStorageItems(); + + //CookieStore + _cookie = (await _cookieStoreService.GetAsync("CustomCookie")) ?? new Cookie(); + _allCookies = await _cookieStoreService.GetAllAsync(); + } + + //LocalStorage + private int _localStorageCount; + private UserInfo _localUserInfo = new UserInfo(); + private IList> _localStorageItems; + + private async Task InsertLocalStorageItems() + { + await _localStorageService.SetItemAsync("Local_Numeric_value", 2.5); + await _localStorageService.SetItemAsync("Local_String_value", "hello"); + await _localStorageService.SetItemAsync("Local_Data time", DateTime.Now); + await _localStorageService.SetItemAsync("Local_Test_object", new UserInfo { Name = "Name", Age = 22 }); + await _localStorageService.SetItemAsync("Local_Null_string", null); + await _localStorageService.SetItemAsync("Local_Null_object", null); + } + private async Task RefreshLocalStorageItems() + { + _localStorageItems = new List>(); + await foreach (var key in _localStorageService.GetAllKeysAsync()) + { + if (key is null) + continue; + + _localStorageItems.Add(new KeyValuePair(key, await _localStorageService.GetItemAsStringAsync(key))); + } + + _localUserInfo = await _localStorageService.GetItemAsync("Local_customData") ?? new UserInfo(); + } + private async Task SaveCustomLocalStorageItem() + { + await _localStorageService.SetItemAsync("Local_customData", _localUserInfo); + await RefreshLocalStorageItems(); + } + + //SessionStorage + private int _sessionStorageCount; + private UserInfo _sessionUserInfo = new UserInfo(); + private IList> _sessionStorageItems; + + private async Task InsertSessionStorageItems() + { + await _sessionStorageService.SetItemAsync("Session_Numeric_value", 2.5); + await _sessionStorageService.SetItemAsync("Session_String_value", "hello"); + await _sessionStorageService.SetItemAsync("Session_Data time", DateTime.Now); + await _sessionStorageService.SetItemAsync("Session_Test_object", new UserInfo { Name = "Name", Age = 22 }); + await _sessionStorageService.SetItemAsync("Session_Null_string", null); + await _sessionStorageService.SetItemAsync("Session_Null_object", null); + } + private async Task RefreshSessionStorageItems() + { + _sessionStorageItems = new List>(); + await foreach (var key in _sessionStorageService.GetAllKeysAsync()) + { + if (key is null) + continue; + + _sessionStorageItems.Add(new KeyValuePair(key, await _sessionStorageService.GetItemAsStringAsync(key))); + } + + _sessionUserInfo = await _sessionStorageService.GetItemAsync("Session_customData") ?? new UserInfo(); + } + private async Task SaveCustomSessionStorageItem() + { + await _sessionStorageService.SetItemAsync("Session_customData", _sessionUserInfo); + await RefreshSessionStorageItems(); + } + + //CookieStore + Cookie _cookie = new Cookie(); + IEnumerable _allCookies; + + private async Task SaveCustomCookieAndRefresh(string value) + { + await _cookieStoreService.SetAsync(new Cookie() + { + Name = "CustomCookie", + Value = value, + //Path = "/", + //Domain = "localhost", + //Secure = true, + //Expires = Cookie.ConvertToUnixDate(DateTime.Now.AddDays(1)) + }); + + _cookie = await _cookieStoreService.GetAsync("CustomCookie"); + _allCookies = await _cookieStoreService.GetAllAsync(); + } + private async Task RemoveCustomCookieAndRefresh() + { + await _cookieStoreService.DeleteAsync("CustomCookie"); + _cookie = new Cookie(); + _allCookies = await _cookieStoreService.GetAllAsync(); + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/Collapse.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/Collapse.razor index 4a37de0b..e53a281e 100644 --- a/demo/Majorsoft.Blazor.Components.DemoApp/Components/Collapse.razor +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/Collapse.razor @@ -28,7 +28,7 @@
- CollapsePanel contet Height (0 is auto): @(_height)px + CollapsePanel content Height (0 is auto): @(_height)px
@@ -91,8 +91,9 @@

Multiple CollapsePanel items

- Example usage of multiple independent CollapsePanels with default settings. - Note: content height: auto prevents some CSS animation to work. + Example usage of multiple independent CollapsePanels with default settings. +
+ Note: content height: auto prevents some CSS animation to work.

diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/CssEvents.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/CssEvents.razor index d687b06c..e734e8b1 100644 --- a/demo/Majorsoft.Blazor.Components.DemoApp/Components/CssEvents.razor +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/CssEvents.razor @@ -1,11 +1,17 @@ 

Css Events

- Blazor Extensions and Components wrapper to notify on CSS Transition and Animation events. For usege see soruce code and docs on + Blazor injectable Services and wrapper Components to notify on CSS Transition and Animation events. For usage see source code and docs on Github.
Majorsoft.Blazor.Components.CssEvents package is available on Nuget

+

CSS event features:

+
    +
  • Transitions
  • +
  • Animations
  • +
+ \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/Index.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/Index.razor index 2c514a40..28374820 100644 --- a/demo/Majorsoft.Blazor.Components.DemoApp/Components/Index.razor +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/Index.razor @@ -14,6 +14,14 @@

Extensions

  • Browser Console logger
  • +
  • + Browser Storage +
      +
    • Local Storage
    • +
    • Session Storage
    • +
    • Cookie Store
    • +
    +

@@ -34,6 +42,8 @@
  • Scroll Js
  • Resize Js
  • Clipboard Js
  • +
  • Language Js
  • +
  • Geo Js
  • @@ -52,6 +62,13 @@
  • Customizable PermaLinkElement
  • +
  • + Maps +
      +
    • Google
    • +
    • Bing
    • +
    +
  • Toggle
      @@ -61,8 +78,14 @@
  • Tabs
  • -
  • Collapse
  • -
  • Drag and Drop
  • -
  • Color Picker
  • +
  • + Collapse +
      +
    • Collapse Panel
    • +
    • Accordion
    • +
    +
  • + @*
  • Drag and Drop
  • +
  • Color Picker
  • *@

    \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JSInterop.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JSInterop.razor index e4d4055a..a08f189f 100644 --- a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JSInterop.razor +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JSInterop.razor @@ -1,464 +1,72 @@ 

    Js Interop controls and extensions

    - Blazor components and extensions that provide useful functionality which can be achieved only with Js Interop. For usege see soruce code and docs on - Github. -
    Majorsoft.Blazor.Components.Common.JsInterop package is available on Nuget + Blazor components, injectable services and extensions that provides useful functionality and event notifications which can be achieved only with JS Interop e.g. + scroll, clipboard, focus, resize, language detection, Geolocation, HTML Head (title, meta, SEO), etc.. +
    + Note: all injected JS services must be disposed! For usage see source code and docs on + Github. +
    Majorsoft.Blazor.Components.Common.JsInterop package is available on Nuget

    +@*Later add scroll components here*@ +
    -

    JS Interop features:

    -
      -
    • Click Js
    • -
    • Mouse Js
    • -
    • Focus Js
    • -
    • Element info Js
    • -
    • Scroll Js
    • -
    • Resize Js
    • -
    • Clipboard Js
    • -
    +

    JS Interop features:

    +
      +
    • Click Js
    • +
    • Mouse Js
    • +
    • Focus Js
    • +
    • Element info Js
    • +
    • Scroll Js
    • +
    • Resize Js
    • +
    • Clipboard Js
    • +
    • Browser Language Js
    • +
    • Geolocation Js
    • +
    • Head Js
    • +
    -
    - -

    Click JS

    -
    -

    - Click JS is a convenient Blazor component which wraps the given content to a DIV and subscribes to all - click events: OnOutsideClick, OnInsideClick. Component will also dispose resources. - Also an injectable IClickBoundariesHandler service for callback event handlers. -
    - - Blazor supports @@onclick event which is equivalent with OnInsideClick. This component useful when need to detect if click - happened outside of the component with OnOutsideClick. - -

    + -
    -
    -
    - Inside A -
    - Inside B -
    -
    -
    -
    + -
    -
    -

    RegisterPageScrollAsync() will add event listener to HTML document/window click

    - -
    + - - -
    -
    -
    - -
    - -

    Global Mouse JS

    -
    -

    - Global Mouse JS is an injectable IGlobalMouseEventHandler service for global mouse callback event handlers. -
    - Blazor supports @@onclick, @@onmousemove, etc. events but only for Blazor rendered elements. With this service you can get similar event notifications - for the whole document/window. -

    - -
    -
    -

    RegisterPageMouseDownAsync(), RegisterPageMouseUpAsync(), RegisterPageMouseMoveAsync() will add event listeners to HTML document/window mouse events.

    - -
    - - - -
    -
    -
    - -
    - -

    Focus JS

    -
    -

    - Focus JS is an injectable IFocusHandler service. Blazor supports FocusAsync() - which can be used in case of ElementReference is available. -
    - Focus JS is able to identify and restore focus on ANY DOM element without using Blazor @@ref="" tag. It is useful when focus should be - restored on a parent component but element reference not stored or it's on a different component. -

    - -
    -
    - - - + -
    -

    Click on ANY focusable elemenet and Hover over to store actul focused element

    -
    -
    -
    - -
    -
    - - -
    -
    -
    - -
    - -

    Element info JS

    -
    -

    - Element info JS is a set of Extension methods for ElementReference objects. -

    - -
    -
    - - - - -
    - - -
    -
    -
    + -
    - -

    Scroll JS

    -
    -

    - Scroll JS is a set of Extension methods for ElementReference objects. - Also an injectable IScrollHandler service for non element level functions and callback event handlers. -

    + -
    -
    - -
    - -
    -
    + -
    + -
    -
    -

    RegisterPageScrollAsync() will add event listener to HTML document/window scroll

    - -
    - - -
    -
    + -
    - -
    -
    - -
    - - -
    -
    - -
    -
    - -
    - -

    Resize JS

    -
    -

    - Resize JS is an injectable IResizeHandler service for Window (global) and HTML Elements resize event callback handlers. -

    - -
    -
    -

    RegisterPageResizeAsync() will add event listener to HTML document/window resize events.

    -

    RegisterResizeAsync() will add event listener to the given HTML Element resize events.

    - - -
    - - -
    - -
    - - -
    -
    - -
    - -
    - -

    Clipboard JS

    -
    -

    - Resize JS is an injectable IClipboardHandler service for accessing computer Clipboard from Blazor Application. -

    - -
    -
    - - -
    -
    - -
    -
    - - -
    -
    -
    + +@*Later add scroll components here*@ +
    - -@using System.Text.Json @implements IAsyncDisposable - @inject IScrollHandler _scrollHandler -@inject IFocusHandler _focusHandler -@inject IResizeHandler _resizeHandler -@inject IClickBoundariesHandler _clickHandler; -@inject IClipboardHandler _clipboardHandler; -@inject IGlobalMouseEventHandler _globalMouseEventHandler; @code { - private string _scrollEventId = null; - protected override async Task OnInitializedAsync() - { - await ScrollEventHandler(); - await ClickEventHandler(); - await GlobalClickEventHandler(); - await ResizeEventHandler(); - } - - //Click examples - private ElementReference _log; - private ElementReference _clickDiv; - private string _logMessage; - private bool _clickSubscribed; - private async Task ClickEventHandler() - { - if (_clickSubscribed) - { - await _clickHandler.RemoveClickBoundariesAsync(_clickDiv); - } - else - { - await _clickHandler.RegisterClickBoundariesAsync(_clickDiv, - async (args) => await PageClick(args, "OUSIDE of registered element"), - async (args) => await PageClick(args, "INSIDE of registered element")); - } - - _clickSubscribed = !_clickSubscribed; - } - private async Task PageClick(MouseEventArgs args, string message) - { - _logMessage += $"{DateTime.Now.TimeOfDay}: Page clicked {message} X pos: {args.ClientX}, Y pos: {args.ClientY}{Environment.NewLine}"; - StateHasChanged(); - - await _log.ScrollToEndAsync(); - } - - //Mouse examples - private ElementReference _log3; - private string _logMessage3; - private bool _clickSubscribed2; - private string _mouseDown, _mouseUp, _mouseMove; - private async Task GlobalClickEventHandler() - { - if (_clickSubscribed2) - { - await _globalMouseEventHandler.RemovePageMouseDownAsync(_mouseDown); - await _globalMouseEventHandler.RemovePageMouseUpAsync(_mouseUp); - await _globalMouseEventHandler.RemovePageMouseMoveAsync(_mouseMove); - } - else - { - _mouseDown = await _globalMouseEventHandler.RegisterPageMouseDownAsync(async (args) => await PageMouseEvents(args, "Mouse DOWN")); - _mouseUp = await _globalMouseEventHandler.RegisterPageMouseUpAsync(async (args) => await PageMouseEvents(args, "Mouse UP")); - _mouseMove = await _globalMouseEventHandler.RegisterPageMouseMoveAsync(async (args) => await PageMouseEvents(args, "Mouse MOVE")); - } - - _clickSubscribed2 = !_clickSubscribed2; - } - private async Task PageMouseEvents(MouseEventArgs args, string message) - { - _logMessage3 += $"{DateTime.Now.TimeOfDay}: Global Mouse event: {message} X pos: {args.ClientX}, Y pos: {args.ClientY}, Buttons: {args.Buttons}{Environment.NewLine}"; - StateHasChanged(); - - await _log3.ScrollToEndAsync(); - } - - //Focus examples - private string _focusMsg; - private async Task StoreFocus() - { - await _focusHandler.StoreFocusedElementAsync(); - _focusMsg = "Focused element stored, click somewhere else and do not hover on RED area."; - } - private async Task RestoreFocus() - { - await _focusHandler.RestoreStoredElementFocusAsync(); - _focusMsg = "Focus restored to previous element."; - } - - //Element info examples - private string _infoMessage; - private ElementReference _info1; - private ElementReference _info2; - private ElementReference _infoMsg; - private ElementReference _infoDiv; - private async Task GetInfo1() - { - var info = await _info1.GetClientRectAsync(); - _infoMessage = JsonSerializer.Serialize(info); - } - private async Task GetInfo2() - { - var info = await _info2.GetClientRectAsync(); - _infoMessage = JsonSerializer.Serialize(info); - } - private async Task InfoMsgClick() - { - var info = await _infoMsg.GetClientRectAsync(); - _infoMessage = JsonSerializer.Serialize(info); - } - private async Task InfoDivClick() - { - var info = await _infoDiv.GetClientRectAsync(); - _infoMessage = JsonSerializer.Serialize(info); - } - - //Scroll examples - private ElementReference _scrollToView; - private async Task ScrollToView() - { - await _scrollToView.ScrollToElementAsync(); - } - - private ElementReference _log2; - private string _logMessage2; - private async Task PageScrolled(ScrollEventArgs args) - { - _logMessage2 += $"{DateTime.Now.TimeOfDay}: Page scrolled: X pos: {args.X}, Y pos: {args.Y}{Environment.NewLine}"; - StateHasChanged(); - - await _log2.ScrollToEndAsync(); - } - - private bool _scrollSubscribed = false; - private async Task ScrollEventHandler() - { - if (_scrollSubscribed) - { - await _scrollHandler.RemovePageScrollAsync(_scrollEventId); - } - else - { - _scrollEventId = await _scrollHandler.RegisterPageScrollAsync(PageScrolled); - } - - _scrollSubscribed = !_scrollSubscribed; - } - - //Resize JS - private bool _resizeSubscribed; - private async Task ResizeEventHandler() - { - if (_resizeSubscribed) - { - await _resizeHandler.RemovePageResizeAsync(_scrollEventId); - await _resizeHandler.RemoveResizeAsync(_resizeElement); - } - else - { - _scrollEventId = await _resizeHandler.RegisterPageResizeAsync(arg => Resized(arg, "Window resized")); - await _resizeHandler.RegisterResizeAsync(_resizeElement, arg => Resized(arg, "Textarea resized")); - } - - _resizeSubscribed = !_resizeSubscribed; - } - - private ElementReference _resizeElement; - private ElementReference _log4; - private string _logMessage4; - private async Task Resized(ResizeEventArgs args, string message) - { - _logMessage4 += $"{DateTime.Now.TimeOfDay}: {message} - Width: {args.Width}, Height: {args.Height}{Environment.NewLine}"; - StateHasChanged(); - - await _log4.ScrollToEndAsync(); - } - - //Clipboard JS - private ElementReference _copyInput; - private string _codedString = "Copy string value form C# code..."; - private async Task CopyToClipboard() - { - await _copyInput.CopyElementTextToClipboardAsync(); - @*await _clipboardHandler.CopyElementTextToClipboardAsync(_copyInput);*@ - } - private async Task CopyTextToClipboard() - { - await _clipboardHandler.CopyTextToClipboardAsync(_codedString); - } - - public async ValueTask DisposeAsync() - { - if(_scrollHandler is not null) - { - await _scrollHandler.RemovePageScrollAsync(_scrollEventId); - await _scrollHandler.DisposeAsync(); - } - - if(_focusHandler is not null) - { - await _focusHandler.DisposeAsync(); - } - - if(_clickHandler is not null) - { - await _clickHandler.DisposeAsync(); - } - - if(_clipboardHandler is not null) - { - await _clipboardHandler.DisposeAsync(); - } - - if(_globalMouseEventHandler is not null) - { - await _globalMouseEventHandler.DisposeAsync(); - } - - if (_resizeHandler is not null) - { - await _resizeHandler.DisposeAsync(); - } - } + public async ValueTask DisposeAsync() + { + if (_scrollHandler is not null) + { + await _scrollHandler.DisposeAsync(); + } + } } \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ClickJs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ClickJs.razor new file mode 100644 index 00000000..606cf44e --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ClickJs.razor @@ -0,0 +1,134 @@ +
    + +

    Click JS

    +
    +

    + Click JS is a convenient Blazor component and also an injectable service to detect mouse clicks + inside and outside of a given HTML element. +
    + + NOTE: Blazor supports @@onclick event which is equivalent with OnInsideClick. This functionality is useful when need to detect if click + happened outside of the component with OnOutsideClick event. + +

    + +
    +

    IClickBoundariesHandler injectable service

    +

    + IClickBoundariesHandler is an injectable service with RenderFragment for content and callback event handlers + for OnOutsideClick, OnInsideClick. +

    + +
    +
    +
    + Inside A +
    + Inside B +
    +
    +
    +
    +
    +
    +

    RegisterPageScrollAsync() will add event listener to HTML document/window click

    + +
    + + + +
    +
    +
    + +
    +

    ClickBoundariesElement component

    +

    + ClickBoundariesElementis a convenient Blazor component which wraps the given content to a DIV and subscribes to all + click events: OnOutsideClick, OnInsideClick. Component will also dispose resources. +

    + +
    +
    + + +
    + ClickBoundariesElement:
    Inside A +
    + Inside B +
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + +@implements IAsyncDisposable +@inject IClickBoundariesHandler _clickHandler; + +@code { + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await ClickEventHandler(); + StateHasChanged(); + } + } + + //Click examples + private ElementReference _clickLog; + private ElementReference _clickDiv; + private string _clickLogMessage; + private bool _clickSubscribed = false; + + private ElementReference _clickLog2; + private string _clickLogMessage2; + + private async Task ClickEventHandler() + { + if (_clickSubscribed) + { + await _clickHandler.RemoveClickBoundariesAsync(_clickDiv); + } + else + { + await _clickHandler.RegisterClickBoundariesAsync(_clickDiv, + async (args) => await PageClick(args, "OUSIDE of registered element"), + async (args) => await PageClick(args, "INSIDE of registered element")); + } + + _clickSubscribed = !_clickSubscribed; + } + private async Task PageClick(MouseEventArgs args, string message) + { + _clickLogMessage += $"{DateTime.Now.TimeOfDay}: IClickBoundariesHandler Page clicked {message} X pos: {args.ClientX}, Y pos: {args.ClientY}{Environment.NewLine}"; + StateHasChanged(); + + await _clickLog.ScrollToEndAsync(); + } + private async Task PageClick2(MouseEventArgs args, string message) + { + _clickLogMessage2 += $"{DateTime.Now.TimeOfDay}: ClickBoundariesElement Page clicked {message} X pos: {args.ClientX}, Y pos: {args.ClientY}{Environment.NewLine}"; + //StateHasChanged(); + + await _clickLog2.ScrollToEndAsync(); + } + + public async ValueTask DisposeAsync() + { + if (_clickHandler is not null) + { + await _clickHandler.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ClipboardJs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ClipboardJs.razor new file mode 100644 index 00000000..b3f92256 --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ClipboardJs.razor @@ -0,0 +1,50 @@ +
    + +

    Clipboard JS

    +
    +

    + Resize JS is an injectable IClipboardHandler service for accessing computer Clipboard from Blazor Application. +

    + +
    +
    + + +
    +
    + +
    +
    + + +
    +
    +
    + +@implements IAsyncDisposable +@inject IClipboardHandler _clipboardHandler; + +@code { + + //Clipboard JS + private ElementReference _copyInput; + private string _codedString = "Copy string value form C# code..."; + private async Task CopyToClipboard() + { + await _copyInput.CopyElementTextToClipboardAsync(); + @*await _clipboardHandler.CopyElementTextToClipboardAsync(_copyInput);*@ + } + private async Task CopyTextToClipboard() + { + await _clipboardHandler.CopyTextToClipboardAsync(_codedString); + } + + public async ValueTask DisposeAsync() + { + + if (_clipboardHandler is not null) + { + await _clipboardHandler.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/FocusJs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/FocusJs.razor new file mode 100644 index 00000000..fc547086 --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/FocusJs.razor @@ -0,0 +1,57 @@ +
    + +

    Focus JS

    +
    +

    + Focus JS is an injectable IFocusHandler service. Blazor supports FocusAsync() + which can be used in case of ElementReference is available. +
    + Focus JS is able to identify and restore focus on ANY DOM element without using Blazor @@ref="" tag. It is useful when focus should be + restored on a parent component but element reference not stored or it's on a different component. +

    + +
    +
    + + + + +
    +

    Click on ANY focusable element and Hover over to store actual focused element

    +
    +
    +
    + +
    +
    + + +
    +
    +
    + +@implements IAsyncDisposable +@inject IFocusHandler _focusHandler + +@code { + //Focus examples + private string _focusMsg; + private async Task StoreFocus() + { + await _focusHandler.StoreFocusedElementAsync(); + _focusMsg = "Focused element stored, click somewhere else and do not hover on RED area."; + } + private async Task RestoreFocus() + { + await _focusHandler.RestoreStoredElementFocusAsync(); + _focusMsg = "Focus restored to previous element."; + } + + public async ValueTask DisposeAsync() + { + if (_focusHandler is not null) + { + await _focusHandler.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/GeoJs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/GeoJs.razor new file mode 100644 index 00000000..d3340d3e --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/GeoJs.razor @@ -0,0 +1,192 @@ +
    + +

    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. +

    +

    + NOTE: Geolocation only accurate for devices with GPS, e.g. smartphones.
    + In most cases users have to enable it and grant permission to access location data!
    + Also some properties of the response might be not available like Speed, Heading because of required hardwares: GPS, compass, etc. +

    + +
    +

    + Show location on Maps +

    + +
    +
    +
    High Accuracy:
    +
    +
    +
    +
    + Maximum timeout (in milliseconds): @(_geoTimeout) ms (0 is infinity). + +
    +
    +
    +
    + Cache time (in milliseconds): @(_geoCacheTime) ms (0 is no Cache). + +
    +
    + +

    GetCurrentPosition method

    +

    + GetCurrentPosition method registers a handler function which will be called only once with the + current position of the device, or occurred error details. +

    +
    +
    + +
    +
    +
    +
    + + @if (!string.IsNullOrWhiteSpace(_currentLocationError)) + { +

    + +

    + } + else if (@_currentLocation is not null) + { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Latitude:@_currentLocation.Latitude
    Longitude:@_currentLocation.Longitude
    Accuracy:@(_currentLocation.Accuracy) m
    Altitude:@_currentLocation.Altitude
    Altitude Accuracy:@(_currentLocation.AltitudeAccuracy) m
    Speed:@(_currentLocation.Altitude) m/s
    Heading:@_currentLocation.Heading
    + } +
    +
    +
    + +

    AddGeolocationWatcher method

    +

    + AddGeolocationWatcher Register a handler function that will be called automatically each time + the position of the device changes with position of the device, or occurred error details. + Position watcher can be removed with RemoveGeolocationWatcher method. +

    + +

    + Show location on Maps +

    + +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    + +@implements IAsyncDisposable +@inject IGeolocationService _geoLocationService; + +@code { + //Geo + private bool _geoAccuracy = false; + private int _geoTimeout = 5000; + private int _geoCacheTime = 0; + private GeolocationCoordinates? _currentLocation; + private string _currentLocationError; + + private async Task CurrentLocationResult(GeolocationResult geolocationResult) + { + _currentLocation = null; + _currentLocationError = null; + + if (geolocationResult.IsSuccess) + { + _currentLocation = geolocationResult.Coordinates; + } + else + { + _currentLocationError = $"Failed to get location. Error code: {geolocationResult.Error?.ErrorCode.ToString()} Reason: {geolocationResult.Error?.ErrorMessage}"; + } + + StateHasChanged(); + } + + private int _geoEventId; + private bool _geoSubscribed; + private async Task GeoEventHandler() + { + if (_geoSubscribed) + { + await _geoLocationService.RemoveGeolocationWatcherAsync(_geoEventId); + } + else + { + _geoEventId = await _geoLocationService.AddGeolocationWatcherAsync(OnLocationChanged, + _geoAccuracy, + TimeSpan.FromMilliseconds(_geoTimeout), + TimeSpan.FromMilliseconds(_geoCacheTime)); + } + + _geoSubscribed = !_geoSubscribed; + } + + private ElementReference _log; + private string _logMessage; + private async Task OnLocationChanged(GeolocationResult geolocationResult) + { + var message = geolocationResult.IsSuccess + ? $"Latitude: {geolocationResult.Coordinates?.Latitude}, Longitude: {geolocationResult.Coordinates?.Longitude}, Accuracy: {geolocationResult.Coordinates?.Accuracy} m, Speed: {geolocationResult.Coordinates?.Speed} m/s." + : $"Failed to get location. Error code: {geolocationResult.Error?.ErrorCode.ToString()} Reason: {geolocationResult.Error?.ErrorMessage}"; + + _logMessage += $"{geolocationResult.TimeStamp}: {message}{Environment.NewLine}"; + StateHasChanged(); + + await _log.ScrollToEndAsync(); + } + + public async ValueTask DisposeAsync() + { + if (_geoLocationService is not null) + { + await _geoLocationService.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/HeadJs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/HeadJs.razor new file mode 100644 index 00000000..6abf6bb0 --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/HeadJs.razor @@ -0,0 +1,71 @@ +
    + +

    HTML Head JS

    +
    +

    + HTML Head JS is an injectable IHtmlHeadService service for accessing and setting HTML document Head tags. + It is useful when Blazor application has dynamic content and you need to change Title, FavIcon or Meta tags for SEO (Search Engine Optimization). +

    + +
    +
    + + + +
    +
    + +
    +
    + + + +
    +
    +
    + +@implements IAsyncDisposable +@inject IHtmlHeadService _htmlHeadService; + +@code { + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + _title = await _htmlHeadService.GetHtmlTitleAsync(); + _icons = await _htmlHeadService.GetHtmlFavIconsAsync(); + _favIcon = _icons.FirstOrDefault()?.Href; + + StateHasChanged(); + } + } + + private string _title; + private string _favIcon; + private IEnumerable _icons; + + private async Task SetPageTitle() + { + await _htmlHeadService.SetHtmlTitleAsync(_title); + } + private async Task SetPageFavIcon() + { + await _htmlHeadService.SetHtmlFavIconsAsync(new HtmlHeadLinkTag[] { + new HtmlHeadLinkTag() + { + Href = _favIcon, + Sizes ="16x16", + Rel = HtmlHeadLinkTagRelTypes.Icon.ToString(), + } + }); + } + + public async ValueTask DisposeAsync() + { + if (_htmlHeadService is not null) + { + await _htmlHeadService.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/InfoJs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/InfoJs.razor new file mode 100644 index 00000000..96f2ffe4 --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/InfoJs.razor @@ -0,0 +1,51 @@ +
    + +

    Element info JS

    +
    +

    + Element info JS is a set of Extension methods for ElementReference objects. +

    + +
    +
    + + + + +
    + + +
    +
    +
    + +@using System.Text.Json + +@code { + //Element info examples + private string _infoMessage; + private ElementReference _info1; + private ElementReference _info2; + private ElementReference _infoMsg; + private ElementReference _infoDiv; + private async Task GetInfo1() + { + var info = await _info1.GetClientRectAsync(); + _infoMessage = JsonSerializer.Serialize(info); + } + private async Task GetInfo2() + { + var info = await _info2.GetClientRectAsync(); + _infoMessage = JsonSerializer.Serialize(info); + } + private async Task InfoMsgClick() + { + var info = await _infoMsg.GetClientRectAsync(); + _infoMessage = JsonSerializer.Serialize(info); + } + private async Task InfoDivClick() + { + var info = await _infoDiv.GetClientRectAsync(); + _infoMessage = JsonSerializer.Serialize(info); + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/LangJs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/LangJs.razor new file mode 100644 index 00000000..d033841e --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/LangJs.razor @@ -0,0 +1,38 @@ +
    + +

    Browser Language JS

    +
    +

    + Browser Language JS is an injectable ILanguageService service for detect the browser language preference. + It is useful when Blazor application is localized and wants to show the app in the user preferred language. +

    + +
    +
    + +
    +
    +
    + +@using System.Globalization + +@implements IAsyncDisposable +@inject ILanguageService _languageService; + +@code { + protected override async Task OnInitializedAsync() + { + _detectedBrowserLang = await _languageService.GetBrowserLanguageAsync(); + } + + //Broswer lang + private CultureInfo _detectedBrowserLang; + + public async ValueTask DisposeAsync() + { + if (_languageService is not null) + { + await _languageService.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/MouseJs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/MouseJs.razor new file mode 100644 index 00000000..886f1992 --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/MouseJs.razor @@ -0,0 +1,70 @@ +
    + +

    Global Mouse JS

    +
    +

    + Global Mouse JS is an injectable IGlobalMouseEventHandler service for global mouse callback event handlers. +
    + Blazor supports @@onclick, @@onmousemove, etc. events but only for Blazor rendered elements. With this service you can get similar event notifications + for the whole document/window. +

    + +
    +
    +

    RegisterPageMouseDownAsync(), RegisterPageMouseUpAsync(), RegisterPageMouseMoveAsync() will add event listeners to HTML document/window mouse events.

    + +
    + + + +
    +
    +
    + +@implements IAsyncDisposable +@inject IGlobalMouseEventHandler _globalMouseEventHandler; + +@code { + protected override async Task OnInitializedAsync() + { + await GlobalClickEventHandler(); + } + + //Mouse examples + private ElementReference _log; + private string _logMessage; + private bool _clickSubscribed; + private string _mouseDown, _mouseUp, _mouseMove; + private async Task GlobalClickEventHandler() + { + if (_clickSubscribed) + { + await _globalMouseEventHandler.RemovePageMouseDownAsync(_mouseDown); + await _globalMouseEventHandler.RemovePageMouseUpAsync(_mouseUp); + await _globalMouseEventHandler.RemovePageMouseMoveAsync(_mouseMove); + } + else + { + _mouseDown = await _globalMouseEventHandler.RegisterPageMouseDownAsync(async (args) => await PageMouseEvents(args, "Mouse DOWN")); + _mouseUp = await _globalMouseEventHandler.RegisterPageMouseUpAsync(async (args) => await PageMouseEvents(args, "Mouse UP")); + _mouseMove = await _globalMouseEventHandler.RegisterPageMouseMoveAsync(async (args) => await PageMouseEvents(args, "Mouse MOVE")); + } + + _clickSubscribed = !_clickSubscribed; + } + private async Task PageMouseEvents(MouseEventArgs args, string message) + { + _logMessage += $"{DateTime.Now.TimeOfDay}: Global Mouse event: {message} X pos: {args.ClientX}, Y pos: {args.ClientY}, Buttons: {args.Buttons}{Environment.NewLine}"; + StateHasChanged(); + + await _log.ScrollToEndAsync(); + } + + public async ValueTask DisposeAsync() + { + if (_globalMouseEventHandler is not null) + { + await _globalMouseEventHandler.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ResizeJs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ResizeJs.razor new file mode 100644 index 00000000..fb3f6fda --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ResizeJs.razor @@ -0,0 +1,79 @@ +
    + +

    Resize JS

    +
    +

    + Resize JS is an injectable IResizeHandler service for Window (global) and HTML Elements resize event callback handlers. +

    + +
    +
    +

    RegisterPageResizeAsync() will add event listener to HTML document/window resize events.

    +

    RegisterResizeAsync() will add event listener to the given HTML Element resize events.

    + + +
    + + +
    + +
    + +
    + + +
    +
    + +
    + +@implements IAsyncDisposable +@inject IResizeHandler _resizeHandler + +@code { + private string _resizeEventId = null; + + protected override async Task OnInitializedAsync() + { + await ResizeEventHandler(); + _pageSize = await _resizeHandler.GetPageSizeAsync(); + } + + //Resize JS + private PageSize _pageSize; + private bool _resizeSubscribed; + private async Task ResizeEventHandler() + { + if (_resizeSubscribed) + { + await _resizeHandler.RemovePageResizeAsync(_resizeEventId); + await _resizeHandler.RemoveResizeAsync(_resizeElement); + } + else + { + _resizeEventId = await _resizeHandler.RegisterPageResizeAsync(arg => Resized(arg, "Window resized")); + await _resizeHandler.RegisterResizeAsync(_resizeElement, arg => Resized(arg, "Textarea resized")); + } + + _resizeSubscribed = !_resizeSubscribed; + } + + private ElementReference _resizeElement; + private ElementReference _log; + private string _logMessage; + private async Task Resized(ResizeEventArgs args, string message) + { + _logMessage += $"{DateTime.Now.TimeOfDay}: {message} - Width: {args.Width}, Height: {args.Height}{Environment.NewLine}"; + StateHasChanged(); + + await _log.ScrollToEndAsync(); + } + + public async ValueTask DisposeAsync() + { + if (_resizeHandler is not null) + { + await _resizeHandler.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ScrollJs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ScrollJs.razor new file mode 100644 index 00000000..2a6ebd63 --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/JsDemo/ScrollJs.razor @@ -0,0 +1,94 @@ +
    + +

    Scroll JS

    +
    +

    + Scroll JS is a set of Extension methods for ElementReference objects. + Also an injectable IScrollHandler service for non element level functions and callback event handlers. +

    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    +

    RegisterPageScrollAsync() will add event listener to HTML document/window scroll

    + +
    + + +
    +
    + +
    + +
    +
    + +
    + + +
    +
    + +
    +
    + +@implements IAsyncDisposable +@inject IScrollHandler _scrollHandler + +@code { + private string _scrollEventId = null; + + protected override async Task OnInitializedAsync() + { + await ScrollEventHandler(); + } + + //Scroll examples + private ElementReference _scrollToView; + private async Task ScrollToView() + { + await _scrollToView.ScrollToElementAsync(); + } + + private ElementReference _log; + private string _logMessage; + private async Task PageScrolled(ScrollEventArgs args) + { + _logMessage += $"{DateTime.Now.TimeOfDay}: Page scrolled: X pos: {args.X}, Y pos: {args.Y}{Environment.NewLine}"; + StateHasChanged(); + + await _log.ScrollToEndAsync(); + } + + private bool _scrollSubscribed = false; + private async Task ScrollEventHandler() + { + if (_scrollSubscribed) + { + await _scrollHandler.RemovePageScrollAsync(_scrollEventId); + } + else + { + _scrollEventId = await _scrollHandler.RegisterPageScrollAsync(PageScrolled); + } + + _scrollSubscribed = !_scrollSubscribed; + } + + public async ValueTask DisposeAsync() + { + if (_scrollHandler is not null) + { + await _scrollHandler.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/Maps.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/Maps.razor new file mode 100644 index 00000000..f1c14b66 --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/Maps.razor @@ -0,0 +1,29 @@ +

    Map Components

    + +

    + Blazor components that renders Google/Bing maps wrapped into Blazor components allowing to control and mange maps with .Net code. For usage see source code and docs on + Github. +
    Majorsoft.Blazor.Components.Maps package is available on Nuget +

    + +
    +

    Maps features:

    +

    Maps using IGeolocationService 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 and demo.

    +
      +
    • Google Maps
    • +
        +
      • Static API
      • +
      • JavaScript API
      • +
      +
    • Bing Maps
    • +
    +
    + + + + + +@code { + +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/MapsBing.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/MapsBing.razor new file mode 100644 index 00000000..13fd994f --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/MapsBing.razor @@ -0,0 +1,24 @@ +
    + +

    Bing Maps

    +
    + +

    + +

    +

    Under development...

    + +
    + +@implements IAsyncDisposable +@inject IGeolocationService _geolocationService; + +@code { + public async ValueTask DisposeAsync() + { + if (_geolocationService is not null) + { + await _geolocationService.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/MapsGoogle.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/MapsGoogle.razor new file mode 100644 index 00000000..09077813 --- /dev/null +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/MapsGoogle.razor @@ -0,0 +1,844 @@ +
    + +

    Google Maps

    +
    +

    + GoogleStaticMap and GoogleMap components are wrapping Google Static and JavaScript Maps services + into Blazor components. Allowing you to configure, control maps, receive events in your Blazor App purely with .Net code. +

    + +
    +
    + + + + + +
    + + + +
      +
    • + To use Google Maps Platform, you must have a billing account. The billing account is used to track costs associated with your projects. +
    • +
    • + Your API key is not tracked or distributed by Maps Blazor Components and Demo application. It is your responsibility to use it safely in your Blazor Apps! +
    • +
    • + Provided Google API key courtesy of Majorsoft and restricted only for this Demo App. Any illegal usage and distribution of API key will result deletion of it. +
    • +
    +
    +
    +
    +
    +
    + +
    +
    + Enter your Google API key here (Check it has selected: Maps Static, Maps JavaScript and Geocoding APIs): + +
    +
    + +
    + +

    Google Maps Static API

    +
    + +

    + The Maps Static API returns an image (either GIF, PNG or JPEG) in response to an HTTP request via a URL. + For each request, you can specify the location of the map, the size of the image, the zoom level, the type of map, and the placement of optional markers at locations on the map. You can additionally label your markers using alphanumeric characters. +

    +

    + A Maps Static API image is embedded within an <img> tag's src attribute, or its equivalent in other programming languages. +
    + You can learn about Google Static Maps features and usage here. +

    + +
    +
    +
    Map center position (Coordinates or Address):
    + Latitude: + Longitude: + + Or full Address (omitted when coordinates supplied): + +

    + Center device location on load (overrides Center property): + NOTE: Async operation which might fail! + +

    + + Maps Center position: @_staticMapCenter.ToString() +
    +
    +
    +
    + Map zoom level: @(_staticMapZoomLevel) + +
    +
    +
    +
    + Maps Image height: @($"{_staticMapHeight}px") + +
    +
    +
    +
    + Maps Image width: @($"{_staticMapWidth}px") + +
    +
    +
    +
    + Maps Type: +
    +
    +
    +
    + Maps Image format: +
    +
    +
    +
    +

    Other features:

    + Show defined Path: + Defined points must visible: + Show defined Markers: +
    + High Resolution Map: +
    +
    + + +
    +
    +

    Rendered Google Static Maps image:

    + + +
    +
    + + + +
    + +
    + +

    Google Maps JavaScript API

    +
    + +

    + The Maps JavaScript API lets you customize maps with your own content and imagery for display on web pages and mobile devices. + The Maps JavaScript API features four basic map types (roadmap, satellite, hybrid, and terrain) which you can modify using layers and styles, controls and events, and various services and libraries. +

    + +

    + A Maps JavaScript API renders a complex customizable map within <div> tag's also allowing to receive events. +
    + You can learn about Google JavaScript Maps features and usage here. +
    + Google Maps JavaScript API has many features and since it renders an interactive control user interactions will change original parameters. Which means for some Map properties two-way bindings must be applied. +

    + +
    +
    +
    Map center position (Coordinates or Address):
    + Latitude: + Longitude: + + Or full Address (omitted when coordinates supplied): + +

    + Center device location on load (overrides Center property): + NOTE: Async operation which might fail! + +
    Center Map on click: + Add Marker on click: +

    + + Maps Center position: @_jsMapCenter.ToString() +
    +
    +
    +
    + Map zoom level: @(_jsMapZoomLevel) + +
    +
    +
    +
    + Maps height: @($"{_jsMapHeight}px") + +
    +
    +
    +
    + Maps width: @($"{_jsMapWidth}px") + +
    +
    +
    +
    + Maps Type: +
    +
    +
    +
    + Maps Gesture Handling Types: +
    +
    +
    +
    + ClickableIcons: + DisableDefaultUI: + DisableDoubleClickZoom: + AnimateCenterChange + FullscreenControl: + KeyboardShortcuts: + MapTypeControl: + RotateControl: + ScaleControl: + StreetViewControl: + ZoomControl: +
    +
    +
    +
    +

    + Maps Pre-render settings: Background color: @_jsMapBackgroundColor + Control size: @(_jsMapControlSize)px +

    +
    +
    + +
    +
    +

    Rendered Google JavaScript Maps:

    + + + + @**@ + +
    +
    +
    +
    + + + + +
    +
    + +
    +
    + +
    + Log Mouse move: + Log other Mouse events: + Log Drag events: + Log Center change: + Log Other events: + Log Marker events: + + +
    +
    + + + +
    + +
    + +@implements IAsyncDisposable +@inject IGeolocationService _geolocationService; + +@code { + private string _googleMapsApiKey = "AIzaSyAv-6SailPQN1R5PytUAkbdaGI9IHZTU5s"; + + protected override void OnInitialized() + { + //Static map + _staticMapMarkers.ElementAt(0).Locations.Add(new GeolocationData(1.111, 2.222)); + _staticMapMarkers.ElementAt(1).Locations.Add(new GeolocationData(17.111, 33.222)); + _staticMapMarkers.ElementAt(1).Locations.Add(new GeolocationData("London")); + _staticMapMarkers.ElementAt(2).Locations.Add(new GeolocationData("New York")); + _staticMapMarkers.ElementAt(3).Locations.Add(new GeolocationData("Budapest")); + _staticMapMarkers.ElementAt(3).Locations.Add(new GeolocationData(5.123, 8.99)); + + //JS map + _jsMapTypeControlOptions.ResetMapTypes(GoogleMapTypes.Roadmap, + GoogleMapTypes.Satellite, + GoogleMapTypes.Terrain, + GoogleMapTypes.Hybrid); + + _jsCustomControls.Add(new GoogleMapCustomControl( + "
    Go to Sydney
    ") + { + OnClickCallback = async (id) => + { + _jsMapCenter = new GeolocationData("Sydney"); + _mapsLog = await WriteLog(_mapsLog, $"Custom control button: 'Go to Sydney' was clicked"); + + StateHasChanged(); + }, + ControlPosition = GoogleMapControlPositions.BOTTOM_CENTER, + }); + _jsCustomControls.Add(new GoogleMapCustomControl( + "
    Center my location
    ") + { + OnClickCallback = async (id) => + { + _mapsLog = await WriteLog(_mapsLog, $"Custom control button: 'Center my location' was clicked"); + await _googleMap.CenterCurrentLocationOnMapAsync(); + }, + ControlPosition = GoogleMapControlPositions.TOP_CENTER, + }); + + _jsMapMarkersClickCallback = async (id) => + { + if (_logMarkerEvents) + { + var clickedMarker = _jsMarkers.SingleOrDefault(x => x.Id == id); + _mapsLog = await WriteLog(_mapsLog, $"User added Marker was clicked Id: '{id}', at position: '{clickedMarker?.Position?.ToString()}'"); + } + }; + _jsMapMarkersDragCallback = async (id, pos) => + { + if (_logMarkerEvents) + { + var clickedMarker = _jsMarkers.SingleOrDefault(x => x.Id == id); + _mapsLog = await WriteLog(_mapsLog, $"User added Marker Id: '{id}', dragging at position: '{pos?.ToString()}'"); + } + }; + _jsMapMarkersDragEndCallback = async (id, pos) => + { + if (_logMarkerEvents) + { + var clickedMarker = _jsMarkers.SingleOrDefault(x => x.Id == id); + _mapsLog = await WriteLog(_mapsLog, $"User added Marker Id: '{id}', drag ended at position: '{pos?.ToString()}'"); + } + }; + _jsMapMarkersDragStartCallback = async (id, pos) => + { + if (_logMarkerEvents) + { + var clickedMarker = _jsMarkers.SingleOrDefault(x => x.Id == id); + _mapsLog = await WriteLog(_mapsLog, $"User added Marker Id: '{id}', drag started at position: '{pos?.ToString()}'"); + } + }; + } + + //Static Maps + private GoogleStaticMap _googleStaticMap; + private GeolocationData _staticMapCenter = new GeolocationData(7.439586759063666, 9.10229996558434); + private byte _staticMapZoomLevel = 10; + private int _staticMapWidth = 450; + private int _staticMapHeight = 200; + private bool _staticMapCenterCurrentLocation = true; //Overrides Center. Async operation which micht fail with Location services + private GoogleMapTypes _staticMapType = GoogleMapTypes.Roadmap; + private GoogleStaticMapImageFormats _staticMapImageFormat = GoogleStaticMapImageFormats.Png; + private bool _staticMapHighResolution = false; + + private bool _staticMapShowPath = false; + private IEnumerable _staticMapPaths = new List() +{ + { new GeolocationData("Budapest")}, + { new GeolocationData("London")} + }; + private bool _staticMapShowVisiblePoints = false; + private IEnumerable _staticMapVisiblePoints = new List() +{ + { new GeolocationData("Budapest" )}, + { new GeolocationData("London")} + }; + private bool _staticMapShowMarkers = false; + private IEnumerable _staticMapMarkers = new List() +{ + { new GoogleStaticMapMarker() }, + { new GoogleStaticMapMarker() + { + CustomIcon = new GoogleMapMarkerCustomIcon() + { Anchor = GoogleMapMarkerCustomIconAnchors.Left, IconUrl = "https://www.google.com/favicon.ico" } + } + }, + { new GoogleStaticMapMarker() + { + Style = new GoogleMapMarkerStyle() + { Color = "green", Label = 'A' } + } + }, + { new GoogleStaticMapMarker() + { + Style = new GoogleMapMarkerStyle() + { Color = "0x11AABB", Label = '2', Size = GoogleMapMarkerSizes.Mid } + } + }, + }; + + //Geolocation current position detection + private async Task CenterMyLocationWithStaticMap() + { + await _googleStaticMap.CenterCurrentLocationOnMapAsync(); + } + private async Task CenterMyLocationWithInjectedService() + { + await _geolocationService.GetCurrentPositionAsync(async (pos) => + { + if (pos.IsSuccess) + { + _staticMapCenter = new GeolocationData(pos.Coordinates.Latitude, pos.Coordinates.Longitude); + StateHasChanged(); + } + }, + false, TimeSpan.FromSeconds(10)); + } + + + //Javascript Maps + private GoogleMap _googleMap; + private GeolocationData _jsMapCenter = new GeolocationData("Times Square New York"); + private string _jsMapBackgroundColor = "lightblue"; + private int _jsMapControlSize = 38; + private byte _jsMapZoomLevel = 10; + private int _jsMapWidth = 450; + private int _jsMapHeight = 250; + private bool _jsMapCenterCurrentLocation = true; //Overrides Center. Async operation which micht fail with Location services + private GoogleMapTypes _jsMapType = GoogleMapTypes.Roadmap; + private byte _jsTilt = 0; + private int _jsHeading = 0; + private bool _jsMapAnimateCenterChange = true; + private bool _jsClickableIcons = true; + private bool _jsDisableDefaultUI = false; + private bool _jsDisableDoubleClickZoom = false; + private bool _jsFullscreenControl = true; + private GoogleMapControlPositions _jsFullscreenControlPositon = GoogleMapControlPositions.TOP_RIGHT; + private GoogleMapGestureHandlingTypes _jsGestureHandling = GoogleMapGestureHandlingTypes.Auto; + private bool _jsKeyboardShortcuts = true; + private bool _jsMapTypeControl = true; + private GoogleMapTypeControlOptions _jsMapTypeControlOptions = new GoogleMapTypeControlOptions() + { + MapTypeControlStyle = GoogleMapTypeControlStyles.DROPDOWN_MENU, + }; + private bool _jsRotateControl = true; + private bool _jsScaleControl = true; + private bool _jsStreetViewControl = true; + private bool _jsZoomControl = true; + + private List _jsCustomControls = new List(); + private ObservableRangeCollection _jsMarkers = new ObservableRangeCollection(); + private ObservableRangeCollection _jsMarkersTmp = new ObservableRangeCollection(); + + private ElementReference _log; + private string _mapsLog; + private bool _logMouseMove = false; + private bool _logOtherMouseEvents = true; + private bool _logDragEvents = true; + private bool _logCenterChangeEvents = true; + private bool _logOtherEvents = true; + private bool _logMarkerEvents = true; + + private bool _jsMapCenterOnClick = true; + private bool _jsMapAddMarkerOnClick = true; + + private Func _jsMapMarkersClickCallback; + private Func _jsMapMarkersDragCallback; + private Func _jsMapMarkersDragEndCallback; + private Func _jsMapMarkersDragStartCallback; + + private bool _jsPhotosAdded = false; + public async Task AddPhotos() + { + if (_jsPhotosAdded) + return; + + _jsPhotosAdded = true; + _jsMarkers.Add(new GoogleMapMarker(new GeolocationCoordinate(36.05257747885196, 14.188007522784417)) + { + Draggable = false, + Clickable = true, + InfoWindow = new GoogleMapInfoWindow() + { + Content = $"

    Azure Window

    ", + MaxWidth = 400 + }, + }); + _jsMarkers.Add(new GoogleMapMarker(new GeolocationCoordinate(34.89426050000001, -115.701769)) + { + Draggable = false, + Clickable = true, + InfoWindow = new GoogleMapInfoWindow() + { + Content = $"

    Kelso Dunes

    ", + MaxWidth = 400 + }, + }); + _jsMarkers.Add(new GoogleMapMarker(new GeolocationCoordinate(46.917456137488685, 19.83780860614538)) + { + Draggable = false, + Clickable = true, + InfoWindow = new GoogleMapInfoWindow() + { + Content = $"

    Sunset Sky

    ", + MaxWidth = 400 + }, + }); + } + + //Events + private async Task OnMapInitialized(string elementId) + { + _mapsLog = await WriteLog(_mapsLog, $"Map was initialized container DIV Id: '{elementId}'"); + } + private async Task OnMapClicked(GeolocationCoordinate coordinate) + { + if (_logOtherMouseEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map was clicked at: '{coordinate.ToString()}'"); + } + + if (_jsMapCenterOnClick) + { + _jsMapCenter = new GeolocationData(coordinate.Latitude, coordinate.Longitude); //Two-way binding with event value + } + + if (_jsMapAddMarkerOnClick) + { + _jsMarkers.Add(new GoogleMapMarker(new GeolocationCoordinate(coordinate.Latitude, coordinate.Longitude)) + { + AnchorPoint = new Point() { X = -10, Y = -35 }, + Animation = GoogleMapMarkerAnimationTypes.DROP, + Clickable = true, + CrossOnDrag = true, + Cursor = "drag", + Draggable = true, + Label = new GoogleMapMarkerLabel("Marker Label"), + Icon = new GoogleMapMarkerLabelIcon("https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png") + { LabelOrigin = new Point() { Y = 20, X = 5 } }, + Opacity = 0.75, + Optimized = true, + Shape = new GoogleMapMarkerShape() + { ShapeType = GoogleMapMarkerShapeTypes.Poly, Coords = new int[] { 1, 1, 1, 20, 18, 20, 18, 1 } }, + Title = "Clicked positon marker...", + Visible = true, + ZIndex = 1, + InfoWindow = new GoogleMapInfoWindow() + { + Content = $"
    User clicked at postion: {coordinate.ToString()}
    ", + MaxWidth = 200 + }, + + OnClickCallback = _jsMapMarkersClickCallback, + OnDragCallback = _jsMapMarkersDragCallback, + OnDragStartCallback = _jsMapMarkersDragStartCallback, + OnDragEndCallback = _jsMapMarkersDragEndCallback + }); + } + } + private async Task OnMapDoubleClicked(GeolocationCoordinate coordinate) + { + if (_logOtherMouseEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map was double clicked at: {coordinate.ToString()}"); + } + } + private async Task OnMapContextMenu(GeolocationCoordinate coordinate) + { + if (_logOtherMouseEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map context menu opened at: {coordinate.ToString()}"); + } + } + private async Task OnMapMouseUp(GeolocationCoordinate coordinate) + { + if (_logOtherMouseEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map mouse button Up at: {coordinate.ToString()}"); + } + } + private async Task OnMapMouseDown(GeolocationCoordinate coordinate) + { + if (_logOtherMouseEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map mouse button Down at: {coordinate.ToString()}"); + } + } + private async Task OnMouseMove(GeolocationCoordinate coordinate) + { + if (_logMouseMove) + { + _mapsLog = await WriteLog(_mapsLog, $"Map mouse moved to: {coordinate.ToString()}"); + } + } + private async Task OnMapMouseOver() + { + if (_logOtherMouseEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map mouse (over) entered into Maps area"); + } + } + private async Task OnMapMouseOut() + { + if (_logOtherMouseEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map mouse (out) leaved Maps area"); + } + } + + private async Task OnMapCenterChanged(GeolocationData coordinate) + { + //Can be used with Binding and custom event: @bind-Center="_jsMapCenter" @bind-Center:event="OnMapCenterChanged" + if (_logCenterChangeEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map Center location changed to: {coordinate.ToString()}"); + } + _jsMapCenter = new GeolocationData(coordinate.Latitude, coordinate.Longitude); //Two-way binding with event value + } + private async Task OnMapZoomLevelChanged(byte zoom) + { + //Can be used with Binding and custom event: @bind-ZoomLevel="_jsMapZoomLevel" @bind-ZoomLevel:event="OnMapZoomLevelChanged" + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map Zoom level changed to: {zoom}"); + } + _jsMapZoomLevel = zoom; //Two-way binding with event value + } + private async Task OnMapTypeChanged(GoogleMapTypes googleMapTypes) + { + //Can be used with Binding and custom event: @bind-MapType="_jsMapType" @bind-MapType:event="OnMapTypeChanged" + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map type changed to: {googleMapTypes}"); + } + _jsMapType = googleMapTypes; //Two-way binding with event value + } + private async Task OnMapHeadingChanged(int heading) + { + //Can be used with Binding and custom event: @bind-Heading="_jsHeading" @bind-ZoomLevel:event="OnMapHeadingChanged" + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map Heading changed to: {heading}"); + } + _jsHeading = heading; //Two-way binding with event value + } + private async Task OnMapTiltChanged(byte tilt) + { + //Can be used with Binding and custom event: @bind-Tilt="_jsTilt" @bind-ZoomLevel:event="OnMapTiltChanged" + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map Tilt changed to: {tilt}"); + } + _jsTilt = tilt; //Two-way binding with event value + } + + private async Task OnMapBoundsChanged() + { + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map boundaries changed"); + } + } + private async Task OnMapProjectionChanged() + { + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map projection changed"); + } + } + private async Task OnMapDraggableChanged() + { + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map draggable changed"); + } + } + private async Task OnMapStreetviewChanged() + { + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map Streetview changed"); + } + } + + private async Task OnMapDrag(GeolocationCoordinate coordinate) + { + if (_logDragEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map Draging at: {coordinate.ToString()}"); + } + } + private async Task OnMapDragEnd(GeolocationCoordinate coordinate) + { + if (_logDragEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map Drag End at: {coordinate.ToString()}"); + } + } + private async Task OnMapDragStart(GeolocationCoordinate coordinate) + { + if (_logDragEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map Drag Start at: {coordinate.ToString()}"); + } + } + + private async Task OnMapResized(Rect rect) + { + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map was resized to: {rect.ToString()}"); + } + } + private async Task OnMapTilesLoaded() + { + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map tiles loaded"); + } + } + private async Task OnMapIdle() + { + if (_logOtherEvents) + { + _mapsLog = await WriteLog(_mapsLog, $"Map idle"); + } + } + + //Geolocation current position detection + private async Task CenterMyLocationWithJavaScriptMap() + { + await _googleMap.CenterCurrentLocationOnMapAsync(); + } + private async Task CenterMyLocationWithInjectedServiceJavaScript() + { + await _geolocationService.GetCurrentPositionAsync(async (pos) => + { + if (pos.IsSuccess) + { + _jsMapCenter = new GeolocationData(pos.Coordinates.Latitude, pos.Coordinates.Longitude); + StateHasChanged(); + } + }, + false, TimeSpan.FromSeconds(10)); + } + + private async Task JavaScripMapLocationDetected(GeolocationData coordinate) + { + _jsMapCenter = coordinate; //JS Map callback when detected current location, must override initial value. + } + + private object _lock = new object(); + private async Task WriteLog(string log, string message) + { + lock (_lock) + { + log += $"{DateTime.Now.TimeOfDay}: {message}. \r\n"; + } + + await _log.ScrollToEndAsync(); + return log; + } + public async ValueTask DisposeAsync() + { + if (_geolocationService is not null) + { + await _geolocationService.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/Components/Tabs.razor b/demo/Majorsoft.Blazor.Components.DemoApp/Components/Tabs.razor index c547df22..223f02aa 100644 --- a/demo/Majorsoft.Blazor.Components.DemoApp/Components/Tabs.razor +++ b/demo/Majorsoft.Blazor.Components.DemoApp/Components/Tabs.razor @@ -43,6 +43,7 @@
    TabsPanel Disabled: Disable Tab: + Hidden Tab:
    Animate Tab changes:
    @@ -73,7 +74,7 @@ Animate="@_isAnimated" OnTabChanged="OnTabChanged"> - + - + - + - +