Expand Div smoothly in react with tailwind not happening - javascript

I am trying to animate a div containing my input elements which should expand smoothly, the text area at first is small and the other two input and buttons are hidden, when clicking the text area input and button are shown, however there is no transition, just a jump in size, I tried to use framer motion but it doesn't accept the tailwindcss values, putting the transitions class in the parent div didn't help either, can someone please help me with this
<div
ref={mainDivRef}
className='z-10 flex justify-center flex-wrap flex-col gap-2 p-2 bg-slate-300 bg-opacity-30 rounded-md'
onClick={() => handleClickInside()}>
<textarea
className='transition ease-in-out delay-150 resize-none rounded-md p-1 focus:outline-none'
value={mainText}
placeholder={"Ask me something"}
rows={rows}
cols={columns}
onChange={(e) => setmainText(e.target.value)} />
{extraElements &&
<div className='transition-all ease-in-out duration-150 delay-150 inline-flex gap-3 min-w-full'>
<input className='rounded-md p-1 grow focus:outline-none ' type="email" name="sender" id="senderText" placeholder='Your email' />
<button className='rounded-md p-1 bg-blue-200'>Send</button>
</div>
}
</div>
const handleClickInside = () => {
setExtraElements(true)
setRows(10)
setColumns(100)
props.blurBackground(true)
}

Since you're indirectly changing the textarea height/width by changing the rows property, the CSS transitions don't apply. Instead, you need to explicitly set the height of the text area.
For example, you can use CSS inline styles:
<textarea
className='transition ease-in-out delay-150 resize-none rounded-md p-1 focus:outline-none'
value={mainText}
placeholder={"Ask me something"}
style={{ height: `${rows * 10}px`, width: `${columns * 10}px` }}
onChange={(e) => setmainText(e.target.value)} />

As an alternative to framer-motion you can use the Transition component from the #headlessui/react package. It works perfectly with the tailwindcss classes for both entry and exit animations.

Related

Template does'nt work with Tailwind in React

If I put the color directly in the tailwind class like "bg-cianTheme" it works. But if i use javascript template like "bg-${primaryColorTheme}" with the code above doesn't work. Why does it happen and how fix ?
The code:
import { BsGithub, BsLinkedin } from 'react-icons/bs'
export interface INewProfileCardProps {
photo: string
github: string
linkedin: string
colors: string[]
primaryColorTheme: string // tailwind refence collor
secondaryColorTheme: String //tailwind reference collor
terciaryColorTheme: string //tailwind reference collor
labelName: string
}
export function NewProfileCard({
photo,
github,
linkedin,
colors,
labelName,
}: INewProfileCardProps) {
const [primaryColorTheme, secondaryColorTheme, terciaryColorTheme] = colors
console.log(`bg-${primaryColorTheme}`)
return (
<div className="flex items-center justify-center">
<div>{/* <p className="text-[250px]">{'<'}</p> */}</div>
<div className=" relative h-[202px] w-[404px] rounded-xl bg-whiteIcon px-4 py-8 pt-2">
<div
className={`bg-${primaryColorTheme} relative flex h-full w-full`}
>
<img
className="absolute bottom-0"
src={photo}
alt="personal-photo"
></img>
</div>
<div className="absolute flex">
<div
className={`flex items-center justify-center font-bold text-${primaryColorTheme} bg-${primaryColorTheme} rounded-l-xl px-6 py-2`}
>
<BsGithub className="text-2xl" />
<p className="ml-3 text-lg">Git Hub</p>
</div>
<div
className={` bg-${secondaryColorTheme} ${primaryColorTheme} flex items-center justify-center rounded-r-xl px-6 py-2 font-bold`}
>
<BsLinkedin className="text-2xl" />
<p className="ml-3 text-lg">Linkedin</p>
</div>
</div>
</div>
<div>
{/* <p className="text-[250px]">
<span className={`text-${primaryColorTheme}`}>/</span>
{'>'}
</p> */}
</div>
</div>
)
}
I try to change the colors with variables in tailwind class with template, but doesn't work.
The problem is the postcss can't find that class when you have interpolated strings like that. You can add this to tailwind.config.js
safelist: ["bg-somecolor"]
or you can find a way to not use interpolation
In addition to pguardiano's answer, you may be able to achieve the same thing by calling the style attribute directly since postCSS only renders what is readily available in the template...
In your case, you can send the hex code directly to the variable and pass it to the style attribute. Example:
<div
className="relative flex h-full w-full"
style="{{ background: primaryColorThemeColor }}"
>
<img
className="absolute bottom-0"
src={photo}
alt="personal-photo"
/>
</div>
If you want to get your hands dirty and do things in a more interesting way, you can take advantage of Tailwind's theming functionality.
It is explained well in this video: https://www.youtube.com/watch?v=MAtaT8BZEAo.
Then you can update your CSS variables with whatever color is coming from your API I guess. Goodluck hacking your way in :)
Ref:
Updating CSS Variables with Javascript: https://css-tricks.com/updating-a-css-variable-with-javascript/

