Multiselect deselect default values in child component - javascript

I'm trying to use this multi-select component as a child component in a Edit.vue component (parent)
Parent component
<template>
<Multiselect v-model="form.user_id" :options="users"></Multiselect>
</template>
<script setup>
const props = defineProps({
users: Array,
activity: Object
});
const form = useForm({
user_id: props.activity.users
});
</script>
Child component
<template>
<Listbox v-model="selected" name="people">
<ListboxLabel class="block text-sm font-medium leading-5 text-gray-700"> Assigned to </ListboxLabel>
<div class="relative">
<span class="inline-block w-full rounded-md shadow-sm">
<ListboxButton
class="focus:shadow-outline-blue relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-2 pr-10 text-left transition duration-150 ease-in-out focus:border-blue-300 focus:outline-none sm:text-sm sm:leading-5">
<span class="block flex flex-wrap gap-2">
<span v-if="selected.length === 0" class="p-0.5">Empty</span>
<span v-for="option in selected" :key="option.id" class="flex items-center gap-1 rounded bg-blue-50 px-2 py-0.5">
<span>{{ option.name }}</span>
<svg class="h-4 w-4 cursor-pointer" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" #click="removePerson(option)">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</span>
</span>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<svg class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="none" stroke="currentColor">
<path d="M7 7l3-3 3 3m0 6l-3 3-3-3" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</span>
</ListboxButton>
</span>
<div class="absolute mt-1 w-full rounded-md bg-white shadow-lg">
<ListboxOptions class="shadow-xs max-h-60 overflow-auto rounded-md py-1 text-base leading-6 focus:outline-none sm:text-sm sm:leading-5">
<ListboxOption v-for="option in options" :key="option.id" :value="option" as="template" v-slot="{ active, selected }">
<li class="relative cursor-default select-none py-2 pl-3 pr-9 focus:outline-none" :class="active ? 'bg-indigo-600 text-white' : 'text-gray-900'">
<span class="block truncate" :class="{ 'font-semibold': selected, 'font-normal': !selected }">
{{ option.name }}
</span>
<span v-if="selected" class="absolute inset-y-0 right-0 flex items-center pr-4" :class="{ 'text-white': active, 'text-indigo-600': !active }">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd" />
</svg>
</span>
</li>
</ListboxOption>
</ListboxOptions>
</div>
</div>
</Listbox>
</template>
<script setup>
import { ref } from 'vue';
import { Listbox, ListboxLabel, ListboxButton, ListboxOptions, ListboxOption } from '#headlessui/vue';
import {computed} from "vue";
const props = defineProps({
options: Array,
modelValue: Array
});
//
const emit = defineEmits(['update:modelValue'])
const selectedOption = ref(props.modelValue);
const selected = computed({
get: () => selectedOption.value,
set: v => {
selectedOption.value = v;
emit('update:modelValue', v.id)
}
});
//
function classNames(...classes) {
return classes.filter(Boolean).join(' ');
}
function removePerson() {
e.stopPropagation();
e.preventDefault();
selectedOption.value = selectedOption.value.filter(p => p !== option);
}
</script>
I want the options in the props.activity.user selected by default in the child component. The values gets passed but when I deselect an option it gets added instead. And non-default values gets deselected
e.g.
The user Emi Webb gets added instead of deselected 'cause it's a default value
I'm stick trying to get this to work

Related

Unable to use Javascript functionality in tailwind css with vite in reactjs

