I am aiming to make messages in a Microsoft Bot Framework Chatbot appear more like 'speech bubbles' and have a triangular 'nub' display to the side of each, eg
The React Webchat client provided as part of the Bot Framework has optional properties to control the size and position of nubs which are
bubbleNubOffset?: number | 'bottom' | 'top';
bubbleNubSize?: number;
My first question is: Regardless of what (positive) bubbleNubOffset value is used, the nub always appears at the top of the message bubble rather than being offset eg
How can I move the nub down to appear more like the first image? Setting bubbleNubOffset to any negative number moves the nub to the bottom of the message bubble.
The source in GitHub for them has a comment
/**
* Nub offset ''bottom' will render nub at the bottom
* A positive or negative number will shift nub offset up/down
* "top" is equivalent to positive zero.
* "bottom" is equivalent to negative zero.
*/
but it doesn't appear to actually work like that.
My second question is: Without adding CSS to set the z-index of the message element, setting the bubbleNubSize causes the nub to be displayed in front of the message eg:
Is there a way to avoid this via configuration or is custom CSS the only way to do it?
My guess is you are supplying too large of a value to the bubbleNubSize property. For instance, here are the values that I use in my test Web Chat client.
bubbleBackground: 'black',
bubbleBorderColor: 'red',
bubbleBorderRadius: 9,
bubbleBorderWidth: 2,
bubbleTextColor: 'white',
bubbleNubSize: 5,
bubbleNubOffset: 'bottom'
bubbleFromUserBackground: 'black',
bubbleFromUserBorderColor: 'green',
bubbleFromUserBorderRadius: 9,
bubbleFromUserBorderWidth: 2,
bubbleFromUserTextColor: 'white',
bubbleFromUserNubSize: 5,
bubbleFromUserNubOffset: 'top'
Thankfully, Web Chat has built-in logic that helps interpret how the bubble and nub displays based on the values you provide. For instance, the nub mirrors vertically depending on the offset, and the border radius of the nub corner to be used is set to 0 to create a visually seamless flow.
Related
I'm encountering some weird behavior in a custom React useMemo hook. I'm trying to confirm that the correct values are being returned (as there's some evidence elsewhere that I'm getting the wrong value back), but I can't inspect using a developer console because the code is getting injected in a way that I can't attach to. (Oh well.)
I have a custom hook defined as follows (with all the relevant console.log statements in place for debugging):
const defaultMargins = {...}
export const usePanelDimensions = (width: number, height: number, panelCount: number, panelSpacing: number, margins?: Margins) => {
return useMemo(() => {
console.log(`Default margins are ${JSON.stringify(defaultMargins)}`)
console.log(`Received margins are ${JSON.stringify(margins)}`)
console.log(`Received width ${width} and height ${height}`)
const netHorizontalMargin = (margins?.left ?? defaultMargins.left) + (margins?.right ?? defaultMargins.right)
const netVerticalMargin = (margins?.top ?? defaultMargins.top) + (margins?.bottom ?? defaultMargins.bottom)
const netPanelSpacing = (panelSpacing * (panelCount - 1))
console.log(`Net margins: ${netHorizontalMargin} ${netVerticalMargin} ${netPanelSpacing}`)
const panelWidth = width - netHorizontalMargin
const panelHeight = (height - netVerticalMargin - netPanelSpacing) / panelCount
console.log(`-------`)
console.log(`** panel width ${panelWidth}`)
console.log(`** should report panel width as ${width - netHorizontalMargin}`)
console.log(`** Returning panel width ${panelWidth} and panel height ${panelHeight}`)
console.log(`-----`)
return {panelWidth, panelHeight}
}, [width, height, panelCount, panelSpacing, margins])
}
So here we are trying to divide a drawing space into one or more equal-sized bands, offset by some vertical distance.
We receive the overall canvas dimension (read from the rendered component), as well as a number of panels, the space between the panels, and some optional margins. If the margins aren't set, we use some defaults.
We compute the total width of one panel as the input width, less the left and right margins. The height of each panel is the height of the canvas, less the top and bottom margins, then divided into panelCount equal-sized units with panelSpacing pixels between them.
But when I run this, I get output for all the console.log statements except the ones between the dashed lines. Those statements are skipped entirely. Not "the variable interpolation leaves the values out," just the console log doesn't happen at all.
I understand that React sometimes suppresses console logging on second render when StrictMode is on (see https://camunda.com/blog/2021/02/be-careful-with-console-log-when-using-react-strictmode/) but I don't think strict mode is active here.
I considered that the problem might have something to do with some of the values not being set on first render. However, the line ** should report panel width ... is printing a value (width - netHorizontalMargin) which is a direct computation on two values that both get printed successfully a few lines up.
I'm kind of at a loss as to a) why these output statements are getting ignored, and b) how to confirm the value that's actually getting returned, as the hook's consumer also can't display information about these computed values.
Edit
CodeSandbox link: https://codesandbox.io/s/hidden-cdn-0k7chz?file=/src/App.js
Not quite a MWE because obviously there's more code here than there needs to be, but this will show the behavior in question. The code at the CodeSandbox link runs without a problem (printing out the computed values between the --- sections) on Firefox 104.0.1 (Ubuntu snap), where I get output:
Default margins are {"left":30,"right":15,"top":20,"bottom":50}
Received margins are {"left":50,"right":25,"top":30,"bottom":50}
Received width 2700 and height 475
Net margins: 75 80 0
-------
** panel width 2625
** should report panel width as 2625
** Returning panel width 2625 and panel height 395
-----
The issue does reproduce on Chrome 105.0.5195.102 (official Ubuntu 64-bit build), where I see output:
Default margins are {"left":30,"right":15,"top":20,"bottom":50}
Received margins are {"left":50,"right":25,"top":30,"bottom":50}
Received width 2700 and height 475
Net margins: 75 80 0
-------
-----
Before I go filing a bug report against React and/or Chrome, though, has anybody else encountered this behavior or have an explanation for why it might be expected/intended?
Looks like this was down to an old filtering statement in the console. The keywords in question probably shouldn't have impacted these variables, but removing it clears up the problem, so there you are.
I'm trying to use the offset parameters for the text label in relation to the position of the point (marker) on the map.
In my app, the user sets their preference to the x & y-axis offset values, which when changed, update the map point properties. I then need to use data-driven expressions to pull the values from each map point properties when updating the point layer options.
When the point is first created, the offset property of the point is set as below:
offset: [0, 0],
When updating the point layer, I've tried using the expression formulas below, but none of them are working without getting warnings in the browser debug console.
layers.pointLayer.setOptions({
iconOptions: {
offset: [ // ******NEEDS FIXING*******
'case', // Use a conditional case expression.
['has', 'offset'], // Check to see if feature has an "offset" property
//["length", ["array", ["get", "offset"]]] // not working
['get', 'offset'], // not working without browser warning
//['get', ['literal', [0, 0]]], // not working
//['literal', [0,0]], // working but not relevant, set locally, not pulling value from properties!!!
'literal', [0, 0]] // If it doesn't, default to array [0,0] (x & y-axis).
]
}
})
If using the example ['get', 'offset'], in the expression, although I can actually modify the offset and it works on the map as shown in the screenshot, I get the following warning in the browser debug console:
I'd like to have a warning free environment as any debug warnings. I obviously need to get the formatting set correctly in the data driven expression when setting the options for the layer (2nd code sample) but none of the syntax I've tried so far are working correctly.
I also tried studying the MS example here, but it seems they don't actually pull the offset values from the map point properties, they are setting the layer options directly from the user form which is no good unless i wanted to implement a global change for all points that belong to this particular layer.
I've got a stacked bar graph that shows two categories of information. Now I have a requirement to show the total of the bars at the end of the bar. I've attached a mock-up showing what I'm trying to do -- the numbers in red are what I'm trying to add.
(source: michaelandlisa.us)
I couldn't find anything in the documentation on how to add totals, or on how to add annotations (which would also work).
Basically, ShieldUI jQuery chart plugin renders the series without text, as shown here.
To alter this behavior, you need to first enable the text.
Then, you can use a format function to either show some cumulative text, or return an empty string. More information on this approach is available here.
This can be coupled with a global counter to determine each Xth iteration.
I managed to get this to work by adding a Scatter chart of total values on top of the existing bar chart.
http://michaelandlisa.us/Images/Forums/stacked_with_totals_scatter.png
I also set the color on the series to "transparent" so the point wouldn't show up, and then I bumped the X and Y by 15 and 12 respectively. I also set the style to Bold, and set the format to "{point.y:n0}". Here's the relevant MVC code (where totals is a List of object):
.DataSeries(series => series.Scatter()
.Data(totals)
.CollectionAlias("Total")
.Color("transparent")
.AddToLegend(false).DataPointText(dtp =>
{
dtp.Enabled(true);
dtp.Format("{point.y:n0}");
dtp.Style(s => s.FontWeight(FontWeight.Bold));
dtp.Color("red");
dtp.X(15);
dtp.Y(12);
}))
I have to accomodate an MREC advertisement in my layout. The ad is 300pixels wide, and cannot resize as the fluid grid otherwise contracts.
Further, the ad needs to be the first item on smartphone, before the headline block. So, on the breakpoint I am setting it to omega to push it "after" the headline, as in this screenshot (gray rules just to make it easier to see).
The headline is 8 cols, the ad is 4. All is fine except on ipad the ad column and the ad reduces to under 300 pixels, which we are not allowed to do. So, how to keep everything fluid except the ad container?
Set max/min widths on the container?
This is not hard to do, but it means the grid up top won't exactly match the flexible grid below. I assume that isn't an issue. You have to use some functions to lay out these two elements, but everything else can be done exactly as it was before.
You can keep the ad flexible down to a minimum width:
.ad {
#include span-columns(4 omega);
min-width: 300px; // you can use any width you want.
}
Or you can make the ad completely static:
.ad {
float: right;
width: columns-width(4); // you can use any width you want.
}
The important part is that you must not set a column width on the headline.
You have a few other options. The simplest might be to set right margins and padding equal to the static ad size (plus gutter):
.headline {
margin-right: columns-width(4) + $gutter-width;
}
Or, if you want that gutter to flex, try:
.headline {
margin-right: columns-width(4);
padding-right: gutter();
}
You can add clear: both; to the main content to make sure it clears the headline and ad.
If this approach doesn't work for you, try creating a new "formatting context" for the headline. One of the classic techniques is simply overflow: hidden;. Nicole Sullivan has a good blog post on how they do it for oocss, but it gets a bit more complex and you may not need all that.
UPDATE:
All these solutions require the ad coming first in the markup. The only way around that is if you know the height of the ad. In that case, you could position the ad absolutely rather than floating it, create space for it in the same way, and set a min-height on the headline (or row-container if you have one).
Im using leaflet to create a photo map, with my own tiles, which works as expected.
Im trying to work out how I can prevent the zoom from following this Quadtree type pattern:
Zoom Level 0 - Entire map width = 256px;
Zoom Level 1 - Entire map width = 512px;
Zoom Level 2 - Entire map width = 1024px;
And so on...
I would like to be able to zoom in say increments of 25% or 100px.
An example of 100px increments:
Zoom Level 0 - Entire map width = 200px;
Zoom Level 1 - Entire map width = 300px;
Zoom Level 2 - Entire map width = 400px;
And so on...
Question:
What is the logic for doing this? If it is at all possible?
My reason for wanting to do this is so that my photo map (which doesnt wrap like a normal map) can be more responsive and fit the users screen size nicely.
I made a demonstration of my issue which can be seen here
The short answer is that you can only show zoom levels for which you have pre-rendered tiles. Leaflet won't create intermediary zoom levels for you.
The long answer is that in order to use do this, you need to define your own CRS scale method and pass it to your map, for example:
L.CRS.CustomZoom = L.extend({}, L.CRS.Simple, {
scale: function (zoom) {
// This method should return the tile grid size
// (which is always square) for a specific zoom
// We want 0 = 200px = 2 tiles # 100x100px,
// 1 = 300px = 3 tiles # 100x100px, etc.
// Ie.: (200 + zoom*100)/100 => 2 + zoom
return 2 + zoom;
}
});
var map = L.map('map', { crs: L.CRS.CustomZoom }).setView([0, 0], 0);
In this example, I've extended L.CRS.Simple, but you can of course extend any CRS from the API you'd like, or even create your own from scratch.
Using a zoom factor which results in a map pixel size that is not a multiple of your tilesize, means your right/bottom edge tiles will only be partially filled with map data. This can be fixed by making the non-map part of such tiles 100% transparent (or same the colour as your background).
However, it is, in my opinion, a much better idea to set the tilesize to match the lowest common denominator, in this case 100px. Remember to reflect this by using the tileSize option in your tile layer. And, of course, you will need to re-render your image into 100x100 pixels tiles instead of the 256x256 tiles you are using currently.
One caveat, the current version of LeafletJS (0.5) has a bug that prevents a custom scale() method from working, due to the TileLayer class being hardcoded to use power-of-2 zoom scaling. However, the change you need to do is minor and hopefully this will be addressed in a future release of Leaflet. Simply change TileLayer._getWrapTileNum() from:
_getWrapTileNum: function () {
// TODO refactor, limit is not valid for non-standard projections
return Math.pow(2, this._getZoomForUrl());
},
To:
_getWrapTileNum: function () {
return this._map.options.crs.scale(this._getZoomForUrl());
},