Run only one pipe at a time - rxjs

I am trying to do service with multiple methods - each method will return pipe. The only one method can be called at a time; otherwise, the switch of pipe should happen.
This is an example of how I achieved it -- https://stackblitz.com/edit/rxjs-3pry8u?devtoolsheight=60 . It looks too complicated and has a potential memory leak, but it works. If you will try to click fast "Make red"/"Make green"/"Make blue" buttons, only the last will be triggered(see the console.log).
As requirement - it is important to control pipe run inside Color, not outside. I can imagine how it can be solved with the outside way: I could merge all click events to one pipe and use switchMap(). But it is interesting for me - how this problem can be solved properly inside Color class.

This is how I managed to solve it inside Color class:
I didn't use switchMap as I'm not going to make use of inner observables. I did get rid of makeRed, makeGreen and makeBlue functions as well.
I merged all the events and applied debounceTime to them.
Color
.mergeEvents (events: Observable<any>[]) {
merge<State>(...events)
.pipe(
debounceTime(500),
)
.subscribe(c => {
console.log(c)
this.state$.next(c);
})
}
const color = new Color();
color.mergeEvents([
fromEvent(document.getElementById("make-red"), "click").pipe(mapTo({ color: 'red' })),
fromEvent(document.getElementById("make-green"), "click").pipe(mapTo({ color: 'green' })),
fromEvent(document.getElementById("make-blue"), "click").pipe(mapTo({ color: 'blue' })),
]);
Here is a StackBlitz demo.

Related

Plotly.js, show tooltips outside of chart container

I need to implement a plotly.js chart on a page with a very restricted width. As a result, a tooltip is partially cut. Is it possible to cause tooltip not to be limited by plotly.js container size?
My code example at codepen: https://codepen.io/anatoly314/pen/gOavXzZ?editors=1111
//my single trace defined as following but it's better to see example at codepen
const yValue1 = [1000];
const trace1 = {
x: [1],
y: yValue1,
name: `Model 1`,
text: yValue1.map(value => Math.abs(value)),
type: 'bar',
textposition: 'outside'
};
It is, by design, not possible for any part of the chart to overflow its container.
I would say it is wrong to say that by design this is not possible! It is a bit hacky, but when you add the following lines, it shows the label outside of svg:
svg.main-svg,svg.main-svg *
{
overflow:visible !important;
}
The answer given by rokdd works. However the css selector should be more specific, otherwise it's natural that you will introduce subtle bugs (particularly if you need to scroll the content where the plotly chart is contained).
If we look at the DOM tree constructed by Plotly, we find that the tooltips are created inside the <g class="hoverlayer"></g> element (which is a direct child of one of the three <svg class="main-svg"></svg>). So that parent (that svg.main-svg element) is only one that needs to affected.
The ideal css selector in this case would be the :has selector. However it's still not supported (as of 2022): https://css-tricks.com/the-css-has-selector/
So the next simplest thing is to use a little bit of javascript right after we call Plotly.newPlot:
// get the correct svg element
var mainSvgEl = document.querySelector('#positive g.hoverlayer').parentElement;
mainSvgEl.style['overflow'] = 'visible';
Or in a more generic way (works for any chart):
Array.from(document.querySelectorAll('g.hoverlayer')).forEach(hoverEl => {
let mainSvgEl = hoverEl.parentElement;
mainSvgEl.style['overflow'] = 'visible';
});

Using HTML Canvas from ReasonML using React Hooks