I am working on a react project using vite and for css I am using tailwind css. From the component section of tailwind css website I have copied template, and I am unable to get responsive functionality and show/hide menu on click
below is my header code
<nav class="bg-gray-800">
<div class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
<div class="relative flex h-16 items-center justify-between">
<div class="absolute inset-y-0 left-0 flex items-center sm:hidden">
<button type="button" class="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" aria-controls="mobile-menu" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
<svg class="hidden h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
<div class="flex flex-shrink-0 items-center">
<img class="block h-8 w-auto lg:hidden" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company" />
<img class="hidden h-8 w-auto lg:block" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company"/>
</div>
<div class="hidden sm:ml-6 sm:block">
<div class="flex space-x-4">
Dashboard
Team
Projects
Calendar
</div>
</div>
</div>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
<button type="button" class="rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
<span class="sr-only">View notifications</span>
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
</svg>
</button>
<div class="relative ml-3">
<div>
<button type="button" class="flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" id="user-menu-button" aria-expanded="false" aria-haspopup="true">
<span class="sr-only">Open user menu</span>
<img class="h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
</button>
</div>
<div class="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
Your Profile
Settings
Sign out
</div>
</div>
</div>
</div>
</div>
<div class="sm:hidden" id="mobile-menu">
<div class="space-y-1 px-2 pt-2 pb-3">
Dashboard
Team
Projects
Calendar
</div>
</div>
</nav>
everything is working fine but by running the above code I am getting only ui and not getting click function on menu bars or other
see the image below
[
let menuMovilButton = document.getElementById('menu-movil-button');
let userMenuButton = document.getElementById('user-menu-button');
let userMenu = document.getElementById('user-menu');
let menuMovil = document.getElementById('menu-movil');
menuMovilButton.addEventListener('click', () =>{
menuMovil.classList.toggle('hidden');
});
userMenuButton.addEventListener('click', () =>{
userMenu.classList.toggle('hidden');
});`

Updating tailwindCSS class attributes on button click

I'm working on a project that uses pre-made TailwindUI component code. If you refer to this gif, you can see that the code on the site is responsive to mobile design and the hamburger menu toggles on button click.
However, the code given for this does not include the necessary JS, so the toggling of the hamburger menu does not work.
I am trying to fix this, this is what i've done so far:
I've wrapped the flyout menu code in a div and gave it an id 'mobile-menu' and a state of 'hidden'. Inside this menu is the X button, which i gave an id 'menu-toggle' since i want this button and the hamburger button to toggle the flyout menu. Below is not the whole code but just the relevant parts
<div class="absolute z-30 top-0 inset-x-0 p-2 transition transform origin-top-right md:hidden">
<div class="rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 bg-white divide-y-2 divide-gray-50">
<div class="pt-5 pb-6 px-5">
<div class="flex items-center justify-between">
<div>
<img class="h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg" alt="Workflow">
</div>
<div class="-mr-2">
<button id="menu-toggle" onclick="" type="button" class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
<span class="sr-only">Close menu</span>
<!-- Heroicon name: outline/x -->
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
Outside of this div and elsewhere in the code is the hamburger menu button, which i also gave an id 'menu-toggle'
<div class="-mr-2 -my-2 md:hidden">
<button id="menu-toggle" type="button" class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500" aria-expanded="false">
<span class="sr-only">Open menu</span>
<!-- Heroicon name: outline/menu -->
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
Finally i've added a script tag to the whole .html file (the file does not contain HTML boilerplate because it is a 'partial' in a Hugo project, similar to a component in React) and that looks like this:
<script>
let menuButton = document.getElementById('menu-toggle');
menuButton.addEventListener('click', function () {
let flyout = document.getElementById('mobile-menu').classList
flyout.toggle('hidden')
flyout.toggle('block')
})
</script>
but this JS doesn't work at all. Looking for insight on how to pull this off properly. Thank you!!
I have written a little code to do the work around. Perhaps it is not the effect you want for your final result, but it is a start. The aproach here is that you can't apply a toggle function for the same button and the same element toggling diferent class, without use some css at least. Besides, there are so many code errors to explain one by one. Here I let you the code that allows to you open with burger button and close with cross button.
If you need to toggle with the same button just use the menuButtonBurger event and add flyout.classlist.toggle('visible), and remove menuButtonCross. Combined with the css I wrote you this must work.
let menuButtonBurger = document.getElementById('menu-toggle-burger');
let menuButtonCross = document.getElementById('menu-toggle-cross');
menuButtonBurger.addEventListener('click', function () {
let flyout = document.getElementById('mobile-menu');
flyout.classList.add('visible');
});
menuButtonCross.addEventListener('click', function () {
let flyout = document.getElementById('mobile-menu');
flyout.classList.remove('visible');
});
#mobile-menu {
display: none;
}
#mobile-menu.visible {
display: block;
}
<div class="mr-2 my-2 d-md-hidden">
<button id="menu-toggle-burger" type="button" class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500" aria-expanded="false">
<span class="sr-only">Open menu</span>
<!-- Heroicon name: outline/menu -->
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<div id="mobile-menu"class="absolute z-30 top-0 inset-x-0 p-2 transition transform origin-top-right d-hidden">
<div class="rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 bg-white divide-y-2 divide-gray-50">
<div class="pt-5 pb-6 px-5">
<div class="flex items-center justify-between">
<div>
<img class="h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg" alt="Workflow">
</div>
<div class="-mr-2">
<button id="menu-toggle-cross" onclick="" type="button" class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
<span class="sr-only">Close menu</span>
<!-- Heroicon name: outline/x -->
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
</div>
</div>
</div>

React sending list-select data not working with formik

I'm using formik to send form data over to my API and some of the form data are selectable dropdowns, i'm using headless ui's Listbox component to render the options
const Region = [
{ id: 'ADDISABEBA', name: 'Addis abeba' },
{ id: 'MEAKELAYTIGRAY', name: 'Meakelay tigray' },
{ id: 'MEBRAKTIGRAY', name: 'Mebrak tigray' },
]
export default function CreateSchool() {
const [selectedRegion, setSelectedRegion] = useState(Region[0])
//const [selectedType, setSelectedType] = useState(Type[0])
//const [email, setEmail] = useState([])
//const [password, setPassword] = useState([])
const [isLoading, setIsLoading] = useState(false)
let navigate = useNavigate();
const onSubmit = async (values) => {
console.log(values)
setIsLoading(true)
try {
let res = await axiosInstance.post(`schools/create/`, values)
if(res.status === 200 && res.statusText === "OK"){
navigate('/app/schools', { replace: true })
setIsLoading(false)
}
console.log(res.data);
} catch (err) {
//setEmail(err.response.data.error);
//setPassword(err.response.data.error);
setIsLoading(false)
}
};
const formik = useFormik({
initialValues: {
...
zone: selectedRegion.id,
...
},
validateOnBlur: true,
onSubmit,
validationSchema: CreateSchoolValidationSchema,
})
console.log('region', selectedRegion.id)
...
}
the Listbox component:-
<Listbox value={selectedType} onChange={setSelectedType} className="my-2 w-72">
<div className="relative mt-1">
<Listbox.Button className="relative w-72 py-2 pl-3 pr-10 text-left bg-Bgdark rounded-lg shadow-sm cursor-default focus:outline-none focus-visible:ring-2 focus-visible:ring-opacity-75 focus-visible:ring-white focus-visible:ring-offset-orange-300 focus-visible:ring-offset-2 focus-visible:border-indigo-500 sm:text-sm">
<span className="block truncate">{selectedType.name}</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<svg className="w-5 h-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
</svg>
</span>
</Listbox.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Listbox.Options className="absolute origin-top w-72 py-1 mt-1 overflow-auto text-base bg-Bgdark rounded-md shadow-lg max-h-60 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{Type.map((Schooltype) => (
<Listbox.Option
value={Schooltype}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
key={Schooltype.id}
id="Schooltype"
name="Schooltype"
className={({ active }) =>
`${active ? 'text-white bg-Btncolor' : 'text-gray-200'}
cursor-default select-none relative py-2 pl-10 pr-4`
}
>
{({ selected, active }) => (
<>
<span
className={`${selected ? 'font-medium' : 'font-normal'
} block truncate`}
>
{Schooltype.name}
</span>
{selected ? (
<span
className={`${active ? 'text-white' : 'text-white'
}
absolute inset-y-0 left-0 flex items-center pl-3`}
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
since the Listbox component saves the selected option in state to display it, I pointed to the state in formik's initialvalues, but when I see the dev tools net tab the payload being sent is always the first option and not the selected one
well since you are using formik's submitted values,
you should be using formik's builtin change handlers instead of setting an independent state(like in your example below)
the other workaround you can do is to use an effect to somehow alter formik's value(only do this if you have really no choice)
<Listbox value={selectedType} onChange={setSelectedType} className="my-2 w-72">
<div className="relative mt-1">
<Listbox.Button className="relative w-72 py-2 pl-3 pr-10 text-left bg-Bgdark rounded-lg shadow-sm cursor-default focus:outline-none focus-visible:ring-2 focus-visible:ring-opacity-75 focus-visible:ring-white focus-visible:ring-offset-orange-300 focus-visible:ring-offset-2 focus-visible:border-indigo-500 sm:text-sm">
<span className="block truncate">{selectedType.name}</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<svg className="w-5 h-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
</svg>
</span>
</Listbox.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Listbox.Options className="absolute origin-top w-72 py-1 mt-1 overflow-auto text-base bg-Bgdark rounded-md shadow-lg max-h-60 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{Type.map((Schooltype) => (
<Listbox.Option
value={Schooltype}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
key={Schooltype.id}
id="Schooltype"
name="Schooltype"
className={({ active }) =>
`${active ? 'text-white bg-Btncolor' : 'text-gray-200'}
cursor-default select-none relative py-2 pl-10 pr-4`
}
>
{({ selected, active }) => (
<>
<span
className={`${selected ? 'font-medium' : 'font-normal'
} block truncate`}
>
{Schooltype.name}
</span>
{selected ? (
<span
className={`${active ? 'text-white' : 'text-white'
}
absolute inset-y-0 left-0 flex items-center pl-3`}
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
Thanks

React Swiper: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'addClass')

I am using react typescript. When I set thumbs={{ swiper: thumbsSwiper }} on my swiper slider component it gives me an error that Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'addClass'). How can I be able to solve it? if I use the below code in the js file it doesn't give me any error but as soon as I use it on the TSX file it through an error.
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null); // Thumbs state
<div key={product._id} className='lg:w-1/2'>
<Swiper
loop={true}
spaceBetween={10}
thumbs={{ swiper: thumbsSwiper }} // PRoblem is Here
modules={[FreeMode, Navigation, Thumbs]}
className="mySwiper2"
>
{product?.images.map(({ src }: { src: string }) => {
return <SwiperSlide style={{ height: '500px', width: '300px' }}>
<img src={src} alt={product?.title} />
</SwiperSlide>
})}
</Swiper>
<Swiper
onSwiper={setThumbsSwiper}
loop={true}
spaceBetween={10}
slidesPerView={4}
freeMode={true}
watchSlidesProgress={true}
modules={[FreeMode, Navigation, Thumbs]}
className="mySwiper mt-4"
>
{product?.images.map(({ src }: { src: string }) => {
return <SwiperSlide style={{ height: '100px', width: '100px' }} >
<img src={src} alt={product?.title} />
</SwiperSlide>
})}
</Swiper>
</div>
Here is my full code:
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import RelatedProducts from '../RelatedProducts/RelatedProducts';
import { Swiper, SwiperSlide } from "swiper/react";
import { FreeMode, Navigation, Thumbs } from "swiper";
import Magnifier from "react-magnifier";
const SingleProduct = () => {
const { id } = useParams();
const [productsDetails, setProductsDetails] = useState<any>([])
const [isLoading, setIsLoading] = useState<boolean>(false)
useEffect(() => {
setIsLoading(true)
fetch(`https://guarded-ocean-73313.herokuapp.com/products`)
.then(res => res.json())
.then((data) => {
console.log(data, 'data')
const product = data.filter(detail => detail._id === id)
setProductsDetails(product);
}).finally(() => setIsLoading(false))
}, [id])
useEffect(() => {
if (!productsDetails.length) {
<h2>Loading</h2>
}
}, [productsDetails.length])
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
return (
<div>
<section className="text-gray-700 body-font overflow-hidden bg-white">
<div className="container px-5 py-10 mx-auto">
<div className="lg:w-4/5 mx-auto flex ">
{isLoading ? "Loading" :
productsDetails.map(product => {
return <>
<div key={product._id} className='lg:w-1/2'>
<Swiper
loop={true}
spaceBetween={10}
thumbs={{ swiper: thumbsSwiper }}
modules={[FreeMode, Navigation, Thumbs]}
className="mySwiper2"
>
{product?.images.map(({ src }: { src: string }) => {
return <SwiperSlide style={{ height: '500px', width: '300px' }}>
<img src={src} alt={product?.title} />
</SwiperSlide>
})}
</Swiper>
<Swiper
onSwiper={setThumbsSwiper}
loop={true}
spaceBetween={10}
slidesPerView={4}
freeMode={true}
watchSlidesProgress={true}
modules={[FreeMode, Navigation, Thumbs]}
className="mySwiper mt-4"
>
{product?.images.map(({ src }: { src: string }) => {
return <SwiperSlide style={{ height: '100px', width: '100px' }} >
<img src={src} alt={product?.title} />
</SwiperSlide>
})}
</Swiper>
</div>
<div className="lg:w-1/2 w-full lg:pl-10 lg:py-6 mt-6 lg:mt-0">
<div>
<h2 className="text-sm title-font text-gray-500 tracking-widest">Brand: <b>{product.brand}</b></h2>
<h1 className="text-gray-900 text-3xl title-font font-medium mb-1">{product.title}</h1>
<div className="flex mb-4">
<span className="flex items-center">
<svg fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" strokeWidth="2" className="w-4 h-4 text-red-500" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
</svg>
<svg fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" strokeWidth="2" className="w-4 h-4 text-red-500" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
</svg>
<svg fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" strokeWidth="2" className="w-4 h-4 text-red-500" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
</svg>
<svg fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" strokeWidth="2" className="w-4 h-4 text-red-500" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
</svg>
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" strokeWidth="2" className="w-4 h-4 text-red-500" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
</svg>
<span className="text-gray-600 ml-3">4 Reviews</span>
</span>
<span className="flex ml-3 pl-3 py-2 border-l-2 border-gray-200">
<a className="text-gray-500">
<svg fill="currentColor" stroke-linecap="round" stroke-linejoin="round" strokeWidth="2" className="w-5 h-5" viewBox="0 0 24 24">
<path d="M18 2h-3a5 5 0 00-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 011-1h3z"></path>
</svg>
</a>
<a className="ml-2 text-gray-500">
<svg fill="currentColor" stroke-linecap="round" stroke-linejoin="round" strokeWidth="2" className="w-5 h-5" viewBox="0 0 24 24">
<path d="M23 3a10.9 10.9 0 01-3.14 1.53 4.48 4.48 0 00-7.86 3v1A10.66 10.66 0 013 4s-4 9 5 13a11.64 11.64 0 01-7 2c9 5 20 0 20-11.5a4.5 4.5 0 00-.08-.83A7.72 7.72 0 0023 3z"></path>
</svg>
</a>
<a className="ml-2 text-gray-500">
<svg fill="currentColor" stroke-linecap="round" stroke-linejoin="round" strokeWidth="2" className="w-5 h-5" viewBox="0 0 24 24">
<path d="M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z"></path>
</svg>
</a>
</span>
</div>
<p className="leading-relaxed"
dangerouslySetInnerHTML={({ __html: product.product_des })}></p>
<div className="flex py-4 space-x-4">
</div>
<div className="flex mt-6 items-center pb-5 border-b-2 border-gray-200 mb-5">
{/* <div className="flex">
<span className="mr-3">Color</span>
<button className="border-2 border-gray-300 rounded-full w-6 h-6 focus:outline-none"></button>
<button className="border-2 border-gray-300 ml-1 bg-gray-700 rounded-full w-6 h-6 focus:outline-none"></button>
<button className="border-2 border-gray-300 ml-1 bg-red-500 rounded-full w-6 h-6 focus:outline-none"></button>
</div> */}
<div className="relative mr-4 mt-3">
<div className="text-center left-0 pt-2 right-0 absolute block text-xs uppercase text-gray-400 tracking-wide font-semibold">Qty</div>
<select className="cursor-pointer appearance-none rounded-xl border border-gray-200 pl-4 pr-8 h-14 flex items-end pb-1">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
{product?.attributes.map(attr => {
return <div className=" mr-6 items-center">
<span className="mr-3"><b>{attr.label}</b></span><br />
<div className="relative">
<select className="rounded appearance-none border border-gray-200 py-2 focus:outline-none focus:border-indigo-500 text-base pl-3 pr-10">
{attr?.selected.map(select => {
return <option>{select.label}</option>
})}
</select>
<span className="absolute right-0 top-0 h-full w-10 text-center text-gray-600 pointer-events-none flex items-center justify-center">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" strokeWidth="2" className="w-4 h-4" viewBox="0 0 24 24">
<path d="M6 9l6 6 6-6"></path>
</svg>
</span>
</div>
</div>
})}
</div>
<span className="title-font font-sm text-gray-900">Category: <b>{product.categories[0].label}</b></span>
<div className="flex mt-4">
<div className="inline-block align-bottom mr-5">
<span className="text-2xl leading-none align-baseline">$</span>
<span className="font-bold text-5xl leading-none align-baseline">{product.sale_price ? product.sale_price : product.reg_price} </span>
<span className="text-2xl leading-none align-baseline">.00</span>
</div>
<div className="inline-block align-bottom mr-5 mt-5 line-through">
<span className="text-2xl leading-none align-baseline">$</span>
<span className="font-bold text-2xl leading-none align-baseline">{product.sale_price ? product.reg_price : ''} </span>
<span className="text-2xl leading-none align-baseline">.00</span>
</div>
{/* <span className="title-font font-medium text-2xl text-gray-900">${product.price | 0} <span className='line-through text-gray-500'>{product?.sale_price}</span>
</span> */}
<button className="flex ml-auto text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded items-center">Add to cart</button>
<button className="rounded-full w-10 h-10 bg-gray-200 p-0 border-0 inline-flex items-center justify-center text-gray-500 ml-4">
<svg fill="currentColor" stroke-linecap="round" stroke-linejoin="round" strokeWidth="2" className="w-5 h-5" viewBox="0 0 24 24">
<path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"></path>
</svg>
</button>
</div>
</div>
</div></>
})
}
</div>
</div>
<div className='bg-slate-300 mb-10 mt-10 shadow'>
<h2 className='text-2xl text-center py-3'>Related Products</h2>
</div>
<RelatedProducts />
</section >
</div >
)
};
export default SingleProduct;
If you open Swiper in a modal, pass null to setThumbsSwiper every time you close the modal. This works for me.
const onCloseModal = () => {
setThumbsSwiper(null)
}
https://snipboard.io/pN3cIl.jpg
I have solved it this way. I am validating first if there are any images available on my images property or not, if there is an image then render swiperSlide and it solved my issue.
{
product.images ? <Swiper
loop={true}
spaceBetween={10}
thumbs={{ swiper: thumbsSwiper }}
modules={[FreeMode, Navigation, Thumbs]}
className="mySwiper2"
>
{product?.images.map(({ src }: { src: string }) => {
return <SwiperSlide style={{ height: '500px', width: '300px' }}>
<img src={src} alt={product?.title} />
</SwiperSlide>
})}
</Swiper> : 'No img'
}
React does not allow className to be passed to the swiper component thou their are two ways of doing this
React Strict from <React.Strict>
Upgrade Swiper Js to the latest version
One of this solution should definitely work for you.

