2

I am new to Qwik and trying to conditionally add/remove classes from items in my navbar component. The complication is that I'm using tailwind and I want to conditionally add/remove the tailwind class (using custom animations), but it seems to have no effect. I have read about doing something like this in React using clsx, but this doesn't appear to be available in Qwik. Is there any way to dynamically add the tailwind class or how else can I achieve the same result?

Component

import { $, component$, useStore } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';

export const Navigation = component$(() => {

  const baseClass = "py-2 bg-white text-black w-[100px]";

  const menuItems = useStore([
    { label: "Home", value: "/", class: baseClass },
    { label: "About", value: "/about", class: baseClass },
    { label: "Contact Us", value: "/contact", class: baseClass },
  ]);

  const fadeout = $((num: number) => {
    menuItems.forEach((ele, i) => {
      if(i < num && !ele.class.includes("animate-fadeout")) {
      ele.class = baseClass + " animate-fadeout";
      }
    })
  });

  const fadein = $((num: number) => {
    menuItems.forEach((ele, i) => {
      if(i < num && ele.class.includes("animate-fadeout")) {
      ele.class = baseClass + " animate-fadein";
      }
    })
  });

  return (
    <div class="flex flex-col space-y-2 py-2 bg-slate-500">
      {menuItems.map((item, index) => (
        <Link className={item.class} key={index} onMouseOver$={() => fadeout(index)} onMouseOut$={() => fadein(index)} href={item.value}>{item.label}</Link>
      ))}
    </div>
  )
});

tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: {
    extend: {
      keyframes: {
        fadein: {
          '0%': { transform: 'translateY(-175%)', opacity: '0'},
          '10%': { transform: 'translateY(-150%)', opacity: '1'},
          '50%': { transform: 'translateY(0)'}
        },
        fadeout: {
          '50%': { transform: 'translateY(-175%)', opacity: '0'},
          '40%': { transform: 'translateY(-150%)', opacity: '1'},
          '0%': { transform: 'translateY(0)'}
        }
      },
      animation: {
        fadeout: 'fadeout 1s ease-in-out 1',
        fadein: 'fadein 1s ease-in-out 1',
      }
    },
  },
  plugins: [],
};
Calvin P.
  • 1,116
  • 2
  • 8
  • 13

2 Answers2

1

You're missing {deep: true} as the options on your call to useStore. This will make the signaling work.

Also take a look at qwik-transition if you want to do transitions like this.

Furthermore, can't you use :hover to achieve what you want?

Finally, I would make a MenuItem component$ instead of composing everything in Navigation.

w00t
  • 17,944
  • 8
  • 54
  • 62
  • Thanks for the advice! I'll have to try it out when I have more time. I can't use `:hover` because what I'm trying to achieve is when a menu item is hovered, I want the items above it to fade in/out and not the hovered item. I hadn't come across anything about qwik-transition before, I'll be sure to check that out. – Calvin P. Mar 14 '23 at 15:46
  • @calvinp Maybe you can use sibling selectors to achieve what you want? – w00t Mar 15 '23 at 16:35
1

I would recommend using classlist:

import { component$, useStore } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';

type ItemMode = 'fadein' | 'fadeout';

export const Navigation = component$(() => {
  const menuItems = useStore<
    { label: string; value: string; mode: ItemMode }[]
  >(
    [
      { label: 'Home', value: '/', mode: 'fadein' },
      { label: 'About', value: '/about', mode: 'fadein' },
      { label: 'Contact Us', value: '/contact', mode: 'fadein' }
    ],
    { deep: true }
  );

  return (
    <div class="flex flex-col space-y-2 py-2 bg-slate-500">
      {menuItems.map((item, index) => (
        <Link
          class={[
            'py-2 bg-white text-black w-[100px]',
            {
              'animate-fadeout': item.mode === 'fadeout',
              'animate-fadein': item.mode === 'fadein'
            }
          ]}
          key={index}
          onMouseOver$={() => {
            menuItems[index].mode = 'fadeout';
          }}
          onMouseOut$={() => {
            menuItems[index].mode = 'fadein';
          }}
          href={item.value}
        >
          {item.label}
        </Link>
      ))}
    </div>
  );
});

Tob
  • 627
  • 6
  • 9