Incorrect component display when passing props and using Tailwind

I pass the color data to the created component to set a specific color in several places. At the output, it turns out that it is installed only in one place. What could it be? In the project I use only tailwind.
<div className="w-full flex justify-between items-center gap-5">
<FilterButtons dataFilter={""} flag="Artists" color="yellowColor" />
<FilterButtons dataFilter={""} flag="Albums" color="orangeColor" />
</div>
<div className="w-full flex justify-between items-center gap-5">
<FilterButtons dataFilter={filterByCategory} flag="Category" color="orangeColor" />
<FilterButtons dataFilter={filterByLanguage} flag="Language" color="yellowColor" />
</div>
Broadcast props
const FilterButtons = ({ dataFilter, flag, color }) => {
return (
<div className={`w-1/2 flex items-center justify-between px-4 py-2 border border-${color} text-${color} rounded-xl cursor-pointer`}>
<p className={`tracking-1 text-lg`}>{flag}</p>
<MdOutlineKeyboardArrowDown className={`text-4xl`} />
</div>
)
}
Usage in the component
The first block displays a field with a yellow border with black text and with orange texts and a white border
Result
I tried to divide into different variables and set conditions with different flags, nothing changes
You might be having some css specificity issue which is very common with tailwind. But tailwind has a workaround for this.
Use exclamation on the class you want to force a higher specificity for. Example (in your case):
!text-${color}

How to transition div using Tailwindcss in React

I have a Menu panel next to my sider. I am trying to add a drawer type animation however I can't seem position open/close within the pink column
const album = ["Album1", "Album2", "Album3"];
export const Menu = () => {
const [open, setOpen] = useState(false);
return (
<div className="flex flex-1 flex-col p-3">
<h2 className="text-lg font-medium text-gray-900">Album</h2>
<div className="flex-1">
<div className="flex flex-1 flex-col space-y-3 py-3">
{album.map((name) => (
<button
key={name}
id={name}
className={`flex space-x-2 items-center`}
onClick={() => setOpen(true)}
>
<span>{name}</span>
</button>
))}
</div>
</div>
<aside
onClick={() => setOpen(false)} //temporary
className={`transform top-0 left-0 w-72 bg-blue-400 fixed h-full overflow-auto ease-in-out transition-all duration-1000 z-30 ${
open ? "translate-x-14" : "-translate-x-full"
}`}
>
hello
</aside>
</div>
);
};
Javascript. Just add a eventListener to the 'button' which will handle the actions. In the eventListener, remove or add a css class. For example, by default the position is '-100px right' with css set position to '300px right', also add a transition 'all 300ms ease'. Now you element start with no css, and when user press button add the class.
<div class="text-3xl font-bold underline transition-all duration-300" id="must-change">
Lorem Ipsu
</div>
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-3 rounded" id="press-me">
Button
</button>
<script>
const btn = document.querySelector("#press-me")
const mustChange = document.querySelector("#must-change")
btn.addEventListener("click", () => {
mustChange.classList.toggle("font-bold")
})
</script>
I set 'transition all' but you can use a specific transition if you want.

Automatic splitting of long Tailwind strings in VSCode

I'm looking for a neat way to split this Mississippi River:
<input
value={props.profile.get('email') ?? ''}
placeholder="Email Address"
className="z-10 flex-shrink-0 w-12 h-12 px-3 py-1 rounded-lg border border-gray-300 placeholder-gray-300 focus:outline-none focus:ring-[5px] focus:ring-indigo-100 focus:border focus:border-indigo-400 hover:outline-none hover:ring-[5px] hover:ring-indigo-100 hover:border hover:border-indigo-200 transform-gpu transition-all duration-50"
onChange={e => props.updateProfile('email', e.currentTarget.value)}
/>
into something more digestible when scrolling through big repos.
Currently I'm using this approach for splitting strings:
<input
value={props.profile.get('email') ?? ''}
placeholder="Email Address"
className={classNames(
"z-10 block w-full h-12 px-3 py-1 rounded-lg border border-gray-300 placeholder-gray-300",
"focus:outline-none focus:ring-[5px] focus:ring-indigo-100 focus:border focus:border-indigo-400",
"hover:outline-none hover:ring-[5px] hover:ring-indigo-100 hover:border hover:border-indigo-200",
"transform-gpu transition-all duration-50",
)}
onChange={e => props.updateProfile('email', e.currentTarget.value)}
/>
But there should be more convenient way, so how do you handle long rules, fellow Tailwinders?
The only way out of this is using twin.macro, but even then the long rules will be pretty much the same.
I'd bundle these into logical groups if I were you and put them in a common file:
styles.js:
const form = 'block h-12 px-3 py-1 focus:outline-none focus: ....';
const button = 'rounded-lg border border-gray-300';
Your file then becomes:
import { form, button, input, grey } from 'styles';
...
classNames(`${form} ${button} ${input} ${grey}`)

