Using Radix UI props does not work in React JS application - javascript

In my react Js application i am using Radix. I need to customizze toast component changing the position where the component should appear.
For this, the API provides the next way:
<ToastProvider swipeDirection="up">
So swipeDirection is responsible for this.
ISSUE: Using the above changes, the component stil appear at the bottom even with changed prop.
Question: How to make the component to appear at the top and why the prop does not work? demo: https://codesandbox.io/s/z688kv?module=App.js&file=/App.js:3909-3944

Sadly there is no "position" prop out of the box for this component. The swipeDirection="up" prop only specify that you can delete a toast by swiping it to the top. You can still specify where to place the toasts with pure css. Just update the styles of the ToastViewport component.
Default styles are:
position: fixed;
bottom: 0px;
right: 0px;
So to have the toasts in the top right corner for example just use:
<ToastViewport style={{ top: "0px" }} />

Adding on #johannchopin answer, if you want to position the toast to the left-bottom for example.
First, some changes to the slideIn animation:
const slideIn = keyframes({
from: { transform: `translateX(calc(-100% + ${VIEWPORT_PADDING}px))` },
to: { transform: "translateX(0)" }
});
const swipeOut = keyframes({
from: { transform: "translateX(var(--radix-toast-swipe-move-y))" },
to: { transform: `translateX(calc(-100% + ${VIEWPORT_PADDING}px))` }
});
notice the use of -100% instead of 100%
and a small change to the StyledViewport default position:
const StyledViewport = styled(ToastPrimitive.Viewport, {
position: "fixed",
bottom: 0,
left: 0,
//other styles removed to keep the code short
});

Related

Tooltip component rendering but not visible, and it is not the z-index

import React, {useState, useEffect} from "react";
// The tooltip component is used for the Tooltip feature that is utilized
const Tooltip = (couponProps) => {
const { couponTheme, verticalMousePosition, data, showTooltip, isSwiper } = couponProps;
const { TooltipText } = data;
const [tooltipStyles, setTooltipStyles] = useState({display: 'none'});
const [tooltipArrowStyles, setTooltipArrowStyles] = useState({display: 'none'});
const { TooltipTextColor, TooltipBackgroundColor, TooltipFontSize } = couponTheme;
// The useEffect hook will first define all the tooltip styling as objects. If the showTooltip variable defined in the parent element is active, render the tooltip.
useEffect(() => {
const topTooltipStyles = {
bottom: '130%',
left: '20%',
right: '20%',
width: '60%'
}
const topTooltipArrowStyles = {
top: '100%',
left: '50%',
marginLeft: '-5px',
borderColor: (TooltipBackgroundColor || 'black') + ' transparent transparent transparent',
marginTop: 'unset'
}
const bottomTooltipStyles = {
top: '125%',
left: '20%',
right: '20%',
width: '60%'
}
const bottomTooltipArrowStyles = {
bottom: '100%',
left: '50%',
marginLeft: '-5px',
borderColor: 'transparent transparent ' + (TooltipBackgroundColor || 'black') + ' transparent'
}
if (showTooltip) {
// The tooltip is configured to either render at the top of the interval bar or at the bottom of the interval bar. That is pased on where the mouse is located on the screen.
let tooltipPositionStyles = {};
let tooltipArrowPositionStyles = {};
// If the vertical mouse position is less than 250px to the top, render the tooltip at the bottom under the parent componet. This means the user is at the top of the screen, so use the bottom styling.
if (verticalMousePosition < 250 || isSwiper) {
// This way, the tooltip will not be cut of from the top of the screen.
tooltipPositionStyles = bottomTooltipStyles;
tooltipArrowPositionStyles = bottomTooltipArrowStyles;
// Else, that means the user is not at the top of the screen
} else {
tooltipPositionStyles = topTooltipStyles;
tooltipArrowPositionStyles = topTooltipArrowStyles;
}
const tooltipArrowStylesObj = {
content: ' ',
position: 'absolute',
borderWidth: '5px',
borderStyle: 'solid',
...tooltipArrowPositionStyles
}
const tooltipStylesObj = {
position: 'absolute',
color: TooltipTextColor || 'white',
background: TooltipBackgroundColor || 'black',
padding: '10px',
borderRadius: '10px',
zIndex: '5000',
textAlign: 'center',
...tooltipPositionStyles
}
// Set all of the arrow styles after determining if the tooltip is on top or on bottom.
setTooltipArrowStyles(tooltipArrowStylesObj);
setTooltipStyles(tooltipStylesObj);
} else {
// If the showTooltip variable is false, hide the tooltip.
setTooltipArrowStyles({});
setTooltipStyles({display: 'none'});
}
}, [showTooltip, verticalMousePosition, TooltipBackgroundColor, TooltipTextColor, isSwiper])
return (
<>{TooltipText !== undefined && TooltipText.trim() !== '' && TooltipText !== 'None' && TooltipText !== 'Inset Tooltip Text Here' && showTooltip ?
<div className='TooltipDiv' style={tooltipStyles}>
<span className="ToolTipText" style={{fontSize: TooltipFontSize || '12px'}}>{TooltipText}</span>
<span className="ToolTipArrow" style={tooltipArrowStyles}></span>
</div>
: null}</>
);
}
export default Tooltip;
So the code is passed the vertical mouse position as a prop and if the mouse is within 250 from the top of the page the tooltip is rendered below the component hovered. It all works fine if the component renders above and the mouse is lower down the page because there is nothing above to obstruct the tooltip, but when the component renders below, with bottomTooltipStyles, it is absolutely positioned but the issue is it is rendering behind another component. My gut said it was obviously the z-index, but this tooltip has a zIndex of 5000 which is significantly larger than the next largest z-index on the page at 60. Checking google chrome dev tools, the styles are all appropriate, the component renders, but it is still somehow behind/hidden behind another component no matter what I do. Any ideas would be greatly appreciated!! Thanks in advance!
So my question can also be asked like, "Is there a css property that would cause an element to render 'above' (over) another element that has an arbitrarily high z-index?"
Most of the gotchas with z-index have to do with "stacking context".
Elements are stacked on the z-axis within their stacking context.
Elements without position or explicit z-index values all share the same stacking context and are rendered in order of appearance in the rendered HTML.
Here are some specific z-index gotchas related to stacking context that may be affecting you:
1. z-index only applies to positioned elements
That is, position: absolute, position: relative, position: fixed, or position: sticky) and flex items. [1]
So first, make sure the elements you want to position on the z-axis are all explicitly positioned.
2. Some css properties can move an element into a new stacking context.
Some common ones are opacity and transform. Here is a list of CSS properties that can affect the stacking context.
And here is a detailed explanation on how opacity values affect stacking context:
Since an element with opacity less than 1 is composited from a single
offscreen image, content outside of it cannot be layered in z-order
between pieces of content inside of it. For the same reason,
implementations must create a new stacking context for any element
with opacity less than 1. If an element with opacity less than 1 is
not positioned, then it is painted on the same layer, within its
parent stacking context, as positioned elements with stack level 0. If
an element with opacity less than 1 is positioned, the ‘z-index’
property applies as described in [CSS21], except that if the used
value is ‘auto’ then the element behaves exactly as if it were ‘0’. [2]
To fix these, explicitly set the position and z-index so that they will be evaluated relative to the other positioned elements.
3. If an element's parent z-index (and position) is set, then that element's z-index will only apply within the parent.
In other words, the parent element is the stacking context.
To fix this, you can either modify the HTML hierarchy, or remove the position of the parent, or modify its z-index.
There are some good visuals and code examples for these situations here: https://www.freecodecamp.org/news/4-reasons-your-z-index-isnt-working-and-how-to-fix-it-coder-coder-6bc05f103e6c/
I had the same issue when rendering multiple components in one parent component. My popup div didn't appear on top of other components even though it had a higher z-index.
After a little bit of research, I found this answer helpful.
I had to move the popup component above everything else.
Also, please fix your code snippet so we can find out exactly what's wrong.

