Rescript-react: Creating a hidden canvas element - reactjs

I am creating a simple react app (HTML) that allows a user to browse to an image on their local pc, and then displays it in an image tag. I want to take a data-url and dynamically create a hidden canvas tag (open to a different approach, but I want to resize the image, not set size contraints on the tag displaying the image). Here is my resize code
MyComponent.res (offending code)
let resize = dataUrl => {
let canvas = React.createElement(_ => React.string("canvas"), {"style": "display: none;"})
canvas.getContext("2d");
dataUrl
}
The error
We've found a bug for you!
/Users/n0321437/projects/rescript-react/from-local-template/src/Upload.res:14:12-21
12 │ let canvas = React.createElement(_ => React.string("canvas"), {"sty
│ le": "display: none;"})
13 │ //let myCanvas = canvas([React.null])
14 │ canvas.getContext("2d");
15 │ dataUrl
16 │ }
The record field getContext can't be found.
I haven't found a much documentation or postings on using createElement or createElementVariadic - so I am guessing here. It looks as though createElement returns an object of type element but there are no associated methods:
React.res
type element
external createElement: (component<'props>, 'props) => element = "createElement"
So I guess there are a couple of questions
Have i infact created an element that would represent the HTML Object of Canvas?
If I have done so, how do I call methods on that code?
If I have not, how do I create a hidden Canvas object?
FInally how might one navigate the documentation and source to discover this on their own?

React is a library that provides a declarative API for creating, inserting and updating the DOM by way of a virtual DOM. React.createElement is an internal API used to create virtual DOM elements.
What you seem to want is to create an actual DOM element, and then NOT insert it into DOM. React is not designed to do this.
Instead what you want is bindings to the DOM API, which you could make yourself if you only need a few of its features, or you could use a full-blown set of DOM bindings such as rescript-webapi, which conveniently also includes bindings for Canvas.
This is how you'd create a canvas element:
open Webapi.Dom
let canvas = document->Document.createElement("canvas")
then to get the canvas context you'd use:
open Webapi.Canvas
let context = canvas->CanvasElement.getContext2d
then use the functions in Canvas2d to do what you want:
let image = context->Canvas2d.createImageDataCoords(~width=100, ~height=100)
...
context->Canvas2d.putImage(image, ~dx=0, ~dy=0)

Related

How to add missing images as per warning in "styleimagemissing" event in azure maps? And how to find out which images/icons are missing?

I'm adding a number of icons created from imageSprite.createFromTemplate() method however sometimes I'm receiving the following error . How to resolve it using "styleimagemissing" event? How to find out which image is missing to add in the callback ?. And the symbol layer created using the icons sometimes overlaps over the bubble layer(cluster layer) too in some clusters. I don't know if that is caused due to missing images. Thanks in advance.
atlas.min.js:55 Image "Scaffold Builder_Inactive_Icon" could not be loaded. Please make sure you have added the image with map.addImage() or a "sprite" property in your style. You can provide missing images by listening for the "styleimagemissing" map event.
The styleimagemissing event will tell you the ID of the image that is missing. It won't tell you which symbol layer is trying to use that image however as a single image may be used by multiple layers.
Here is an example of how to use this event:
var iconLoaded = {};
//Add an event to handle the situation when an image is missing from the sprite.
map.events.add('styleimagemissing', function (id) {
//Check to see if the image is has been or is being loaded. Don't try and load the image multiple times.
if (!iconLoaded[id]) {
iconLoaded[id] = true;
//Load the custom image icon into the map resources.
map.imageSprite.add(id, '../Common/images/icons/showers.png').then();
}
});
A complete sample is available here: https://github.com/Azure-Samples/AzureMapsCodeSamples/blob/master/AzureMapsCodeSamples/Symbol%20Layer/Load%20missing%20image%20into%20sprite.html

How are Javascript widgets made without iFrames?

I have a chat widget that I want to embed it other people's websites. It looks just like Intercom and all the other chat popups. I want to make the chat popup stick to the bottom-right hand corner of the screen regardless of where you scroll. However, when I import the chat app as an iframe and give it position: fixed; bottom: 0px; right: 15px;, the iframe does not go to where I expect it to go.
I realize that iframes are suboptimal for embedded JS widgets, and all the best embedded apps are importing .js files from file storage. After searching online for hours I have yet to find an explanation/tutorial on how to make those JS files that hook onto a and render the widget. How do you even make one of those pure javascript apps, and what are they called? (Not web components I assume, because there have been widgets for a long time).
Sorry if this question is kinda noob. I never knew this was a thing until I tried implementing it myself. Can anyone point me in the right direction on how to get started making JS web widgets? Thank you! (Maybe a ReactJS to VanillaJS converter would be super cool)
A pure Javascript App is called a SPA - Single Page Application - and they have full control over the document (page). But since you ask about embeding a widget, I don't think that is what this question is about (there are tons of info. on the web on SPAs).
I was going to suggest that going forward you do this using Web Components - there are polyfills available today that make this work on nearly all browsers - but since your question mentioned that you wanted to know how it is done without it, I detail below one of my approaches.
When creating a pure JS widget you need to ensure that you are aware that a) you do NOT have control over the global space and b) that it needs to play nice with the the rest of the page. Also, since you are not using Web Components (and are looking for a pure javascript (no libs)), then you also have to initialize the widget "manually" and then insert it to the page at the desired location - as oposed to a declaritive approach where you have an assigned HTML tag name for your widget that you just add to the document and magic happens :)
Let me break it down this way:
Widget Factory
Here is a simple Javascript Widget factory - the create() returns an HTML element with your widget:
const Widget = Object.create({
create(chatId) {
const wdg = document.createElement("div")
wdg.classList.add("chat-box");
wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
// Load your chat data into UI
return wdg;
}
});
To create a new widget (HTML Element) using the above you would:
const myWidgetInstance = Widget.create("chat-12345");
and to insert this widget into the page at a given location (ex. inside of a DIV element with id "chat_box", you would:
document.getElementById("chat_box").appendChild(myWidgetInstance);
So this is the basics of creating a Widget using the native (web) platform :)
Creating a reusable/embeddable Component
One of the key goals when you deliver a reusable and embeddable component is to ensure you don't rely on the global space. So your delivery approach (more like your build process) would package everything together in a JavaScript IIFD which would also create a private scope for all your code.
The other important aspect of these type of singleton reusable/embeddable components is that your styles for the Element needs to ensure they don't "leak" out and impact the remainder of the page (needs to play nice with others). I am not going into detail on this area here. (FYI: this also the area where Web Component really come in handy)
Here is an example of a Chat component that you could add to a page anywhere you would like it to appear. The component is delivered as a <script> tag with all code inside:
<script>(function() {
const Widget = Object.create({
create(chatId) {
const wdg = document.createElement("div");
wdg.classList.add("chat-box");
wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
// Load your chat data into UI
return wdg;
}
});
const myWidgetInstance = Widget.create("chat-12345");
const id = `chat-${ Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1) }`;
document.write(`<div id="${ id }"></div>`);
document.getElementById(id).appendChild(myWidgetInstance);
})();</script>
So you could use this in multiple places just by droping in this script tag in the desired locations:
<body>
<div>
<h1>Chat 1</h1>
<script>/* script tag show above */</script>
</div>
...
<div>
<h1>Chat 2</h1>
<script>/* script tag show above */</script>
</div>
</body>
This is just a sample approach of how it could be done. You would have to add more in order to support passing options to each widget (ex. the chat id), defining styles as well other possible improvements that would make the runtime more efficient.
Another approach
You could add your "script" once and wait for the rest of the page to load, then search the document for a "known" set of elements (ex. any element having a CSS Class of chat-box) and then initialize a widget inside of them (jQuery made this approach popular).
Example:
Note how data attributes can be used in DOM elements to store more data specific to your widget.
<div class="chat-box" data-chatid="123"></div>
<script>(function() {
const Widget = Object.create({
create(chatId) {
const wdg = document.createElement("div");
wdg.classList.add("chat-box");
wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
// Load your chat data into UI
return wdg;
}
});
const initWhenReady = () => {
removeEventListener("DOMContentLoaded", initWhenReady);
Array.prototype.forEach.call(document.querySelectorAll(".chat-box"), ele => {
const myWidgetInstance = Widget.create(ele.dataset.chatid);
ele.appendChild(myWidgetInstance);
});
};
addEventListener('DOMContentLoaded', initWhenReady);
})();</script>
Hope this helps.
The best way to create Javascript widget without third-party library is to create Custom Elements.
The following link : Custom Elements v1 is a good introduction to this technology.
See a minimal example below:
class Chat extends HTMLElement {
connectedCallback () {
this.innerHTML = "<textarea>Hello</textarea>"
}
}
customElements.define( "chat-widget", Chat )
<chat-widget>
</chat-widget>

