React Router V6 activeStyle issues - react-router

So my issue is that i have read the documentation and it sais that my button should be like this:
<NavLink
style={({ isActive }) =>
isActive ? activeStyle : undefined
}
to="overview"
className="group flex items-center px-2 py-2 text-base font-medium rounded-md"
>
Overview
</NavLink>
And that I should add:
let activeStyle = {
textDecoration: "underline",
};
let activeClassName = "underline";
but the issue is that I'm using Tailwind, and I have no idea on how to change the "let activeStyle". I cant add
let activeStyle = {
textDecoration: "bg-gray-900 text-white",
};
let activeClassName = "underline";
which is what is intended.. can anyone please help with this?.

I believe Tailwind works with CSS classes if I'm not mistaken. The logic should be applied to the className prop.
Example:
<NavLink
to="overview"
className={({ isActive }) =>
[
"group flex items-center px-2 py-2 text-base font-medium rounded-md",
isActive ? "bg-gray-900 text-white" : null,
]
.filter(Boolean)
.join(" ");
}
>
Overview
</NavLink>

Try doing it like this, should work.
<Navlink to="/overview" className={(navData)=> navData.isActive ? "bg-gray-900 text-white" : null }> <a className="group flex items-center px-2 py-2 text-base font-medium rounded-md">Overview</a></Navlink>

Navlink provides activeClassName props in which you can put your classes
Also use exact keyword so it will get active when url will match
Your code can be written as below:

using react-router 6, className (or style) props can take a function as argument.
Quoting the docs example:
https://reactrouter.com/en/6.8.1/components/nav-link
Using style
<NavLink
to="messages"
style={({ isActive }) =>
isActive ? activeStyle : undefined
}
>
Messages
</NavLink>
Notice the ({ isActive }) syntax (we are destructuring the argument)
Using className
<NavLink
to="tasks"
className={({ isActive }) =>
isActive ? activeClassName : undefined
}
>
Tasks
</NavLink>
Or using a function to render the component
<NavLink to="tasks">
{({ isActive }) => (
<span
className={
isActive ? activeClassName : undefined
}
>
Tasks
</span>
)}
</NavLink>
To answer the original question, let's say we want to apply the tailwind class "bg-red-400 underline" when the element is active and "bg-neutral-400" when it is inactive and having some permanent styles, we could do (using clsx which is a utility to combine class names):
<NavLink
to="overview"
className={({ isActive }) => clsx(
"group flex items-center px-2 py-2 text-base font-medium rounded-md",
isActive ? "bg-red-400 underline" : "bg-neutral-400"
)}
>
Overview
</NavLink>

Related

Buttons stop working when I add new div element

