Merge pull request #7 from RiccardoSenica/flipping-backgound
style: flipping cards as background
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
# Hackernews newsletter
|
||||
|
||||
## To do
|
||||
|
||||
- Check breakpoint for background: on mobile there are white bars
|
||||
- Email layout (must contain link to newsletter page)
|
||||
- Ico file
|
||||
|
||||
## Vercel basics
|
||||
|
||||
Install vercel cli
|
||||
|
||||
@@ -6,7 +6,7 @@ import { cn } from '../utils/utils';
|
||||
import './globals.css';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Hacker News newsletter',
|
||||
title: 'Hacker News newsletter by FromPixels',
|
||||
description: 'Newsletter delivering the best posts from Hacker News',
|
||||
keywords: 'newsletter, hackernews, technology, coding, programming, news'
|
||||
};
|
||||
|
||||
@@ -434,7 +434,7 @@ export default function Privacy() {
|
||||
title='Privacy Policy'
|
||||
description='Last updated: December 03, 2023'
|
||||
content={body}
|
||||
style='w-2/3 overflow-auto h-[90vh]'
|
||||
style='w-2/3 h-[90vh]'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { z } from 'zod';
|
||||
import { NewsSchema } from '../../../../utils/types';
|
||||
import { Story } from './story';
|
||||
import { CardContent } from './cardContent';
|
||||
|
||||
type CardProps = {
|
||||
newsA?: z.infer<typeof NewsSchema>;
|
||||
newsB?: z.infer<typeof NewsSchema>;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export function Card({ newsA, newsB }: CardProps) {
|
||||
export function Card({ newsA, newsB, width, height }: CardProps) {
|
||||
const [switched, setSwitched] = useState(false);
|
||||
const [active, setActive] = useState(Math.random() < 0.5);
|
||||
const [delayed, setDelayed] = useState(true);
|
||||
@@ -38,7 +40,9 @@ export function Card({ newsA, newsB }: CardProps) {
|
||||
<div className={`transform ${switched ? 'animate-rotate' : ''}`}>
|
||||
<div className='transform-gpu'>
|
||||
<div className={`absolute left-0 top-0 w-full ${''}`}>
|
||||
{active ? Story(newsA, true) : Story(newsB, false)}
|
||||
{active
|
||||
? CardContent({ story: newsA, width, height, side: true })
|
||||
: CardContent({ story: newsB, width, height, side: false })}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
58
components/custom/background/components/cardContent.tsx
Normal file
58
components/custom/background/components/cardContent.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { useState } from 'react';
|
||||
import { z } from 'zod';
|
||||
import { NewsSchema } from '../../../../utils/types';
|
||||
|
||||
type CardContentProps = {
|
||||
story: z.infer<typeof NewsSchema>;
|
||||
side: boolean;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
function getRandomColor() {
|
||||
const letters = '456789ABCDEF';
|
||||
let color = '#';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 12)];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
export function CardContent({ story, width, height, side }: CardContentProps) {
|
||||
const [firstColor, setFirstColor] = useState(getRandomColor());
|
||||
const [secondColor, setSecondColor] = useState(getRandomColor());
|
||||
const [switched, setSwitched] = useState(true);
|
||||
|
||||
if (switched !== side) {
|
||||
setFirstColor(getRandomColor());
|
||||
setSecondColor(getRandomColor());
|
||||
|
||||
setSwitched(side);
|
||||
}
|
||||
|
||||
const color = side ? firstColor : secondColor;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`overflow-hidden p-6 shadow-sm`}
|
||||
style={{
|
||||
backgroundColor: `${color}`,
|
||||
height: `${height * 4}px`,
|
||||
width: `${width * 4}px`
|
||||
}}
|
||||
>
|
||||
<h1 className='overflow-auto font-semibold'>{story.title}</h1>
|
||||
<p className='overflow-auto italic'>{story.by}</p>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
height: '33.33%',
|
||||
background: `linear-gradient(to bottom, transparent, ${color})`
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { z } from 'zod';
|
||||
import { NewsSchema } from '../../../../utils/types';
|
||||
|
||||
export function Story(story: z.infer<typeof NewsSchema>, side: boolean) {
|
||||
const backgroundColors = [
|
||||
'bg-red-300',
|
||||
'bg-blue-300',
|
||||
'bg-green-300',
|
||||
'bg-yellow-300',
|
||||
'bg-indigo-300',
|
||||
'bg-purple-300',
|
||||
'bg-pink-300',
|
||||
'bg-gray-300',
|
||||
'bg-teal-300',
|
||||
'bg-orange-300'
|
||||
];
|
||||
|
||||
const fadingColors = [
|
||||
'to-red-300',
|
||||
'to-blue-300',
|
||||
'to-green-300',
|
||||
'to-yellow-300',
|
||||
'to-indigo-300',
|
||||
'to-purple-300',
|
||||
'to-pink-300',
|
||||
'to-gray-300',
|
||||
'to-teal-300',
|
||||
'to-orange-300'
|
||||
];
|
||||
|
||||
const firstRandom = Math.floor(Math.random() * backgroundColors?.length);
|
||||
const secondRandom = Math.floor(Math.random() * backgroundColors?.length);
|
||||
|
||||
const [firstColor, setFirstColor] = useState(firstRandom);
|
||||
const [secondColor, setSecondColor] = useState(secondRandom);
|
||||
const [switched, setSwitched] = useState(true);
|
||||
|
||||
if (switched !== side) {
|
||||
setFirstColor(firstRandom);
|
||||
setSecondColor(secondRandom);
|
||||
|
||||
setSwitched(side);
|
||||
}
|
||||
|
||||
const colorIndex = side ? firstColor : secondColor;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`h-40 w-40 overflow-hidden p-6 shadow-sm ${backgroundColors[colorIndex]}`}
|
||||
>
|
||||
<h1 className='font-semibold'>{story.title}</h1>
|
||||
<p className='italic'>{story.by}</p>
|
||||
<div
|
||||
className={`absolute inset-x-0 bottom-0 h-1/3 bg-gradient-to-b from-transparent ${fadingColors[colorIndex]}`}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -21,6 +21,9 @@ export const Tiles = ({ children }: TilesProps) => {
|
||||
});
|
||||
const [news, setNews] = useState<z.infer<typeof NewsSchema>[]>();
|
||||
|
||||
const baseWidth = 40;
|
||||
const baseHeight = 40;
|
||||
|
||||
useEffect(() => {
|
||||
async function getNews() {
|
||||
const news = await fetch('/api/news').then(res => res.json());
|
||||
@@ -63,8 +66,20 @@ export const Tiles = ({ children }: TilesProps) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<div key={key} className='h-40 w-40'>
|
||||
<Card newsA={news[randomA]} newsB={news[randomB]} />
|
||||
<div
|
||||
key={key}
|
||||
className={`m-1`}
|
||||
style={{
|
||||
height: `${baseHeight * 4}px`,
|
||||
width: `${baseWidth * 4}px`
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
newsA={news[randomA]}
|
||||
newsB={news[randomB]}
|
||||
width={baseHeight}
|
||||
height={baseHeight}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -78,8 +93,8 @@ export const Tiles = ({ children }: TilesProps) => {
|
||||
}
|
||||
|
||||
function renderGrid() {
|
||||
const columns = Math.ceil(windowSize.width / (40 * 4));
|
||||
const rows = Math.ceil(windowSize.height / (40 * 4));
|
||||
const columns = Math.ceil(windowSize.width / (baseWidth * 4));
|
||||
const rows = Math.ceil(windowSize.height / (baseHeight * 4));
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardTitle
|
||||
} from '../../components/ui/card';
|
||||
import Footer from './footer';
|
||||
|
||||
@@ -22,17 +22,19 @@ export const CustomCard = ({
|
||||
description,
|
||||
content,
|
||||
style,
|
||||
footer = true,
|
||||
footer = true
|
||||
}: CustomCardProps) => {
|
||||
return (
|
||||
<Card className={style ?? 'w-full sm:w-2/3 md:w-2/5 lg:w-1/3 xl:w-1/4'}>
|
||||
<CardHeader className="text-center">
|
||||
<Card
|
||||
className={`${style ?? 'w-full sm:w-2/3 md:w-2/5 lg:w-1/3 xl:w-1/4'} p-4`}
|
||||
>
|
||||
<CardHeader className='text-center'>
|
||||
<CardTitle>{title}</CardTitle>
|
||||
<CardDescription>{description}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>{content}</CardContent>
|
||||
<CardContent className='h-[80%] overflow-auto'>{content}</CardContent>
|
||||
{footer && (
|
||||
<CardFooter className="flex justify-center space-x-4">
|
||||
<CardFooter className=' flex justify-center p-4'>
|
||||
<Footer />
|
||||
</CardFooter>
|
||||
)}
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
window.onload = function () {
|
||||
var body = document.getElementsByTagName('body')[0];
|
||||
var size = 160; // size of each square
|
||||
var squaresPerRow = Math.ceil(window.innerWidth / size);
|
||||
var squaresPerColumn = Math.ceil(window.innerHeight / size);
|
||||
var margin = 8; // margin between squares
|
||||
var squaresPerRow = Math.ceil(window.innerWidth / (size + margin));
|
||||
var squaresPerColumn = Math.ceil(window.innerHeight / (size + margin));
|
||||
|
||||
for (var i = 0; i < squaresPerRow; i++) {
|
||||
for (var j = 0; j < squaresPerColumn; j++) {
|
||||
@@ -15,8 +16,8 @@
|
||||
square.style.height = size + 'px';
|
||||
square.style.backgroundColor = getRandomColor();
|
||||
square.style.position = 'absolute';
|
||||
square.style.left = i * size + 'px';
|
||||
square.style.top = j * size + 'px';
|
||||
square.style.left = i * (size + margin) + 'px';
|
||||
square.style.top = j * (size + margin) + 'px';
|
||||
body.appendChild(square);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user