Transition between images in vuejs

I want to create a smooth transition between 2 images with a legend. The images come from an object-array of images.
Because works only on single tags and components, I've created a component to define the image+legend.
<transition>
<home-image :slide="slide" :key="slide"></home-image>
</transition>
The classes I define are like this
.v-enter-active,
.v-leave-active {
transition: opacity 2s ease-in-out;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
.v-enter,
.v-leave-to {
opacity: 0;
}
The new image is returned by a method
updateSlide() {
this.slide = this.entries[ Math.floor( Math.random() * this.entries.length ) ];
}
where entries is my array defined in data
this.slide is updated in regular intervals, every 10seconds like this, which is defined in the created() section
this.updateSlide();
this.uSlide = setInterval( this.updateSlide, 10000);
The code works, in the sense that a new image is loaded in this.slide every 10 seconds. However, the transitions work only "half-way".
There is no transition fading out: the "old image" disappears and makes way for the new image fading in.
However, what I'd like is a smooth transition from one to the other.
I've tried more than a couple of ideas including using mode="out-in" and "in-out" but nothing works as I want.
What am I overlooking?
Found out position in v-enter and v-leave had to be set.
Code is now:
.v-leave,
.v-enter-to {
position: relative;
opacity: 1;
}
.v-enter,
.v-leave-to {
position: absolute;
opacity: 0;
}

React: Set background image with props from another component

I hope I'm asking this question correctly (I'm still green with React).
I am writing a component that will set the background image (from a folder of jpgs) from props of another component. So let's say the component is "projects" then the background image would be set as "project.jpg" (and component "About" would have a background image of "about.jpg")
I've tried writing this and seems like I get close but not all the way there. Any React.js Gurus that could help me crack this code would have me singing praises of your screen name
My code
import React from 'react';
import styled from 'styled-components';
const pathToBgImages = require.context('../../../src/static', true);
const bgImages = [
'about.jpg',
'blog.jpg',
'contact.jpg',
'projects.jpg'
]
const getImages = () => bgImages.map(name => `<img src='${pathToBgImages(name, true)}'/>`);
const BgBackground = styled.div`
width: 100%;
&::after {
content: "";
background: url(${getImages});
background-size: cover;
opacity: 0.25;
height: 400px;
top: 0;
left: 0;
bottom: 0;
right: 0;
position: absolute;
z-index: -1;
}
`
class BgImage extends React.Component {
render() {
return (
<BgBackground />
);
}
}
export default BgImage;
I know there are some blanks in that component and not written correctly, I'm still trying to figure this out. Thank you in advance!