How to toggle class when click outside element with Javascript?

I'm using the free example navbar on tailwindui. I wrote some javascript to be able to toggle the menu on mobile and toggle the profile pic tooltip. However the profile pic tooltip doesn't close on clicks outside it.
My current javascript code only toggles the profile pic tooltip on and off when it's clicked. It doesn't hide the tooltip when outside it is clicked.
I tried using a click event listener and toggle the tooltip off when the event listener fired but that didn't work.
Using Javascript, how would you hide the profile pic tooltip on outside clicks?
document.getElementById('togglemebutton').onclick = function () {
document.getElementById("resultnav").classList.toggle("hidden");
}
//document.getElementById('togglemebutton').onclick = function () {
// document.getElementById("toggleme").classList.toggle("hidden");
//}
document.getElementById('toggleprofile').onclick = function () {
document.getElementById("resultprofile").classList.toggle("hidden");
}
<link href="https://unpkg.com/tailwindcss#^1.0/dist/tailwind.min.css" rel="stylesheet">
<nav class="bg-gray-800">
<div class="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
<div class="relative flex items-center justify-between h-16">
<div class="absolute inset-y-0 left-0 flex items-center sm:hidden">
<!-- Mobile menu button-->
<button id="togglemebutton" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:bg-gray-700 focus:text-white transition duration-150 ease-in-out" aria-label="Main menu" aria-expanded="false">
<!-- Icon when menu is closed. -->
<!-- Menu open: "hidden", Menu closed: "block" -->
<svg class="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
<!-- Icon when menu is open. -->
<!-- Menu open: "block", Menu closed: "hidden" -->
<svg class="hidden h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="flex-1 flex items-center justify-center sm:items-stretch sm:justify-start">
<div class="flex-shrink-0">
<img class="block lg:hidden h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-mark-on-dark.svg" alt="Workflow logo">
<img class="hidden lg:block h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-logo-on-dark.svg" alt="Workflow logo">
</div>
<div class="hidden sm:block sm:ml-6">
<div class="flex">
Dashboard
Team
Projects
Calendar
</div>
</div>
</div>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
<button class="p-1 border-2 border-transparent text-gray-400 rounded-full hover:text-white focus:outline-none focus:text-white focus:bg-gray-700 transition duration-150 ease-in-out" aria-label="Notifications">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
</button>
<!-- Profile dropdown -->
<div class="ml-3 relative">
<div>
<button id="toggleprofile" class="flex text-sm border-2 border-transparent rounded-full focus:outline-none focus:border-white transition duration-150 ease-in-out" id="user-menu" aria-label="User menu" aria-haspopup="true">
<img class="h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
</button>
</div>
<!--
Profile dropdown panel, show/hide based on dropdown state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<div id="resultprofile" class="hidden z-40 origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg">
<div class="py-1 rounded-md bg-white shadow-xs" role="menu" aria-orientation="vertical" aria-labelledby="user-menu">
Your Profile
Settings
Sign out
</div>
</div>
</div>
</div>
</div>
</div>
<!--
Mobile menu, toggle classes based on menu state.
Menu open: "block", Menu closed: "hidden"
-->
<div id="resultnav" class="hidden sm:hidden">
<div class="px-2 pt-2 pb-3">
Dashboard
Team
Projects
Calendar
</div>
</div>
</nav>
I tried using a click event listener and toggle the tooltip off when
the event listener fired but that didn't work.
This is exactly what you need, event listener on all elements:
const toggleprofile = document.getElementById("toggleprofile");
const resultprofile = document.getElementById("resultprofile");
[...document.querySelectorAll('body')].forEach(el => {
//in order to get all elements
el.addEventListener('click', event => {
//add click event to all of them
if (event.target.parentElement.id !== "toggleprofile") {
// if element is not profile picture - parent button has id, easier to target
console.clear();
console.log(event.target.parentElement.id)
resultprofile.classList.add("hidden")
//add class
}else{
resultprofile.classList.toggle("hidden")}
//if it is profile, toggle class
})
})
Keep in mind now you have click event listener on all elements on page, so if you need something else with other element, just expand this.
Working example:
document.getElementById('togglemebutton').onclick = function() {
document.getElementById("resultnav").classList.toggle("hidden");
}
//document.getElementById('togglemebutton').onclick = function () {
// document.getElementById("toggleme").classList.toggle("hidden");
//}
const toggleprofile = document.getElementById("toggleprofile");
const resultprofile = document.getElementById("resultprofile");
[...document.querySelectorAll('body')].forEach(el => {
el.addEventListener('click', event => {
if (event.target.parentElement.id !== "toggleprofile") {
console.clear();
console.log(event.target.parentElement.id)
resultprofile.classList.add("hidden")
}else{
resultprofile.classList.toggle("hidden")}
})
})
body{
height:300px}
<link href="https://unpkg.com/tailwindcss#^1.0/dist/tailwind.min.css" rel="stylesheet">
<nav class="bg-gray-800">
<div class="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
<div class="relative flex items-center justify-between h-16">
<div class="absolute inset-y-0 left-0 flex items-center sm:hidden">
<!-- Mobile menu button-->
<button id="togglemebutton" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:bg-gray-700 focus:text-white transition duration-150 ease-in-out" aria-label="Main menu"
aria-expanded="false">
<!-- Icon when menu is closed. -->
<!-- Menu open: "hidden", Menu closed: "block" -->
<svg class="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
<!-- Icon when menu is open. -->
<!-- Menu open: "block", Menu closed: "hidden" -->
<svg class="hidden h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="flex-1 flex items-center justify-center sm:items-stretch sm:justify-start">
<div class="flex-shrink-0">
<img class="block lg:hidden h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-mark-on-dark.svg" alt="Workflow logo">
<img class="hidden lg:block h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-logo-on-dark.svg" alt="Workflow logo">
</div>
<div class="hidden sm:block sm:ml-6">
<div class="flex">
Dashboard
Team
Projects
Calendar
</div>
</div>
</div>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
<button class="p-1 border-2 border-transparent text-gray-400 rounded-full hover:text-white focus:outline-none focus:text-white focus:bg-gray-700 transition duration-150 ease-in-out" aria-label="Notifications">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
</button>
<!-- Profile dropdown -->
<div class="ml-3 relative">
<div>
<button id="toggleprofile" class="flex text-sm border-2 border-transparent rounded-full focus:outline-none focus:border-white transition duration-150 ease-in-out" id="user-menu" aria-label="User menu" aria-haspopup="true">
<img class="h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
</button>
</div>
<!--
Profile dropdown panel, show/hide based on dropdown state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<div id="resultprofile" class="hidden z-40 origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg">
<div class="py-1 rounded-md bg-white shadow-xs" role="menu" aria-orientation="vertical" aria-labelledby="user-menu">
Your Profile
Settings
Sign out
</div>
</div>
</div>
</div>
</div>
</div>
<!--
Mobile menu, toggle classes based on menu state.
Menu open: "block", Menu closed: "hidden"
-->
<div id="resultnav" class="hidden sm:hidden">
<div class="px-2 pt-2 pb-3">
Dashboard
Team
Projects
Calendar
</div>
</div>
</nav>
Or as mentioned in comment:
window.addEventListener('click', event => {
if (event.target.parentElement.id !== "toggleprofile") {
console.clear();
console.log(event.target.parentElement.id);
resultprofile.classList.add("hidden")
} else {
resultprofile.classList.toggle("hidden")
}
})
I'll try to improve this others are requested to do the same

Categories

Resources