From 09013c9289d4c86dc144c2d04be78d1542965dfd Mon Sep 17 00:00:00 2001 From: Major Date: Fri, 19 Mar 2021 21:31:54 +0100 Subject: [PATCH 01/69] Cleanup after all maps instance disposed. --- .../Google/GoogleMap.razor | 5 +++++ .../wwwroot/googleMaps.js | 13 ++++++++++++- .../wwwroot/googleMaps.min.js | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor index 0ab65899..153d756e 100644 --- a/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor +++ b/src/Majorsoft.Blazor.Components.Maps/Google/GoogleMap.razor @@ -1089,6 +1089,11 @@ { await _geolocationService.DisposeAsync(); } + + if(_mapService is not null) + { + await _mapService.DisposeAsync(); + } if (_markers is not null) { diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js index 76338773..fe7bb981 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js @@ -572,6 +572,17 @@ export function dispose(elementId) { mapWithDotnetRef.map = null; mapWithDotnetRef.ref = null; - removeElementIdWithDotnetRef(elementId); + removeElementIdWithDotnetRef(_mapsElementDict, elementId); + + if (_mapsElementDict.length === 0) { //Last item cleanup headers. + let scriptTags = document.querySelectorAll('head > script'); + scriptTags.forEach(scriptTag => { + if (scriptTag.getAttribute('src').startsWith("https://maps.googleapis.com/maps")) { + document.head.removeChild(scriptTag); + } + }); + + google = null; + } } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js index dbd8bc4e..385cf52c 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 s=!1,c=document.querySelectorAll("head > script");if(c.forEach(n=>{if(n.getAttribute("src").startsWith("https://maps.googleapis.com/maps/api/js?key=")){s=!0;return}}),!s){let h=document.createElement("script");h.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(h);let l="https://maps.googleapis.com/maps/api/js?key="+t+"&callback=initGoogleMaps&libraries=&v=weekly",o=document.createElement("script");o.src=l;o.defer=!0;document.head.appendChild(o)}}}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(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,f,e){if(t&&i&&r){u(n,i,r,f,e);let s=!1,c=document.querySelectorAll("head > script");if(c.forEach(n=>{if(n.getAttribute("src").startsWith("https://maps.googleapis.com/maps/api/js?key=")){s=!0;return}}),s){window.initGoogleMaps();return}let h=document.createElement("script");h.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(h);let l="https://maps.googleapis.com/maps/api/js?key="+t+"&callback=initGoogleMaps&libraries=&v=weekly",o=document.createElement("script");o.src=l;o.defer=!0;document.head.appendChild(o)}}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 From aa3414314ec017e0f9aaa8d01ffa85eba0a5ee6c Mon Sep 17 00:00:00 2001 From: Major Date: Fri, 19 Mar 2021 21:49:42 +0100 Subject: [PATCH 02/69] Fixed issue with navigated back to page which already has Google map initialized. --- .../wwwroot/googleMaps.js | 18 +++++++----------- .../wwwroot/googleMaps.min.js | 2 +- .../Components/MapsGoogle.razor | 5 ++++- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js index fe7bb981..bdb034c9 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js @@ -15,6 +15,9 @@ }); if (scriptsIncluded) { //Prevent adding JS scripts to page multiple times. + if (window.google) { + window.initGoogleMaps(); //Page was navigated + } return; } @@ -37,6 +40,10 @@ window.initGoogleMaps = () => { let elementId = _mapsElementDict[i].key; let mapInfo = _mapsElementDict[i].value; + if (_mapsElementDict[i].value.map) { //Map already created + continue; + } + //Create Map let map = new google.maps.Map(document.getElementById(elementId), { backgroundColor: mapInfo.bgColor, @@ -573,16 +580,5 @@ export function dispose(elementId) { mapWithDotnetRef.ref = null; removeElementIdWithDotnetRef(_mapsElementDict, elementId); - - if (_mapsElementDict.length === 0) { //Last item cleanup headers. - let scriptTags = document.querySelectorAll('head > script'); - scriptTags.forEach(scriptTag => { - if (scriptTag.getAttribute('src').startsWith("https://maps.googleapis.com/maps")) { - document.head.removeChild(scriptTag); - } - }); - - google = null; - } } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js index 385cf52c..26b1cf06 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 s=!1,c=document.querySelectorAll("head > script");if(c.forEach(n=>{if(n.getAttribute("src").startsWith("https://maps.googleapis.com/maps/api/js?key=")){s=!0;return}}),s){window.initGoogleMaps();return}let h=document.createElement("script");h.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(h);let l="https://maps.googleapis.com/maps/api/js?key="+t+"&callback=initGoogleMaps&libraries=&v=weekly",o=document.createElement("script");o.src=l;o.defer=!0;document.head.appendChild(o)}}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,f,e){if(t&&i&&r){u(n,i,r,f,e);let s=!1,c=document.querySelectorAll("head > script");if(c.forEach(n=>{if(n.getAttribute("src").startsWith("https://maps.googleapis.com/maps/api/js?key=")){s=!0;return}}),s){window.google&&window.initGoogleMaps();return}let h=document.createElement("script");h.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(h);let l="https://maps.googleapis.com/maps/api/js?key="+t+"&callback=initGoogleMaps&libraries=&v=weekly",o=document.createElement("script");o.src=l;o.defer=!0;document.head.appendChild(o)}}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 diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor index 3bf184e5..2740e542 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor @@ -312,7 +312,10 @@ OnMapIdle="@OnMapIdle" ApiKey="@_googleMapsApiKey" /> - @**@ + @**@ From e77c135c8a2f611bd1b36e41703987c606bb8384 Mon Sep 17 00:00:00 2001 From: Major Date: Sat, 27 Mar 2021 21:04:07 +0100 Subject: [PATCH 03/69] Created scroll components. --- .../Scroll/ScrollToPageBottom.razor | 36 ++++++++++++++++++ .../Scroll/ScrollToPageTop.razor | 37 +++++++++++++++++++ .../Components/JSInterop.razor | 18 ++++++++- 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor create mode 100644 src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor new file mode 100644 index 00000000..261d1953 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor @@ -0,0 +1,36 @@ +
+ @Content +
+ + + +@implements IAsyncDisposable +@inject IScrollHandler _scrollHandler; + +@code { + protected override async Task OnInitializedAsync() + { + await _scrollHandler.RegisterPageScrollAsync(async (e) => + { + + }); + } + + [Parameter] public RenderFragment Content { get; set; } + + public async ValueTask DisposeAsync() + { + if (_scrollHandler is not null) + { + await _scrollHandler.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor new file mode 100644 index 00000000..75879d8c --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor @@ -0,0 +1,37 @@ +
+ @Content +
+ + + +@implements IAsyncDisposable + +@inject IScrollHandler _scrollHandler; + +@code { + protected override async Task OnInitializedAsync() + { + await _scrollHandler.RegisterPageScrollAsync(async (e) => + { + + }); + } + + [Parameter] public RenderFragment Content { get; set; } + + public async ValueTask DisposeAsync() + { + if (_scrollHandler is not null) + { + await _scrollHandler.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor index a08f189f..25bbe4d1 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor @@ -14,7 +14,22 @@
Majorsoft.Blazor.Components.Common.JsInterop package is available on Nuget

-@*Later add scroll components here*@ + + +
+ +
+
+
+ + + +
+ +
+
+
+
@@ -54,7 +69,6 @@ -@*Later add scroll components here*@
From 95da8b667f7e18f96d015c16771d1f9d0b28de20 Mon Sep 17 00:00:00 2001 From: Major Date: Sat, 27 Mar 2021 21:33:16 +0100 Subject: [PATCH 04/69] Added new GetScreenSizeAsync() function. --- .github/docs/JsInterop.md | 5 ++++- .../Resize/IResizeHandler.cs | 8 +++++++- .../Resize/ResizeHandler.cs | 8 ++++++++ .../wwwroot/resize.js | 8 ++++++++ .../wwwroot/resize.min.js | 2 +- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.github/docs/JsInterop.md b/.github/docs/JsInterop.md index e0326515..131d78f0 100644 --- a/.github/docs/JsInterop.md +++ b/.github/docs/JsInterop.md @@ -152,7 +152,10 @@ Implements `IAsyncDisposable` interface the injected service should be Disposed. ### Functions - **`GetPageSizeAsync`**: **`Task GetPageSizeAsync()`**
-Returns Browser Window size (height and width in Pixel). It is useful to call when page loaded, then use `RegisterPageResizeAsync` to get notifications +Returns Browser Window inner size (height and width in Pixel). It is useful to call when page loaded, then use `RegisterPageResizeAsync` to get notifications +on each page resize. +- **`GetScreenSizeAsync`**: **`Task GetScreenSizeAsync()`**
+Returns Browser Window screen size (height and width in Pixel). It is useful to call when page loaded, then use `RegisterPageResizeAsync` to get notifications on each page resize. - **`RegisterPageResizeAsync`**: **`Task RegisterPageResizeAsync(Func resizeCallback)`**
Adds event listener for 'resize' HTML event for the whole document/window. diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Resize/IResizeHandler.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Resize/IResizeHandler.cs index 94dc3d1a..9c83c6b6 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Resize/IResizeHandler.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Resize/IResizeHandler.cs @@ -26,11 +26,17 @@ public interface IResizeHandler : IAsyncDisposable Task RemovePageResizeAsync(string eventId); /// - /// Returns Browser Window size (height and width in Pixel). + /// Returns Browser Window inner size (height and width in Pixel). /// /// Async Task with Window size Task GetPageSizeAsync(); + /// + /// Returns Browser Window screen size (height and width in Pixel). + /// + /// Async Task with Window size + Task GetScreenSizeAsync(); + /// /// Adds event listener for 'resize' HTML event for the given element with property filter. /// diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Resize/ResizeHandler.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Resize/ResizeHandler.cs index e70ebf09..e4c68a16 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Resize/ResizeHandler.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Resize/ResizeHandler.cs @@ -57,6 +57,14 @@ public async Task GetPageSizeAsync() return size; } + public async Task GetScreenSizeAsync() + { + await CheckJsObjectAsync(); + + var size = await _resizeJs.InvokeAsync("getScreenSize"); + return size; + } + public async Task RegisterResizeAsync(ElementReference elementRef, Func resizeCallback = null) { await CheckJsObjectAsync(); diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/resize.js b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/resize.js index ab5a22ca..ea32b3db 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/resize.js +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/resize.js @@ -176,5 +176,13 @@ export function getPageSize() { Width: window.innerWidth } + return args; +} +export function getScreenSize() { + let args = { + Height: window.screen.height, + Width: window.screen.width + } + return args; } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/resize.min.js b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/resize.min.js index 42c82eb5..7551abaa 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/resize.min.js +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/resize.min.js @@ -1 +1 @@ -function i(n,t){return function(){var i=t.getBoundingClientRect();let r={Height:i.height,Width:i.width};n.invokeMethodAsync("ResizeEvent",r)}}function r(n,t,i){let r=!1;for(let i=0;i Date: Sat, 27 Mar 2021 22:35:51 +0100 Subject: [PATCH 05/69] Implemented scroll GetPageScrollSize() --- .github/docs/JsInterop.md | 6 +- ...oft.Blazor.Components.Common.JsInterop.xml | 61 ++++++++++++++++++- .../Scroll/IScrollHandler.cs | 9 ++- .../Scroll/ScrollEventArgs.cs | 9 +++ .../Scroll/ScrollHandler.cs | 9 ++- .../Scroll/ScrollResult.cs | 17 ++++++ .../Scroll/ScrollToPageBottom.razor | 14 ++++- .../Scroll/ScrollToPageTop.razor | 27 ++++++-- .../_Imports.razor | 4 +- .../wwwroot/scroll.js | 16 +++-- .../wwwroot/scroll.min.js | 2 +- 11 files changed, 153 insertions(+), 21 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollResult.cs diff --git a/.github/docs/JsInterop.md b/.github/docs/JsInterop.md index 131d78f0..e0863ccc 100644 --- a/.github/docs/JsInterop.md +++ b/.github/docs/JsInterop.md @@ -124,8 +124,10 @@ Scrolls to top of the page (X top). Scrolls to X position on the page. - **`ScrollToPageYAsync`**: **`Task ScrollToPageYAsync(double y)`**
Scrolls to Y position on the page. -- **`GetPageScrollPosAsync`**: **`Task GetPageScrollPosAsync()`**
-Returns page X,Y scroll position as `ScrollEventArgs`. +- **`GetPageScrollPosAsync`**: **`Task GetPageScrollPosAsync()`**
+Returns page X,Y scroll current position as `ScrollResult`. +- **`GetPageScrollSizeAsync`**: **`Task GetPageScrollSizeAsync()`**
+Returns page X,Y scroll size (max values) as `ScrollResult`. - **`RegisterPageScrollAsync`**: **`Task RegisterPageScrollAsync(Func scrollCallback)`**
Adds event listener for 'scroll' HTML event for the whole document/window. **Also returns with Event Id event id to unsubscribe from event.** - **`RemovePageScrollAsync`**: **`Task RemovePageScrollAsync(string eventId)`**
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 f1185219..e9c0a1e1 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 @@ -525,7 +525,13 @@ - Returns Browser Window size (height and width in Pixel). + Returns Browser Window inner size (height and width in Pixel). + + Async Task with Window size + + + + Returns Browser Window screen size (height and width in Pixel). Async Task with Window size @@ -718,7 +724,13 @@ - Returns page X,Y scroll position as . + Returns page X,Y scroll current position as . + + Async Task + + + + Returns page X,Y scroll size (max values) as . Async Task @@ -741,10 +753,55 @@ PageScrollEventInfo event info to handle JS callback + + + Scroll event args + + + + + Scroll X position + + + + + Scroll Y position + + Implementation of + + + Scroll result + + + + + Scroll X value + + + + + Scroll Y value + + + + + + + + + + + + + + + + + diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs index 6cbe7b32..75e7c948 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/IScrollHandler.cs @@ -52,10 +52,15 @@ public interface IScrollHandler : IAsyncDisposable /// Async Task Task ScrollToPageYAsync(double y); /// - /// Returns page X,Y scroll position as . + /// Returns page X,Y scroll current position as . /// /// Async Task - Task GetPageScrollPosAsync(); + Task GetPageScrollPosAsync(); + /// + /// Returns page X,Y scroll size (max values) as . + /// + /// Async Task + Task GetPageScrollSizeAsync(); /// /// Adds event listener for 'scroll' HTML event for the whole document/window. diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollEventArgs.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollEventArgs.cs index 7fe2a35c..a494ba8c 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollEventArgs.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollEventArgs.cs @@ -2,9 +2,18 @@ namespace Majorsoft.Blazor.Components.Common.JsInterop.Scroll { + /// + /// Scroll event args + /// public sealed class ScrollEventArgs : EventArgs { + /// + /// Scroll X position + /// public double X { get; set; } + /// + /// Scroll Y position + /// public double Y { get; set; } } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollHandler.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollHandler.cs index 32b8c8d0..d2b43947 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollHandler.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollHandler.cs @@ -43,10 +43,15 @@ public async Task ScrollToPageYAsync(double y) await CheckJsObjectAsync(); await _scrollJs.InvokeVoidAsync("scrollToPageY", y); } - public async Task GetPageScrollPosAsync() + public async Task GetPageScrollPosAsync() { await CheckJsObjectAsync(); - return await _scrollJs.InvokeAsync("getPageScrollPosition"); + return await _scrollJs.InvokeAsync("getPageScrollPosition"); + } + public async Task GetPageScrollSizeAsync() + { + await CheckJsObjectAsync(); + return await _scrollJs.InvokeAsync("getPageScrollSize"); } public async Task ScrollToElementAsync(ElementReference elementReference) diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollResult.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollResult.cs new file mode 100644 index 00000000..8461d601 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollResult.cs @@ -0,0 +1,17 @@ +namespace Majorsoft.Blazor.Components.Common.JsInterop.Scroll +{ + /// + /// Scroll result + /// + public sealed class ScrollResult + { + /// + /// Scroll X value + /// + public double X { get; set; } + /// + /// Scroll Y value + /// + public double Y { get; set; } + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor index 261d1953..22566c60 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor @@ -1,6 +1,9 @@ -
- @Content -
+@if (_isVisible) +{ +
+ @Content +
+} @@ -20,8 +19,20 @@ @inject IScrollHandler _scrollHandler @inject ILogger _logger -@code { +@code { private bool _isVisible = false; + private bool IsVisible + { + get => _isVisible; + set + { + if (value != _isVisible) + { + _isVisible = value; + StateHasChanged(); + } + } + } protected override async Task OnInitializedAsync() { @@ -30,21 +41,25 @@ var scrollSize = await _scrollHandler.GetPageScrollSizeAsync(); var percentage = (e.Y / scrollSize.Y) * 100; - _isVisible = percentage >= VisibleFromPagePercentage; - StateHasChanged(); + IsVisible = percentage >= VisibleFromPagePercentage; }); } /// - /// + /// /// [Parameter] public RenderFragment Content { get; set; } /// - /// + /// /// [Parameter] public byte VisibleFromPagePercentage { get; set; } = 30; + /// + /// + /// + [Parameter] public bool SmootScroll { get; set; } = true; + public async ValueTask DisposeAsync() { diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js index b7ef8a4b..293eaa83 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/wwwroot/scroll.js @@ -103,21 +103,41 @@ export function getScrollXPosition(element) { /* Injectable functions */ //HTLM page scroll -export function scrollToPageEnd() { - document.body.scrollTop = document.body.scrollHeight; - document.documentElement.scrollTop = document.body.scrollHeight; +export function scrollToPageEnd(smooth) { + if (!smooth) { + document.body.scrollTop = document.body.scrollHeight; + document.documentElement.scrollTop = document.body.scrollHeight; + } + else { + window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); + } } -export function scrollToPageTop() { - document.body.scrollTop = 0; - document.documentElement.scrollTop = 0; +export function scrollToPageTop(smooth) { + if (!smooth) { + document.body.scrollTop = 0; + document.documentElement.scrollTop = 0; + } + else { + window.scrollTo({ top: 0, behavior: 'smooth' }); + } } -export function scrollToPageX(x) { - document.body.scrollTop = x; - document.documentElement.scrollTop = x; +export function scrollToPageX(x, smooth) { + if (!smooth) { + document.body.scrollLeft = x; + document.documentElement.scrollLeft = x; + } + else { + window.scrollTo({ left: 0, behavior: 'smooth' }); + } } -export function scrollToPageY(y) { - document.body.scrollLeft = y; - document.documentElement.scrollLeft = y; +export function scrollToPageY(y, smooth) { + if (!smooth) { + document.body.scrollTop = y; + document.documentElement.scrollTop = y; + } + else { + window.scrollTo({ top: y, behavior: 'smooth' }); + } } export function getPageScrollPosition() { let top = window.pageYOffset || document.documentElement.scrollTop; 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 9519f673..9d0115a0 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.offsetTop0&&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 Date: Sun, 28 Mar 2021 13:26:26 +0200 Subject: [PATCH 07/69] Implemented ScrollToPageTop component. --- ...oft.Blazor.Components.Common.JsInterop.xml | 49 +++++++++++-- .../Scroll/PageScrollHorizontalOrientation.cs | 17 +++++ .../Scroll/ScrollToPageTop.razor | 69 ++++++++++++------- .../Scroll/ScrollToPageTop.razor.css | 6 ++ .../Scroll/ScrollToPageTop.razor.min.css | 1 + .../bundleconfig.json | 6 ++ .../Components/JSInterop.razor | 28 +++----- .../Components/JsDemo/ScrollJs.razor | 19 ++++- 8 files changed, 146 insertions(+), 49 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/PageScrollHorizontalOrientation.cs create mode 100644 src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css create mode 100644 src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css 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 15416f9b..94211681 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 @@ -757,6 +757,21 @@ PageScrollEventInfo event info to handle JS callback
+ + + Page scroll elements orientation on page and elements + + + + + Element placed to the Right side + + + + + Element placed to the Left side + + Scroll event args @@ -802,19 +817,39 @@ + + + Exposes a Blazor of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + + - - - + + HTML Content of the scroll panel. + - - - + + Element should be visible when scroll reached page % of given value. + - + Scroll shold be jump or smoothly scroll. + + + + + Required space from page bottom in px. + + + + + Required space from page (left/right) side in px. + + + + + Element orientation on page. diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/PageScrollHorizontalOrientation.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/PageScrollHorizontalOrientation.cs new file mode 100644 index 00000000..0c62e6e0 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/PageScrollHorizontalOrientation.cs @@ -0,0 +1,17 @@ +namespace Majorsoft.Blazor.Components.Common.JsInterop.Scroll +{ + /// + /// Page scroll elements orientation on page and elements + /// + public enum PageScrollHorizontalOrientation + { + /// + /// Element placed to the Right side + /// + Right, + /// + /// Element placed to the Left side + /// + Left + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor index 2192d329..3ad108ba 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor @@ -1,16 +1,18 @@ -
+
@Content
@@ -21,45 +23,66 @@ @code { private bool _isVisible = false; - private bool IsVisible - { - get => _isVisible; - set - { - if (value != _isVisible) - { - _isVisible = value; - StateHasChanged(); - } - } - } protected override async Task OnInitializedAsync() { + WriteDiag("Component initialized..."); + await _scrollHandler.RegisterPageScrollAsync(async (e) => { var scrollSize = await _scrollHandler.GetPageScrollSizeAsync(); var percentage = (e.Y / scrollSize.Y) * 100; - IsVisible = percentage >= VisibleFromPagePercentage; + var visible = percentage >= VisibleFromPagePercentage; + if (visible != _isVisible) + { + _isVisible = visible; + StateHasChanged(); + + WriteDiag($"Component visibilit changed visible: {_isVisible}."); + } }); } + private ElementReference _inputRef; /// - /// + /// Exposes a Blazor of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + /// + public ElementReference InnerElementReference => _inputRef; + + /// + /// HTML Content of the scroll panel. /// [Parameter] public RenderFragment Content { get; set; } /// - /// + /// Element should be visible when scroll reached page % of given value. /// [Parameter] public byte VisibleFromPagePercentage { get; set; } = 30; /// - /// + /// Scroll shold be jump or smoothly scroll. /// [Parameter] public bool SmootScroll { get; set; } = true; + /// + /// Required space from page bottom in px. + /// + [Parameter] public int PaddingFromBottom { get; set; } = 20; + /// + /// Required space from page (left/right) side in px. + /// + [Parameter] public int PaddingFromSide { get; set; } = 24; + /// + /// Element orientation on page. + /// + [Parameter] public PageScrollHorizontalOrientation HorizontalOrientation { get; set; } = PageScrollHorizontalOrientation.Right; + + + private void WriteDiag(string message) + { + _logger.LogDebug($"Component {this.GetType()}: {message}"); + } public async ValueTask DisposeAsync() { diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css new file mode 100644 index 00000000..ab507144 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css @@ -0,0 +1,6 @@ +.scrollToPageTop { + position: fixed; + z-index: 20; + cursor: pointer; + transition: opacity 0.25s linear; +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css new file mode 100644 index 00000000..dbcc64b4 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css @@ -0,0 +1 @@ +.scrollToPageTop{position:fixed;z-index:20;cursor:pointer;transition:opacity .25s linear} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/bundleconfig.json b/src/Majorsoft.Blazor.Components.Common.JsInterop/bundleconfig.json index 24d82cd4..48ca414e 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/bundleconfig.json +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/bundleconfig.json @@ -58,5 +58,11 @@ "inputFiles": [ "wwwroot/head.js" ] + }, + { + "outputFileName": "Scroll/ScrollToPageTop.razor.min.css", + "inputFiles": [ + "Scroll/ScrollToPageTop.razor.css" + ] } ] diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor index 25bbe4d1..f357bc21 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor @@ -14,24 +14,11 @@
Majorsoft.Blazor.Components.Common.JsInterop package is available on Nuget

- - -
- -
-
-
- - - -
- -
-
-
-
- +@*Scroll to page end with ScrollHandler for custom floating component see ScrollToPageBottom*@ + +Smooth scroll: +

JS Interop features:

@@ -70,7 +57,10 @@
- +@*Scroll to page end with ScrollHandler for custom floating component see ScrollToPageTop*@ + +Smooth scroll: + @implements IAsyncDisposable @inject IScrollHandler _scrollHandler @@ -83,4 +73,6 @@ await _scrollHandler.DisposeAsync(); } } + + private bool _smmothScroll = true; } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor index 2a6ebd63..c2f567dd 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor @@ -1,4 +1,21 @@ -
+@*Scroll on page components, can be placed anywhere on a page but should be used one per page*@ + + +
+ +
+
+
+ + + +
+ +
+
+
+ +

Scroll JS

From 33f753f8a70904de161921b51ad434754d2f8e38 Mon Sep 17 00:00:00 2001 From: Major Date: Sun, 28 Mar 2021 13:42:52 +0200 Subject: [PATCH 08/69] Implemented ScrollToPageBottom component. --- ...oft.Blazor.Components.Common.JsInterop.xml | 34 +++++++- .../Scroll/ScrollToPageBottom.razor | 81 +++++++++++++++---- .../Scroll/ScrollToPageBottom.razor.css | 7 ++ .../Scroll/ScrollToPageBottom.razor.min.css | 1 + .../Scroll/ScrollToPageTop.razor | 2 +- .../Scroll/ScrollToPageTop.razor.css | 1 + .../Scroll/ScrollToPageTop.razor.min.css | 2 +- .../bundleconfig.json | 6 ++ 8 files changed, 114 insertions(+), 20 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.css create mode 100644 src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.min.css 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 94211681..c193d8a7 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 @@ -807,14 +807,44 @@ Scroll Y value + + + Exposes a Blazor of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + + - + HTML Content of the scroll panel. + + + + + Element should be visible when scroll reached page % of given value. + + + + + Element should be visible untill scroll reached page % of given value. - + Scroll shold be jump or smoothly scroll. + + + + + Required space from page bottom in px. + + + + + Required space from page (left/right) side in px. + + + + + Element orientation on page. diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor index 104b6643..6e2ada15 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor @@ -1,44 +1,93 @@ -@if (_isVisible) -{ -
- @Content -
-} +
+ @Content +
@implements IAsyncDisposable -@inject IScrollHandler _scrollHandler; -@code { - private bool _isVisible = true; +@inject IScrollHandler _scrollHandler +@inject ILogger _logger + +@code { + private bool _isVisible = false; protected override async Task OnInitializedAsync() { + WriteDiag("Component initialized..."); + await _scrollHandler.RegisterPageScrollAsync(async (e) => { + var scrollSize = await _scrollHandler.GetPageScrollSizeAsync(); + var percentage = (e.Y / scrollSize.Y) * 100; + + var visible = percentage >= VisibleFromPagePercentage && percentage <= VisibleUntilPagePercentage; + if (visible != _isVisible) + { + _isVisible = visible; + StateHasChanged(); + WriteDiag($"Component visibilit changed visible: {_isVisible}."); + } }); } + private ElementReference _inputRef; + /// + /// Exposes a Blazor of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + /// + public ElementReference InnerElementReference => _inputRef; + /// - /// + /// HTML Content of the scroll panel. /// [Parameter] public RenderFragment Content { get; set; } /// - /// + /// Element should be visible when scroll reached page % of given value. + /// + [Parameter] public byte VisibleFromPagePercentage { get; set; } = 5; + /// + /// Element should be visible untill scroll reached page % of given value. + /// + [Parameter] public byte VisibleUntilPagePercentage { get; set; } = 80; + + /// + /// Scroll shold be jump or smoothly scroll. /// [Parameter] public bool SmootScroll { get; set; } = true; + /// + /// Required space from page bottom in px. + /// + [Parameter] public int PaddingFromTop { get; set; } = 24; + /// + /// Required space from page (left/right) side in px. + /// + [Parameter] public int PaddingFromSide { get; set; } = 24; + /// + /// Element orientation on page. + /// + [Parameter] public PageScrollHorizontalOrientation HorizontalOrientation { get; set; } = PageScrollHorizontalOrientation.Right; + + + private void WriteDiag(string message) + { + _logger.LogDebug($"Component {this.GetType()}: {message}"); + } + public async ValueTask DisposeAsync() { if (_scrollHandler is not null) diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.css b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.css new file mode 100644 index 00000000..ff2659d2 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.css @@ -0,0 +1,7 @@ +.scrollToPageBottom { + position: fixed; + z-index: 20; + cursor: pointer; + transition: opacity 0.25s linear; + outline: 0px; +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.min.css b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.min.css new file mode 100644 index 00000000..173d961d --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.min.css @@ -0,0 +1 @@ +.scrollToPageBottom{position:fixed;z-index:20;cursor:pointer;transition:opacity .25s linear;outline:0} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor index 3ad108ba..de09421a 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor @@ -1,4 +1,4 @@ -
@Content
diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css index ab507144..8545dc75 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css @@ -3,4 +3,5 @@ z-index: 20; cursor: pointer; transition: opacity 0.25s linear; + outline: 0px; } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css index dbcc64b4..20d81bac 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css @@ -1 +1 @@ -.scrollToPageTop{position:fixed;z-index:20;cursor:pointer;transition:opacity .25s linear} \ No newline at end of file +.scrollToPageTop{position:fixed;z-index:20;cursor:pointer;transition:opacity .25s linear;outline:0} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/bundleconfig.json b/src/Majorsoft.Blazor.Components.Common.JsInterop/bundleconfig.json index 48ca414e..bcb81836 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/bundleconfig.json +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/bundleconfig.json @@ -64,5 +64,11 @@ "inputFiles": [ "Scroll/ScrollToPageTop.razor.css" ] + }, + { + "outputFileName": "Scroll/ScrollToPageBottom.razor.min.css", + "inputFiles": [ + "Scroll/ScrollToPageBottom.razor.css" + ] } ] From ec1b3cdc8fd6ae7daf8aa0a0c79b97a9de715d27 Mon Sep 17 00:00:00 2001 From: Major Date: Sun, 28 Mar 2021 20:40:59 +0200 Subject: [PATCH 09/69] Scroll elements demo. --- ...oft.Blazor.Components.Common.JsInterop.xml | 14 ++--- ...ion.cs => PageScrollHorizontalPosition.cs} | 2 +- .../Scroll/ScrollToPageBottom.razor | 6 +- .../Scroll/ScrollToPageTop.razor | 6 +- .../Components/JSInterop.razor | 10 +-- .../Components/JsDemo/ScrollJs.razor | 61 ++++++++++++++++++- 6 files changed, 78 insertions(+), 21 deletions(-) rename src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/{PageScrollHorizontalOrientation.cs => PageScrollHorizontalPosition.cs} (89%) 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 c193d8a7..1dc1c367 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 @@ -757,17 +757,17 @@ PageScrollEventInfo event info to handle JS callback
- + Page scroll elements orientation on page and elements - + Element placed to the Right side - + Element placed to the Left side @@ -842,9 +842,9 @@ Required space from page (left/right) side in px. - + - Element orientation on page. + Element position on page. @@ -877,9 +877,9 @@ Required space from page (left/right) side in px. - + - Element orientation on page. + Element position on page. diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/PageScrollHorizontalOrientation.cs b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/PageScrollHorizontalPosition.cs similarity index 89% rename from src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/PageScrollHorizontalOrientation.cs rename to src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/PageScrollHorizontalPosition.cs index 0c62e6e0..602d3638 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/PageScrollHorizontalOrientation.cs +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/PageScrollHorizontalPosition.cs @@ -3,7 +3,7 @@ /// /// Page scroll elements orientation on page and elements /// - public enum PageScrollHorizontalOrientation + public enum PageScrollHorizontalPosition { /// /// Element placed to the Right side diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor index 6e2ada15..d7c24440 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor @@ -1,4 +1,4 @@ -
@Content
@@ -78,9 +78,9 @@ ///
[Parameter] public int PaddingFromSide { get; set; } = 24; /// - /// Element orientation on page. + /// Element position on page. /// - [Parameter] public PageScrollHorizontalOrientation HorizontalOrientation { get; set; } = PageScrollHorizontalOrientation.Right; + [Parameter] public PageScrollHorizontalPosition HorizontalPosition { get; set; } = PageScrollHorizontalPosition.Right; private void WriteDiag(string message) diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor index de09421a..d5cea1e0 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor @@ -1,4 +1,4 @@ -
@Content
@@ -74,9 +74,9 @@ /// [Parameter] public int PaddingFromSide { get; set; } = 24; /// - /// Element orientation on page. + /// Element position on page. /// - [Parameter] public PageScrollHorizontalOrientation HorizontalOrientation { get; set; } = PageScrollHorizontalOrientation.Right; + [Parameter] public PageScrollHorizontalPosition HorizontalPosition { get; set; } = PageScrollHorizontalPosition.Right; private void WriteDiag(string message) diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor index f357bc21..7247062a 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor @@ -16,9 +16,9 @@
@*Scroll to page end with ScrollHandler for custom floating component see ScrollToPageBottom*@ - + Smooth scroll: - +

JS Interop features:

@@ -58,9 +58,9 @@ Smooth scroll:
@*Scroll to page end with ScrollHandler for custom floating component see ScrollToPageTop*@ - + Smooth scroll: - + @implements IAsyncDisposable @inject IScrollHandler _scrollHandler @@ -74,5 +74,5 @@ Smooth scroll: } } - private bool _smmothScroll = true; + private bool _smoothScroll = true; } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor index c2f567dd..b43f8993 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor @@ -1,5 +1,10 @@ @*Scroll on page components, can be placed anywhere on a page but should be used one per page*@ - +
@@ -7,7 +12,11 @@ - +
@@ -34,6 +43,47 @@
+

+ +

+ +
+
+ Top/Bottom margin (can be set separately for both types of controls): @(_scrcollTopBottonMargin)px + +
+
+ +
+
+ Side right/left margin (can be set separately for both types of controls): @(_scrcollTopSideMargin)px + +
+
+ +
+
+ Smooth scroll (can be set separately for both types of controls): +
+
+ +
+
+ Horizontal Position right/left (can be set separately for both types of controls): +
+
+ +
+

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

@@ -101,6 +151,13 @@ _scrollSubscribed = !_scrollSubscribed; } + //Page scroll elements + private int _scrcollTopBottonMargin = 20; + private int _scrcollTopSideMargin = 24; + private byte _scrcollBefore = 5; + private PageScrollHorizontalPosition _scrollHorizontalPosition = PageScrollHorizontalPosition.Right; + private bool _smoothScroll = true; + public async ValueTask DisposeAsync() { if (_scrollHandler is not null) From 09ae2a9ef34d85515d0dd5ee09575de0fdcdace7 Mon Sep 17 00:00:00 2001 From: Major Date: Sun, 28 Mar 2021 21:17:32 +0200 Subject: [PATCH 10/69] Scroll elements docs. --- .github/docs/JsInterop.md | 54 ++++++++++++++++++- ...oft.Blazor.Components.Common.JsInterop.xml | 10 ++-- .../Scroll/ScrollToPageBottom.razor | 6 +-- .../Scroll/ScrollToPageTop.razor | 4 +- .../Components/JsDemo/ScrollJs.razor | 4 +- 5 files changed, 65 insertions(+), 13 deletions(-) diff --git a/.github/docs/JsInterop.md b/.github/docs/JsInterop.md index efc941b1..22a95cfa 100644 --- a/.github/docs/JsInterop.md +++ b/.github/docs/JsInterop.md @@ -21,7 +21,8 @@ You can try it out by using the [demo app](https://blazorextensions.z6.web.core. - **Global Mouse JS**: is an **injectable `IGlobalMouseEventHandler` service** for global mouse callback event handlers. - **Focus JS**: is an injectable `IFocusHandler` service. **Focus JS is able to identify and restore focus on ANY DOM element without using Blazor `@ref=""` tag.** - **Element info JS**: is a set of **Extension methods** for `ElementReference` objects. -- **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. +- **Scroll JS**: is a set of **Extension methods** for `ElementReference` objects. **`IScrollHandler` injectable service** for non element level functions and callback event handlers. + Also `ScrollToPageBottom` and `ScrollToPageTop` components will render "floating" element with customizable placing and content for wrapping Scroll JS scroll to page top or bottom functions. - **Resize JS**: is an **injectable `IResizeHandler` service** for Window (global) and HTML Elements resize event callback handlers. - **Clipboard JS**: is an **injectable `IClipboardHandler` service** for accessing computer Clipboard from Blazor Application. - **Language JS**: is an **injectable `ILanguageService` service** for detect the browser language preference. @@ -108,6 +109,57 @@ Returns the given HTML element ClintBoundRect data as `DomRect`. ## Scroll JS (See: [demo app](https://blazorextensions.z6.web.core.windows.net/jsinterop#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. +`ScrollToPageBottom` and `ScrollToPageTop` components will render "floating" element with customizable placing and content for wrapping `Scroll JS` scroll to page top or bottom functions. +**Note**: Both component can be set inside ANY components which will apply for the whole page. Hence both components should be added only once per page! + +### `ScrollToPageBottom` component +`ScrollToPageBottom` component will render "floating" element with customizable placing and content for wrapping Scroll JS **scroll to page bottom** function. + +#### Properties +- **`Content`: `RenderFragment` HTML content - Required**
+Required HTML content which will be wrapped into a `` which has the Click events listener registered. +- **`VisibleFromPagePercentage`: `byte { get; set; }` (default: 5)**
+Element should be visible when scroll reached page % of given value. +- **`VisibleUntilPagePercentage`: `byte { get; set; }` (default: 80)**
+Element should be visible until scroll reached page % of given value. +- **`SmootScroll`: `bool { get; set; }` (default: true)**
+Scroll should be jump or smoothly scroll. +- **`PaddingFromTop`: `int { get; set; }` (default: 24)**
+Required space from page bottom in px. +- **`PaddingFromSide`: `int { get; set; }` (default: 24)**
+Required space from page (left/right) side in px. +- **`HorizontalPosition`: `PageScrollHorizontalPosition { get; set; }` (default: 24)**
+Element position on page {Right, Left}. +- **`InnerElementReference`: `ElementReference { get; }`**
+Exposes a Blazor `ElementReference` of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + +#### Functions +- **`DisposeAsync()`: `ValueTask IAsyncDisposable()` interface**
+Component implements `IAsyncDisposable` interface Blazor framework components also can `@implements IAsyncDisposable` where the injected service should be Disposed. + +### `ScrollToPageTop` component +`ScrollToPageTop` component will render "floating" element with customizable placing and content for wrapping Scroll JS **scroll to page top** function. + +#### Properties +- **`Content`: `RenderFragment` HTML content - Required**
+Required HTML content which will be wrapped into a `` which has the Click events listener registered. +- **`VisibleFromPagePercentage`: `byte { get; set; }` (default: 30)**
+Element should be visible when scroll reached page % of given value. +- **`SmootScroll`: `bool { get; set; }` (default: true)**
+Scroll should be jump or smoothly scroll. +- **`PaddingFromTop`: `int { get; set; }` (default: 24)**
+Required space from page bottom in px. +- **`PaddingFromSide`: `int { get; set; }` (default: 24)**
+Required space from page (left/right) side in px. +- **`HorizontalPosition`: `PageScrollHorizontalPosition { get; set; }` (default: 24)**
+Element position on page {Right, Left}. +- **`InnerElementReference`: `ElementReference { get; }`**
+Exposes a Blazor `ElementReference` of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + +#### Functions +- **`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)`**
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 1dc1c367..645f1617 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 @@ -824,12 +824,12 @@ - Element should be visible untill scroll reached page % of given value. + Element should be visible until scroll reached page % of given value. - Scroll shold be jump or smoothly scroll. + Scroll should be jump or smoothly scroll. @@ -844,7 +844,7 @@ - Element position on page. + Element position on page {Right, Left}. @@ -864,7 +864,7 @@ - Scroll shold be jump or smoothly scroll. + Scroll should be jump or smoothly scroll. @@ -879,7 +879,7 @@ - Element position on page. + Element position on page {Right, Left}. diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor index d7c24440..dac749f0 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor @@ -60,12 +60,12 @@ /// [Parameter] public byte VisibleFromPagePercentage { get; set; } = 5; /// - /// Element should be visible untill scroll reached page % of given value. + /// Element should be visible until scroll reached page % of given value. /// [Parameter] public byte VisibleUntilPagePercentage { get; set; } = 80; /// - /// Scroll shold be jump or smoothly scroll. + /// Scroll should be jump or smoothly scroll. /// [Parameter] public bool SmootScroll { get; set; } = true; @@ -78,7 +78,7 @@ /// [Parameter] public int PaddingFromSide { get; set; } = 24; /// - /// Element position on page. + /// Element position on page {Right, Left}. /// [Parameter] public PageScrollHorizontalPosition HorizontalPosition { get; set; } = PageScrollHorizontalPosition.Right; diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor index d5cea1e0..55a4bc6e 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor @@ -61,7 +61,7 @@ [Parameter] public byte VisibleFromPagePercentage { get; set; } = 30; /// - /// Scroll shold be jump or smoothly scroll. + /// Scroll should be jump or smoothly scroll. /// [Parameter] public bool SmootScroll { get; set; } = true; @@ -74,7 +74,7 @@ /// [Parameter] public int PaddingFromSide { get; set; } = 24; /// - /// Element position on page. + /// Element position on page {Right, Left}. /// [Parameter] public PageScrollHorizontalPosition HorizontalPosition { get; set; } = PageScrollHorizontalPosition.Right; diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor index b43f8993..d2bf7019 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor @@ -3,8 +3,8 @@ PaddingFromSide="@_scrcollTopSideMargin" HorizontalPosition="@_scrollHorizontalPosition" SmootScroll="@_smoothScroll" - VisibleFromPagePercentage="5" - VisibleUntilPagePercentage="70"> + VisibleFromPagePercentage="2" + VisibleUntilPagePercentage="75">
From c28f40133b02583c0606b33c83645f34ae0289c5 Mon Sep 17 00:00:00 2001 From: Major Date: Mon, 29 Mar 2021 10:06:50 +0200 Subject: [PATCH 11/69] JS docs updates. --- .github/docs/JsInterop.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/docs/JsInterop.md b/.github/docs/JsInterop.md index 22a95cfa..d86aa0d2 100644 --- a/.github/docs/JsInterop.md +++ b/.github/docs/JsInterop.md @@ -16,13 +16,16 @@ You can try it out by using the [demo app](https://blazorextensions.z6.web.core. # 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. +- **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. - **Global Mouse JS**: is an **injectable `IGlobalMouseEventHandler` service** for global mouse callback event handlers. - **Focus JS**: is an injectable `IFocusHandler` service. **Focus JS is able to identify and restore focus on ANY DOM element without using Blazor `@ref=""` tag.** - **Element info JS**: is a set of **Extension methods** for `ElementReference` objects. -- **Scroll JS**: is a set of **Extension methods** for `ElementReference` objects. **`IScrollHandler` injectable service** for non element level functions and callback event handlers. - Also `ScrollToPageBottom` and `ScrollToPageTop` components will render "floating" element with customizable placing and content for wrapping Scroll JS scroll to page top or bottom functions. +- **Scroll JS**: + - Set of **Extension methods** for `ElementReference` objects. + - **`IScrollHandler` injectable service** for non element level functions and callback event handlers. + - Also `ScrollToPageBottom` and `ScrollToPageTop` components will render "floating" element with customizable placing and content for wrapping Scroll JS scroll to page top or bottom functions. - **Resize JS**: is an **injectable `IResizeHandler` service** for Window (global) and HTML Elements resize event callback handlers. - **Clipboard JS**: is an **injectable `IClipboardHandler` service** for accessing computer Clipboard from Blazor Application. - **Language JS**: is an **injectable `ILanguageService` service** for detect the browser language preference. From 1b78b7615e489a0c5d51396289a4b5138301e218 Mon Sep 17 00:00:00 2001 From: Major Date: Mon, 29 Mar 2021 11:40:45 +0200 Subject: [PATCH 12/69] Scroll on page components issue fixes with opacity. --- .../Scroll/ScrollToPageBottom.razor | 2 +- .../Scroll/ScrollToPageBottom.razor.css | 5 +++++ .../Scroll/ScrollToPageBottom.razor.min.css | 2 +- .../Scroll/ScrollToPageTop.razor | 2 +- .../Scroll/ScrollToPageTop.razor.css | 5 +++++ .../Scroll/ScrollToPageTop.razor.min.css | 2 +- .../Components/JsDemo/ScrollJs.razor | 2 +- 7 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor index dac749f0..4d32e6a7 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor @@ -1,4 +1,4 @@ -
@Content
diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.css b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.css index ff2659d2..312d1bbc 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.css +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.css @@ -4,4 +4,9 @@ cursor: pointer; transition: opacity 0.25s linear; outline: 0px; +} + +.scrollToPageBottom.hidden { + cursor: pointer; + pointer-events: none; } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.min.css b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.min.css index 173d961d..356aa571 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.min.css +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor.min.css @@ -1 +1 @@ -.scrollToPageBottom{position:fixed;z-index:20;cursor:pointer;transition:opacity .25s linear;outline:0} \ No newline at end of file +.scrollToPageBottom{position:fixed;z-index:20;cursor:pointer;transition:opacity .25s linear;outline:0}.scrollToPageBottom.hidden{cursor:pointer;pointer-events:none} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor index 55a4bc6e..be9ba0cf 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor @@ -1,4 +1,4 @@ -
@Content
diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css index 8545dc75..d4abf22c 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.css @@ -4,4 +4,9 @@ cursor: pointer; transition: opacity 0.25s linear; outline: 0px; +} + +.scrollToPageTop.hidden { + cursor: pointer; + pointer-events: none; } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css index 20d81bac..db204003 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor.min.css @@ -1 +1 @@ -.scrollToPageTop{position:fixed;z-index:20;cursor:pointer;transition:opacity .25s linear;outline:0} \ No newline at end of file +.scrollToPageTop{position:fixed;z-index:20;cursor:pointer;transition:opacity .25s linear;outline:0}.scrollToPageTop.hidden{cursor:pointer;pointer-events:none} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor index d2bf7019..c0e68995 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor @@ -1,4 +1,4 @@ -@*Scroll on page components, can be placed anywhere on a page but should be used one per page*@ +@*Scroll on page components, can be placed anywhere on a page, moved to a new component, etc. but should be used once per page*@ Date: Mon, 29 Mar 2021 11:57:12 +0200 Subject: [PATCH 13/69] Update demo app with page scroll --- .../Components/BrowserStorage.razor | 4 +++- .../Components/Collapse.razor | 4 +++- .../Components/CssEvents.razor | 2 ++ .../Components/Debounce.razor | 4 +++- .../Components/Dialog.razor | 4 +++- .../Components/Index.razor | 4 +++- .../Components/JSInterop.razor | 4 ++-- .../Components/Loading.razor | 1 + .../Components/Maps.razor | 2 ++ .../Components/Permalink.razor | 1 + .../Components/Tabs.razor | 4 +++- .../Components/Toggle.razor | 4 +++- .../Components/Typeahead.razor | 1 + .../Shared/PageScroll.razor | 16 ++++++++++++++++ 14 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.TestApps.Common/Shared/PageScroll.razor diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/BrowserStorage.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/BrowserStorage.razor index 4e214371..867aeb57 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/BrowserStorage.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/BrowserStorage.razor @@ -1,4 +1,6 @@ -
+ + +

Browser Storage extensions

Enables Browser Local and Session storages and diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Collapse.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Collapse.razor index e53a281e..66e7db01 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Collapse.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Collapse.razor @@ -1,4 +1,6 @@ -

Collapse Components

+ + +

Collapse Components

Blazor component that renders customizable Collapsible/Expandable panel and Accordion with many but only one active panel also custom content and header. For usege see soruce code and docs on Github. diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/CssEvents.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/CssEvents.razor index e734e8b1..4dccb7af 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/CssEvents.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/CssEvents.razor @@ -12,6 +12,8 @@

  • Animations
  • + + \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Debounce.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Debounce.razor index b78c3384..911fef99 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Debounce.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Debounce.razor @@ -1,4 +1,6 @@ -

    Deboudnce Input controls

    + + +

    Deboudnce Input controls

    Blazor component that renders an Input, InputText, Textarea or InputTextarea, etc. element with debounced onChange. For usege see soruce code and docs on Github. diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Dialog.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Dialog.razor index d19078d4..1a21b5b9 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Dialog.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Dialog.razor @@ -1,4 +1,6 @@ -

    Modal Component

    + + +

    Modal Component

    Blazor component that can be used to render Modal dialog window with customizable content and parameterized Overlay, etc.. For usege see soruce code and docs on Github. diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Index.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Index.razor index 28374820..3aaeb98c 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Index.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Index.razor @@ -1,4 +1,6 @@ -

    ASP.NET Blazor Components

    + + +

    ASP.NET Blazor Components

    Blazor Components is a set of UI Components and other useful Extensions for Blazor applications. diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor index 7247062a..0434ef8c 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JSInterop.razor @@ -16,7 +16,7 @@


    @*Scroll to page end with ScrollHandler for custom floating component see ScrollToPageBottom*@ - + Smooth scroll: @@ -58,7 +58,7 @@ Smooth scroll:
    @*Scroll to page end with ScrollHandler for custom floating component see ScrollToPageTop*@ - + Smooth scroll: diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Loading.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Loading.razor index 3751c083..3151049c 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Loading.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Loading.razor @@ -1,4 +1,5 @@ @using System.ComponentModel.DataAnnotations; +

    Loading Components

    diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Maps.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Maps.razor index f1c14b66..8e487a44 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Maps.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Maps.razor @@ -20,6 +20,8 @@

    + + diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor index 9d9988bf..9ed6acb3 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor @@ -3,6 +3,7 @@ textarea { height: 150px !important; } +

    Permalink (#link) extension and component

    diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Tabs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Tabs.razor index 223f02aa..4fa8ad56 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Tabs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Tabs.razor @@ -1,4 +1,6 @@ -

    Tabs Components

    + + +

    Tabs Components

    Blazor component that renders customizable Tabs element panel with many tabs and custom content. For usege see soruce code and docs on Github. diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Toggle.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Toggle.razor index 5522c9c4..1adcd984 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Toggle.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Toggle.razor @@ -1,4 +1,6 @@ -

    Toggler Components

    + + +

    Toggler Components

    Blazor component that renders customizable Toggle Switch and Toggle Button components. For usege see soruce code and docs on Github. diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Typeahead.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Typeahead.razor index b17480d4..a1b87cfa 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Typeahead.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Typeahead.razor @@ -3,6 +3,7 @@ textarea { height: 150px !important; } +

    Typeahead Input controls

    diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/PageScroll.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/PageScroll.razor new file mode 100644 index 00000000..3cdd2ae6 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/PageScroll.razor @@ -0,0 +1,16 @@ +@*Scroll on page components wrapped into it's own component and applied on some demo pages*@ + + +

    + +
    + + + + + +
    + +
    +
    +
    \ No newline at end of file From 3b9756e681acfdcc45f3d9a89f5bda13ce12a45d Mon Sep 17 00:00:00 2001 From: Major Date: Sun, 4 Apr 2021 19:04:49 +0200 Subject: [PATCH 14/69] Created Majorsoft.Blazor.Extensions.Analytics project --- src/Majorsoft.Blazor.Components.sln | 7 +++ .../ExampleJsInterop.cs | 40 ++++++++++++++ ...jorsoft.Blazor.Extensions.Analytics.csproj | 54 +++++++++++++++++++ .../Majorsoft.Blazor.Extensions.Analytics.xml | 8 +++ .../_Imports.razor | 1 + .../bundleconfig.json | 8 +++ .../wwwroot/googleAnalytics.js | 3 ++ .../wwwroot/googleAnalytics.min.js | 1 + 8 files changed, 122 insertions(+) create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/ExampleJsInterop.cs create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.csproj create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/_Imports.razor create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/bundleconfig.json create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js diff --git a/src/Majorsoft.Blazor.Components.sln b/src/Majorsoft.Blazor.Components.sln index 715272e3..7f29ea39 100644 --- a/src/Majorsoft.Blazor.Components.sln +++ b/src/Majorsoft.Blazor.Components.sln @@ -85,6 +85,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Pipelines", "_Pipelines", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsoft.Blazor.Extensions.BrowserStorage", "Majorsoft.Blazor.Extensions.BrowserStorage\Majorsoft.Blazor.Extensions.BrowserStorage.csproj", "{B0BDDF05-5508-4D05-B766-6DB53C33D004}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsoft.Blazor.Extensions.Analytics", "Majorsoft.Blazor.Extensions.Analytics\Majorsoft.Blazor.Extensions.Analytics.csproj", "{261B879A-4E63-4D4B-9B59-E8C8F84531FF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -215,6 +217,10 @@ Global {B0BDDF05-5508-4D05-B766-6DB53C33D004}.Debug|Any CPU.Build.0 = Debug|Any CPU {B0BDDF05-5508-4D05-B766-6DB53C33D004}.Release|Any CPU.ActiveCfg = Release|Any CPU {B0BDDF05-5508-4D05-B766-6DB53C33D004}.Release|Any CPU.Build.0 = Release|Any CPU + {261B879A-4E63-4D4B-9B59-E8C8F84531FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {261B879A-4E63-4D4B-9B59-E8C8F84531FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {261B879A-4E63-4D4B-9B59-E8C8F84531FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {261B879A-4E63-4D4B-9B59-E8C8F84531FF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -250,6 +256,7 @@ Global {3AD2A154-A197-46BE-A213-51328C13B008} = {B6905E0B-759A-4928-B38A-9210B5CC8988} {CE8AF2E1-6860-4F1B-BC96-F84042E55A2F} = {020CAF9C-D289-470A-BB97-E58D73DDCE70} {B0BDDF05-5508-4D05-B766-6DB53C33D004} = {121A4471-EF7A-4F9A-A856-67E8376A4AD2} + {261B879A-4E63-4D4B-9B59-E8C8F84531FF} = {121A4471-EF7A-4F9A-A856-67E8376A4AD2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F3E8B7F7-3F7C-4D6C-8B7B-48F1AF4694B9} diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/ExampleJsInterop.cs b/src/Majorsoft.Blazor.Extensions.Analytics/ExampleJsInterop.cs new file mode 100644 index 00000000..807334e7 --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/ExampleJsInterop.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading.Tasks; + +using Microsoft.JSInterop; + +namespace Majorsoft.Blazor.Extensions.Analytics +{ + // This class provides an example of how JavaScript functionality can be wrapped + // in a .NET class for easy consumption. The associated JavaScript module is + // loaded on demand when first needed. + // + // This class can be registered as scoped DI service and then injected into Blazor + // components for use. + + public class ExampleJsInterop : IAsyncDisposable + { + private readonly Lazy> moduleTask; + + public ExampleJsInterop(IJSRuntime jsRuntime) + { + moduleTask = new(() => jsRuntime.InvokeAsync( + "import", "./_content/Majorsoft.Blazor.Analytics/exampleJsInterop.js").AsTask()); + } + + public async ValueTask Prompt(string message) + { + var module = await moduleTask.Value; + return await module.InvokeAsync("showPrompt", message); + } + + public async ValueTask DisposeAsync() + { + if (moduleTask.IsValueCreated) + { + var module = await moduleTask.Value; + await module.DisposeAsync(); + } + } + } +} diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.csproj b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.csproj new file mode 100644 index 00000000..2fc77c1e --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.csproj @@ -0,0 +1,54 @@ + + + + net5.0 + enable + 1.0.0.0 + true + blazor.components.png + Majorsoft.Blazor.Extensions.Analytics + Imre Toth + Majorsoft + Blazor Components + ©2021 Imre Toth + + https://github.com/majorimi/blazor-components/blob/master/.github/docs/BrowserStorage.md + https://github.com/majorimi/blazor-components + Git + .Net5 Blazor Analytics Google Tracking Statistics + See Releases here: https://github.com/majorimi/blazor-components/releases + Blazor extension that enables WebAssemply site analytics e.g. Google, etc. Part of Majorsoft Blazor library. + Analytics Google Tracking + License.txt + + + + .\Majorsoft.Blazor.Extensions.Analytics.xml + + + + + + + + + True + + + + True + + + + + + + + + + + + + + + diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml new file mode 100644 index 00000000..10b14e47 --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml @@ -0,0 +1,8 @@ + + + + Majorsoft.Blazor.Extensions.Analytics + + + + diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/_Imports.razor b/src/Majorsoft.Blazor.Extensions.Analytics/_Imports.razor new file mode 100644 index 00000000..77285129 --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/_Imports.razor @@ -0,0 +1 @@ +@using Microsoft.AspNetCore.Components.Web diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/bundleconfig.json b/src/Majorsoft.Blazor.Extensions.Analytics/bundleconfig.json new file mode 100644 index 00000000..991f9ed5 --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/bundleconfig.json @@ -0,0 +1,8 @@ +[ + { + "outputFileName": "wwwroot/googleAnalytics.min.js", + "inputFiles": [ + "wwwroot/googleAnalytics.js" + ] + } +] diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js new file mode 100644 index 00000000..4c9cb888 --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js @@ -0,0 +1,3 @@ +export function showPrompt(message) { + return prompt(message, 'Type anything here'); +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js new file mode 100644 index 00000000..d88e4114 --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js @@ -0,0 +1 @@ +export function showPrompt(n){return prompt(n,"Type anything here")} \ No newline at end of file From 4afa519341d0d06fc675ebd1158ef598a89fc033 Mon Sep 17 00:00:00 2001 From: Major Date: Sun, 4 Apr 2021 21:06:33 +0200 Subject: [PATCH 15/69] Created analytics menu and demo page. --- .../Pages/AnalyticsPage.razor | 3 +++ .../Components/GoogleAnalytics.razor | 21 +++++++++++++++++++ .../Pages/AnalyticsPage.razor | 3 +++ .../Shared/NavMenu.razor | 5 ++++- .../Pages/AnalyticsPage.razor | 3 +++ 5 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/Majorsoft.Blazor.Components.TestApp/Pages/AnalyticsPage.razor create mode 100644 src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor create mode 100644 src/Majorsoft.Blazor.Components.TestApps.Common/Pages/AnalyticsPage.razor create mode 100644 src/Majorsoft.Blazor.Components.TestServerApp/Pages/AnalyticsPage.razor diff --git a/src/Majorsoft.Blazor.Components.TestApp/Pages/AnalyticsPage.razor b/src/Majorsoft.Blazor.Components.TestApp/Pages/AnalyticsPage.razor new file mode 100644 index 00000000..9850231d --- /dev/null +++ b/src/Majorsoft.Blazor.Components.TestApp/Pages/AnalyticsPage.razor @@ -0,0 +1,3 @@ +@page "/analytics" + + \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor new file mode 100644 index 00000000..68590c80 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor @@ -0,0 +1,21 @@ + + +
    +

    Website Analytics extensions

    +

    + Blazor extension that enables WebAssemply site analytics e.g. Google, etc. For usage see source code and docs on + Github. +
    Majorsoft.Blazor.Extensions.Analytics package is available on Nuget +

    + +
    +

    Browser Storage features:

    +
      +
    • Google Analytics
    • +
    +
    +
    + +@code { + +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Pages/AnalyticsPage.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Pages/AnalyticsPage.razor new file mode 100644 index 00000000..9850231d --- /dev/null +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Pages/AnalyticsPage.razor @@ -0,0 +1,3 @@ +@page "/analytics" + + \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/NavMenu.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/NavMenu.razor index 018c8127..729bcfce 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/NavMenu.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/NavMenu.razor @@ -24,7 +24,10 @@ Console logger +
    diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Pages/AnalyticsPage.razor b/src/Majorsoft.Blazor.Components.TestServerApp/Pages/AnalyticsPage.razor new file mode 100644 index 00000000..9850231d --- /dev/null +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Pages/AnalyticsPage.razor @@ -0,0 +1,3 @@ +@page "/analytics" + + \ No newline at end of file From a81b6a95bca5fc6d0c1d69aae4d99e11ab097586 Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 6 Apr 2021 21:31:25 +0200 Subject: [PATCH 16/69] Google analytics service. --- .../Components/GoogleAnalytics.razor | 15 ++++++- .../ExampleJsInterop.cs | 40 ----------------- .../Google/GoogleAnalyticsService.cs | 44 +++++++++++++++++++ .../Majorsoft.Blazor.Extensions.Analytics.xml | 10 +++++ 4 files changed, 68 insertions(+), 41 deletions(-) delete mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/ExampleJsInterop.cs create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor index 68590c80..8148812d 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor @@ -9,11 +9,24 @@

    -

    Browser Storage features:

    +

    Supported Analytics service providers:

    • Google Analytics
    + +
    + +

    Google Analytics

    +
    +

    + Google Analytics is a web analytics service offered by Google that tracks and reports website traffic, etc. inside the Google Marketing Platform. +
    + IGoogleAnalyticsService is an injectable service for enabling + Google Analytics page tracking in Blazor Apps. +

    +
    +
    @code { diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/ExampleJsInterop.cs b/src/Majorsoft.Blazor.Extensions.Analytics/ExampleJsInterop.cs deleted file mode 100644 index 807334e7..00000000 --- a/src/Majorsoft.Blazor.Extensions.Analytics/ExampleJsInterop.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Threading.Tasks; - -using Microsoft.JSInterop; - -namespace Majorsoft.Blazor.Extensions.Analytics -{ - // This class provides an example of how JavaScript functionality can be wrapped - // in a .NET class for easy consumption. The associated JavaScript module is - // loaded on demand when first needed. - // - // This class can be registered as scoped DI service and then injected into Blazor - // components for use. - - public class ExampleJsInterop : IAsyncDisposable - { - private readonly Lazy> moduleTask; - - public ExampleJsInterop(IJSRuntime jsRuntime) - { - moduleTask = new(() => jsRuntime.InvokeAsync( - "import", "./_content/Majorsoft.Blazor.Analytics/exampleJsInterop.js").AsTask()); - } - - public async ValueTask Prompt(string message) - { - var module = await moduleTask.Value; - return await module.InvokeAsync("showPrompt", message); - } - - public async ValueTask DisposeAsync() - { - if (moduleTask.IsValueCreated) - { - var module = await moduleTask.Value; - await module.DisposeAsync(); - } - } - } -} diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs new file mode 100644 index 00000000..9ee3f049 --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs @@ -0,0 +1,44 @@ +using System; +using System.Threading.Tasks; + +using Microsoft.JSInterop; + +namespace Majorsoft.Blazor.Extensions.Analytics.Google +{ + /// + /// + /// + public interface IGoogleAnalyticsService : IAsyncDisposable + { + + } + + /// + /// + /// + public class GoogleAnalyticsService : IGoogleAnalyticsService + { + private readonly Lazy> moduleTask; + + public GoogleAnalyticsService(IJSRuntime jsRuntime) + { + moduleTask = new(() => jsRuntime.InvokeAsync( + "import", "./_content/Majorsoft.Blazor.Analytics/googleAnalytics.js").AsTask()); + } + + public async ValueTask Configure(string trackingId) + { + var module = await moduleTask.Value; + await module.InvokeVoidAsync("configure", trackingId); + } + + public async ValueTask DisposeAsync() + { + if (moduleTask.IsValueCreated) + { + var module = await moduleTask.Value; + await module.DisposeAsync(); + } + } + } +} diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml index 10b14e47..6913aca1 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml @@ -4,5 +4,15 @@ Majorsoft.Blazor.Extensions.Analytics + + + + + + + + + + From 8d84c5b548474ae1f0eeac3ad32675075aec1384 Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 6 Apr 2021 22:04:54 +0200 Subject: [PATCH 17/69] Added scroll to page elements AnimateOnHover functionality. --- .github/docs/JsInterop.md | 4 ++++ .../Majorsoft.Blazor.Components.Common.JsInterop.xml | 10 ++++++++++ .../Scroll/ScrollToPageBottom.razor | 10 +++++++++- .../Scroll/ScrollToPageTop.razor | 10 +++++++++- .../Components/JsDemo/ScrollJs.razor | 6 +++++- .../Shared/PageScroll.razor | 4 ++-- 6 files changed, 39 insertions(+), 5 deletions(-) diff --git a/.github/docs/JsInterop.md b/.github/docs/JsInterop.md index d86aa0d2..80667793 100644 --- a/.github/docs/JsInterop.md +++ b/.github/docs/JsInterop.md @@ -127,6 +127,8 @@ Element should be visible when scroll reached page % of given value. Element should be visible until scroll reached page % of given value. - **`SmootScroll`: `bool { get; set; }` (default: true)**
    Scroll should be jump or smoothly scroll. +- **`AnimateOnHover`: `bool { get; set; }` (default: true)**
    +Apply animation (opacity) on icon when mouse hovered. - **`PaddingFromTop`: `int { get; set; }` (default: 24)**
    Required space from page bottom in px. - **`PaddingFromSide`: `int { get; set; }` (default: 24)**
    @@ -150,6 +152,8 @@ Required HTML content which will be wrapped into a `` which has the Click Element should be visible when scroll reached page % of given value. - **`SmootScroll`: `bool { get; set; }` (default: true)**
    Scroll should be jump or smoothly scroll. +- **`AnimateOnHover`: `bool { get; set; }` (default: true)**
    +Apply animation (opacity) on icon when mouse hovered. - **`PaddingFromTop`: `int { get; set; }` (default: 24)**
    Required space from page bottom in px. - **`PaddingFromSide`: `int { get; set; }` (default: 24)**
    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 645f1617..fb42b7bd 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 @@ -832,6 +832,11 @@ Scroll should be jump or smoothly scroll. + + + Apply animation (opacity) on icon when mouse hovered. + + Required space from page bottom in px. @@ -867,6 +872,11 @@ Scroll should be jump or smoothly scroll. + + + Apply animation (opacity) on icon when mouse hovered. + + Required space from page bottom in px. diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor index 4d32e6a7..df63813a 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageBottom.razor @@ -6,7 +6,10 @@ @implements IAsyncDisposable @@ -25,82 +25,85 @@ @inject ILogger _logger @code { - private bool _isVisible = false; - - protected override async Task OnInitializedAsync() - { - WriteDiag("Component initialized..."); - - await _scrollHandler.RegisterPageScrollAsync(async (e) => - { - var scrollSize = await _scrollHandler.GetPageScrollSizeAsync(); - var percentage = (e.Y / scrollSize.Y) * 100; - - var visible = percentage >= VisibleFromPagePercentage && percentage <= VisibleUntilPagePercentage && !scrollSize.IsPageBottom; - if (visible != _isVisible) - { - _isVisible = visible; - StateHasChanged(); - - WriteDiag($"Component visibilit changed visible: {_isVisible}."); - } - }); - } - - private ElementReference _inputRef; - /// - /// Exposes a Blazor of the wrapped around HTML element. It can be used e.g. for JS interop, etc. - /// - public ElementReference InnerElementReference => _inputRef; - - /// - /// HTML Content of the scroll panel. - /// - [Parameter] public RenderFragment Content { get; set; } - - /// - /// Element should be visible when scroll reached page % of given value. - /// - [Parameter] public byte VisibleFromPagePercentage { get; set; } = 5; - /// - /// Element should be visible until scroll reached page % of given value. - /// - [Parameter] public byte VisibleUntilPagePercentage { get; set; } = 80; - - /// - /// Scroll should be jump or smoothly scroll. - /// - [Parameter] public bool SmootScroll { get; set; } = true; - - /// - /// Apply animation (opacity) on icon when mouse hovered. - /// - [Parameter] public bool AnimateOnHover { get; set; } = true; - - /// - /// Required space from page bottom in px. - /// - [Parameter] public int PaddingFromTop { get; set; } = 24; - /// - /// Required space from page (left/right) side in px. - /// - [Parameter] public int PaddingFromSide { get; set; } = 24; - /// - /// Element position on page {Right, Left}. - /// - [Parameter] public PageScrollHorizontalPosition HorizontalPosition { get; set; } = PageScrollHorizontalPosition.Right; - - - private void WriteDiag(string message) - { - _logger.LogDebug($"Component {this.GetType()}: {message}"); - } - - public async ValueTask DisposeAsync() - { - if (_scrollHandler is not null) - { - await _scrollHandler.DisposeAsync(); - } - } + private bool _isVisible = false; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if(firstRender) + { + WriteDiag("Component rendered..."); + + await _scrollHandler.RegisterPageScrollAsync(async (e) => + { + var scrollSize = await _scrollHandler.GetPageScrollSizeAsync(); + var percentage = (e.Y / scrollSize.Y) * 100; + + var visible = percentage >= VisibleFromPagePercentage && percentage <= VisibleUntilPagePercentage && !scrollSize.IsPageBottom; + if (visible != _isVisible) + { + _isVisible = visible; + StateHasChanged(); + + WriteDiag($"Component visibilit changed visible: {_isVisible}."); + } + }); + } + } + + private ElementReference _inputRef; + /// + /// Exposes a Blazor of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + /// + public ElementReference InnerElementReference => _inputRef; + + /// + /// HTML Content of the scroll panel. + /// + [Parameter] public RenderFragment Content { get; set; } + + /// + /// Element should be visible when scroll reached page % of given value. + /// + [Parameter] public byte VisibleFromPagePercentage { get; set; } = 5; + /// + /// Element should be visible until scroll reached page % of given value. + /// + [Parameter] public byte VisibleUntilPagePercentage { get; set; } = 80; + + /// + /// Scroll should be jump or smoothly scroll. + /// + [Parameter] public bool SmootScroll { get; set; } = true; + + /// + /// Apply animation (opacity) on icon when mouse hovered. + /// + [Parameter] public bool AnimateOnHover { get; set; } = true; + + /// + /// Required space from page bottom in px. + /// + [Parameter] public int PaddingFromTop { get; set; } = 24; + /// + /// Required space from page (left/right) side in px. + /// + [Parameter] public int PaddingFromSide { get; set; } = 24; + /// + /// Element position on page {Right, Left}. + /// + [Parameter] public PageScrollHorizontalPosition HorizontalPosition { get; set; } = PageScrollHorizontalPosition.Right; + + + private void WriteDiag(string message) + { + _logger.LogDebug($"Component {this.GetType()}: {message}"); + } + + public async ValueTask DisposeAsync() + { + if (_scrollHandler is not null) + { + await _scrollHandler.DisposeAsync(); + } + } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor index 0e65bac3..e9d9e97a 100644 --- a/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor +++ b/src/Majorsoft.Blazor.Components.Common.JsInterop/Scroll/ScrollToPageTop.razor @@ -27,24 +27,27 @@ @code { private bool _isVisible = false; - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { - WriteDiag("Component initialized..."); - - await _scrollHandler.RegisterPageScrollAsync(async (e) => + if (firstRender) { - var scrollSize = await _scrollHandler.GetPageScrollSizeAsync(); - var percentage = (e.Y / scrollSize.Y) * 100; + WriteDiag("Component rendered..."); - var visible = percentage >= VisibleFromPagePercentage; - if (visible != _isVisible) + await _scrollHandler.RegisterPageScrollAsync(async (e) => { - _isVisible = visible; - StateHasChanged(); - - WriteDiag($"Component visibilit changed visible: {_isVisible}."); - } - }); + var scrollSize = await _scrollHandler.GetPageScrollSizeAsync(); + var percentage = (e.Y / scrollSize.Y) * 100; + + var visible = percentage >= VisibleFromPagePercentage; + if (visible != _isVisible) + { + _isVisible = visible; + StateHasChanged(); + + WriteDiag($"Component visibilit changed visible: {_isVisible}."); + } + }); + } } private ElementReference _inputRef; From a172378dbeadc0e81501cb7e4a5de2671cc612c8 Mon Sep 17 00:00:00 2001 From: Major Date: Mon, 21 Jun 2021 19:06:45 +0200 Subject: [PATCH 31/69] Change Google analytics Get() to accept ExpandoObject. --- .../Components/GoogleAnalytics.razor | 67 ++++++++++++------- .../_Imports.razor | 4 +- .../Startup.cs | 3 + .../AnalyticsExtension.cs | 4 +- .../Google/GoogleAnalyticsService.cs | 9 +-- .../Majorsoft.Blazor.Extensions.Analytics.xml | 6 +- .../wwwroot/googleAnalytics.js | 19 ++++-- .../wwwroot/googleAnalytics.min.js | 4 +- 8 files changed, 74 insertions(+), 42 deletions(-) diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor index 8148812d..12523988 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor @@ -1,34 +1,49 @@ 
    -

    Website Analytics extensions

    -

    - Blazor extension that enables WebAssemply site analytics e.g. Google, etc. For usage see source code and docs on - Github. -
    Majorsoft.Blazor.Extensions.Analytics package is available on Nuget -

    - -
    -

    Supported Analytics service providers:

    -
      -
    • Google Analytics
    • -
    -
    - -
    - -

    Google Analytics

    -
    -

    - Google Analytics is a web analytics service offered by Google that tracks and reports website traffic, etc. inside the Google Marketing Platform. -
    - IGoogleAnalyticsService is an injectable service for enabling - Google Analytics page tracking in Blazor Apps. -

    -
    +

    Website Analytics extensions

    +

    + Blazor extension that enables WebAssemply site analytics e.g. Google, etc. For usage see source code and docs on + Github. +
    Majorsoft.Blazor.Extensions.Analytics package is available on Nuget +

    + +
    +

    Supported Analytics service providers:

    +
      +
    • Google Analytics
    • +
    +
    + +
    + +

    Google Analytics

    +
    +

    + Google Analytics is a web analytics service offered by Google that tracks and reports website traffic, etc. inside the Google Marketing Platform. +
    + IGoogleAnalyticsService is an injectable service for enabling + Google Analytics page tracking in Blazor Apps. +

    +
    -@code { +@using System.Dynamic; +@inject IGoogleAnalyticsService _googleAnalytincsService; + +@code { + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if(firstRender) + { + await _googleAnalytincsService.Initialize("G-1QD2VGTEWX"); + + dynamic exp = new ExpandoObject(); + exp.test = 27; + await _googleAnalytincsService.Set(exp); + await _googleAnalytincsService.Get("test"); + } + } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor index d7a6adc7..5702ed14 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor @@ -46,4 +46,6 @@ @using Majorsoft.Blazor.Components.Maps @using Majorsoft.Blazor.Components.Maps.Google -@using Majorsoft.Blazor.Extensions.BrowserStorage \ No newline at end of file +@using Majorsoft.Blazor.Extensions.BrowserStorage + +@using Majorsoft.Blazor.Extensions.Analytics.Google \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs b/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs index 892505d8..4456c637 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs @@ -11,6 +11,7 @@ using Majorsoft.Blazor.Server.Logging.Console; using Majorsoft.Blazor.Components.Maps; using Majorsoft.Blazor.Extensions.BrowserStorage; +using Majorsoft.Blazor.Extensions.Analytics; namespace Majorsoft.Blazor.Components.TestServerApp { @@ -35,6 +36,8 @@ public void ConfigureServices(IServiceCollection services) services.AddMapExtensions(); services.AddBrowserStorage(); + + services.AddGoogleAnalytics(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs b/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs index 2a3e54c7..f3eaf7a1 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs +++ b/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs @@ -13,11 +13,11 @@ public static class AnalyticsExtension /// Registers required Google Analytic services into IServiceCollection ///
    /// IServiceCollection instance - /// Google Tracking Id when provided will be called. + /// Google Tracking Id when provided will be called. DO NOT CALL with value in case of Blazor Server! /// IServiceCollection public static IServiceCollection AddGoogleAnalytics(this IServiceCollection services, string trackingId = "") { - services.AddSingleton(); + services.AddTransient(); if (!string.IsNullOrWhiteSpace(trackingId)) { services.BuildServiceProvider().GetRequiredService()?.Initialize(trackingId); diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs index 447dca18..81206d26 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Dynamic; using System.Linq; using System.Threading.Tasks; @@ -44,9 +45,9 @@ public interface IGoogleAnalyticsService : IAsyncDisposable /// /// Allows you to set values that persist across all the subsequent gtag() calls on the page. /// - /// Is a key name and the value that is to persist across gtag() calls. + /// Is a key name and the value that is to persist across gtag() calls. /// Async ValueTask - ValueTask Set(Dictionary? parameterValuePair = null); + ValueTask Set(ExpandoObject parameters); /// /// Use the event command to send event data. @@ -101,10 +102,10 @@ public async ValueTask Get(string fieldName, string trackingId = "") var module = await moduleTask.Value; await module.InvokeVoidAsync("get", string.IsNullOrWhiteSpace(trackingId) ? TrackingId : trackingId, fieldName); //TODO: callback results } - public async ValueTask Set(Dictionary? parameterValuePair = null) + public async ValueTask Set(ExpandoObject parameters) { var module = await moduleTask.Value; - await module.InvokeVoidAsync("set", parameterValuePair?.ToList()); + await module.InvokeVoidAsync("set", parameters); } public async ValueTask Event(string eventName, Dictionary eventParams) { diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml index a699fc81..bbd86d39 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml @@ -14,7 +14,7 @@ Registers required Google Analytic services into IServiceCollection IServiceCollection instance - Google Tracking Id when provided will be called. + Google Tracking Id when provided will be called. DO NOT CALL with value in case of Blazor Server! IServiceCollection
    @@ -51,11 +51,11 @@ Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property Async ValueTask - + Allows you to set values that persist across all the subsequent gtag() calls on the page. - Is a key name and the value that is to persist across gtag() calls. + Is a key name and the value that is to persist across gtag() calls. Async ValueTask diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js index 22c893d1..a5ddd969 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js +++ b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js @@ -4,13 +4,23 @@ export function init(trackingId) { } let src = "https://www.googletagmanager.com/gtag/js?id="; + let scriptsIncluded = false; + let scriptTags = document.querySelectorAll('head > script'); scriptTags.forEach(scriptTag => { - if (scriptTag.getAttribute('src').startsWith(src)) { - return; + if (scriptTag) { + let srcAttribute = scriptTag.getAttribute('src'); + if (srcAttribute && srcAttribute.startsWith(src)) { + scriptsIncluded = true; + return; + } } }); + if (scriptsIncluded && window.gtag) { //Prevent adding JS scripts to page multiple times. + return; + } + //Inject required Google JS scripts to HTML (only once!) document.head.appendChild(document.createComment("Global site tag (gtag.js) - Google Analytics")); @@ -38,13 +48,14 @@ export function config(trackingId, data) { export function get(trackingId, fieldName) { if (trackingId && window.gtag) { gtag('get', trackingId, fieldName, (result) => { - //callback method here... + //TOOD: callback method here... + console.log(result); }); } } export function set(data) { if (window.gtag && data) { - gtag('set', trackingId, data); + gtag('set', data); } } export function event(eventName, data) { diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js index 5594c0f8..0a1a89f5 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js +++ b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js @@ -1,5 +1,5 @@ -export function init(n){if(n){let i="https://www.googletagmanager.com/gtag/js?id=",u=document.querySelectorAll("head > script");u.forEach(n=>{n.getAttribute("src").startsWith(i)});document.head.appendChild(document.createComment("Global site tag (gtag.js) - Google Analytics"));let t=document.createElement("script");t.src=i+n;t.async=!0;document.head.appendChild(t);let r=document.createElement("script");r.textContent=`window.dataLayer = window.dataLayer || []; +export function init(n){if(n){let i="https://www.googletagmanager.com/gtag/js?id=",r=!1,f=document.querySelectorAll("head > script");if(f.forEach(n=>{if(n){let t=n.getAttribute("src");if(t&&t.startsWith(i)){r=!0;return}}}),!r||!window.gtag){document.head.appendChild(document.createComment("Global site tag (gtag.js) - Google Analytics"));let t=document.createElement("script");t.src=i+n;t.async=!0;document.head.appendChild(t);let u=document.createElement("script");u.textContent=`window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); - gtag('config', '{0}');`.replace("{0}",n);document.head.appendChild(r)}}export function config(n,t){n&&window.gtag&>ag("config",n,t)}export function get(n,t){n&&window.gtag&>ag("get",n,t,()=>{})}export function set(n){window.gtag&&n&>ag("set",trackingId,n)}export function event(n,t){window.gtag&&n&&t&>ag("event",n,t)} \ No newline at end of file + gtag('config', '{0}');`.replace("{0}",n);document.head.appendChild(u)}}}export function config(n,t){n&&window.gtag&>ag("config",n,t)}export function get(n,t){n&&window.gtag&>ag("get",n,t,n=>{console.log(n)})}export function set(n){window.gtag&&n&>ag("set",n)}export function event(n,t){window.gtag&&n&&t&>ag("event",n,t)} \ No newline at end of file From 17adac3cf9812f4d0331903acf7807ac72554aa3 Mon Sep 17 00:00:00 2001 From: Major Date: Mon, 21 Jun 2021 19:07:00 +0200 Subject: [PATCH 32/69] Fix GoogleMaps JS script insert. --- .../wwwroot/googleMaps.js | 13 +++++++++---- .../wwwroot/googleMaps.min.js | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js index bdb034c9..76db8114 100644 --- a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js +++ b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.js @@ -5,12 +5,17 @@ storeElementIdWithDotnetRef(_mapsElementDict, elementId, dotnetRef, backgroundColor, controlSize); //Store map info + let src = "https://maps.googleapis.com/maps/api/js?key="; let scriptsIncluded = false; + let scriptTags = document.querySelectorAll('head > script'); scriptTags.forEach(scriptTag => { - if (scriptTag.getAttribute('src').startsWith("https://maps.googleapis.com/maps/api/js?key=")) { - scriptsIncluded = true; - return; + if (scriptTag) { + let srcAttribute = scriptTag.getAttribute('src'); + if (srcAttribute && srcAttribute.startsWith(src)) { + scriptsIncluded = true; + return; + } } }); @@ -26,7 +31,7 @@ importedPoly.src = "https://polyfill.io/v3/polyfill.min.js?features=default"; document.head.appendChild(importedPoly); - let src = "https://maps.googleapis.com/maps/api/js?key=" + key + "&callback=initGoogleMaps&libraries=&v=weekly"; + src = src + key + "&callback=initGoogleMaps&libraries=&v=weekly"; let importedMaps = document.createElement('script'); importedMaps.src = src; importedMaps.defer = true; diff --git a/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js b/src/Majorsoft.Blazor.Components.Maps/wwwroot/googleMaps.min.js index 26b1cf06..68bbd396 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 s=!1,c=document.querySelectorAll("head > script");if(c.forEach(n=>{if(n.getAttribute("src").startsWith("https://maps.googleapis.com/maps/api/js?key=")){s=!0;return}}),s){window.google&&window.initGoogleMaps();return}let h=document.createElement("script");h.src="https://polyfill.io/v3/polyfill.min.js?features=default";document.head.appendChild(h);let l="https://maps.googleapis.com/maps/api/js?key="+t+"&callback=initGoogleMaps&libraries=&v=weekly",o=document.createElement("script");o.src=l;o.defer=!0;document.head.appendChild(o)}}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,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 From a2759dbaf440032d2592358fdcc2b91cfab4a2a0 Mon Sep 17 00:00:00 2001 From: Major Date: Mon, 21 Jun 2021 21:33:30 +0200 Subject: [PATCH 33/69] Implement Google analytics get/set/event --- .../Components/GoogleAnalytics.razor | 34 ++++++++-- .../Shared/MainLayout.razor | 15 ++++- .../Startup.cs | 2 +- .../_Imports.razor | 4 +- .../AnalyticsExtension.cs | 15 ++++- .../Google/GoogleAnalyticsCustomEventArgs.cs | 29 +++++++++ .../Google/GoogleAnalyticsEventTypes.cs | 33 ++++++++++ .../Google/GoogleAnalyticsService.cs | 45 ++++++++----- .../Majorsoft.Blazor.Extensions.Analytics.xml | 64 ++++++++++++++++--- .../wwwroot/googleAnalytics.js | 10 +++ .../wwwroot/googleAnalytics.min.js | 2 +- 11 files changed, 218 insertions(+), 35 deletions(-) create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsCustomEventArgs.cs create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsEventTypes.cs diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor index 12523988..cd4eec9f 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor @@ -29,21 +29,45 @@
    -@using System.Dynamic; -@inject IGoogleAnalyticsService _googleAnalytincsService; +@using System.Dynamic +@inject IGoogleAnalyticsService _googleAnalytincsService +@implements IAsyncDisposable @code { protected override async Task OnAfterRenderAsync(bool firstRender) { if(firstRender) { - await _googleAnalytincsService.Initialize("G-1QD2VGTEWX"); + /*var sessionId = */ + await _googleAnalytincsService.GetAsync("session_id"); + //custom set/get dynamic exp = new ExpandoObject(); exp.test = 27; - await _googleAnalytincsService.Set(exp); - await _googleAnalytincsService.Get("test"); + await _googleAnalytincsService.SetAsync(exp); + /*var test =*/ + await _googleAnalytincsService.GetAsync("test"); + + dynamic exp2 = new ExpandoObject(); + exp2.search_term = "heeeeeeeeeeee"; + await _googleAnalytincsService.EventAsync(GoogleAnalyticsEventTypes.search, exp2); + + await _googleAnalytincsService.CustomEventAsync("testEvent", new GoogleAnalyticsCustomEventArgs() + { + Action = "Test action", + Category = "cat", + Label = "label", + Value = 22 + }); + } + } + + public async ValueTask DisposeAsync() + { + if(_googleAnalytincsService is not null) + { + await _googleAnalytincsService.DisposeAsync(); } } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor index 2f4eab97..e9a55e4f 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor @@ -14,7 +14,7 @@
    -@*Permalink*@ +@*Permalink initialize*@ @using Majorsoft.Blazor.Components.PermaLink @using Microsoft.Extensions.Logging @using Majorsoft.Blazor.Components.Common.JsInterop.Scroll @@ -23,10 +23,13 @@ @inject NavigationManager _navigationManager @inject ILogger _logger -@*Server hoster Blazor console log*@ +@*Server hosted Blazor console log*@ @using Majorsoft.Blazor.Server.Logging.Console @inject IBrowserConsoleLoggerService _browserConsoleLogger +@* Google Analytics *@ +@inject IGoogleAnalyticsService _googleAnalytincsService + @implements IDisposable @implements IAsyncDisposable @@ -43,6 +46,9 @@ //setup console log await _browserConsoleLogger.StartLoggerAsync(); + + //setup Google analitics + await _googleAnalytincsService.InitializeAsync("G-1QD2VGTEWX"); } } @@ -57,5 +63,10 @@ { await _browserConsoleLogger.DisposeAsync(); } + + if(_googleAnalytincsService is not null) + { + await _googleAnalytincsService.DisposeAsync(); + } } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs b/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs index 4456c637..aff9b45b 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs @@ -37,7 +37,7 @@ public void ConfigureServices(IServiceCollection services) services.AddMapExtensions(); services.AddBrowserStorage(); - services.AddGoogleAnalytics(); + services.AddGoogleAnalyticsForBlazorServer(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/_Imports.razor b/src/Majorsoft.Blazor.Components.TestServerApp/_Imports.razor index 31bebc3b..26c534e7 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/_Imports.razor +++ b/src/Majorsoft.Blazor.Components.TestServerApp/_Imports.razor @@ -11,4 +11,6 @@ @using Majorsoft.Blazor.Components.TestApps.Common @using Majorsoft.Blazor.Components.TestApps.Common.Components -@using Majorsoft.Blazor.Components.TestApps.Common.Shared \ No newline at end of file +@using Majorsoft.Blazor.Components.TestApps.Common.Shared + +@using Majorsoft.Blazor.Extensions.Analytics.Google \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs b/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs index f3eaf7a1..89e3373d 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs +++ b/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs @@ -11,19 +11,30 @@ public static class AnalyticsExtension { /// /// Registers required Google Analytic services into IServiceCollection + /// DO NOT CALL with value in case of Blazor Server! /// /// IServiceCollection instance - /// Google Tracking Id when provided will be called. DO NOT CALL with value in case of Blazor Server! + /// Google Tracking Id when provided will be called. DO NOT CALL with value in case of Blazor Server! /// IServiceCollection public static IServiceCollection AddGoogleAnalytics(this IServiceCollection services, string trackingId = "") { services.AddTransient(); if (!string.IsNullOrWhiteSpace(trackingId)) { - services.BuildServiceProvider().GetRequiredService()?.Initialize(trackingId); + services.BuildServiceProvider().GetRequiredService()?.InitializeAsync(trackingId); } return services; } + + /// + /// Registers required Google Analytic services into IServiceCollection for Blazor Server + /// + /// IServiceCollection instance + /// IServiceCollection + public static IServiceCollection AddGoogleAnalyticsForBlazorServer(this IServiceCollection services) + { + return services.AddGoogleAnalytics(); + } } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsCustomEventArgs.cs b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsCustomEventArgs.cs new file mode 100644 index 00000000..da782dfe --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsCustomEventArgs.cs @@ -0,0 +1,29 @@ +namespace Majorsoft.Blazor.Extensions.Analytics.Google +{ + /// + /// Data for custom event. + /// For more details see: https://developers.google.com/analytics/devguides/collection/gtagjs/events + /// + public class GoogleAnalyticsCustomEventArgs + { + /// + /// The value that will appear as the event action in Google Analytics Event reports. + /// + public string Action { get; set; } + + /// + /// The category of the event. + /// + public string Category { get; set; } + + /// + /// The label of the event. + /// + public string Label { get; set; } + + /// + /// A non-negative integer that will appear as the event value. + /// + public int Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsEventTypes.cs b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsEventTypes.cs new file mode 100644 index 00000000..46d22712 --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsEventTypes.cs @@ -0,0 +1,33 @@ +namespace Majorsoft.Blazor.Extensions.Analytics.Google +{ + /// + /// Event types for Google Analytics + /// for event and parameter details see: https://developers.google.com/gtagjs/reference/event + /// + public enum GoogleAnalyticsEventTypes + { + add_payment_info, + add_to_cart, + add_to_wishlist, + begin_checkout, + checkout_progress, + exception, + generate_lead, + login, + page_view, + purchase, + refund, + remove_from_cart, + screen_view, + search, + select_content, + set_checkout_option, + share, + sign_up, + timing_complete, + view_item, + view_item_list, + view_promotion, + view_search_results + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs index 81206d26..73a35280 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs @@ -14,7 +14,7 @@ namespace Majorsoft.Blazor.Extensions.Analytics.Google public interface IGoogleAnalyticsService : IAsyncDisposable { /// - /// Google analytics uniquely Id which was used in method. + /// Google analytics uniquely Id which was used in method. /// string TrackingId { get; } @@ -23,7 +23,7 @@ public interface IGoogleAnalyticsService : IAsyncDisposable /// /// Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property /// Async ValueTask - ValueTask Initialize(string trackingId); + ValueTask InitializeAsync(string trackingId); /// /// Allows you to add additional configuration information to targets. This is typically product-specific configuration for a product @@ -32,7 +32,7 @@ public interface IGoogleAnalyticsService : IAsyncDisposable /// Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property /// Is one or more optional parameter-value pairs /// Async ValueTask - ValueTask Config(string trackingId = "", Dictionary? configInfo = null); + ValueTask ConfigAsync(string trackingId = "", Dictionary? configInfo = null); /// /// Allows you to get various values from gtag.js including values set with the set command. @@ -40,22 +40,30 @@ public interface IGoogleAnalyticsService : IAsyncDisposable /// The name of the field to get. /// Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property /// Async ValueTask - ValueTask Get(string fieldName, string trackingId = ""); + ValueTask GetAsync(string fieldName, string trackingId = ""); /// /// Allows you to set values that persist across all the subsequent gtag() calls on the page. /// /// Is a key name and the value that is to persist across gtag() calls. /// Async ValueTask - ValueTask Set(ExpandoObject parameters); + ValueTask SetAsync(ExpandoObject parameters); /// /// Use the event command to send event data. /// - /// A recommended event or a custom event name. + /// A recommended event /// Is one or more parameter-value pairs. /// Async ValueTask - ValueTask Event(string eventName, Dictionary eventParams); + ValueTask EventAsync(GoogleAnalyticsEventTypes eventType, ExpandoObject eventParams); + + /// + /// Use the event command to send custom event data. + /// + /// Custom event name. + /// Custom event data + /// Async ValueTask + ValueTask CustomEventAsync(string customEventName, GoogleAnalyticsCustomEventArgs eventData); } /// @@ -64,8 +72,9 @@ public interface IGoogleAnalyticsService : IAsyncDisposable public class GoogleAnalyticsService : IGoogleAnalyticsService { private readonly Lazy> moduleTask; + private static string _trackingId; //Service cannot registered as Singleton. - public string TrackingId { get; private set; } + public string TrackingId => _trackingId; public GoogleAnalyticsService(IJSRuntime jsRuntime) { @@ -79,38 +88,44 @@ public GoogleAnalyticsService(IJSRuntime jsRuntime) moduleTask = new(() => jsRuntime.InvokeAsync("import", js).AsTask()); } - public async ValueTask Initialize(string trackingId) + public async ValueTask InitializeAsync(string trackingId) { if(!string.IsNullOrWhiteSpace(TrackingId) || string.IsNullOrWhiteSpace(trackingId)) //Already initializer or wrong id { return; } - TrackingId = trackingId; + _trackingId = trackingId; var module = await moduleTask.Value; await module.InvokeVoidAsync("init", trackingId); } - public async ValueTask Config(string trackingId = "", Dictionary? configInfo = null) + public async ValueTask ConfigAsync(string trackingId = "", Dictionary? configInfo = null) { var module = await moduleTask.Value; await module.InvokeVoidAsync("config", string.IsNullOrWhiteSpace(trackingId) ? TrackingId : trackingId, configInfo?.ToList()); } - public async ValueTask Get(string fieldName, string trackingId = "") + public async ValueTask GetAsync(string fieldName, string trackingId = "") { var module = await moduleTask.Value; await module.InvokeVoidAsync("get", string.IsNullOrWhiteSpace(trackingId) ? TrackingId : trackingId, fieldName); //TODO: callback results } - public async ValueTask Set(ExpandoObject parameters) + public async ValueTask SetAsync(ExpandoObject parameters) { var module = await moduleTask.Value; await module.InvokeVoidAsync("set", parameters); } - public async ValueTask Event(string eventName, Dictionary eventParams) + public async ValueTask EventAsync(GoogleAnalyticsEventTypes eventType, ExpandoObject eventParams) + { + var module = await moduleTask.Value; + await module.InvokeVoidAsync("event", eventType.ToString(), eventParams); + } + + public async ValueTask CustomEventAsync(string customEventName, GoogleAnalyticsCustomEventArgs eventData) { var module = await moduleTask.Value; - await module.InvokeVoidAsync("event", eventName, eventParams?.ToList()); + await module.InvokeVoidAsync("customEvent", customEventName, eventData); } public async ValueTask DisposeAsync() diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml index bbd86d39..16b8d887 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml @@ -12,11 +12,51 @@ Registers required Google Analytic services into IServiceCollection + DO NOT CALL with value in case of Blazor Server! IServiceCollection instance - Google Tracking Id when provided will be called. DO NOT CALL with value in case of Blazor Server! + Google Tracking Id when provided will be called. DO NOT CALL with value in case of Blazor Server! IServiceCollection + + + Registers required Google Analytic services into IServiceCollection for Blazor Server + + IServiceCollection instance + IServiceCollection + + + + Data for custom event. + For more details see: https://developers.google.com/analytics/devguides/collection/gtagjs/events + + + + + The value that will appear as the event action in Google Analytics Event reports. + + + + + The category of the event. + + + + + The label of the event. + + + + + A non-negative integer that will appear as the event value. + + + + + Event types for Google Analytics + for event and parameter details see: https://developers.google.com/gtagjs/reference/event + + Injectable service to handle Google analytics gtag.js. @@ -24,17 +64,17 @@ - Google analytics uniquely Id which was used in method. + Google analytics uniquely Id which was used in method. - + Initialize Google analytics by registering gtag.js to the HTML document. Should be called once. Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property Async ValueTask - + Allows you to add additional configuration information to targets. This is typically product-specific configuration for a product such as Google Ads or Google Analytics. @@ -43,7 +83,7 @@ Is one or more optional parameter-value pairs Async ValueTask - + Allows you to get various values from gtag.js including values set with the set command. @@ -51,21 +91,29 @@ Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property Async ValueTask - + Allows you to set values that persist across all the subsequent gtag() calls on the page. Is a key name and the value that is to persist across gtag() calls. Async ValueTask - + Use the event command to send event data. - A recommended event or a custom event name. + A recommended event Is one or more parameter-value pairs. Async ValueTask + + + Use the event command to send custom event data. + + Custom event name. + Custom event data + Async ValueTask + Implementation of diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js index a5ddd969..8c325b8f 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js +++ b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js @@ -62,4 +62,14 @@ export function event(eventName, data) { if (window.gtag && eventName && data) { gtag('event', eventName, data); } +} +export function customEvent(eventName, data) { + if (window.gtag && eventName && data) { + gtag('event', eventName, { + 'event_action': data.Action, + 'event_category': data.Category, + 'event_label': data.Label, + 'value': data.Value + }); + } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js index 0a1a89f5..e4a28637 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js +++ b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js @@ -2,4 +2,4 @@ export function init(n){if(n){let i="https://www.googletagmanager.com/gtag/js?id function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); - gtag('config', '{0}');`.replace("{0}",n);document.head.appendChild(u)}}}export function config(n,t){n&&window.gtag&>ag("config",n,t)}export function get(n,t){n&&window.gtag&>ag("get",n,t,n=>{console.log(n)})}export function set(n){window.gtag&&n&>ag("set",n)}export function event(n,t){window.gtag&&n&&t&>ag("event",n,t)} \ No newline at end of file + gtag('config', '{0}');`.replace("{0}",n);document.head.appendChild(u)}}}export function config(n,t){n&&window.gtag&>ag("config",n,t)}export function get(n,t){n&&window.gtag&>ag("get",n,t,n=>{console.log(n)})}export function set(n){window.gtag&&n&>ag("set",n)}export function event(n,t){window.gtag&&n&&t&>ag("event",n,t)}export function customEvent(n,t){window.gtag&&n&&t&>ag("event",n,{event_action:t.Action,event_category:t.Category,event_label:t.Label,value:t.Value})} \ No newline at end of file From b8fa9f993f84a5671a812742ef07e66152b6d446 Mon Sep 17 00:00:00 2001 From: Major Date: Mon, 21 Jun 2021 22:13:21 +0200 Subject: [PATCH 34/69] Finished Google Analytics features. --- .../Components/GoogleAnalytics.razor | 14 ++-- .../_Imports.razor | 1 + .../Google/GoogleAnalyticsGetEventInfo.cs | 26 +++++++ .../Google/GoogleAnalyticsService.cs | 77 ++++--------------- .../Google/IGoogleAnalyticsService.cs | 65 ++++++++++++++++ .../Majorsoft.Blazor.Extensions.Analytics.xml | 20 +++-- .../wwwroot/googleAnalytics.js | 6 +- .../wwwroot/googleAnalytics.min.js | 2 +- 8 files changed, 134 insertions(+), 77 deletions(-) create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsGetEventInfo.cs create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/Google/IGoogleAnalyticsService.cs diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor index cd4eec9f..ea523a14 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor @@ -25,12 +25,18 @@ IGoogleAnalyticsService is an injectable service for enabling Google Analytics page tracking in Blazor Apps.

    + +

    + To see how Majorsoft.Blazor.Extensions.Analytics works please check the demo code. And Google + analytics features. +

    @using System.Dynamic @inject IGoogleAnalyticsService _googleAnalytincsService +@inject ILogger _logger @implements IAsyncDisposable @code { @@ -38,19 +44,17 @@ { if(firstRender) { - /*var sessionId = */ - await _googleAnalytincsService.GetAsync("session_id"); + await _googleAnalytincsService.GetAsync("session_id", async (res) => { _logger.LogInformation($"Google analytics Get result: {res}"); }); //custom set/get dynamic exp = new ExpandoObject(); exp.test = 27; await _googleAnalytincsService.SetAsync(exp); - /*var test =*/ - await _googleAnalytincsService.GetAsync("test"); + await _googleAnalytincsService.GetAsync("test", async (res) => { _logger.LogInformation($"Google analytics Get result: {res}"); }); dynamic exp2 = new ExpandoObject(); - exp2.search_term = "heeeeeeeeeeee"; + exp2.search_term = "Searching custom event..."; await _googleAnalytincsService.EventAsync(GoogleAnalyticsEventTypes.search, exp2); await _googleAnalytincsService.CustomEventAsync("testEvent", new GoogleAnalyticsCustomEventArgs() diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor index 5702ed14..3340f34a 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor @@ -2,6 +2,7 @@ @using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components @using Microsoft.AspNetCore.Components.Routing +@using Microsoft.Extensions.Logging @using System.ComponentModel.DataAnnotations @using System.Collections.ObjectModel diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsGetEventInfo.cs b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsGetEventInfo.cs new file mode 100644 index 00000000..094f9b80 --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsGetEventInfo.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; + +using Microsoft.JSInterop; + +namespace Majorsoft.Blazor.Extensions.Analytics.Google +{ + /// + /// Google analytics Get response event info to handle JS callback + /// + internal sealed class GoogleAnalyticsGetEventInfo + { + private readonly Func _googleAnalyticsGetEventCallback; + + public GoogleAnalyticsGetEventInfo(Func googleAnalyticsGetEventCallback) + { + _googleAnalyticsGetEventCallback = googleAnalyticsGetEventCallback; + } + + [JSInvokable("GoogleAnalyticsResult")] + public async Task TransitionEvent(object args) + { + await _googleAnalyticsGetEventCallback(args); + } + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs index 73a35280..e52ddcfb 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs @@ -8,69 +8,12 @@ namespace Majorsoft.Blazor.Extensions.Analytics.Google { - /// - /// Injectable service to handle Google analytics gtag.js. - /// - public interface IGoogleAnalyticsService : IAsyncDisposable - { - /// - /// Google analytics uniquely Id which was used in method. - /// - string TrackingId { get; } - - /// - /// Initialize Google analytics by registering gtag.js to the HTML document. Should be called once. - /// - /// Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property - /// Async ValueTask - ValueTask InitializeAsync(string trackingId); - - /// - /// Allows you to add additional configuration information to targets. This is typically product-specific configuration for a product - /// such as Google Ads or Google Analytics. - /// - /// Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property - /// Is one or more optional parameter-value pairs - /// Async ValueTask - ValueTask ConfigAsync(string trackingId = "", Dictionary? configInfo = null); - - /// - /// Allows you to get various values from gtag.js including values set with the set command. - /// - /// The name of the field to get. - /// Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property - /// Async ValueTask - ValueTask GetAsync(string fieldName, string trackingId = ""); - - /// - /// Allows you to set values that persist across all the subsequent gtag() calls on the page. - /// - /// Is a key name and the value that is to persist across gtag() calls. - /// Async ValueTask - ValueTask SetAsync(ExpandoObject parameters); - - /// - /// Use the event command to send event data. - /// - /// A recommended event - /// Is one or more parameter-value pairs. - /// Async ValueTask - ValueTask EventAsync(GoogleAnalyticsEventTypes eventType, ExpandoObject eventParams); - - /// - /// Use the event command to send custom event data. - /// - /// Custom event name. - /// Custom event data - /// Async ValueTask - ValueTask CustomEventAsync(string customEventName, GoogleAnalyticsCustomEventArgs eventData); - } - /// /// Implementation of /// public class GoogleAnalyticsService : IGoogleAnalyticsService { + private List> _dotNetObjectReferences; private readonly Lazy> moduleTask; private static string _trackingId; //Service cannot registered as Singleton. @@ -86,6 +29,7 @@ public GoogleAnalyticsService(IJSRuntime jsRuntime) #endif moduleTask = new(() => jsRuntime.InvokeAsync("import", js).AsTask()); + _dotNetObjectReferences = new List>(); } public async ValueTask InitializeAsync(string trackingId) @@ -101,15 +45,21 @@ public async ValueTask InitializeAsync(string trackingId) await module.InvokeVoidAsync("init", trackingId); } - public async ValueTask ConfigAsync(string trackingId = "", Dictionary? configInfo = null) + public async ValueTask ConfigAsync(string trackingId = "", ExpandoObject? configInfo = null) { var module = await moduleTask.Value; await module.InvokeVoidAsync("config", string.IsNullOrWhiteSpace(trackingId) ? TrackingId : trackingId, configInfo?.ToList()); } - public async ValueTask GetAsync(string fieldName, string trackingId = "") + public async ValueTask GetAsync(string fieldName, Func callback, string trackingId = "") { var module = await moduleTask.Value; - await module.InvokeVoidAsync("get", string.IsNullOrWhiteSpace(trackingId) ? TrackingId : trackingId, fieldName); //TODO: callback results + + var info = new GoogleAnalyticsGetEventInfo(callback); + var dotnetRef = DotNetObjectReference.Create(info); + + _dotNetObjectReferences.Add(dotnetRef); + + await module.InvokeVoidAsync("get", string.IsNullOrWhiteSpace(trackingId) ? TrackingId : trackingId, fieldName, dotnetRef); } public async ValueTask SetAsync(ExpandoObject parameters) { @@ -135,6 +85,11 @@ public async ValueTask DisposeAsync() var module = await moduleTask.Value; await module.DisposeAsync(); } + + foreach (var item in _dotNetObjectReferences) + { + item.Dispose(); + } } } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/IGoogleAnalyticsService.cs b/src/Majorsoft.Blazor.Extensions.Analytics/Google/IGoogleAnalyticsService.cs new file mode 100644 index 00000000..9cfaa5dc --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/IGoogleAnalyticsService.cs @@ -0,0 +1,65 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; + +namespace Majorsoft.Blazor.Extensions.Analytics.Google +{ + /// + /// Injectable service to handle Google analytics gtag.js. + /// + public interface IGoogleAnalyticsService : IAsyncDisposable + { + /// + /// Google analytics uniquely Id which was used in method. + /// + string TrackingId { get; } + + /// + /// Initialize Google analytics by registering gtag.js to the HTML document. Should be called once. + /// + /// Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property + /// Async ValueTask + ValueTask InitializeAsync(string trackingId); + + /// + /// Allows you to add additional configuration information to targets. This is typically product-specific configuration for a product + /// such as Google Ads or Google Analytics. + /// + /// Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property + /// Is one or more optional parameter-value pairs + /// Async ValueTask + ValueTask ConfigAsync(string trackingId = "", ExpandoObject? configInfo = null); + + /// + /// Allows you to get various values from gtag.js including values set with the set command. + /// + /// The name of the field to get. + /// A function that will be invoked with the requested field, or undefined if it is unset. + /// Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property + /// Async ValueTask + ValueTask GetAsync(string fieldName, Func callback, string trackingId = ""); + + /// + /// Allows you to set values that persist across all the subsequent gtag() calls on the page. + /// + /// Is a key name and the value that is to persist across gtag() calls. + /// Async ValueTask + ValueTask SetAsync(ExpandoObject parameters); + + /// + /// Use the event command to send event data. + /// + /// A recommended event + /// Is one or more parameter-value pairs. + /// Async ValueTask + ValueTask EventAsync(GoogleAnalyticsEventTypes eventType, ExpandoObject eventParams); + + /// + /// Use the event command to send custom event data. + /// + /// Custom event name. + /// Custom event data + /// Async ValueTask + ValueTask CustomEventAsync(string customEventName, GoogleAnalyticsCustomEventArgs eventData); + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml index 16b8d887..4b17a8e4 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml @@ -57,6 +57,16 @@ for event and parameter details see: https://developers.google.com/gtagjs/reference/event
    + + + Google analytics Get response event info to handle JS callback + + + + + Implementation of + + Injectable service to handle Google analytics gtag.js. @@ -74,7 +84,7 @@ Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property Async ValueTask - + Allows you to add additional configuration information to targets. This is typically product-specific configuration for a product such as Google Ads or Google Analytics. @@ -83,11 +93,12 @@ Is one or more optional parameter-value pairs Async ValueTask - + Allows you to get various values from gtag.js including values set with the set command. The name of the field to get. + A function that will be invoked with the requested field, or undefined if it is unset. Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property Async ValueTask @@ -114,10 +125,5 @@ Custom event data Async ValueTask - - - Implementation of - - diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js index 8c325b8f..7aa43aac 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js +++ b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js @@ -45,11 +45,11 @@ export function config(trackingId, data) { gtag('config', trackingId, data); } } -export function get(trackingId, fieldName) { +export function get(trackingId, fieldName, dotnetRef) { if (trackingId && window.gtag) { gtag('get', trackingId, fieldName, (result) => { - //TOOD: callback method here... - console.log(result); + dotnetRef.invokeMethodAsync("GoogleAnalyticsResult", result); + //console.log(result); }); } } diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js index e4a28637..f8f74cf8 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js +++ b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.min.js @@ -2,4 +2,4 @@ export function init(n){if(n){let i="https://www.googletagmanager.com/gtag/js?id function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); - gtag('config', '{0}');`.replace("{0}",n);document.head.appendChild(u)}}}export function config(n,t){n&&window.gtag&>ag("config",n,t)}export function get(n,t){n&&window.gtag&>ag("get",n,t,n=>{console.log(n)})}export function set(n){window.gtag&&n&>ag("set",n)}export function event(n,t){window.gtag&&n&&t&>ag("event",n,t)}export function customEvent(n,t){window.gtag&&n&&t&>ag("event",n,{event_action:t.Action,event_category:t.Category,event_label:t.Label,value:t.Value})} \ No newline at end of file + gtag('config', '{0}');`.replace("{0}",n);document.head.appendChild(u)}}}export function config(n,t){n&&window.gtag&>ag("config",n,t)}export function get(n,t,i){n&&window.gtag&>ag("get",n,t,n=>{i.invokeMethodAsync("GoogleAnalyticsResult",n)})}export function set(n){window.gtag&&n&>ag("set",n)}export function event(n,t){window.gtag&&n&&t&>ag("event",n,t)}export function customEvent(n,t){window.gtag&&n&&t&>ag("event",n,{event_action:t.Action,event_category:t.Category,event_label:t.Label,value:t.Value})} \ No newline at end of file From 5b77a790ff1d917e97b6e6d7c72d95cef9985fbe Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 22 Jun 2021 08:21:01 +0200 Subject: [PATCH 35/69] Simplify and unify Google analytics initialization. --- .../Program.cs | 2 +- .../Shared/MainLayout.razor | 4 +++ .../Shared/MainLayout.razor | 13 +++------ .../Startup.cs | 2 +- .../AnalyticsExtension.cs | 19 +------------ .../Google/GoogleAnalyticsInitializer.razor | 27 +++++++++++++++++++ .../Majorsoft.Blazor.Extensions.Analytics.xml | 16 +++++------ .../wwwroot/googleAnalytics.js | 1 - 8 files changed, 43 insertions(+), 41 deletions(-) create mode 100644 src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsInitializer.razor diff --git a/src/Majorsoft.Blazor.Components.TestApp/Program.cs b/src/Majorsoft.Blazor.Components.TestApp/Program.cs index bbdbcf61..6ac84a0b 100644 --- a/src/Majorsoft.Blazor.Components.TestApp/Program.cs +++ b/src/Majorsoft.Blazor.Components.TestApp/Program.cs @@ -29,7 +29,7 @@ public static async Task Main(string[] args) builder.Services.AddMapExtensions(); builder.Services.AddBrowserStorage(); - builder.Services.AddGoogleAnalytics("G-1QD2VGTEWX"); + builder.Services.AddGoogleAnalytics(); builder.Logging.AddBrowserConsole() .SetMinimumLevel(LogLevel.Debug).AddFilter("Microsoft", LogLevel.Information); diff --git a/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor b/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor index 51ef1511..ba1aaea7 100644 --- a/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor +++ b/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor @@ -16,6 +16,10 @@
    +@* Google Analytics initialize*@ +@using Majorsoft.Blazor.Extensions.Analytics.Google + + @using Majorsoft.Blazor.Components.PermaLink @inject IPermaLinkWatcherService _permalinkWatcher @implements IDisposable diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor index e9a55e4f..77e894ea 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor @@ -27,8 +27,9 @@ @using Majorsoft.Blazor.Server.Logging.Console @inject IBrowserConsoleLoggerService _browserConsoleLogger -@* Google Analytics *@ -@inject IGoogleAnalyticsService _googleAnalytincsService +@* Google Analytics initialize*@ +@using Majorsoft.Blazor.Extensions.Analytics.Google + @implements IDisposable @implements IAsyncDisposable @@ -46,9 +47,6 @@ //setup console log await _browserConsoleLogger.StartLoggerAsync(); - - //setup Google analitics - await _googleAnalytincsService.InitializeAsync("G-1QD2VGTEWX"); } } @@ -63,10 +61,5 @@ { await _browserConsoleLogger.DisposeAsync(); } - - if(_googleAnalytincsService is not null) - { - await _googleAnalytincsService.DisposeAsync(); - } } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs b/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs index aff9b45b..4456c637 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Startup.cs @@ -37,7 +37,7 @@ public void ConfigureServices(IServiceCollection services) services.AddMapExtensions(); services.AddBrowserStorage(); - services.AddGoogleAnalyticsForBlazorServer(); + services.AddGoogleAnalytics(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs b/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs index 89e3373d..51be9b4e 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs +++ b/src/Majorsoft.Blazor.Extensions.Analytics/AnalyticsExtension.cs @@ -11,30 +11,13 @@ public static class AnalyticsExtension { /// /// Registers required Google Analytic services into IServiceCollection - /// DO NOT CALL with value in case of Blazor Server! /// /// IServiceCollection instance - /// Google Tracking Id when provided will be called. DO NOT CALL with value in case of Blazor Server! /// IServiceCollection - public static IServiceCollection AddGoogleAnalytics(this IServiceCollection services, string trackingId = "") + public static IServiceCollection AddGoogleAnalytics(this IServiceCollection services) { services.AddTransient(); - if (!string.IsNullOrWhiteSpace(trackingId)) - { - services.BuildServiceProvider().GetRequiredService()?.InitializeAsync(trackingId); - } - return services; } - - /// - /// Registers required Google Analytic services into IServiceCollection for Blazor Server - /// - /// IServiceCollection instance - /// IServiceCollection - public static IServiceCollection AddGoogleAnalyticsForBlazorServer(this IServiceCollection services) - { - return services.AddGoogleAnalytics(); - } } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsInitializer.razor b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsInitializer.razor new file mode 100644 index 00000000..6907157d --- /dev/null +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsInitializer.razor @@ -0,0 +1,27 @@ +@inject IGoogleAnalyticsService _googleAnalytincsService + +@implements IAsyncDisposable + +@code { + /// + /// Google Analytics Tracking id + /// + [Parameter] public string TrackingId { get; set; } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + //Initialize Google Analytics once + await _googleAnalytincsService.InitializeAsync(TrackingId); + } + } + + public async ValueTask DisposeAsync() + { + if (_googleAnalytincsService is not null) + { + await _googleAnalytincsService.DisposeAsync(); + } + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml index 4b17a8e4..a5e176d2 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml @@ -9,18 +9,9 @@ Extension methods to register required Analytic services into IServiceCollection - + Registers required Google Analytic services into IServiceCollection - DO NOT CALL with value in case of Blazor Server! - - IServiceCollection instance - Google Tracking Id when provided will be called. DO NOT CALL with value in case of Blazor Server! - IServiceCollection - - - - Registers required Google Analytic services into IServiceCollection for Blazor Server IServiceCollection instance IServiceCollection @@ -125,5 +116,10 @@ Custom event data Async ValueTask + + + Google Analytics Tracking id + + diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js index 7aa43aac..ce57247b 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js +++ b/src/Majorsoft.Blazor.Extensions.Analytics/wwwroot/googleAnalytics.js @@ -49,7 +49,6 @@ export function get(trackingId, fieldName, dotnetRef) { if (trackingId && window.gtag) { gtag('get', trackingId, fieldName, (result) => { dotnetRef.invokeMethodAsync("GoogleAnalyticsResult", result); - //console.log(result); }); } } From c7a0f249c3d09892dad4100bfa82e885bfcd065d Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 22 Jun 2021 08:40:39 +0200 Subject: [PATCH 36/69] Component rename. --- README.md | 1 + .../Majorsoft.Blazor.Components.TestApp.csproj | 3 +++ .../Pages/AnalyticsPage.razor | 2 +- .../{GoogleAnalytics.razor => SiteAnalytics.razor} | 6 +++--- .../Pages/AnalyticsPage.razor | 2 +- .../Majorsoft.Blazor.Components.TestServerApp.csproj | 3 +++ .../Pages/AnalyticsPage.razor | 2 +- 7 files changed, 13 insertions(+), 6 deletions(-) rename src/Majorsoft.Blazor.Components.TestApps.Common/Components/{GoogleAnalytics.razor => SiteAnalytics.razor} (88%) diff --git a/README.md b/README.md index 649343f4..27425504 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Check out our planned components and extensions on the project [Wiki page](https - **Majorsoft.Blazor.Server.Logging.Console**: Enables [Browser console logging](https://github.com/majorimi/blazor-components/blob/master/.github/docs/ServerHostedLogging.md) for Blazor applications using **Server Hosted model**. - **Majorsoft.Blazor.WebAssembly.Logging.Console**: Enables [Browser console logging](https://github.com/majorimi/blazor-components/blob/master/.github/docs/WebAssemblyHostedLogging.md) for Blazor applications using **WebAssembly Hosting model**. - **Majorsoft.Blazor.Extensions.BrowserStorage**: Enables [Browser Local and Session storages and Cookies store](https://github.com/majorimi/blazor-components/blob/master/.github/docs/BrowserStorage.md) access for Blazor applications. +- **Majorsoft.Blazor.Extensions.Analytics**: Enables [Analytics services usage](https://github.com/majorimi/blazor-components/blob/master/.github/docs/Analytics.md) in WebAssemply Apps e.g. Google Analytics, etc. ### **Majorsoft Blazor Components** diff --git a/src/Majorsoft.Blazor.Components.TestApp/Majorsoft.Blazor.Components.TestApp.csproj b/src/Majorsoft.Blazor.Components.TestApp/Majorsoft.Blazor.Components.TestApp.csproj index de3f4c56..99091612 100644 --- a/src/Majorsoft.Blazor.Components.TestApp/Majorsoft.Blazor.Components.TestApp.csproj +++ b/src/Majorsoft.Blazor.Components.TestApp/Majorsoft.Blazor.Components.TestApp.csproj @@ -43,6 +43,9 @@ + + true + true diff --git a/src/Majorsoft.Blazor.Components.TestApp/Pages/AnalyticsPage.razor b/src/Majorsoft.Blazor.Components.TestApp/Pages/AnalyticsPage.razor index 9850231d..a6cd0ab8 100644 --- a/src/Majorsoft.Blazor.Components.TestApp/Pages/AnalyticsPage.razor +++ b/src/Majorsoft.Blazor.Components.TestApp/Pages/AnalyticsPage.razor @@ -1,3 +1,3 @@ @page "/analytics" - \ No newline at end of file + \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/SiteAnalytics.razor similarity index 88% rename from src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor rename to src/Majorsoft.Blazor.Components.TestApps.Common/Components/SiteAnalytics.razor index ea523a14..905dbc7b 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/GoogleAnalytics.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/SiteAnalytics.razor @@ -3,7 +3,7 @@

    Website Analytics extensions

    - Blazor extension that enables WebAssemply site analytics e.g. Google, etc. For usage see source code and docs on + Blazor extension that enables analytics services usage in WebAssemply Apps e.g. Google Analytics, etc. For usage see source code and docs on Github.
    Majorsoft.Blazor.Extensions.Analytics package is available on Nuget

    @@ -28,7 +28,7 @@

    To see how Majorsoft.Blazor.Extensions.Analytics works please check the demo code. And Google - analytics features. + analytics features since this extension mostly a JS wrapper for gtag JS.

    @@ -36,7 +36,7 @@ @using System.Dynamic @inject IGoogleAnalyticsService _googleAnalytincsService -@inject ILogger _logger +@inject ILogger _logger @implements IAsyncDisposable @code { diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Pages/AnalyticsPage.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Pages/AnalyticsPage.razor index 9850231d..a6cd0ab8 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Pages/AnalyticsPage.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Pages/AnalyticsPage.razor @@ -1,3 +1,3 @@ @page "/analytics" - \ No newline at end of file + \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Majorsoft.Blazor.Components.TestServerApp.csproj b/src/Majorsoft.Blazor.Components.TestServerApp/Majorsoft.Blazor.Components.TestServerApp.csproj index d03986f3..e344d3e8 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Majorsoft.Blazor.Components.TestServerApp.csproj +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Majorsoft.Blazor.Components.TestServerApp.csproj @@ -11,6 +11,9 @@
    + + true + true diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Pages/AnalyticsPage.razor b/src/Majorsoft.Blazor.Components.TestServerApp/Pages/AnalyticsPage.razor index 9850231d..a6cd0ab8 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Pages/AnalyticsPage.razor +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Pages/AnalyticsPage.razor @@ -1,3 +1,3 @@ @page "/analytics" - \ No newline at end of file + \ No newline at end of file From 4244e2722cdc282f55e7b33c9d0309ffa1c28dfb Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 22 Jun 2021 19:07:55 +0200 Subject: [PATCH 37/69] Docs page for Google Analytics. --- .github/docs/Analytics.md | 168 ++++++++++++++++++ README.md | 2 +- .../Components/SiteAnalytics.razor | 18 +- .../Google/GoogleAnalyticsInitializer.razor | 4 +- .../Google/GoogleAnalyticsService.cs | 2 +- .../Google/IGoogleAnalyticsService.cs | 1 + .../Majorsoft.Blazor.Extensions.Analytics.xml | 3 +- 7 files changed, 187 insertions(+), 11 deletions(-) create mode 100644 .github/docs/Analytics.md diff --git a/.github/docs/Analytics.md b/.github/docs/Analytics.md new file mode 100644 index 00000000..42dcf152 --- /dev/null +++ b/.github/docs/Analytics.md @@ -0,0 +1,168 @@ +Blazor Components Analytics extension +============ +[![Build Status](https://dev.azure.com/major-soft/GitHub/_apis/build/status/blazor-components/blazor-components-build-check)](https://dev.azure.com/major-soft/GitHub/_build/latest?definitionId=6) +[![Package Version](https://img.shields.io/nuget/v/Majorsoft.Blazor.Components.Analytics?label=Latest%20Version)](https://www.nuget.org/packages/Majorsoft.Blazor.Components.Analytics/) +[![NuGet Downloads](https://img.shields.io/nuget/dt/Majorsoft.Blazor.Components.Analytics?label=Downloads)](https://www.nuget.org/packages/Majorsoft.Blazor.Components.Analytics/) +[![License](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/majorimi/blazor-components/blob/master/LICENSE) + +# About + +Blazor extension that enables analytics services usage for Blazor applications e.g. Google Analytics, etc. +**All components work with WebAssembly and Server hosted models** (Blazor server side configuration is different). +For code examples [see usage](https://github.com/majorimi/blazor-components/blob/master/src/Majorsoft.Blazor.Components.TestApps.Common/Components/SiteAnalytics.razor). + +You can try it out by using the [demo app](https://blazorextensions.z6.web.core.windows.net/analytics). + +# Services + +- **`Google Analytics`**: is a web analytics service offered by Google that tracks and reports website traffic, etc. inside the Google Marketing Platform. +`IGoogleAnalyticsService` is an injectable service for enabling [Google Analytics](https://support.google.com/analytics/answer/1008015?hl=en#) page tracking in Blazor Apps. +To make the initialization simple use `GoogleAnalyticsInitializer` component in your `MainLayout.razor` page and provide Google Analytics `TrackingId`. + +## `Google Analytics` extension +This is a JS wrapper for web analytics service offered by Google that tracks and reports website traffic, etc. inside the Google Marketing Platform. + +### `PermaLinkElement` component +A convenient wrapper component for `IGoogleAnalyticsService` to make Google Analytics initialize simple. + +#### Properties +- **`TrackingId`**: **`string TrackingId { get; set; }` - Required**
    +Google Analytics TrackingId provided on Google Analytics manage page. + +### `IGoogleAnalyticsService` service +Injectable service to handle Google analytics `gtag.js`. + +#### Functions +- **`InitializeAsync()`**: **`ValueTask InitializeAsync(string trackingId)`**
    +Initialize Google analytics by registering gtag.js to the HTML document. **Should be called once. + Do not call this method if you used `GoogleAnalyticsInitializer`.** +- **`ConfigAsync()`**: **`ValueTask ConfigAsync(string trackingId = "", ExpandoObject? configInfo = null)`**
    +Allows you to add additional configuration information to targets. This is typically product-specific configuration for a product +such as Google Ads or Google Analytics. +- **`GetAsync()`**: **``**
    +Allows you to get various values from gtag.js including values set with the set command. +- **`SetAsync()`**: **``**
    + Allows you to set values that persist across all the subsequent gtag() calls on the page. +- **`EventAsync()`**: **``**
    +Use the event command to send event data. +- **`CustomEventAsync()`**: **``**
    +Use the event command to send custom event data. + +# Configuration + +## Installation + +**Majorsoft.Blazor.Components.Analytics** is available on [NuGet](https://www.nuget.org/packages/Majorsoft.Blazor.Components.Analytics/). + +```sh +dotnet add package Majorsoft.Blazor.Components.Analytics +``` +Use the `--version` option to specify a [preview version](https://www.nuget.org/packages/Majorsoft.Blazor.Components.Analytics/absoluteLatest) to install. + +## Usage + +Add using statement to your Blazor .razor file. Or globally reference it into `_Imports.razor` file. +``` +@using Majorsoft.Blazor.Components.Analytics +@*Google Analytics*@ +@using Majorsoft.Blazor.Components.Analytics.Google +``` + +#### WebAssembly projects + +**In case of WebAssembly project register services in your `Program.cs` file:** +``` +using Majorsoft.Blazor.Components.Analytics; +... +public static async Task Main(string[] args) +{ + var builder = WebAssemblyHostBuilder.CreateDefault(args); + + //Register service + builder.Services.AddGoogleAnalytics(); +} +``` + +**Use `MainLayout.razor` file for WebAssembly project to initialize Google gtag.js only once:** + +``` +@*Google Analytics initialize*@ + + +@code { +} +``` + +#### Server hosted projects +**In case of Server hosted project register dependency services in your `Startup.cs` file:** + +``` +@using Majorsoft.Blazor.Components.Analytics +... + +public void ConfigureServices(IServiceCollection services) +{ + //Register service + services.AddGoogleAnalytics(); +} +``` + +**Use `MainLayout.razor` file for WebAssembly project to initialize Google gtag.js only once:** +``` +@*Google Analytics initialize*@ + + +@code { +} +``` + +#### Using `IGoogleAnalyticsService` service + +Following code example shows how to Set and Get custom values. Also shows sending events and custom events. +For full features supported by Google Analytics please see [docs page](https://developers.google.com/gtagjs/reference/api). + +``` +@using System.Dynamic +@inject IGoogleAnalyticsService _googleAnalytincsService +@implements IAsyncDisposable + +@code{ + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if(firstRender) + { + await _googleAnalytincsService.GetAsync("session_id", async (res) => { _logger.LogInformation($"Google analytics Get result: {res}"); }); + + //Custom set + dynamic exp = new ExpandoObject(); + exp.test = 27; + + await _googleAnalytincsService.SetAsync(exp); + + //Get cutoms set value + await _googleAnalytincsService.GetAsync("test", async (res) => { _logger.LogInformation($"Google analytics Get result: {res}"); }); + + //Built in Search event usage + dynamic exp2 = new ExpandoObject(); + exp2.search_term = "Searching custom event..."; + await _googleAnalytincsService.EventAsync(GoogleAnalyticsEventTypes.search, exp2); + + //Custom event usage + await _googleAnalytincsService.CustomEventAsync("testEvent", new GoogleAnalyticsCustomEventArgs() + { + Action = "Test action", + Category = "Test category", + Label = "Test label", + Value = 1234 + }); + + + public async ValueTask DisposeAsync() + { + if(_googleAnalytincsService is not null) + { + await _googleAnalytincsService.DisposeAsync(); + } + } +} +``` \ No newline at end of file diff --git a/README.md b/README.md index 27425504..111d3c53 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Check out our planned components and extensions on the project [Wiki page](https - **Majorsoft.Blazor.Server.Logging.Console**: Enables [Browser console logging](https://github.com/majorimi/blazor-components/blob/master/.github/docs/ServerHostedLogging.md) for Blazor applications using **Server Hosted model**. - **Majorsoft.Blazor.WebAssembly.Logging.Console**: Enables [Browser console logging](https://github.com/majorimi/blazor-components/blob/master/.github/docs/WebAssemblyHostedLogging.md) for Blazor applications using **WebAssembly Hosting model**. - **Majorsoft.Blazor.Extensions.BrowserStorage**: Enables [Browser Local and Session storages and Cookies store](https://github.com/majorimi/blazor-components/blob/master/.github/docs/BrowserStorage.md) access for Blazor applications. -- **Majorsoft.Blazor.Extensions.Analytics**: Enables [Analytics services usage](https://github.com/majorimi/blazor-components/blob/master/.github/docs/Analytics.md) in WebAssemply Apps e.g. Google Analytics, etc. +- **Majorsoft.Blazor.Extensions.Analytics**: Enables [Analytics services usage](https://github.com/majorimi/blazor-components/blob/master/.github/docs/Analytics.md) for Blazor applications e.g. Google Analytics, etc. ### **Majorsoft Blazor Components** diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/SiteAnalytics.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/SiteAnalytics.razor index 905dbc7b..c1e24e34 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/SiteAnalytics.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/SiteAnalytics.razor @@ -3,7 +3,7 @@

    Website Analytics extensions

    - Blazor extension that enables analytics services usage in WebAssemply Apps e.g. Google Analytics, etc. For usage see source code and docs on + Blazor extension that enables analytics services usage for Blazor applications e.g. Google Analytics, etc. For usage see source code and docs on Github.
    Majorsoft.Blazor.Extensions.Analytics package is available on Nuget

    @@ -24,11 +24,13 @@
    IGoogleAnalyticsService is an injectable service for enabling Google Analytics page tracking in Blazor Apps. + To make the initialization simple use GoogleAnalyticsInitializer component in your MainLayout.razor page and provide Google Analytics TrackingId.

    To see how Majorsoft.Blazor.Extensions.Analytics works please check the demo code. And Google - analytics features since this extension mostly a JS wrapper for gtag JS. + analytics features since this extension mostly a JS wrapper for gtag.js. +

    @@ -46,23 +48,27 @@ { await _googleAnalytincsService.GetAsync("session_id", async (res) => { _logger.LogInformation($"Google analytics Get result: {res}"); }); - //custom set/get + //Custom set dynamic exp = new ExpandoObject(); exp.test = 27; await _googleAnalytincsService.SetAsync(exp); + + //Get cutoms set value await _googleAnalytincsService.GetAsync("test", async (res) => { _logger.LogInformation($"Google analytics Get result: {res}"); }); + //Built in Search event usage dynamic exp2 = new ExpandoObject(); exp2.search_term = "Searching custom event..."; await _googleAnalytincsService.EventAsync(GoogleAnalyticsEventTypes.search, exp2); + //Custom event usage await _googleAnalytincsService.CustomEventAsync("testEvent", new GoogleAnalyticsCustomEventArgs() { Action = "Test action", - Category = "cat", - Label = "label", - Value = 22 + Category = "Test category", + Label = "Test label", + Value = 1234 }); } } diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsInitializer.razor b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsInitializer.razor index 6907157d..6d003891 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsInitializer.razor +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsInitializer.razor @@ -4,9 +4,9 @@ @code { /// - /// Google Analytics Tracking id + /// Google Analytics TrackingId. /// - [Parameter] public string TrackingId { get; set; } + [Parameter] public string TrackingId { get; set; } = ""; protected override async Task OnAfterRenderAsync(bool firstRender) { diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs index e52ddcfb..9ff5815e 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/GoogleAnalyticsService.cs @@ -15,7 +15,7 @@ public class GoogleAnalyticsService : IGoogleAnalyticsService { private List> _dotNetObjectReferences; private readonly Lazy> moduleTask; - private static string _trackingId; //Service cannot registered as Singleton. + private static string _trackingId = ""; //Service cannot registered as Singleton. public string TrackingId => _trackingId; diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Google/IGoogleAnalyticsService.cs b/src/Majorsoft.Blazor.Extensions.Analytics/Google/IGoogleAnalyticsService.cs index 9cfaa5dc..79762ad7 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Google/IGoogleAnalyticsService.cs +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Google/IGoogleAnalyticsService.cs @@ -16,6 +16,7 @@ public interface IGoogleAnalyticsService : IAsyncDisposable /// /// Initialize Google analytics by registering gtag.js to the HTML document. Should be called once. + /// Do not call this method if you used . /// /// Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property /// Async ValueTask diff --git a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml index a5e176d2..c4d5ba03 100644 --- a/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml +++ b/src/Majorsoft.Blazor.Extensions.Analytics/Majorsoft.Blazor.Extensions.Analytics.xml @@ -71,6 +71,7 @@ Initialize Google analytics by registering gtag.js to the HTML document. Should be called once. + Do not call this method if you used . Is an identifier that uniquely identifies the target for hits, such as a Google Analytics property Async ValueTask @@ -118,7 +119,7 @@ - Google Analytics Tracking id + Google Analytics TrackingId. From 23419112be3a290667c8eb9a5017888f7ebf507a Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 22 Jun 2021 19:51:02 +0200 Subject: [PATCH 38/69] Created PermalinkWatcher initialize component. --- .../PermaLinkInitializer.razor | 36 +++++++++++++++++++ .../Shared/MainLayout.razor | 17 +++------ .../Shared/MainLayout.razor | 19 +--------- 3 files changed, 41 insertions(+), 31 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.PermaLink/PermaLinkInitializer.razor diff --git a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkInitializer.razor b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkInitializer.razor new file mode 100644 index 00000000..9f46c100 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkInitializer.razor @@ -0,0 +1,36 @@ +@using Microsoft.Extensions.Logging +@using Majorsoft.Blazor.Components.Common.JsInterop.Scroll + +@inject IScrollHandler _scrollHandler +@inject NavigationManager _navigationManager +@inject ILogger _logger + +@implements IDisposable +@implements IAsyncDisposable + +@code { + private IPermaLinkWatcherService _permalinkWatcher; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + //setup permalink + _permalinkWatcher = new PermaLinkWatcherService(_scrollHandler, _navigationManager, _logger); + _permalinkWatcher.WatchPermaLinks(); + } + } + + public async ValueTask DisposeAsync() + { + if (_scrollHandler is not null) + { + await _scrollHandler.DisposeAsync(); + } + } + + public void Dispose() + { + _permalinkWatcher?.Dispose(); + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor b/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor index ba1aaea7..1231fe45 100644 --- a/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor +++ b/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor @@ -16,22 +16,13 @@
    +@*Permalink initialize*@ +@using Majorsoft.Blazor.Components.PermaLink + + @* Google Analytics initialize*@ @using Majorsoft.Blazor.Extensions.Analytics.Google -@using Majorsoft.Blazor.Components.PermaLink -@inject IPermaLinkWatcherService _permalinkWatcher -@implements IDisposable - @code { - protected override void OnInitialized() - { - _permalinkWatcher.WatchPermaLinks(); - } - - public void Dispose() - { - _permalinkWatcher.Dispose(); - } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor index 77e894ea..29fdbe2d 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor @@ -16,12 +16,7 @@ @*Permalink initialize*@ @using Majorsoft.Blazor.Components.PermaLink -@using Microsoft.Extensions.Logging -@using Majorsoft.Blazor.Components.Common.JsInterop.Scroll - -@inject IScrollHandler _scrollHandler -@inject NavigationManager _navigationManager -@inject ILogger _logger + @*Server hosted Blazor console log*@ @using Majorsoft.Blazor.Server.Logging.Console @@ -31,30 +26,18 @@ @using Majorsoft.Blazor.Extensions.Analytics.Google -@implements IDisposable @implements IAsyncDisposable @code { - private IPermaLinkWatcherService _permalinkWatcher; - protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - //setup permalink - _permalinkWatcher = new PermaLinkWatcherService(_scrollHandler, _navigationManager, _logger); - _permalinkWatcher.WatchPermaLinks(); - //setup console log await _browserConsoleLogger.StartLoggerAsync(); } } - public void Dispose() - { - _permalinkWatcher?.Dispose(); - } - public async ValueTask DisposeAsync() { if (_browserConsoleLogger is not null) From 83760339201725c75c15523e338210f76fa86c1a Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 22 Jun 2021 19:51:25 +0200 Subject: [PATCH 39/69] Make demo components works with Sever side Blazor. --- .../Components/BrowserStorage.razor | 5 ++++- .../Components/JsDemo/LangJs.razor | 6 +++++- .../Components/JsDemo/MouseJs.razor | 5 ++++- .../Components/JsDemo/ResizeJs.razor | 5 ++++- .../Components/JsDemo/ScrollJs.razor | 5 ++++- .../Components/MapsGoogle.razor | 5 ++++- 6 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/BrowserStorage.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/BrowserStorage.razor index 867aeb57..98e6e0fa 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/BrowserStorage.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/BrowserStorage.razor @@ -193,8 +193,11 @@ public int Age { get; set; } } - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { + if (!firstRender) + return; + //LocalStorage await InsertLocalStorageItems(); _localStorageCount = await _localStorageService.CountAsync(); diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/LangJs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/LangJs.razor index d033841e..c4b64816 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/LangJs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/LangJs.razor @@ -20,9 +20,13 @@ @inject ILanguageService _languageService; @code { - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { + if (!firstRender) + return; + _detectedBrowserLang = await _languageService.GetBrowserLanguageAsync(); + StateHasChanged(); } //Broswer lang diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/MouseJs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/MouseJs.razor index 886f1992..3684f2d9 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/MouseJs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/MouseJs.razor @@ -25,8 +25,11 @@ @inject IGlobalMouseEventHandler _globalMouseEventHandler; @code { - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { + if (!firstRender) + return; + await GlobalClickEventHandler(); } diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ResizeJs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ResizeJs.razor index fb3f6fda..3611eb52 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ResizeJs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ResizeJs.razor @@ -33,8 +33,11 @@ @code { private string _resizeEventId = null; - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { + if (!firstRender) + return; + await ResizeEventHandler(); _pageSize = await _resizeHandler.GetPageSizeAsync(); } diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor index 432c6cc2..2eaf9dc5 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/JsDemo/ScrollJs.razor @@ -117,8 +117,11 @@ @code { private string _scrollEventId = null; - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { + if (!firstRender) + return; + await ScrollEventHandler(); } diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor index 2740e542..1f97aa32 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MapsGoogle.razor @@ -355,8 +355,11 @@ @code { private string _googleMapsApiKey = "AIzaSyAv-6SailPQN1R5PytUAkbdaGI9IHZTU5s"; - protected override void OnInitialized() + protected override async Task OnAfterRenderAsync(bool firstRender) { + if (!firstRender) + return; + //Static map _staticMapMarkers.ElementAt(0).Locations.Add(new GeolocationData(1.111, 2.222)); _staticMapMarkers.ElementAt(1).Locations.Add(new GeolocationData(17.111, 33.222)); From 222e5d34cf8eb2020f2b5d480fc21bd38a14e460 Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 22 Jun 2021 21:03:55 +0200 Subject: [PATCH 40/69] Renamed Permalink initializers. --- ...azor => PermaLinkBlazorServerInitializer.razor} | 0 .../PermalinkBlazorWasmInitializer.razor | 14 ++++++++++++++ .../Shared/MainLayout.razor | 2 +- .../Shared/MainLayout.razor | 2 +- 4 files changed, 16 insertions(+), 2 deletions(-) rename src/Majorsoft.Blazor.Components.PermaLink/{PermaLinkInitializer.razor => PermaLinkBlazorServerInitializer.razor} (100%) create mode 100644 src/Majorsoft.Blazor.Components.PermaLink/PermalinkBlazorWasmInitializer.razor diff --git a/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkInitializer.razor b/src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor similarity index 100% rename from src/Majorsoft.Blazor.Components.PermaLink/PermaLinkInitializer.razor rename to src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor diff --git a/src/Majorsoft.Blazor.Components.PermaLink/PermalinkBlazorWasmInitializer.razor b/src/Majorsoft.Blazor.Components.PermaLink/PermalinkBlazorWasmInitializer.razor new file mode 100644 index 00000000..f59b1e6f --- /dev/null +++ b/src/Majorsoft.Blazor.Components.PermaLink/PermalinkBlazorWasmInitializer.razor @@ -0,0 +1,14 @@ +@inject IPermaLinkWatcherService _permalinkWatcher +@implements IDisposable + +@code { + protected override void OnInitialized() + { + _permalinkWatcher.WatchPermaLinks(); + } + + public void Dispose() + { + _permalinkWatcher.Dispose(); + } +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor b/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor index 1231fe45..fe39508f 100644 --- a/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor +++ b/src/Majorsoft.Blazor.Components.TestApp/Shared/MainLayout.razor @@ -18,7 +18,7 @@ @*Permalink initialize*@ @using Majorsoft.Blazor.Components.PermaLink - + @* Google Analytics initialize*@ @using Majorsoft.Blazor.Extensions.Analytics.Google diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor index 29fdbe2d..57515469 100644 --- a/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Shared/MainLayout.razor @@ -16,7 +16,7 @@ @*Permalink initialize*@ @using Majorsoft.Blazor.Components.PermaLink - + @*Server hosted Blazor console log*@ @using Majorsoft.Blazor.Server.Logging.Console From 45f9d447cdeb296e5806a82e8e17a6e9e51b78a6 Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 22 Jun 2021 21:09:31 +0200 Subject: [PATCH 41/69] Permalink initializer docs. --- .github/docs/PermaLink.md | 26 ++++++++++++++++++- .../Components/Permalink.razor | 3 ++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/docs/PermaLink.md b/.github/docs/PermaLink.md index a085b063..1dd3437d 100644 --- a/.github/docs/PermaLink.md +++ b/.github/docs/PermaLink.md @@ -19,8 +19,10 @@ You can try it out by using the [demo app](https://blazorextensions.z6.web.core. - **`PermaLinkElement`**: is a wrapper component it renders the given content with `` tag and will add anchor icon with on hover activated Link copy function. Hover over the top Header item to copy or navigate to URL as well. -- **`IPermaLinkWatcherService`**: . It is registered as Singleton and should be injected only once for the whole application. +- **`IPermaLinkWatcherService`**: is registered as Singleton and should be injected only once for the whole application. Best way to use `MainLayout.razor`. +- **`PermaLinkBlazorServerInitializer`**: (from v1.4.0) convenient wrapper component to initialize navigation watcher in your Blazor Server App `MainLayout.razor` page. +- **`PermalinkBlazorWasmInitializer`**: (from v1.4.0) convenient wrapper component to initialize navigation watcher in your Blazor WebAssembly App `MainLayout.razor` page. ## `IPermaLinkWatcherService` extension This is the main service which makes Permalink navigation possible. **Should be used as a Singleton** only in `MainLayout.razor` file. @@ -125,6 +127,13 @@ Also instance should be disposed. } ``` +**From v1.4.0 a simpler initializer is available!** +``` +@*Permalink initialize*@ +@using Majorsoft.Blazor.Components.PermaLink + +``` + #### Server hosted projects **In case of Server hosted project register dependency services in your `Startup.cs` file:** @@ -157,6 +166,7 @@ It has to be instantiated manually by using the following code. Also instance sh @inject ILogger _logger @implements IDisposable +@implements IAsyncDisposable @code{ private IPermaLinkWatcherService _permalinkWatcher; @@ -170,6 +180,13 @@ It has to be instantiated manually by using the following code. Also instance sh } } + public async ValueTask DisposeAsync() + { + if (_scrollHandler is not null) + { + await _scrollHandler.DisposeAsync(); + } + } public void Dispose() { _permalinkWatcher?.Dispose(); @@ -177,6 +194,13 @@ It has to be instantiated manually by using the following code. Also instance sh } ``` +**From v1.4.0 a simpler initializer is available!** +``` +@*Permalink initialize*@ +@using Majorsoft.Blazor.Components.PermaLink + +``` + #### Creating permalink (#) navigation points inside a Blazor page This is a standard HTML `a` tag. Apply the well known **``** anchor element in your document diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor index 9ed6acb3..32cb67ce 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Permalink.razor @@ -9,7 +9,8 @@ textarea {

    Blazor injectable IPermaLinkWatcherService service and PermaLinkElement wrapper component which allows navigation inside Blazor pages (#permalink). - For usege see soruce code and docs on Github. + To initialize navigation watcher use PermalinkBlazorWasmInitializer or PermaLinkBlazorServerInitializer in your Blazor app MainLayout.razor page. + For usage see source code and docs on Github.
    Majorsoft.Blazor.Components.PermaLink package is available on Nuget

    From bba246c17c5a94148d9d3a2d176c32b2f3d569cc Mon Sep 17 00:00:00 2001 From: Major Date: Thu, 1 Jul 2021 21:28:01 +0200 Subject: [PATCH 42/69] Remove semicolons for @Typeparams --- .../TypeaheadInput.razor | 6 +++--- .../TypeaheadInputText.razor | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor b/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor index 3d19b033..84976eb5 100644 --- a/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor +++ b/src/Majorsoft.Blazor.Components.Typeahead/TypeaheadInput.razor @@ -1,7 +1,7 @@ -@typeparam TItem; +@typeparam TItem -@inject ILogger> _logger; -@inject IClickBoundariesHandler _clickHandler; +@inject ILogger> _logger +@inject IClickBoundariesHandler _clickHandler > _logger; -@inject IClickBoundariesHandler _clickHandler; +@inject ILogger> _logger +@inject IClickBoundariesHandler _clickHandler Date: Thu, 1 Jul 2021 21:50:41 +0200 Subject: [PATCH 43/69] Updated loading on Index page. --- .../wwwroot/index.html | 17 ++++++++++++++++- .../wwwroot/blazor.components.png | Bin 0 -> 8130 bytes .../wwwroot/index.html | 17 ++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.TestApp/wwwroot/blazor.components.png diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/wwwroot/index.html b/demo/Majorsoft.Blazor.Components.DemoApp/wwwroot/index.html index 8da5f191..80b9e60c 100644 --- a/demo/Majorsoft.Blazor.Components.DemoApp/wwwroot/index.html +++ b/demo/Majorsoft.Blazor.Components.DemoApp/wwwroot/index.html @@ -14,7 +14,22 @@ -
    Loading...
    +
    + +
    + +

    Majorsoft Blazor Component
    Loading Demo...

    +
    +
    An unhandled error has occurred. diff --git a/src/Majorsoft.Blazor.Components.TestApp/wwwroot/blazor.components.png b/src/Majorsoft.Blazor.Components.TestApp/wwwroot/blazor.components.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb777cea0acfe5cac33f9df5ef685f56917047a GIT binary patch literal 8130 zcmX9@1ymK^*B$!N-AK2TbjPE+mF9scAl+S$6!FpB9fGKYbf-R~>(MCP-SF}I{%g(L zIp^$i_daLtT{E-ReALlW#>Jw-0ssJ4RYgJX`MUbw2|{@uxzDmD0RUN6N5eqz*;OOU z(;zN1<4)8kEQr`uj7|gpmVxaSV4fE!83op$K%Y7Lf6Pum z+Y@k331oKwh-dc+IP(EI+kr(w-e9070vH$pR-=G{ zbKnFY$TVY5eFM}#H`M_wb^?nyz<*U40zaPb!!2+^0AvyZSsTFKvs@xD>;ZH@fJRxM ziW?|-t}Pk(^%|%P1N!rUF=e3U8Jz3)ZMtg$4O~iCV!{R@Dx$$^G z&iNp>oc4GxKq<@&r{c=pI}V)`eMCG$wV4hd`Qq+)cOz|DkG|AuDRsHIE&a)EDl2=i zsovl>)_^G+K91VR$OM{=<$rtVNOF7BvWPSr8jyABc1_$r?Yex7N&4=vA0+<8YUZjb z^whL6U0y5H$uJ*Qw+b#;@ot<98177eJF_^`y6@8mA6tU zIl7P%Bj!p2buZdBPTC^lJ}<#7vl0sJ3zq+UxG)c?hre+AG4vfg{??j`k(&CgNbR5B zCC146{y$@@Z;Dm79v>n;rtA_bj{nt_64~ODIuCF_Y4$U;PqKfDK7=!iz<_oH!L4UyEjd!Wu))OagNAZ8OVP@I3{->0OoQx)0rgxFh@Rrf`x z{ooJbkconZ`6{4;vkJo=SuBquLO!2ZYK&LW;&N9+u~$V(Yh5EAdKkp+K%^+)Z?e0N zQPF)DvAfVPXbV;lA+Hn@szG|CU8i>ozT0Q*=bu7240)Dt=y63#{@_(xX4*QWwvq*X zyR`h*Jz0Pz4HOJT5C!Zj~ohU=`&&y@6ggLN`>B}Vz;QNJzFNv zNyT-rRDA5-7I}FtP1pgUUO7%NMRBW{Boc=N5h%`vTEzC6c&_Vj#|UB--0wB8VNdRe zZRq?rVpz<(-5U!wp(48{v-k}ZE(%`wjVdg^y`Y|+xsJVWZP&{4>o1oqr8gS@eIC) zb+j-4&TYA0*9LKN9*dnRwqfLc<9p<=S^t7K{#k(3Bw-YadI`N}@XDp^XzLhb(~*T^ z#iI4=;S=>{k%hz)-J#gvO8zkx_sBGAdMr;Lx&UjSD^R^r6k-WQ`xen6oWWAU5CBmo z;*z_bL#ATM?f}-&Op(8SfN9Qz1xLg1Dn8ApZC+-zw1x2AL%F98M zQvk~ypbS42>zEZh76565YAeXZf?Ac8bE3p05Hw|lK~xsx%XJz^Kml;K{@G~llEc8@ zlSfnq%#@IMHKPyDd*PuA$|@o>#^{bmirXvtE+#Lfcb#bZq7c8L9ok3UAKrsRw@+7k z6#Id`$Y_-tm(tH@GJ3h#8)JJA2zMo5KX_Te$a*ajU0VE8qx0((TbG4gFeSAwRN^=} zt)sJ&R@ANO)d;c0)Y8Fke^wdkLHU60>-&PF z|JQt_UwPSJtCMs+yU#at#Mx@A#Md1kCuyU<5-a`ax4XO;5sg-%{{Ju_Y~ZA&K3|Th z7s&Mi3bQA+m=$6+Tee4kHnhKm5ly|oVzxr}o!cZtJgPQK;(DXb35ylg^bwboH6x+H zhy!`(Mllz>`u*BWg03QL!fDwP8G_!iyH`Zz6K$AvdzQL;Z|={lu}E!fK#1$_ok>yZ zW$G23bWYhc6M_#uloc~(9$35u0=r<5H|ECgP*|D_5OTaKFX;K;%j*M zXD3WL@|%4`6LrK3>#*uM1s+h`K07%Lw#1b+O)uISiAp>HneM7E0jI9}OZzte0Cnb* zSUSmb1;6(D29d$Jy}InBi@b)j$}-^YmB0#IOh5u18(F&TWJXBcmb1gyy7P=T2lut0 zpAFt3TGX8_dcuu}&3e~7L+x70?$BLm!AsSz96Z4Q>Ju`f|A`64;UwIWAo$1(_hrMc z-rn}71sNX(Nm*9BC|z3u-j+m)8_qCt5aXwd_0LzKt1HVPUgL;HQj9#}VBQh6VwdX( ztD|E+gF+v@3MQ4zeWl@*xhhoY!;-h1oT>Eu#Bi6rMJzf4(PY?F>8Xt#|I^f+(#(mGi5j)yPVP;~w zT|;BFX9l;JM85&)Jt9nNLA~lno&I*ybbGup;lW{i<(&0QwpUjlk#sRu{}3^wZ6?Fp~kK>-l-v z0CYhEEKT(heQEXics)w2$L0n~h_9=pKzoEy=*<@)%;|F;^cAHY2k$}k{+1JF+7D#s z$&EE6GtE16Fnl~b=TXPe@1j*}XeVP@-YapHHG0eHOf58AoBKp$+0cO7Dk2h+xbOAC z@)XOD?oO($pTt!`7K$&Rt^64R^Ho#{U!wK+x2x3F^b?Rig*XRnULF)LC|iDwq@kk3 zlVoKWD#W8V=CE5+QigD{Uy-t=24B|MLwr5YkKnfvbPglXBOK2zd0f8$LkTRl^Qs@; zz8Lql)aNz_ogWoH(>PT0xYcL{(AZ@|_?@cwyGRGaG!+9DrijyQy{I)nL)FnHdVOBl zjTm*a7Vn_kxlH?>gbEY^1GF}e4@bSd$}jh5;r4FhmpBwxs`+CiXE)Xb!ncsgF;9fw zn1`(Jc)i%c5_R1z*5{ET$w1)==pJDNeDGw0OO=T^xm5NVv|u-6E$$4{yO+??-GY)MDI|YQgXtGm>%F{vx-jE~rvXCB4$|qpj3x z>Cb-yk$n7;wV4U+>Ow0zqki4Ky3NoU_813QQz>#~_%(^f+*u_w0hRzeG-Pt%^vo_1 z!C^_((`ly0X_y;3RH{B+*0L}=rnPBbc7bG!=$A9Mu-IzfQk0DKS8_U4`;_k7-DY{cRi1MC{x8{wV zn7p~HHKHWZCHGVvPIYE4!u&Cz$V=2F&@Gma#;VB>QH8A$TV*HsO0b@Y%9Q98qq~Dk zZ9uGk>lChx44MGx1$tImPs*3_>k4{NlKp0D9oZS4W?Zhlms2U-(~Pd*2VC~8GlXRS zS=jB6N)R``b0VXLl0w%8I||8fXRVS(-C<2Tty~DntDY9*21JSF1Mo42S8J2QB5+L* zyD!DOk-ZAirum98?)Ow7Pu0$tnyNX{42T9LSn5jmrKn|6V&YTo?L*|+e245D0Uidr zQfM{TqWA4beIAAvToq}Oa=5Hq^hy!3;5#2?ZVlUgdDFR%2g`c-riLZ z+ohT?j-9D27W)`0vtRfNmPR6ru|qKA@S;Kx&E5Hdx!AR}eQTQ+~&vN_~m zV8h&aVFGeTjndF+{%*~_7)8F-U#Jst$3v5?ol+jRi#v6FP(E?SXp)0M^wxhAZ%*1@ zr{Tov_m}-Z`V1$+QiKL*bfR`K{)Ojpem`L@|bwW-CZp)2*|XV zke#1^ASBu$C12D~cX}NQ<%xcA!=MpS1TS5KMz!gZpjtM@iOd6V12qHIjI;8)QJoe- z5W>81Q8G!2hkH-Q-@409N)ac-qjMuI5ly@+NqEQDxnuiNe_(f$LU!`!1!3tK=}yq` z-qTc3ul$_~srYXOv@y3gt9?j@j=M?CJ4omvUN80J>z$tZ9#vO;6ftB-Ix4M3@p;BX z0^O#+LK#@OU;Px^#a05nY*&_)L%h3MUd+p{*fyz`C4H~ggsn$h+V^5230W*Gi$)&f zW0xsyA6{Lfl+2mo?vVPMw0RPuLdT-JaG--xZ_LPSt;@;nx+X7XdUw@^#WO{PeWq>; z<2rXwpVZQX^;hjhpJTY&S|Z#OcOff{d{Hvc*j?WYghTc2+O3W4M@wB}d;0_A!&@iE zoeR0Lub-v(3gox)@;!W}ZpKsGjV7{zCimqUU(N;{tw|fRGBe>l;w_oGvYZQ=S?{t= zxDD)sKyo~c)|_(1mo@ljG}DV4XLCgjr_oU#Dud@`)GcaN7o>aB!14o$a#QSZ7?TUjRM%^M(hQVKN8J3n8LmbvUIgL<%>jY&C=ZcIP@nS17lE@ z55`X7*k@$itbQD0+vR3X_#i82&bB#hjK7ujB0IO zU(?sMQ>}>>PR2RBl}_dpxzW92tg)(52X}|ILu5bC1=iTUer5l$eS?{Z03T8a ztNTiLH?p6g>*4Pq9`@ETus-DHlUfY?{#+K7XHBGYEBd$ax+=u(9AWxRee~_GcSZfI z#!p(TZh^-eAIQ_b*mD(D#t#Xapxz8#Y|5Tx20iZ%=exZiIiJc^y;FFO2#;PGcXI6x z4{RDPw|*^Gfo*kjSZe#DwX-uMJbxP;IFf|)F6CWtB#d!HFKe2P9ofir_`wKNlbbJZ^#pe> z{Le~H!Cf&W6(p+;uF0~qo{Vny8%dFyY(myHG!!r}*QJJ$=fMqwsZxe@IuAJL-9f&g z&G`VcKv`OJI*eH03)ytTj_XK$E5>%-L^nR#VS=Ik`#yB(1<$^EYI`vM5fj1`Oi~T$ z>Z?boBw!Y96KTQ56F=ve2rF;*Z;XSqtk_%P)f+4~iI{#G1rv??iYBYT;gu&St_jF8 zjQT#oQiLk6OL3_Q1dhwpB{xc{t%u#md`|NZ{9X}1d9I6oFS9CsC|{yGz|(~E;r#Wv zYq`3If0}6J$j;YRQ|RA@rJse5)akuq!Tj1x2P2k&Tj84ayBu6{?Iw>p*9_%?-dF%G($a_$2x!uq(Z@EpDu^5EzbRS_Rne5{0* z(cL`a59*ZI`}(%yIOKCpT^6yT_`L&THbQ;6D@f=)1PGwP=K~Pdhd+}@Q>=Vz6z7>{ zL?2i!g~eUER4CP5adLvgB_Iy@2SV_MVZ3}D4a2lg()uVJJ&<}5pX@|V+V`RKMyHy= zqS=q!Oe5hOS5Jq3Zht0nef*oszT;mzeY#^}qAm^ZUp_fW!ly~$X89pWBG&sCXTv%& zQy0OEfl~PHeLX2f)W49{sza;5!&cauz~B7h+A7NnhbsJs!Tofs*v#-*9@BEaH{X_T zzS9~>Xk!%d6TrBLk?d3C()trc0}p7-nOzhsI6#I7QhNxHEmnWu-L1ymTynqO^m$D)z%W?O*MV|^RW~L z`wsga&&JAXzvgAe76m{W1Ngk?!rOLqLhYWJS*6`YyMoa?d;Z8vm2p z$^dw*sIF5eW3WjC#*(-a=1ua`lxPM|8{llG!?U-8~JQ-=y;5gt5II^4zN>@1;Ue4UHwE~8}-Fhvx* zEFg$Fh>G-;0Wym4>0}Dqsu5W+*Y|U(c2C-yO$;0$(_5T2#UkJgk(`r{m$<8jwnpEN zPz`fu9?d-7_P`J$2WRa=0zU7k*xKgvkqr<-mlJ>gvO$pHkM9MZ=4zkT$vq3)`=*cbF4QdMV@s6-n z47Hl-ekrw=^YurqmY@c9yQN#-y-haBM+zBTy!2Ld%05~CFl)kZ-c#}&(kF6zD7g>)TrCbjFEw*dM;*jO_lq2 z3JG~Pc?8;sUlDD}4%&kb4)OjsOIWk)3(022Qayn!M2W8Pq==ICVu-3N>W^(_t`EC! z*ddMH7OLfwnVw}uj8ZvewkPSYLMY>ym((NOIZm_~3(}D-i4qnj<0PCK zKhL@zC)iM*4R$B;wE1B@o3S^pUtmSk9s3 zZV<~kk)mWx*zY0PQ(`q0ojyIRIXS$gccp=2Q$N11$x4;`X@qz#%iJajZ4G)6;EL-Q zuc{j2P^6N$aum#!Qw^KV{QD;?rgGfJIYGIc--7?k^(x@s5wkdJ`FleYIdRx)S9aTofp>X5))-BoJ+|{mZgm=P*XeVQ=-aKn_L&qS&&5 z5$f==;)6Qj(OOOKZXPJh+OVdl>VrRSzrBH7z9fGwk(!xA29t_X^?TjFv=PYtaO zp)o;aE0Eg4u=@Fmoe@)VMHhjxhm9hFPDR7<{F<%ZN2n>twB;;wNeOsq z1v%w`7BXNhc5I3QsN>vR+jkvI@+sB=xgh6`H#GaFQ zoxHI}urDPW+I*60A{!S=j ztt@6oC&zrl8|`UPZMjnh`u<(q3Q(DBlbrY==RihphPQh4f9(!v5`q4RR)LQzq$)T< zUcq>ifTWUlR3Q2s_|3`I(L8=um1&fU>+lT?s=@=QgMmrMW-rjc(9|q81|DPsU;1jD z+WEXVC3U2*?%e1q;>nd_-7WeZGsgapyU719t5o84Ejj_ryO60+qh$vF=J!c=A#RbI z>i{YS{@s1;zX0r1&1&`~X0rDhnNHwWJ-0ECJeLW$;)mjF5n*b4=0L{Zq(fU>uaRy( z^AT||+Ty`|_vbzs3E+u`Cp=xlc!fuW`Mp-Y>(8HNlt zF}xQz(apDPV`r?Db$Llb0``B8w_k^r7i2CSsHHTuPC8Rj5ADqnleJ_UCxGR3te)Lg zUVIGv9NN>%7Z9*p&A?d_Je8L^M*f!)Lz-PBLW^*I#jkA3;Yk2lsa>){HM~c`jwBH+ z@6DdSzvf9jGR(vym;j3m=dwc+>0{pO&P&fa9b6JYs#-YhyHwtny33H<(K>|G{{%L+0AtD{p8T%S( zp+HpDa&|k1^pyf3n(d53b0n3HAn1v`ol1@TNRUsnbL;X$xHUK)7RAZ0z)pr1FT|-C zz1~CqS$CO;6hO1ZD5T^@*jfQoal9hFJcdRQTzjeIwf|u wHwc|=J8BZ%9!Dukp@NEWW)ca|lky|BFE6XE++V5L=O3wns-l*{S1<(nfBRNq_y7O^ literal 0 HcmV?d00001 diff --git a/src/Majorsoft.Blazor.Components.TestApp/wwwroot/index.html b/src/Majorsoft.Blazor.Components.TestApp/wwwroot/index.html index 7a7c35b0..1ca74d77 100644 --- a/src/Majorsoft.Blazor.Components.TestApp/wwwroot/index.html +++ b/src/Majorsoft.Blazor.Components.TestApp/wwwroot/index.html @@ -12,7 +12,22 @@ - Loading... + + +
    + +

    Majorsoft Blazor Component
    Loading Demo...

    +
    +
    An unhandled error has occurred. From a389786c386aa685b4aa68c5f780c9a8a09b0f03 Mon Sep 17 00:00:00 2001 From: Major Date: Fri, 2 Jul 2021 18:20:05 +0200 Subject: [PATCH 44/69] Created Majorsoft.Blazor.Components.Inputs component --- .../wwwroot/index.html | 2 +- .../Majorsoft.Blazor.Components.Inputs.csproj | 45 +++++++++++++++++++ .../Majorsoft.Blazor.Components.Inputs.xml | 8 ++++ .../MaxLengthInput.razor | 5 +++ .../_Imports.razor | 1 + ...Majorsoft.Blazor.Components.TestApp.csproj | 1 + .../Pages/MaxLengthInputPage.razor | 3 ++ .../wwwroot/index.html | 2 +- .../Components/MaxLengthInputs.razor | 5 +++ ...t.Blazor.Components.TestApps.Common.csproj | 1 + .../Pages/MaxLengthInputPage.razor | 3 ++ .../Shared/NavMenu.razor | 3 ++ .../Pages/MaxLengthInputPage.razor | 3 ++ src/Majorsoft.Blazor.Components.sln | 7 +++ 14 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.csproj create mode 100644 src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml create mode 100644 src/Majorsoft.Blazor.Components.Inputs/MaxLengthInput.razor create mode 100644 src/Majorsoft.Blazor.Components.Inputs/_Imports.razor create mode 100644 src/Majorsoft.Blazor.Components.TestApp/Pages/MaxLengthInputPage.razor create mode 100644 src/Majorsoft.Blazor.Components.TestApps.Common/Components/MaxLengthInputs.razor create mode 100644 src/Majorsoft.Blazor.Components.TestApps.Common/Pages/MaxLengthInputPage.razor create mode 100644 src/Majorsoft.Blazor.Components.TestServerApp/Pages/MaxLengthInputPage.razor diff --git a/demo/Majorsoft.Blazor.Components.DemoApp/wwwroot/index.html b/demo/Majorsoft.Blazor.Components.DemoApp/wwwroot/index.html index 80b9e60c..77a2de42 100644 --- a/demo/Majorsoft.Blazor.Components.DemoApp/wwwroot/index.html +++ b/demo/Majorsoft.Blazor.Components.DemoApp/wwwroot/index.html @@ -27,7 +27,7 @@
    -

    Majorsoft Blazor Component
    Loading Demo...

    +

    Majorsoft Blazor Components
    Loading Demo...

    diff --git a/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.csproj b/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.csproj new file mode 100644 index 00000000..3a7d5657 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.csproj @@ -0,0 +1,45 @@ + + + + net5.0 + Majorsoft.Blazor.Components.Inputs + enable + true + 1.0.0.0 + Imre Toth + Majorsoft + Blazor Components + ©2021 Imre Toth + License.txt + https://github.com/majorimi/blazor-components/blob/master/.github/docs/Inputs.md + blazor.components.png + https://github.com/majorimi/blazor-components + Git + See Releases here: https://github.com/majorimi/blazor-components/releases + .Net5 Blazor Htlm.Input Inputs MaxLength Counter + Blazor components that renders an Input, InputText, Textarea or InputTextarea, etc. element with maxlength set and counter to show remaining characters. Part of Majorsoft Blazor library. + + + + .\Majorsoft.Blazor.Components.Inputs.xml + + + + + + + + + + + + + True + + + + True + + + + \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml b/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml new file mode 100644 index 00000000..21e0a407 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml @@ -0,0 +1,8 @@ + + + + Majorsoft.Blazor.Components.Inputs + + + + diff --git a/src/Majorsoft.Blazor.Components.Inputs/MaxLengthInput.razor b/src/Majorsoft.Blazor.Components.Inputs/MaxLengthInput.razor new file mode 100644 index 00000000..d592bde4 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Inputs/MaxLengthInput.razor @@ -0,0 +1,5 @@ + + +@code { + +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Inputs/_Imports.razor b/src/Majorsoft.Blazor.Components.Inputs/_Imports.razor new file mode 100644 index 00000000..77285129 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Inputs/_Imports.razor @@ -0,0 +1 @@ +@using Microsoft.AspNetCore.Components.Web diff --git a/src/Majorsoft.Blazor.Components.TestApp/Majorsoft.Blazor.Components.TestApp.csproj b/src/Majorsoft.Blazor.Components.TestApp/Majorsoft.Blazor.Components.TestApp.csproj index 99091612..26cd7115 100644 --- a/src/Majorsoft.Blazor.Components.TestApp/Majorsoft.Blazor.Components.TestApp.csproj +++ b/src/Majorsoft.Blazor.Components.TestApp/Majorsoft.Blazor.Components.TestApp.csproj @@ -27,6 +27,7 @@ + diff --git a/src/Majorsoft.Blazor.Components.TestApp/Pages/MaxLengthInputPage.razor b/src/Majorsoft.Blazor.Components.TestApp/Pages/MaxLengthInputPage.razor new file mode 100644 index 00000000..47643464 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.TestApp/Pages/MaxLengthInputPage.razor @@ -0,0 +1,3 @@ +@page "/maxLengthInput" + + \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApp/wwwroot/index.html b/src/Majorsoft.Blazor.Components.TestApp/wwwroot/index.html index 1ca74d77..3fa6dc51 100644 --- a/src/Majorsoft.Blazor.Components.TestApp/wwwroot/index.html +++ b/src/Majorsoft.Blazor.Components.TestApp/wwwroot/index.html @@ -25,7 +25,7 @@
    -

    Majorsoft Blazor Component
    Loading Demo...

    +

    Majorsoft Blazor Components
    Loading Demo...

    diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MaxLengthInputs.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MaxLengthInputs.razor new file mode 100644 index 00000000..62d4af4d --- /dev/null +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/MaxLengthInputs.razor @@ -0,0 +1,5 @@ +

    MaxLengthInputs

    + +@code { + +} \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Majorsoft.Blazor.Components.TestApps.Common.csproj b/src/Majorsoft.Blazor.Components.TestApps.Common/Majorsoft.Blazor.Components.TestApps.Common.csproj index ee5cc31c..f9a7f3a9 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Majorsoft.Blazor.Components.TestApps.Common.csproj +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Majorsoft.Blazor.Components.TestApps.Common.csproj @@ -29,6 +29,7 @@ + diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Pages/MaxLengthInputPage.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Pages/MaxLengthInputPage.razor new file mode 100644 index 00000000..47643464 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Pages/MaxLengthInputPage.razor @@ -0,0 +1,3 @@ +@page "/maxLengthInput" + + \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/NavMenu.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/NavMenu.razor index 729bcfce..f539cc92 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/NavMenu.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Shared/NavMenu.razor @@ -42,6 +42,9 @@ +
    diff --git a/src/Majorsoft.Blazor.Components.TestServerApp/Pages/MaxLengthInputPage.razor b/src/Majorsoft.Blazor.Components.TestServerApp/Pages/MaxLengthInputPage.razor new file mode 100644 index 00000000..47643464 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.TestServerApp/Pages/MaxLengthInputPage.razor @@ -0,0 +1,3 @@ +@page "/maxLengthInput" + + \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.sln b/src/Majorsoft.Blazor.Components.sln index 7f29ea39..b988b268 100644 --- a/src/Majorsoft.Blazor.Components.sln +++ b/src/Majorsoft.Blazor.Components.sln @@ -87,6 +87,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsoft.Blazor.Extensions EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsoft.Blazor.Extensions.Analytics", "Majorsoft.Blazor.Extensions.Analytics\Majorsoft.Blazor.Extensions.Analytics.csproj", "{261B879A-4E63-4D4B-9B59-E8C8F84531FF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Majorsoft.Blazor.Components.Inputs", "Majorsoft.Blazor.Components.Inputs\Majorsoft.Blazor.Components.Inputs.csproj", "{A1FF7E56-1ADE-4A53-A517-A9A0F3A70ECC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -221,6 +223,10 @@ Global {261B879A-4E63-4D4B-9B59-E8C8F84531FF}.Debug|Any CPU.Build.0 = Debug|Any CPU {261B879A-4E63-4D4B-9B59-E8C8F84531FF}.Release|Any CPU.ActiveCfg = Release|Any CPU {261B879A-4E63-4D4B-9B59-E8C8F84531FF}.Release|Any CPU.Build.0 = Release|Any CPU + {A1FF7E56-1ADE-4A53-A517-A9A0F3A70ECC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1FF7E56-1ADE-4A53-A517-A9A0F3A70ECC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1FF7E56-1ADE-4A53-A517-A9A0F3A70ECC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1FF7E56-1ADE-4A53-A517-A9A0F3A70ECC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -257,6 +263,7 @@ Global {CE8AF2E1-6860-4F1B-BC96-F84042E55A2F} = {020CAF9C-D289-470A-BB97-E58D73DDCE70} {B0BDDF05-5508-4D05-B766-6DB53C33D004} = {121A4471-EF7A-4F9A-A856-67E8376A4AD2} {261B879A-4E63-4D4B-9B59-E8C8F84531FF} = {121A4471-EF7A-4F9A-A856-67E8376A4AD2} + {A1FF7E56-1ADE-4A53-A517-A9A0F3A70ECC} = {B6905E0B-759A-4928-B38A-9210B5CC8988} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F3E8B7F7-3F7C-4D6C-8B7B-48F1AF4694B9} From 4e0f6eafe8e47f3b62371530af1b77ce394ca658 Mon Sep 17 00:00:00 2001 From: Major Date: Fri, 2 Jul 2021 19:23:23 +0200 Subject: [PATCH 45/69] Implement MaxLengthInput component with demo --- .../Majorsoft.Blazor.Components.Inputs.xml | 41 +++++++++ .../MaxLengthInput.razor | 85 ++++++++++++++++++- .../_Imports.razor | 4 +- .../Components/Debounce.razor | 10 +-- .../Components/MaxLengthInputs.razor | 55 +++++++++++- .../_Imports.razor | 4 +- 6 files changed, 190 insertions(+), 9 deletions(-) diff --git a/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml b/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml index 21e0a407..57cd1913 100644 --- a/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml +++ b/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml @@ -4,5 +4,46 @@ Majorsoft.Blazor.Components.Inputs + + + Exposes a Blazor of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + + + + + Value of the rendered HTML element. Initial field value can be set to given string or omitted (leave empty). + Also control actual value can be read out (useful when MinLenght not reached). + + + + + Maximum allowed characters to type in. + + + + + Contdown label text to change or localize message. + + + + + Contdown label and value CSS calss property to style message. + + + + + Callback function called when HTML control received keyboard inputs. + + + + + Callback function called when HTML control received keyboard inputs remaining allowed chars calculated and sent as even args. + + + + + Blazor capture for any unmatched HTML attributes. + + diff --git a/src/Majorsoft.Blazor.Components.Inputs/MaxLengthInput.razor b/src/Majorsoft.Blazor.Components.Inputs/MaxLengthInput.razor index d592bde4..5e5b48b4 100644 --- a/src/Majorsoft.Blazor.Components.Inputs/MaxLengthInput.razor +++ b/src/Majorsoft.Blazor.Components.Inputs/MaxLengthInput.razor @@ -1,5 +1,88 @@ - + + + +@inject ILogger _logger; @code { + protected override async Task OnParametersSetAsync() + { + await CalculateRemaining(); + } + + private int _remainingChars; + private ElementReference _inputRef; + /// + /// Exposes a Blazor of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + /// + public ElementReference InnerElementReference => _inputRef; + + /// + /// Value of the rendered HTML element. Initial field value can be set to given string or omitted (leave empty). + /// Also control actual value can be read out (useful when MinLenght not reached). + /// + [Parameter] public string? Value { get; set; } + + /// + /// Maximum allowed characters to type in. + /// + [Parameter] public int MaxAllowedChars { get; set; } = 50; + + /// + /// Contdown label text to change or localize message. + /// + [Parameter] public string CountdownText { get; set; } = "Remaining characters: "; + + /// + /// Contdown label and value CSS calss property to style message. + /// + [Parameter] public string CountdownTextClass { get; set; } + + //Events + /// + /// Callback function called when HTML control received keyboard inputs. + /// + [Parameter] public EventCallback OnInput { get; set; } + + /// + /// Callback function called when HTML control received keyboard inputs remaining allowed chars calculated and sent as even args. + /// + [Parameter] public EventCallback OnRemainingCharsChanged { get; set; } + + + /// + /// Blazor capture for any unmatched HTML attributes. + /// + [Parameter(CaptureUnmatchedValues = true)] + public Dictionary AllOtherAttributes { get; set; } + + private async Task OnTextChange(ChangeEventArgs e) + { + WriteDiag($"{nameof(OnTextChange)} event: '{e.Value}', MaxAllowedChars: '{MaxAllowedChars}'."); + + if (OnInput.HasDelegate) //Immediately notify listeners of text change e.g. @bind + { + await OnInput.InvokeAsync(e.Value?.ToString()); + } + + Value = e.Value?.ToString(); + await CalculateRemaining(); + } + + private async Task CalculateRemaining() + { + var tmp = _remainingChars; + _remainingChars = MaxAllowedChars - (Value?.Length ?? 0); + + WriteDiag($"{nameof(CalculateRemaining)} event value Length: '{Value?.Length}', _remainingChars: '{_remainingChars}'."); + + if (OnRemainingCharsChanged.HasDelegate && tmp != _remainingChars) + { + await OnRemainingCharsChanged.InvokeAsync(_remainingChars); + } + } + private void WriteDiag(string message) + { + _logger.LogDebug($"Component {this.GetType()}: {message}"); + } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.Inputs/_Imports.razor b/src/Majorsoft.Blazor.Components.Inputs/_Imports.razor index 77285129..848647bc 100644 --- a/src/Majorsoft.Blazor.Components.Inputs/_Imports.razor +++ b/src/Majorsoft.Blazor.Components.Inputs/_Imports.razor @@ -1 +1,3 @@ -@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.Extensions.Logging; \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Debounce.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Debounce.razor index 911fef99..f8158b1b 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Debounce.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/Components/Debounce.razor @@ -2,7 +2,7 @@

    Deboudnce Input controls

    - Blazor component that renders an Input, InputText, Textarea or InputTextarea, etc. element with debounced onChange. For usege see soruce code and docs on + Blazor component that renders an Input, InputText, Textarea or InputTextarea, etc. element with debounced onChange. For usage see source code and docs on Github.
    Majorsoft.Blazor.Components.Debounce package is available on Nuget

    @@ -24,7 +24,7 @@
    -
    @**@ -
    -
    @**@ - MaxLengthInputs + + + + +

    Max allowed length Input controls with Counter

    +

    + Blazor components that renders an Input, InputText, Textarea or InputTextarea, etc. element with maxlength set and counter to show remaining characters. For usage see source code and docs on + Github. +
    Majorsoft.Blazor.Components.Inputs package is available on Nuget +

    + + +
    +

    MaxLengthInput component

    +

    Wraps around HTML Input control and sets maxlength property with notification onChange.

    + +
    +
    +
    Max allowed chars: @_maxLengthInputAllowed
    +
    +
    + +
    +
    + +
    +
    + +
    Actual value: @_maxLengthInputValue
    +
    @code { + private int _maxLengthInputAllowed = 20; + private string _maxLengthInputValue = ""; + private string _maxLengthInputTextClass = "countDownText"; + + private void MaxLengthInputRemainingCharsChanged(int remainingChars) + { + _maxLengthInputTextClass = "countDownText"; + if (_maxLengthInputAllowed * 0.2 >= remainingChars) + { + _maxLengthInputTextClass = "countDownText red"; + } + } } \ No newline at end of file diff --git a/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor b/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor index 3340f34a..dd78556a 100644 --- a/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor +++ b/src/Majorsoft.Blazor.Components.TestApps.Common/_Imports.razor @@ -49,4 +49,6 @@ @using Majorsoft.Blazor.Extensions.BrowserStorage -@using Majorsoft.Blazor.Extensions.Analytics.Google \ No newline at end of file +@using Majorsoft.Blazor.Extensions.Analytics.Google + +@using Majorsoft.Blazor.Components.Inputs \ No newline at end of file From 921ac075a3f62ea69208302cd9dae61a87d196c8 Mon Sep 17 00:00:00 2001 From: Major Date: Fri, 2 Jul 2021 19:40:45 +0200 Subject: [PATCH 46/69] Implement MaxLengthTextarea with demo. --- .../Majorsoft.Blazor.Components.Inputs.xml | 41 +++++++++ .../MaxLengthTextarea.razor | 88 +++++++++++++++++++ .../Components/MaxLengthInputs.razor | 39 +++++++- 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 src/Majorsoft.Blazor.Components.Inputs/MaxLengthTextarea.razor diff --git a/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml b/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml index 57cd1913..2f88adf2 100644 --- a/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml +++ b/src/Majorsoft.Blazor.Components.Inputs/Majorsoft.Blazor.Components.Inputs.xml @@ -45,5 +45,46 @@ Blazor capture for any unmatched HTML attributes. + + + Exposes a Blazor of the wrapped around HTML element. It can be used e.g. for JS interop, etc. + + + + + Value of the rendered HTML element. Initial field value can be set to given string or omitted (leave empty). + Also control actual value can be read out (useful when MinLenght not reached). + + + + + Maximum allowed characters to type in. + + + + + Contdown label text to change or localize message. + + + + + Contdown label and value CSS calss property to style message. + + + + + Callback function called when HTML control received keyboard inputs. + + + + + Callback function called when HTML control received keyboard inputs remaining allowed chars calculated and sent as even args. + + + + + Blazor capture for any unmatched HTML attributes. + + diff --git a/src/Majorsoft.Blazor.Components.Inputs/MaxLengthTextarea.razor b/src/Majorsoft.Blazor.Components.Inputs/MaxLengthTextarea.razor new file mode 100644 index 00000000..4f890c70 --- /dev/null +++ b/src/Majorsoft.Blazor.Components.Inputs/MaxLengthTextarea.razor @@ -0,0 +1,88 @@ +"); + label.MarkupMatches(@""); + } + + [TestMethod] + public void MaxLengthTextarea_should_rendered_initial_value() + { + var rendered = _testContext.RenderComponent(parameters => parameters + .Add(p => p.Value, "test")); + + var input = rendered.Find("textarea"); + var label = rendered.Find("label"); + + Assert.IsNotNull(input); + Assert.IsNotNull(label); + input.MarkupMatches(@"