Angularjs - Charts.js: Same chart element doesn't redraw on other view

I am new to angularjs, trying to create my first directive. I am creating a directive to load Charts.js2.0(beta) into my application.
I have 2 views managed by angular-route, both html view has ng-included a html page that contains only charts-element.
The problem is the first page properly draws the chart, when i go to other view the charts div is loaded but charts is not re-drawn. And now if i go back to first view its blank.
Link to Plunker
What i am doing wrong? Is there any issue with my directive?
Thanks in advance.
There appears to be an issue with the Charts library modifying the existing object on the root scope, and thereby ignoring it forever afterward. I can't really trace down what is doing it, but here's a fix for you: http://plnkr.co/edit/jDQFV62FSeXAQJ6o7jE8
Here is what you had
scope.$watch('config', function(newVal) {
if(angular.isDefined(newVal)) {
if(charts) {
charts.destroy();
}
var ctx = element[0].getContext("2d");
charts = new Chart(ctx, scope.config);
//scope.$emit('create', charts);
}
});
Above, you can see that you're passing scope.config directly into the charts method. That appears to be modifying the data somehow, and since that's passed by reference, you're actually modifying $rootScope.sales.charts. If you copy that object and use it locally like below, you don't have that problem.
Here's how I fixed it.
scope.$watch('config', function(newVal) {
var config = angular.copy(scope.config);
if(angular.isDefined(newVal)) {
if(charts) {
charts.destroy();
}
var ctx = element[0].getContext("2d");
charts = new Chart(ctx, config);
//scope.$emit('create', charts);
}
});
You can see that instead of passing that object directly in, we use angular to make a copy (angular.copy()), and that's the object we pass in.
I think it has relation with the id of the canvas where you are drawing. I've had this problem too amd it was because i was using the same id for the canvas of two graphs in different views. Be sure that those ids are different and that the javasrcipt of each graph is in the controller of each view or in each view itself.
Taking a look at your pluker I see that you are using the same html for the graph and I guess that when angular moves from one of your views to the other thinks that the graph is already drawn. Differentiating two graphs will solve the problem. I don't know of there is any other approach that allows using the same html for the canvas of the graph.
Hope it helps you solve it