React: trigger onChange on checkbox input when changing checked state programmatically?

I have a ref to an <input type="checkbox"/> element, and when I programmatically set checked=false on the element, the element's onChange callback does not get called.
I tried using ref.dispatchEvent(new Event('input')) and ref.dispatchEvent(new Event('change')) and neither caused the React onChange callback to get executed.
All the questions and answers I could find on StackOverflow about this have to do with <input type="text"/> elements, none dealing with changing the checked property programmatically on an <input type="checkbox"/> element and its onChange handler not being invoked.
Here's a CodePen that demonstrates the issue:
https://codepen.io/dossy/pen/QWKVNzZ/left/?editors=0011
You can check and uncheck the checkbox, and the <div>Checked!</div> will appear and disappear as expected. However, clicking the <button>Reset</button> will uncheck the checkbox if it's checked, but since the input's onChange handler isn't being executed, the div isn't being hidden as it should be.
...
Yes, I know that I could do this as a Controlled Component but that's not the point: I have a use case where using refs is required so I must implement this as an Uncontrolled Component, and getting the onChange handler to execute when the DOM element changes is the problem I need to solve.
Thanks!
Here is the working code. working link https://codesandbox.io/s/blissful-wozniak-cc1sn?file=/src/Test.js:0-1753
import React, { useEffect, useRef } from "react";
export function Test() {
const ref_input = useRef(null);
const ref_text = useRef(null);
useEffect(() => {
ref_input.current.addEventListener("change", function (event) {
alert(event.target.checked);
});
}, []);
function triggerEvent(element, eventName) {
var event = document.createEvent("HTMLEvents");
event.initEvent(eventName, false, true);
element.dispatchEvent(event);
}
return (
<div className="h-screen flex bg-white text-gray-900 justify-center items-center">
<div className="flex items-start w-64">
<div className="flex items-center gap-4">
<button
className="inline-flex items-center px-4 py-2 border border-gray-500 rounded-md"
onClick={() => {
ref_input.current.checked = false;
triggerEvent(ref_input.current, "change");
//ref_input.dispatchEvent(new Event("input"));
//ref_input.current.dispatchEvent(new Event("onChange"));
}}
>
Reset
</button>
<div>Checkbox:</div>
<input
ref={ref_input}
type="checkbox"
className="h-4 w-4 border-gray-300 rounded"
// onChange={(e) => {
// console.log("onChange called", e.target.checked);
// e.target.checked
// ? ref_text.current.classList.remove("hidden")
// : ref_text.current.classList.add("hidden");
// }}
/>
<div ref={ref_text} className="hidden">
Checked!
</div>
</div>
</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
It's better to do things the "react way".
That means, instead of manipulating dom elements with imperative code (if you're using refs, you're using imperative code), do it declaratively with state/props:
function App() {
const [checked,setChecked] = React.useState(false);
return (
<div className="h-screen flex bg-white text-gray-900 justify-center items-center">
<div className="flex items-start w-64">
<div className="flex items-center gap-4">
<button
className="inline-flex items-center px-4 py-2 border border-gray-500 rounded-md"
onClick={() => setChecked(false)}
>
Reset
</button>
<div>Checkbox:</div>
<input
type="checkbox"
checked={checked}
onChange={() => setChecked(!checked)}
className="h-4 w-4 border-gray-300 rounded"
/>
<div className={checked ? '' : 'hidden'}>
Checked!
</div>
</div>
</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
Here's a link to the updated pen: https://codepen.io/zuze-lab/pen/QWKVEbY?editors=0011
EDIT: I want to be really clear, ref's aren't bad, not at all. Lots of react libraries expose refs because imperative code makes sense for those libraries APIs. When using refs to add event listeners, or imperatively manipulate elements, you're doing things wrong and you need to back up.

Categories