I am using sveltekit, typscript, and tailwind for this. Ok, I have a website that I am making and I have buttons that have no background and are formated like this:
When opened
- Label
stuff
when closed
+ Label
It worked and all but when I added a new div to be right next to those buttons the buttons stopped working completely. It would not even show the pointer hand.
Code for buttons:
<script lang="ts">
let expanded: boolean = true;
export let item: string = '';
export let value: string[] = [];
import Page from '../Components/page.svelte';
let pagen: string = 'About';
</script>
<Page bind:page={pagen} run="" />
<div class="dropdown flex flex-col mt-5">
<button
class="dropdown-toggle"
on:click={() => {
expanded = !expanded;
}}
>
<span class="dropdown-label text-gray-400 ml-5 flex text-lg"
>{expanded ? `- ${item}` : `+ ${item}`}</span
>
</button>
<div class="dropdown-content flex" style:display={expanded ? 'block' : 'none'}>
{#each value as val}
<div class="dropdown-item text-gray-400 ml-10">
<p class="inline-block text-gray-400">#</p>
<button
on:click={() => {
pagen = `{val}`;
}}>{val}</button
>
</div>
{/each}
</div>
</div>
The pagen that is binded is for the div that goes right next to it. (this is not important I think...)
Code for the page:
<script lang="ts">
export let page: string = 'About';
export let run: string = '';
</script>
{#if run == 'true'}
<div class="w-screen h-screen -z-10">
<div class="page ml-80 bg-gray-800 h-screen">
{#if page == 'About'}
<div class="flex flex-col items-center justify-center">
<h1 class="text-gray-400 text-3xl font-bold tracking-wider mt-
10">Some Title</h1>
<p class="text-gray-400 text-2xl font-bold tracking-wider">Some
Label</p>
</div>
{/if}
</div>
</div>
{/if}
These are both components in svelte and are imported in index.svelte. Buttons on top and page on bottom.
Helpful Images:
The website is being styled to look like discord.
Any help is greatly appreciated!
There are a few things I'm noticing that may help you get this working:
Nothing may be showing because values may be empty. When I added values = [ "a", "b", "c" ] I saw them no problem (see repl).
You're not setting run, so Page will never render. Also, using run as a string is weird since it appears it is a boolean?
If you're using Tailwind, instead use conditional classes:
<div class="dropdown-content flex" style:display={expanded ? 'block' : 'none'}>
<!-- should be: -->
<div class="dropdown-content flex" style:block={expanded} style:hidden={!expanded}>
Stylistic recommendation:
<span class="dropdown-label text-gray-400 ml-5 flex text-lg">
{expanded ? `- ${item}` : `+ ${item}`}
</span>
<!-- simpler: -->
<span class="dropdown-label text-gray-400 ml-5 flex text-lg">
{expanded ? "-" : "+"} {item}
</span>
Here is a repl with what I believe is working code.

How to return simple Flash message with Inertia React & Laravel?

I'm trying to return a Flash message for a user in my app when the complete a form and are redirected back to the 'dashboard'.
I'm fairly new to Inertia so maybe its me but the docs don't seem to be too in depth, I wondered how to correctly call this Flash function and fetch it on the frontend side in the Dashboard.js file.
This is my controller and Dashboard file code:
public function completeForm(Request $request){
$request->validate([
'commitment' => 'required|string',
'partner' => 'required|string',
'question' => 'required|string',
]);
$user = Auth::user();
$user->update([
'partner' => $request->partner,
'commitment' => $request->commitment,
'question' => $request->question,
]);
return redirect('/dashboard')->with('success', 'Your profile has been updated!');
}
And Dashboard:
export default function Dashboard(props) {
const [modalVisible, setModalVisible] = useState(false);
console.log(props);
const handleModal = () => {
setModalVisible(true);
};
return (
<Empty
auth={props.auth}
errors={props.errors}
header={
<h2 className="font-semibold text-xl text-gray-800 leading-tight">
Dashboard
</h2>
}
>
<Head title="Dashboard" />
<div className="py-12">
<div className="max-w-3xl mx-auto sm:px-6 lg:px-8">
{/* <div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div className="p-6 bg-white border-b border-gray-200">
You're logged in!
</div>
</div> */}
<div className="bg-yellow-500 px-1 py-1 inline-block ">
<p className="text-6xl font-bold uppercase text-white custom">
Your Details
</p>
</div>
{/* {flash.message && <div class="alert">{flash.message}</div>} */}
<div className="mt-16 border-2 border-yellow-500 p-2">
<p className="text-xl font-bold italic">
Next, we'd need you to submit your questions to us
for review
</p>
<div className="flex flex-col">
<p className="italic text-gray-400 text-lg font-medium mt-2">
Please use the button below to start filling out
your form:
</p>
<a
href="/commitment-questions"
className="text-center bg-yellow-500 py-2 px-3 self-center mt-8 text-2xl custom text-white font-bold mb-4"
>
Complete Form
</a>
</div>
</div>
</div>
</div>
</Empty>
);
}
I know its probably straightforward, thanks in advance for any help on this one!
Share a flash message to be globally available, then make use of it where needed in your React components: https://inertiajs.com/shared-data#flash-messages
class HandleInertiaRequests extends Middleware
{
public function share(Request $request)
{
return array_merge(parent::share($request), [
'flash' => [
// in your case, you named your flash message "success"
'message' => fn () => $request->session()->get('message')
],
]);
}
}
Then use it in any React component:
import { usePage } from '#inertiajs/inertia-react'
export default function Dashboard() {
const { flash } = usePage().props
return (
<main>
<div>
{flash.message && (
<div class="alert">{flash.message}</div>
)}
</div>
</main>
)
}

How to group clases in CSS - Tailwind

I am trying to group clases so the code will be cleaner and legible. In the documentation of Tailwind it talks about "#apply", that can be used for this objective but I am using the CDN and therefore this is not working for me. So my question is, ¿Is there any form I can accomplish what I am looking for? Maybe by using SASS/SCSS or LESS?
Here is an example of what I wnat:
<ul class="md:flex md:items-center z-[-1] md:z-auto md:static absolute bg-gray-800 w-full left-0 md:w-auto md:py-0 py-4 md:pr-0 pr-7 md:pl-0 pl-7 md:opacity-100 opacity-0 top-[-400px] transition-all ease-in duration-200">
<li class="nav-element">
Home
</li>
<li class="px-4 py-6 md:py-0 hover:bg-yellow-500 md:hover:bg-transparent text-white duration-500">
About Us
</li>
<li class="px-4 py-6 md:py-0 hover:bg-yellow-500 md:hover:bg-transparent text-white duration-500">
Services
</li>
<li class="px-4 py-6 md:py-0 hover:bg-yellow-500 md:hover:bg-transparent text-white duration-500">
Contact Us
</li>
<button class="md:w-auto w-full bg-transparent text-white font-[Poppins] duration-500 px-6 py-2 hover:bg-white hover:text-gray-800 border border-white border-dotted rounded-lg">
Log In
</button>
<button class="md:w-auto w-full bg-yellow-500 text-white font-[Poppins] duration-500 px-6 py-2 md:mx-4 hover:bg-yellow-600 rounded-lg">
Sign In
</button>
</ul>
<ul class="nav-elemnts">
<li class="nav-element">
Home
</li>
<li class="nav-element">
About Us
</li>
<li class="nav-element">
Services
</li>
<li class="nav-element">
Contact Us
</li>
<button class="button-login">
Log In
</button>
<button class="button-signin">
Sign In
</button>
</ul>
Tailwind encourages you to use components. Instead of copy pasting the classes all over the place, you should use a system that allows you to create and use components.
Since your question is HTML + CSS only, you don't really have the right tools for this. But if you were using a scripting language like JS, Python, PHP etc., you could create components from elements and reuse them. Since I am familiar with React framework, I can show an example of that:
function NavElement(props) {
return (
<li class="px-4 py-6 md:py-0 hover:bg-yellow-500 md:hover:bg-transparent text-white duration-500">
{props.children}
</li>
)
}
and then use it as
function NavElements() {
return (
<ul class="md:flex md:items-center z-[-1] md:z-auto md:static absolute bg-gray-800 w-full left-0 md:w-auto md:py-0 py-4 md:pr-0 pr-7 md:pl-0 pl-7 md:opacity-100 opacity-0 top-[-400px] transition-all ease-in duration-200">
<NavElement href="/">Home</NavElement>
<NavElement href="/services">Services</NavElement>
<NavElement href="/about-us">About us</NavElement>
</ul>
)
}
As you can see, with this approach, you extract the huge list of modifiers into a small component that you can use multiple times without much repetition in the code.
You are free to choose any tool, language, system that will enable making components. That is what Tailwind kind of expects you to do.
I know you asked for SASS/LESS type approaches, but I think that adds additional complexity that you may not need. I think some simple JS would be a good candidate solution.
The way I've done it is to sort of replace CSS or StyledComponents with a JSON object of class names and a utility function to turn these into one big string.
First, the utility. Put this somewhere shared:
// turns a JSON object's values into a single string (keys are irrelevant)
export const classify = (classes) => Object.values(classes).join(' ')
Then in a file like styles.js that's next to the index.js, I'd have:
import { classify } from 'shared/utils'
export const nav = classify({
base: 'absolute bg-gray-800 w-full left-0 pr-7 pl-7 py-4 opacity-0 top-[-400px] z-[-1]',
animation: 'transition-all ease-in duration-200',
larger: 'md:flex md:items-center md:z-auto md:static md:w-auto md:py-0 md:pr-0 md:pl-0 md:opacity-100'
})
export const navItem = classify({
base: 'px-4 py-6 hover:bg-yellow-500 text-white',
resp: 'md:py-0 md:hover:bg-transparent',
anim: 'duration-500'
})
This is just like your CSS file. Yes, you have to name the variables, but you'd also have to name your components doing it the other way and this keeps things way less littered with files. You also get the benefit of seeing your HTML structure rather than a bunch of component names (which are more than likely just divs, smh). Also, you can have any number of key/values in your JSON, however you see fit to organize for reduced cognitive load while maintaining.
Then, the HTML/JSX:
import * as styles from './styles'
export default Component = (props) => (
<ul className={styles.nav}>
<li className={styles.navItem}> ... </li>
<li className={styles.navItem}> ... </li>
<li className={styles.navItem}> ... </li>
</ul>
)
The thing I like about this approach is that it's very similar to the CSS workflow and the JSON objects allow you to organize your class names in any way you want (ideally not doing it nested or you'll need a more complex classify()). Using components, you still have big long strings to mess with which is annoying when you have tens of classes applied. You can even do these in the same file if you'd like, it's just JavaScript.
You can even create utility styles/classes this way and concat them with template strings:
import * as utilStyles from 'utils/styles'
import * as styles from './styles'
<section className={`${utilStyles.shadowPanel} ${styles.mainSection}`>
...
</section>
have you tried doing:
<style type="text/tailwindcss">
#layer components {
.some-class {
#apply px-4 py-6 md:py-0 hover:bg-yellow-500 md:hover:bg-transparent text-white duration-500;
}
}
</style>
You should add 'group' class to parent and after work with group subclasses:
<div class="group p-4">
<p class="group-hover:bg-red-400">lorem ipsum</p>
</div>
after this code if you hover on div element, p elements backgroundColor will be change to red.

react-router-dom v6 NavLink is always active

I followed Upgrading from v5 guide and I cannot get the NavLink component to work correctly.
https://reactrouter.com/docs/en/v6/upgrading/v5#upgrading-from-v5
v6 Navlinks:
<NavLink
className={(isActive) =>
cx(isActive ? classes.linkActive : classes.link)
}
to="/seafarers"
end
>
Seafarers
</NavLink>
<NavLink
className={(isActive) =>
cx(isActive ? classes.linkActive : classes.link)
}
end
to="/"
>
Planning
</NavLink>
Routes
<BrowserRouter>
<Routes>
<Route path="/" element={<LoginScreen />} />
<Route path="login" element={<LoginScreen />} />
<Route path="forgot-password" element={<ForgotPasswordScreen />} />
<Route path="seafarers" element={<SeafarersScreen />} />
</Routes>
</BrowserRouter>
Both "/" and "/seafarers" have active class
Note: NavLink elements are located in SeafarersScreen screen
How can I correct this issue?
Turns out I had to deconstruct the property of className as ternary operator always returned true for objects
<NavLink
className={({isActive}) => //(isActive) --> ({isActive})
cx(isActive ? classes.linkActive : classes.link)
}
to="/seafarers"
end
>
Seafarers
</NavLink>
For react-router-dom v6
This example demonstrates how to make a custom <Link> component to render something different when the link is "active" using the useMatch() and useResolvedPath() hooks.
Official doc for active link
Add end prop on parent route. This work for react-router-dom: 6.4.4.
<NavLink
to="/"
end
className={({ isActive }) =>`nav-link ${isActive && 'active'}`}>
Notes
</NavLink>
From react-router documentation, here is the link: https://reactrouter.com/en/main/components/nav-link
<NavLink
to="/"
style={({ isActive }) => ({ color: isActive ? "green" : "blue" })}
className={yourClasses}
>
Profile
</NavLink>

When I try to change colour depends on the count it show's the error TypeError: Cannot read property 'style' of null

In the below I would like to change the colour to black if the count is === 0
if(Count === 0){
document.getElementById('value').style.color = 'black';
}
return(
<div className='container_new'>
<h1>Counter</h1>
<span id='value'>{Count}</span>
<div>
<button className="btn decrease" onClick={handleDecrement} >decrease</button>
<button className="btn reset" style={{color: 'black'}} onClick={() => handleReset()}>reset</button>
<button className="btn increase" onClick={() => handleIncrement()}>increase</button>
</div>
</div>
)
You can achieve what you wanted this way.
return(
<div className='container_new'>
<h1>Counter</h1>
<span id='value' style={{ color: Count === 0 ? 'black' : 'white' }}>{Count}</span>
<div>
<button className="btn decrease" onClick={handleDecrement} >decrease</button>
<button className="btn reset" style={{color: 'black'}} onClick={() => handleReset()}>reset</button>
<button className="btn increase" onClick={() => handleIncrement()}>increase</button>
</div>
</div>
)
in react.js i would approach this differently because it render and change the DOM (html tree)
so maybe instead of using
**document.getElementById('value').style.color = 'black';**
make a class in CSS and change the className based on the Count
like this
css class
.black {
color : black;
}
.green {
color : green;
}
react.js code
<span id='value' className={Count === 0 ? "black" : "green" } >{Count}</span> // tell me if this line is understandable
replace this line with your line and see if is working !
it would help me understand better if you show us the the handle increment function handle reset and handle decrement :)
Create a local state for the count and track the update. Based on the count value in the state, update the template style.
const [count, setCount] = useState(0);
return(
<div className='container_new'>
<h1>Counter</h1>
<span id='value'>{count}</span>
<div>
<button className="btn decrease" onClick={handleDecrement} >decrease</button>
<button className="btn reset" style={{color: (count === 0 ? 'black': 'green')}}
onClick={() => setCount(0)}>reset</button>
<button className="btn increase" onClick={() => handleIncrement()}>increase</button>
</div>
</div>
I believe, your react state is state = { Count: 0 }, If yes, when react starts initial rendering, you're trying to get the element by id, at this time, there no element(not rendered) with <span id='value'>{Count}</span> which returns null. That's why you're getting this error Cannot read property 'style' of null.
But, you can simply use following solution
<span id="value" style={{ color: Count === 0 ? 'black' : 'green' }}>{Count}</span>