Drive Time and Distance from Leaflet Routing Machine

I’m building embedded content for a CRM using JQuery Datatables, Leaflet with OSM tiles, and Leaflet Routing Machine. Markers on the map and the rows of the table are based on the SAME JSON data, and I’m building interactions (using SHARED JavaScript functions) between the two libraries. For example, when a Datatable row is clicked, the row is highlighted, a popup is opened over the corresponding map marker, and a route is calculated using LRM which places the route line on the map. Conversely, when a map marker is clicked, all the same events happen because I'm calling the SAME function.
By default, the Itinerary LRM creates is hidden on the map, but I would like to parse out the Drive Time and Distance, and insert them in the popup opened by the shared function. I have spent four days pouring over the API documentation and searching the internet looking for clear instructions or code samples on how to access these values from the Itinerary object, but with no success. I have inspected every object I can figure out how to log to the console, but the data I need is in properties that come up ‘undefined’ when I try to access them.
Please, from start to finish, how do I access the Itinerary Summary?
When I init the map, I also init the Routing Control with null waypoints:
ctrl = L.Routing.control({
waypoints: null,
units: 'imperial',
show: false,
createMarker: function() { return null; }
}).addTo(map);
When a marker/row is clicked, I call this function:
function clickEffects(id, latlon) {
// set waypoints for routing control
ctrl.setWaypoints([ ctr, latlon])
// scroll the table to the row for the clicked marker
table.$('tr.selected').removeClass('selected');
var idx = table.row("#" + id).index()
table.row(idx).show().draw(false);
table.row(idx).nodes().to$().addClass('selected');
// create and open a popup
var popup = L.popup()
.setLatLng(latlon)
.setContent("Dan was here!!!")
.openOn(map);
}
Figured it out:
Declared popup in global scope
Bound default "Loading..." content when creating marker
In shared function, set div's with target id's in new popup content
(in addition to freshly retrieved ajax content.
In "routesfound" event listener, used JQuery to insert formatted
drive time and distance on target divs.
Works like a charm. Just need to expand on ajax data now...

Toggle KML overlay for Google Maps API v3 using checkbox

Does anyone have some sample code for toggling a KML overlay layer with a checkbox? I can get a kml layer on my map to toggle off when I uncheck the checkbox, but I can't get it to toggle back on. I've viewed all sorts of sample sites and code, but can't get this thing to work. The site in question is at www.fhitestsite.com/mdctest.
Thanks.
1) Your problem is that map var is not defined -
you defined var map inside a one function but trying to call it from other one. Define it outside of the function
...
var map;
var todayLayer;
var todayShown = 1;
var todayWdgt;
...
2) minor error. todayWdgt is NULL. Its just that you try to assign something that doesn't exist yet.
You should run this code after the document is ready.
todayWdgt = document.getElementById("todayBtn");
todayWdgt.checked = true;
Try using firebug.

Resources