How to read size and move hidden element before Vue transition starts on it?

How to read dimensions and move a div that is hidden before Vue transition starts? For example, a user clicks a button and I want to move a hidden div to appear under the button with a fade-in transition. I need to be able to both read the dimensions and move the top/left position of the hidden div before the transition starts.
Let's say I'm using v-show="active" on the div, where active is my reactive data property I want to set to true and be able to move the div before transition starts.
I've tried all these:
Move the div first, then on nextTick set active = true.
Use the javascript hook beforeEnter to try to move the div before transitions start.
Use the javascript hook enter (and 'done' callback) to try to move the div before transition starts.
Tried all the above with updating the DOM immediately with the new position before setting active = true. (In other words, not via data binding, but actually setting element style properties directly like this.$refs.content.style.top = '500px' to avoid any waiting on the virtual DOM.) However, ideally I would like to accomplish this without directly touching the DOM, but using nextTicks instead. Both approaches fail.
Tried with some success with a hacky transition: all .8ms ease-in, top 1ms, left 1ms.
Tried with success with moving the div first then setting active in a setTimeout. This is not the right solution though.
Update
Thanks to the accepted answer I was able to see that I can read dimensions on nextTick (by which time v-show has turned on display). However, it turns out I needed the transition to be all transition all .3s and that would cause the movement to be included. The DOM will gather up all the changes and apply them together, which means they get lumped into the transition that is later added by Vue. The solution ended up being that I needed to make the movements, then trigger the DOM to repaint first, then trigger the v-show to turn on. Here's an example method:
startTransition () {
this.$refs.content.offsetHeight // <-- Force DOM to repaint first.
this.isContentActive = true // <-- Turns on v-show.
},
Use v-bind:style to move your window and it all works as intended.
Update: To check the size of the popup itself, it has to be shown, so I'm using v-show instead of v-if. The first thing I do is make it visible; on the next tick, I can measure it and place it.
new Vue({
el: '.container',
data: {
top: 0,
left: 0,
width: 0,
show: false
},
methods: {
showFloater: function(evt) {
const t = evt.target;
this.show = true;
Vue.nextTick(() => {
const fEl = this.$el.querySelector('.floating');
this.top = t.offsetTop + 30;
this.left = t.offsetLeft;
this.width = fEl.offsetWidth;
setTimeout(() => this.show = false, 1000);
});
}
}
});
.container {
position: relative;
}
.floating {
border: thin solid black;
padding: 3em;
position: absolute;
}
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ {
opacity: 0
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<div class="container">
<button #click="showFloater">Could go here</button>
<button #click="showFloater">Or here</button>
<transition name="fade">
<div v-show="show" class="floating" v-bind:style="{
top: top + 'px',
left: left + 'px'
}">
This window is {{width}}px wide.
</div>
</transition>
</div>

How do I change the location of the dat.gui dropdown?

I am using dat.gui, and I would like to position it somewhere different than the top right, preferably at the top overlapping a three.js canvas, is this accomplished through commands, or is there some css that will do the trick?
You need some JavaScript and CSS to do this.
The GUI constructor can be passed a paramaters object. You can tell the control not to be autoplaced. You can also attach an element ID to make styling easier
var gui = new dat.GUI( { autoPlace: false } );
gui.domElement.id = 'gui';
And then the CSS to place it can be something like this:
#gui { position: absolute; top: 2px; left: 2px }
The accepted answer answers my question but is not quite what I went for to solve the problem, do to the gui scrolling with me when I go up and down the page. Instead of setting an ID for the gui domElement, I appended the element to an existing element which I can control better.
css:
.moveGUI{
position: absolute;
top: 13.1em;
right: -1em;
}
JS:
// Create GUI
gui = new dat.GUI( { autoPlace: false } );
{
// create fill and open folders
}
var customContainer = $('.moveGUI').append($(gui.domElement));
HTML:
<div class = 'moveGUI'>
</div>
Override CSS:
.dg.a { margin-right:60px !important; }
Personally I like to use:
function datgui(){
let gui = new dat.GUI({
width : 300
});

Categories