I am using Tailwind CSS with next.js.
I have 8 images of different sizes and want to keep 4 images in a row for mid and large-size devices and 2 images in a row for small-size devices.
I want to give equal space between the items in a row but I want the first item in a row to be at the leftmost place and the last item in a row to be at the rightmost place.
The rest of the items should maintain equal distance between them.
How can I achieve this using Tailwind CSS? Normally with some conditions, I know how to do it, but is there any way to do it directly with Tailwind?
JSON response from API:
const ourPartners = [
{
id: "partner01",
pic: partner_01
},
{
id: "partner02",
pic: partner_02,
},
{
id: "partner03",
pic: partner_03,
},
{
id: "partner04",
pic: partner_04,
},
{
id: "partner05",
pic: partner_05,
},
{
id: "partner06",
pic: partner_06,
},
{
id: "partner07",
pic: partner_07,
},
{
id: "partner08",
pic: partner_08,
}
]
The code where I'm mapping over the array:
<div className="flex flex-row flex-wrap justify-between items-center">
{
ourPartners.map((item, index) => {
return (
<div className={`md:w-1/4 w-1/2 mb-10 ${(index % 4 !== 0 ? ('flex justify-center') : ('flex justify-start'))}`} key={item.id}>
<div className="w-32 object-contain">
<Image
alt="partner"
width="auto"
height="auto"
src={item.pic}
quality={100}
/>
</div>
</div>
)
})
}
</div>
EDITED:
I actually learned something new here too. You can use nth-child now with Tailwind.
const Partners = () => {
return (
<div className="grid md:grid-cols-4 grid-cols-2 gap-y-10 justify-between">
{ourPartners.map((item, index) => {
return (
<div
key={item.id}
className="odd:justify-self-start even:justify-self-end md:[&:nth-child(4n+2)]:justify-self-center md:[&:nth-child(4n+3)]:justify-self-center"
>
<div className="w-32">
<img alt="partner" width="auto" height="auto" src={item.pic} />
</div>
</div>
);
})}
</div>
);
};
export default Partners;
Related
I am creating a directory of users in Next.js. The users are stored in Supabase. I need for all the users to be displayed in the index.js file, looping through them and showing them on a grid. This is working with getStaticProps, fetching the data and passing it as props profiles.
However, when clicking on each profile, it does redirect me to the [id].js page, but it appends /undefined to the url, rather than the id.
My file tree looks as follows:
pages
people
index.js
[id].js
export default function People({ profiles }) {
return (
<div className="body min-h-[90vh]">
<Head>
<title>People</title>
<link rel="icon" href="/logo" />
</Head>
<div
key={profiles.id}
profiles={profiles}
className="flex flex-col items-center py-16"
>
<div className="grid md:grid-flow-col md:grid-cols-3 xl:grid-cols-4 gap-8 lg:gap-12">
{profiles.map((profile) => (
<Link href={`/people/${profiles.id}`} key={profiles.id}>
<div
profile={profile}
id={profile.id}
className="flex flex-col w-full justify-center items-center p-8 shadow-md hover:shadow-lg"
>
{profile.avatar_url && (
<Image
src={profile.avatar_url}
alt="profile picture"
width={200}
height={200}
className="rounded-full"
object-fit="cover"
/>
)}
<h1 className="text-2xl pt-8 text-center">
{profile.full_name}
</h1>
<p>{profile.skills.skill}</p>
<button className="button w-full">See lessons</button>
</div>
</Link>
))}
</div>
</div>
</div>
);
}
export async function getStaticProps() {
const supabaseAdmin = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
process.env.SUPABASE_SERVICE_ROLE_KEY || ""
);
const { data } = await supabaseAdmin
.from("profiles")
.select("*, skills(skill)")
.order("id");
console.log(data);
return {
props: {
profiles: data,
},
};
}
Any ideas as to what I am doing wrong are highly appreciated.
Thanks.
I am trying to create a Vue component which wraps a Popoverfrom headlessui
I am trying to toggle open close using the slot binding as suggested in the popover docs. I cannot get this to work. Nothing appears in the UI, even if I had it working ok from within the element itself, granted that is neither a good place to put it because the elements render as many rows there are in the table.
Does anyone have any wisdom surrounding headless UI popovers in Vue3 on a table row?
EDIT:
Ok adding static to the PopoverPanelmakes it render when open=true
, the issue now being that multiple instances render based on the number of elements in the table. So this is a new issue.
<template>
<div>
<Popover :slot="{pop}">
<transition
enter-active-class="transition duration-200 ease-out"
enter-from-class="translate-y-1 opacity-0"
enter-to-class="translate-y-0 opacity-100"
leave-active-class="transition duration-150 ease-in"
leave-from-class="translate-y-0 opacity-100"
leave-to-class="translate-y-1 opacity-0"
>
<PopoverPanel static class="absolute z-9 mt-3 max-w-sm -translate-x-1/2 transform sm:px-0 lg:max-w-3xl">
<div class="rounded-lg shadow-lg w-full m-2">
<div
v-if="pop"
ref="popover"
class="flex flex-col p-2"
>
<ButtonTemp label="Send reminder" btn-type="m-0.5 bg-neutral-200 text-neutral-900 text-left text-caption hover:bg-neutral-900 hover:text-neutral-100" icon-left>
<IconsMail />
</ButtonTemp>
<ButtonTemp label="Share candidate" btn-type="m-0.5 bg-neutral-200 text-neutral-900 text-caption hover:bg-neutral-900 hover:text-neutral-100" icon-left>
<IconsExternalLink />
</ButtonTemp>
<ButtonTemp label="Remove from assignment" btn-type="m-0.5 bg-neutral-200 text-neutral-900 text-caption hover:bg-danger-100 hover:text-neutral-100" icon-left>
<IconsUserMinus />
</ButtonTemp>
</div>
</div>
</PopoverPanel>
</transition>
</Popover>
<script setup lang="ts">
import { Ref } from '#vue/runtime-core';
import { Popover, PopoverButton, PopoverPanel } from '#headlessui/vue';
import { ButtonTemp, IconsMail, IconsExternalLink, IconsUserMinus } from '#/.nuxt/components';
const pop: Ref<boolean> = ref(true);
interface IContextMenu {
open: boolean;
}
const props = defineProps<IContextMenu>();
const isOpen = toRef(props, 'open');
watch(isOpen, (is) => {
if (is) {
console.log('open', is);
pop.value = !pop.value;
}
});
onMounted(() => {
pop.value = true;
});
</script>
Component where it is to be used:
......
<td class="group-hover:text-neutral-100 rounded-r">
<button
class="m-auto h-8 w-8 rounded-sm flex justify-center items-center group-hover:bg-neutral-700 cursor-pointer"
#click="handleClick(candidate.id, '')"
>
<ContextMenu :open="open" /> // Popover wrapped component
<IconsVerticalMenu />
</button>
</td>
</tr>
I'm building a portfolio and i'm currently stuck in a section where i'm looping thru an object which contains the respective color that I want to use (eg:
export const skills = [
{
id: 1,
name: "front-end",
Icon: FaReact,
color: "#61DAFB",
},
In the component mapped receiving these props, I already logged the color variable and it's logging correctly. But when I try to use that variable to dynamic change the color of the component, it doesn't work at all.
const SkillCard = ({ name, Icon, tools, color }) => {
console.log(`[${color}]`);
return (
<article
className={`bg-black text-gray-300 w-full hover:shadow-lg hover:shadow-gray-800 flex flex-col gap-4 p-8 rounded-lg grayscale hover:grayscale-0 duration-200 border-b-[${color}] `}
>
<Icon style={style} size={50} />
<h1 className="font-bold text-xl capitalize">{name}</h1>
<p>{tools}</p>
</article>
);
};
Here you can see that I'm trying to use the border-bottom property to change depending on the color contained in the array of objects, but I just couldn't find the solution.
I already tried chanching the value of the property, to contain the square brackets, but didn't work as well.
Update January 23:
const SkillCard = ({ skill }) => {
const { name, Icon, tools, color } = skill;
const style = { color };
return (
<article
style={{
borderBottomStyle: "solid",
borderBottomColor: color,
borderBottomWidth: "8px",
}}
className="bg-black text-gray-400 hover:text-white w-full hover:shadow-lg hover:shadow-gray-800 flex flex-col gap-4 p-8 rounded-lg grayscale hover:grayscale-0 duration-200"
>
<Icon style={style} size={50} />
<h1 className="font-bold text-xl capitalize">{name}</h1>
<p>{tools}</p>
</article>
);
};
Just added that color property as an inline style and now it works as intended. Not shure if it's best practice, but it's what I achieved so far.
It seems that this is because Tailwind need the full class name (complete unbroken strings) to assign the correct style, according to Tailwind document.
Live demo of the example: stackblitz
For example, perhaps try something like:
export const skills = [
{
id: 1,
name: "front-end",
Icon: FaReact,
borderBotttomColor: "border-b-[#61DAFB]",
},
Then apply to the component, perhaps also add a bottom border width such as border-b-4:
const SkillCard = ({ name, Icon, tools, borderBotttomColor }) => {
return (
<article
className={`${borderBotttomColor} border-b-4 bg-black text-gray-300 w-full hover:shadow-lg hover:shadow-gray-800 flex flex-col gap-4 p-8 rounded-lg grayscale hover:grayscale-0 duration-200`}
>
<Icon style={style} size={50} />
<h1 className="font-bold text-xl capitalize">{name}</h1>
<p>{tools}</p>
</article>
);
};
Alternatively, dynamic class names could be defined as safelist in Tailwind configuration, although it might not be suitable for this use case.
I've been making a website to showcase my personal photography, and I've been getting my photos from Google Photos, using their features. (There's a medium post on how to do this)!
However, one problem I have been having is with scaling. I would like my images to be as quality as possible but also at a small enough size for them to fit into a grid on my page. However, when I use the built in parameters to do this (according to the docs) scaling is not preserved!
Example:
My page's code is as follows:
const axios = require('axios');
const Image = require('next/image');
const _ = require('lodash');
export default function Home({ pictures }) {
return (
<div>
<div className="hero min-h-screen" style={{ backgroundImage: 'url(https://lh3.googleusercontent.com/wwilPCF5L98Osl7_HVohVi34EP4SHUNKbxCe-fBooyNcTdvAWawcP3paqGxvAW3gCzXBl4aQOT_oxwYuXXaMG3ICM7cOkWJH6eYcozUqr9agShnjQu8kWFsPxtL7WD7H5sF5rkR9Vdk=w2048)' }}>
<div className="hero-overlay bg-opacity-60" />
<div className="hero-content text-center text-neutral-content">
<div className="max-w-md">
<h1 className="mb-5 text-5xl font-bold">Photography</h1>
<p className="mb-5">Aside from programming, I also love photography! </p>
</div>
</div>
</div>
<div className="flex flex-col flex-wrap m-10">
<div className="flex flex-row basis-1 flex-wrap m-4">
{
pictures.map(((x) => (
<img src={`${x}`} alt="Hello." key={`${x}?`} className=" flex-col flex-auto m-3 rounded-md shadow-md" />
)))
}
</div>
</div>
</div>
);
}
export async function getServerSideProps({ req, res }) {
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
const regex = /\["(https:\/\/lh3\.googleusercontent\.com\/[a-zA-Z0-9\-_]*)"/g;
function extractPhotos(content) {
const links = new Set();
let match;
while (match = regex.exec(content)) {
links.add(match[1]);
}
return Array.from(links);
}
async function getAlbum(id) {
const response = await axios.get(`https://photos.app.goo.gl/${id}`);
const photos = _.shuffle(extractPhotos(response.data));
return photos;
}
const pictures = await getAlbum('ZbGaHdrs62q5Jyrk8');
return {
props: { pictures }, // will be passed to the page component as props
};
}
How can I ensure the images are properly scaled? Is this even possible? Will I need some CSS trick? Thanks!
while searching, the results should appear as a div like below :
i use jquery to search in table,how to get the result like above.
my component code is:
<div id="modaldash" style={{ display: (searching ? 'block' : 'none') }}>
<p className="font-weight-medium" id="name"> <img id="logo" className="logo" src={jessica} alt="pam-logo" /> Jessica James </p>
<button id="Addlist" onClick={this.onSubmitdata} className="btn info">{this.state.shown ? "Addded" : "Add to list"}</button>
<p id="mailid">jessicajames#gmail.com </p>
<p id= "address">Mountain view,Ave</p>
</div>
its just a static content for css. how to use search and get results like above.
export default function App() {
// considering the data object to search on name
const [searchedData, setSearchedData] = useState([]);
const users = [
{
name: "abc1",
emailid: "abc1#gmail.com",
address: "23rd main, 2nd street"
},
{
name: "adb2",
emailid: "abc2#gmail.com",
address: "23rd main, 2nd street"
},
{
name: "adc3",
emailid: "abc3#gmail.com",
address: "23rd main, 2nd street"
}
];
const handleSearch = event => {
const data = users.filter(
user => user.name.indexOf(event.target.value) !== -1
);
setSearchedData(data);
};
const showSearchedData = () => {
return searchedData.map(user => (
<div key={user.emailid}>
<p className="font-weight-medium" id="name">
{" "}
<img id="logo" className="logo" src="" alt="pam-logo" />
{user.name}
</p>
<button id="Addlist"> Added/ add to list</button>
<p id="mailid">{user.emailid} </p>
<p id="address">{user.address}</p>
</div>
));
};
return (
<div className="App">
<input type="text" onChange={handleSearch} />
<div id="modaldash">{searchedData.length > 0 && showSearchedData()}</div>
</div>
);
}
You can add CSS to make a look and feel like shown in image attached.
Check the working example here https://codesandbox.io/s/falling-sun-r3rim