I'm using OverlayTrigger, Tooltip from react-bootstrap, and want to change tooltip position depending on viewport. I use a default placement right for now, but when I test it on iPhone X (375px), it's not working, I suppose because of not enough space to render. How to do this to automatically update on a small screen?
For LONGTextLon does not work:
const CustomTooltip = ({ placement = 'right', text = null }) => {
return (
<OverlayTrigger
placement={placement}
popperConfig={{
modifiers: {
// preventOverflow: {
// enabled: true
// // priority: ['top', 'right'],
// // boundariesElement: 'viewport'
// }
}
}}
overlay={
<Tooltip>
<div className="tc-text">{text}</div>
</Tooltip>
}
>
<Icon/>
</OverlayTrigger>
);
};
Current state: does not change position automativcaly when not enouph space
Expected: flip or change position to 'bottom' depends on viewport or space
Try the event resize.
window.addEventListener('resize', () => {
// Check for the size of the window and re render
});
Related
I am working on a map with react-leaflet. I placed a button on the map that will ideally open a menu on the left side of the map. The way I want to open up the menu is by changing the width of the map from 100% to 80%. The menu button toggles a boolean, which ideally the map will rerender and resize when the button clicks.
this is the code in my App.js
export default function App() {
const [isMarkerClick, setIsMarkerClick] = React.useState(false);
const [isMenuOn, setIsMenuOn] = React.useState(false);
function toggleMarker() {
setIsMarkerClick(prevClick => !prevClick);
}
function toggleMenu() {
setIsMenuOn(prevMenu => !prevMenu);
}
return (
<div>
<MainMap isMenuOn={isMenuOn} />
<MarkerButton isMarkerClick={isMarkerClick} toggleMarker={toggleMarker} />
<MenuButton toggleMenu={toggleMenu} />
</div>
)
}
this is where the Map Code lives
export default function MainMap(props) {
const isMenuOn = props.isMenuOn;
const isMarkerClick = props.isMarkerClick;
const zoom = 15;
const position = ['39.9526', '-75.1652'];
const [marker, setMarker] = React.useState(["", ""])
return (
<div>
<MapContainer
center={position}
zoom={zoom}
zoomControl={false}
style={{
height: "100vh",
width: isMenuOn ? "80vw" : "100vw"
}}
>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={marker}></Marker>
{isMarkerClick && <ClickTrack setMarker={setMarker} />}
</MapContainer>
</div>
)
}
as of now, when I click the menu button, the isMenuOn state updates and which then feeds back into the MapContainer and should rerender with a new width but it doesn't. Any Ideas on how to get change the map size with a click of button using react?
example of the menu button
I thought that when clicking the menu button and changing the state of isMenuOn to "true", the mapcontainer would be listening and rerender with using the new width. Although it seems like setting the width through style ={{}}, might not allow for changes?
React Leaflet will only set the width and height when the component mounts and does not update it with the state changes, in order to update the map's width you need to re-render the component fully and to do that set the component's key to something that will change with the isMenuOpen value
here is an example
<MapContainer
center={position}
zoom={zoom}
zoomControl={false}
key={isMenuOn ? "open-state": "closed-state"}
style={{
height: "100vh",
width: isMenuOn ? "80vw" : "100vw"
}}
>
The accepted answer works, but I suspect it would cause a full map reload. You can try keeping the width value static in the MapContainer element, and instead change it the wrapping div like this:
<div style={{ width: isMenuOn ? '80vw' : '100vw' }}>
<MapContainer
center={position}
zoom={zoom}
zoomControl={false}
style={{
height: '100vh',
width: '100%',
}}
/>
</div>
There is a caveat though; the map needs to be informed of the change in size, otherwise it will seem buggy. I suspect that's why the developers don't update the style after the map mount.
You can however add a map child that monitors the isMenuOn prop (or any other prop that changes your layout), and let the map instance know they should invalidate everything about sizing:
function LayoutListener({ isMenuOn }) {
const map = useMap();
useEffect(() => {
map?.invalidateSize();
}, [map, isMenuOn]);
return null;
}
And then just put in the MapContainer children:
<MapContainer>
<LayoutListener isMenuOn={isMenuOn} />
{otherChildren}
</MapContainer>
I would really like to make this design but I don't know how to implement it in code, any suggestions?The issue is:I don't know how to show only one item at a time on the FlatList and how to make the centered item look closer.Lastly,How can I make the scroll go from one item to the other,without beeing sort of in the middle?
I tried looking online but I haven't been able to find what I need, if you found something please let me know.
I think you are looking for a carousel. Please refer to this library.
A list that allows both dragging and pressing a button.
For that i would use a horizontal FlatList. Then, make each item in the list take up 100% of the screen width and use the FlatList's ref param to scroll to the next item in the list when pressing a button and have a useState to keep track of which index the user is at to align button and swiping.
Heres how i would do it.
Get 100% screen width
import { Dimensions } from 'react-native';
const { width } = Dimensions.get('window');
Set up horizontal FlatList
The key here is the params: pagingEnabled and horizontal.
onViewableItemsChanged is called whenever focused item changes.
useCallback is for optimization. import it with useState
// Method passed to FlatList. Displays the data's text.
const renderItem = useCallback(
({ item }) => (
<View style={[styles.card, { width }]}>
<Text style={styles.cardItem}>{item.text}</Text>
</View>
),
[]
);
<FlatList
ref={flatListRef} // Reference to list. Used to scroll to specific items.
horizontal
pagingEnabled // cause snapping to items
data={data}
renderItem={renderItem}
onViewableItemsChanged={onScroll} // Calling with anonymous function here causes issues
viewabilityConfig={{
itemVisiblePercentThreshold: 100,
}}
/>
Button
Using the flatListRef and my useState, to scroll to the next index and keep track of where the user is at.
import React, { useState, useRef, useCallbac } from 'react';
const flatListRef = useRef(null);
const [currentSectionIndex, setCurrentSectionIndex] = useState(0);
const onButtonPress = useCallback(() => {
if (currentSectionIndex === data.length - 1) {
// What you want to do at the end of the list.
} else if (flatListRef.current) {
// Go to the next item
flatListRef.current.scrollToIndex({
index: currentSectionIndex + 1,
});
setCurrentSectionIndex(currentSectionIndex + 1);
}
}, [currentSectionIndex, data.length]);
OnScroll function
We wanted to allow dragging and pressing a button to go move on in the list.
So when the user scrolls, we use this function, which we passed to the FlatLists onViewableItemsChanged
// When scrolling set useState to keep track of where the user is at.
const onScroll = useCallback(({ viewableItems }) => {
if (viewableItems.length === 1) {
setCurrentSectionIndex(viewableItems[0].index);
}
}, []);
I am having issues with react Plotly displaying correctly when a graph loaded in a hidden tab is selected. It seems this is a known issue where every tab but the default tab will not resize appropriately because it doesn't know the hidden graph's dimensions.
To get around this I would like to dynamically update the tab height to be equal to the height of the default tab. Something like this: Change div height when tab is selected.
The issue is, I am unable to select the tab height value on DOM load. I thought I could add a componentDidMount function with an evenlistener for window load like such:
constructor(props) {
super(props)
this.state = {
value: 0
};
this.handleLoad = this.handleLoad.bind(this);
}
componentDidMount() {
window.addEventListener('load', this.handleLoad);
}
handleLoad() {
console.log("firstTab height: " + $('#firstTab').offsetHeight)
}
This issue is, the console log is outputting firstTab height: undefined.
When i inspect the web page and put the command $('#firstTab').offsetHeight into the console I am able to come up with a height of 697 px.
I don't want to hardcode the tab pixel size. Can anyone guide me as to why I am failing to grab the element height on window load?
Try using clientHeight property.
componentDidMount() {
const height = document.getElementById('firstTab').clientHeight;
}
I think instead of using event listener, you can do something like this.
componentDidMount() {
const height = this.firstTab.clientHeight;
console.log(height) // => gives you height
}
render() {
return (
<div
id="firstTab"
ref={ (firsTabElement) => { this.divElement = firstTabElement } }
>
Tab Content
</div>
)
}
After the first render if you are not hiding or putting any condition to remove the firstTab from the DOM. It will be available to you in componentDidMount lifecycle.
I have a problem with the colors palette appearance in summernote editor.
When the color palette is shown and I decrease the size of the browser window, I see that half of the colors don't show.
What I want is to find a way to make the colors palette responsive, in order to see all colors.
Screenshots:
this is how the problem looks like:
Now the solution I'm thinking (I didn't know how to implement it) is to make the colors palette responsive just like the "image options".
this is how it looks like initially:
and this is how it becomes responsive:
So, how can we achieve this responsiveness??
After all this time, I had to implement a resize functionality for the palette when the colors caret is clicked:
$('div[data-name="color"]').on('click', function (e) {
var rt = ($(window).width() - ($('div[data-name="color"]').offset().left + $('div[data-name="color"]').outerWidth())); //get offset on the right
var lt = $('div[data-name="color"]').offset().left; //get offset on the left
var $colorsPalette = $('[data-name="color"] ul.dropdown-menu');
//if language is arabic and the offset on the left is not big enough to display the horizontal palette>> then display a vertical palette
if ((__IsArabic == '1' && lt < 340) || (__IsArabic != '1' && rt < 340)) //smaller size needed
{
//here I add en empty row to keep distance between the background colors (at the top) and the font colors (bottom)
if (($colorsPalette.find('li').children().length == 2) && !$(this).parent().hasClass('open')) { //just opened
//make changes to style to display it correctly
$colorsPalette.find('.btn-group').first().after('<div class="btn-group" style="margin-top: 30px;"></div>');
$colorsPalette.css({ 'min-width': '120px', 'height': '470px' });
$colorsPalette.find('li').css({ 'width': '120px' });
$colorsPalette.find('li').children().each(function () { $(this).css({ 'margin-right': '5px', 'margin-left': '5px' }) });
$colorsPalette.find('li').children(":nth-child(2)").width(120);
}
} else {
if ($colorsPalette.find('li').children().length == 3) {
// undo the changes made (palette back to normal)
$colorsPalette.find('li').children(":nth-child(2)").remove();
$colorsPalette.find('li').children().each(function () { $(this).css({ 'margin-right': '', 'margin-left': '' }) });
$colorsPalette.find('li').css({ 'width': '338px' });
$colorsPalette.css({ 'min-width': '340px', 'height': '' });
}
}
});
I hope this helps someone facing the same problem
I would like to customize the shape of Kendo Tooltips for a grid.
I saw the example on kendo site, it has the arrow outside the box, and the box has a nice rounded shape.
Working on css, using .k-tooltip I can change width, height, background. But I get a square box with the arrow inside which sometimes overrides part of the text content.
I thought that callout would help but I was not able to get anything.
How can I change shape, image and position of the arrows, shape of the box ?
Moreover, how can I trigger the tooltip only when part of the text in a grid cell is visible ?
Thanks a lot for any hint
regards
Marco
I think "arrow" you mean callout. You can turn off callout by:
$(document).ready(function() {
$("#target").kendoTooltip({
callout: false
});
});
About your question "Moreover, how can I trigger the tooltip only when part of the text in a grid cell is visible?"
If I understand you correctly you would like to show tooltip only when there is text with ellipsis (partially visible in the cell), but you don't want to show a tooltip if there is a full text is visible or if there is no text in the cell. If that is the case, you can do this way:
function initializeTooltip(element, filter) {
return element.kendoTooltip({
autoHide: true,
filter: filter,
callout: false,
content: function (e) {
var target = e.target,
tooltip = e.sender,
tooltipText = "";
if (isEllipsisActive(target[0])) {
tooltipText = $(target).text();
}
tooltip.popup.unbind("open");
tooltip.popup.bind("open", function (arg) {
tooltip.refreshTooltip = false;
if (!isEllipsisActive(target[0])) {
arg.preventDefault();
} else if (tooltipText !== $(target).text()) {
tooltip.refreshTooltip = true;
}
});
return tooltipText;
},
show: function () {
if (this.refreshTooltip) {
this.refresh();
}
}
}).data("kendoTooltip");
};
// determanes if text has ellipsis
function isEllipsisActive(e) {
return e.offsetWidth < e.scrollWidth;
}
$(function () {
initializeTooltip($("#yourGridId"), ".tooltip");
});
tooltip in this case is class name of the column that you would like to use tooltip for, but you can call that class anyway you wish. In case if you are using Kendo ASP.NET MVC it will look something like this
c.Bound(p => p.ClientName)
.Title("Client")
.HtmlAttributes(new {#class = "tooltip"});