I'm looking for a quick example on how to get started using the following technologies together:
HTML5 Canvas
ReasonML
ReasonReact: "ReasonReact is a safer, simpler way to build React components, in Reason."
bs-webapi: Web API bindings for Reason
React Hooks
To get me started a snippet that does the following would be great:
Manages a reference to the HTML5 Canvas element elegantly and correctly
Is a simple react component
Clears the canvas and draws something
I already have the basic ReasonML React project setup.
Here is a sample that shows one way to put everything together:
// Helper type to pass canvas size
type dimensions = {
width: float,
height: float,
};
// Actual drawing happens here, canvas context and size as parameters.
let drawOnCanvas =
(context: Webapi.Canvas.Canvas2d.t, dimensions: dimensions): unit => {
open Webapi.Canvas.Canvas2d;
clearRect(context, ~x=0., ~y=0., ~w=dimensions.width, ~h=dimensions.height);
setFillStyle(context, String, "rgba(0,128,169,0.1)");
fillRect(context, ~x=10.0, ~y=10.0, ~w=30.0, ~h=30.0);
};
// Extract canvas dimensions from canvas element
let canvasDimensions = (canvasElement: Dom.element): dimensions =>
Webapi.Canvas.CanvasElement.{
width: float_of_int(width(canvasElement)),
height: float_of_int(height(canvasElement)),
};
// An adapter to give nicer parameters to drawOnCanvas above
let drawOnCanvasElement = (canvasElement: Dom.element): unit =>
Webapi.Canvas.CanvasElement.(
drawOnCanvas(
getContext2d(canvasElement),
canvasDimensions(canvasElement),
)
);
[#react.component]
let make = () => {
open React;
let canvasElementRef: Ref.t(option(Dom.element)) = useRef(None);
useLayoutEffect0(() => {
Ref.current(canvasElementRef)
|> Belt.Option.map(_, drawOnCanvasElement)
|> ignore;
None;
});
<canvas
width="200"
height="100"
ref={ReactDOMRe.Ref.callbackDomRef(elem =>
React.Ref.setCurrent(canvasElementRef, Js.Nullable.toOption(elem))
)}
/>;
};
Here are some random links I used when learning how to do this. (Adding them here in case they are useful for others too.):
The bs-webapi test file to quickly learn the basics
A specific answer on how to use the setFillStyle (and where I learned the link to the test file above)
An answer in reason-react project showing how to work with React Refs.
The code has a bit more type declarations than necessary and some open
statements could be added, but I like my answers a bit on the verbose
side for a bit more instructiveness.
It should be relatively easy to shorten the code.
The intermediate functions canvasDimensions and drawOnCanvasElement add
a bit of structure to the code in my opinion, but I'm not sure if they make the sample more or less clear for readers or if there would be a more elegant way to work with the canvas size.

Multiple views/renders of the same kineticjs model

I am building a graph utility that displays a rather large graph containing a lot of data.
One of the things I would like to be able to support is having multiple views of the data simultaneously in different panels of my application.
I've drawn a picture to try and demonstrate what i mean. Suppose i've built the gradiented image in the background using kinetic.
I'd like to be able to grab show the part outlined in red and the part outlined in green simultaneously, without having to rebuild the entire image.
var stage1 = new Kinetic.Stage({
container: 'container1',
width: somewidth,
height: someheight
});
var stage2 = new Kinetic.Stage({
container: 'container1',
width: someotherwidth,
height: someotherheight
});
var Layer1 = new Kinetic.Layer({
y: someY,
scale: someScale
});
// add stuff to first layer here...
var Layer2 = new Kinetic.Layer({
y: otherY,
scale: otherScale
});
// add other stuff to second layer here...
stage1.add(mapLayer);
stage1.add(topLayer);
stage2.add(mapLayer);
stage2.add(topLayer);
at the point at which I've added my layers to stage1, everything is fine, but as soon as i try to add them to stage2 as well, it breaks down. I'm sifting through the source but I cant see anything forcing data to be unique to a stage. Is this possible? Or do i have to duplicate all of my shapes?
Adding a node into multiple parents is not possible by KineticJS design. Each Layer has <canvas> element. As I know it is not possible to insert a DOM element into document twice.

d3.js tween factory return function applied to non-interpolable property values

This question builds on the (correct) answer provided to this. I simply haven't been able to get any further..
With the help of an interpolator function, d3.js's tween allows smooth graphical transition between existing and new (ie to be set) DOM element values. At the simplest level, for a given animation we have a target element, an start state, an end state, a transition, a tween function and an interpolator.
Now, say I want every so often to programmatically update the contents of an input (text field) element. The value to be entered is non-interpolable (either the text is submitted, or it is not. There is no in-between state). In providing a closure (allowing for text retrieval at the scheduled transition time), tween would seem to be a good vehicle for the updates. Either I replace the interpolator with a fixed value, ensure the start and end values are identical, or find some other way of forcing it to fire at t=1. That's the theory..
To this end, in my case each property (not value) is modified in it's own update call, into which are passed transition, element index and parent element selection.
First cut:
an outer, 'governing' transition with delay values staggered using a multiple of the current element's index
playback_transition = d3.transition()
.delay(function(d, i, j) {
return (time_interval * i);
})
.duration(function() {
return 1; // the minimum
});
within a call to playback_transition.each() pass the transition as a parameter to a dependent animation by means of an update() interface
within this dependent animation, apply the transition and tween to the current element(s):
input // a UI dialog element
.transition()
.tween(i.toString(), setChordname( waveplot.chordname ));
Where:
function setChordname(newValue) {
return function() {
var i = newValue; // a string
return function(t) {
this.value = i;
inputChanged.call(this);
};
};
};
and
function inputChanged() {
if (!this.value) return;
try {
var chord = chordify.chordObjFromChordName(this.value);
purge(); // rid display of superceded elements
plotChord(chord, options); // calculate & draw chord using new input property
} catch (e) {
console.log(e.toString());
}
}
PROBLEM
While setChordname always fires (each chord is in turn correctly found and it's value stored), of the scheduled returned functions, only the first fires and results in display of the associated waveform. For all subsequent return function occurrences, it is as if they had never been scheduled.
From the display:
direct user update to the input field still works fine
only the first of setChordname's return functions fire, but, for this initial chord, carries right through, correctly displaying the cluster of associated chord and note waves.
From this, we can say that the problem has nothing to do with the integrity of the waveplotting functions.
From the console
transitions are accumulating correctly.
chord supply is all good
associated (ie initial) tween fires at t=1. (specifically, tween appears to accept omission of an interpolator function).
looking at the output of transition.toSource(), though the associated outer index increases by single figure leaps, tween itself is always paired with an empty pair of curly brackets.
transition = [[{__transition__:{8:{tween:{}, time:1407355314749, eas..
For the moment, apart from this and the initial execution, the tween factory return function behaviour is a mystery.
From Experiment
Neither of the following have any impact:
Extending the period before the initial transition takes effect
Extending (by a multiple) each staggered transition delay
Furthermore
the same transition configuration used in a different scenario works fine.
These seem to eliminate timing issues as a possible cause, leaving the focus more on the integrity of the tween setup, or conditions surrounding waveplot append/remove.
Afraid it might be interfering with input property text submission via the tween, I also tried disabling a parallel event listener (listening for 'change' events, triggering a call to inputChanged()). Apart from no longer being able to enter own chordnames by hand, no impact.
In addition to 'change', I tried out a variety of event.types ('submit', 'input', 'click' etc). No improvement.
The single most important clue is (to my mind) that only the first setChordname() return function is executed. This suggests that some fundamental rule of tween usage is being breached. The most likely candidate seems to be that the return value of tween **must* be an interpolator.
3 related questions, glad of answers to any:
Anything blatently wrong in this approach?
For a shared transition scenario such as this, do you see a better approach to transitioning a non-interpolable (and normally user-supplied) input property than using tween ?
Provided they are staggered in time, multiple transitions may be scheduled on the same element - but what about multiple tweens? Here, as the staggered transition/tween combos are operating on only one element, they seem likely to be passed identical data (d) and index(i) in every call. Impact?
I'm now able to answer my own question. Don't be put off by the initial couple of paragraphs: there are a couple of valuable lessons further down..
Ok, there were one or two trivial DOM-to-d3 reworking issues in my adoption of the original code. Moreover, an extra returned function construct managed to find it's way into this:
Was:
function setChordname(newValue) {
return function() { <--- Nasty..
var i = newValue;
return function(t) {
this.value = i;
inputChanged.call(this);
};
};
};
Should have been:
function setChordname(newValue) {
var i = newValue;
return function(t) {
this.value = i;
inputChanged.call(this);
};
};
The fundamental problem, however, was that the transition -passed in as a parameter to an update() function- seems in this case to have been blocked or ignored.
Originally (as documented in the question) defined as:
input // a UI dialog element
.transition()
.tween(i.toString(), setChordname( waveplot.chordname ));
..but should have been defined as:
transition
.select("#chordinput") // id attribute of the input element
.tween(i.toString(), setChordname( waveplot.chordname ));
My guess is that the first version tries to create a new transition (with no delay or duration defined), whereas the second uses the transition passed in through the update() interface.
Strange is that:
what worked for another dependent animation did not for this.
the staggered delays and their associated durations were nevertheless accepted by the original version, allowing me to be misled by console logs..
Just to round this topic off, I can point out the the following (event-based) approach seems to work just as well as the tween variant with non-interpolable values documented above. I can switch freely between the two with no apparent difference in the resulting animations:
transition
.select("#chordinput") // id attribute of the input element
.each("start", setChordname( waveplot.chordname ));
Thug

Need pauses - timing not working on iterating through text strings w/ d3/svg (piling up on each other)

I am trying to use d3 to animate text using an svg text with d3 transitions. I have it working as desired for a single string.
I want to iterate through strings from an array of json objects.
I can do this as well.
All the painting and transitions work great. Problem is, they all happen at once, and appear piled up on each other, and all animate all at once.
I have tried putting them in a setTimeout() to get them to appear sequentially.
Still does not work.
for ( i in haikuStr ) {
if( i !=0 ){
//Make it wait if an appropriate time it is not the first one
setTimeout( function() {
showText();
}, 11000 * i );
} else {
//if i=0, don't make folks wait
showText();
}
}
The showText() function is the full create container -> finish transitions.
I use 11000 * i to ensure that >2 iterations have 11 additional seconds per i.
I have spent quite a bit of time reading and trying to figure out how to get the loop to pause before cycling through to paint the next line.
Any thoughts or ideas would be appreciated.
The un-timed example is here, if you wish to see the text jumble up:
http://www.mysalmagundi.com/js/svg-d3-no-timing.html
Have you read Thinking with Joins? Or some of the other introductory D3 tutorials, such as those by Scott Murray? Or Three Little Circles, or Working with Selections? I ask because your showText function is misusing data joins; it creates text elements for every element in the global haikuStr array:
var text = haikuContainer.selectAll("text")
.data(haikuStr)
.html(String)
.enter().append("text");
And all your text elements are overlapping because you set them to have the same y-attribute:
var thisHaiku = text
.attr("x", -800)
.attr("y", 120)
(Also, that selection.html call is a no-op because the update selection is guaranteed to be empty, since you just created haikuContainer it is guaranteed to not have any descendant text elements. And thisHaiku is the same value as the var text, because when method chaining selection.attr and similar methods return the current selection; so there’s no reason to create a separate var. Also, you shouldn’t use a for-in loop to iterate over arrays.)
If you wait 11 seconds, you’ll see the second SVG appear, but because of your data join as described above, it has the same overlapping text content.
If you just want to show a single piece of text, then pass that string to your showText function (e.g., showText("hello")). Then, since you’re just creating individual elements, just selection.append them rather than using a data-join. You only need the data-join when you’re creating (or updating or removing) a variable number of elements based on data; in this case it looks like you’re trying to just create a single element.

Resources