Skip to content

Commit

Permalink
React: Grid lights challenge
Browse files Browse the repository at this point in the history
  • Loading branch information
sadanandpai committed Oct 20, 2024
1 parent 9a3000b commit 47372cb
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 78 deletions.
File renamed without changes.
27 changes: 27 additions & 0 deletions apps/css/src/challenges/timeline/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css" />
<script src="../../helpers/header.js" type="module"></script>
<script src="./script.ts" type="module"></script>
</head>
<body>
<div class="container text-center">
<div class="wrapper">
<div class="timeline">
<ul class="timeline_list"></ul>
</div>
</div>
</div>
</body>

<template id="timeline-template">
<li>
<div class="timeline_content">
<h3 class="date"></h3>
<h1 class="title"></h1>
<p class="content"></p>
</div>
</li>
</template>
</html>
12 changes: 12 additions & 0 deletions apps/css/src/challenges/timeline/script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import DATA from './data.js';

const timelineTemplate = document.getElementById('timeline-template')! as HTMLTemplateElement;
const timelineListEl = document.querySelector('.timeline_list')!;

DATA.forEach((item) => {
const listItem = timelineTemplate.content.cloneNode(true) as HTMLElement;
listItem.querySelector('.date')!.textContent = item.date;
listItem.querySelector('.title')!.textContent = item.title;
listItem.querySelector('.content')!.textContent = item.content;
timelineListEl.appendChild(listItem);
});
Original file line number Diff line number Diff line change
@@ -1,120 +1,120 @@
.AppContainer {
.wrapper {
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;
justify-content: center;
padding: 10px 20px;
}
.container {
min-height: 100vh;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 100px 0;
min-height: 100vh;
padding: 2rem 0;
}

.timeline {
width: 80%;
height: auto;
position: relative;
width: 90%;
max-width: 800px;
height: auto;
margin: 0 auto;
position: relative;
}

.timeline ul {
list-style: none;
}

.timeline ul li {
position: relative;
width: 50%;
padding: 20px;
background-color: #1e1f22;
margin-bottom: 50px;
color: white;
background-color: #1e1f22;
border-radius: 10px;
margin-bottom: 20px;
}

.timeline ul li:last-child {
margin-bottom: 0;
}

.date {
color: black;
font-size: 12px;
color: black;
}

.timeline_content h1 {
font-weight: 500;
margin-bottom: 10px;
font-size: 25px;
font-weight: 500;
line-height: 30px;
margin-bottom: 10px;
color: white;
}

.timeline_content p {
font-size: 16px;
line-height: 30px;
font-weight: 300;
line-height: 30px;
}

.timeline_content .date {
position: absolute;
top: -30px;
margin-bottom: 10px;
font-size: 12px;
font-weight: 300;
margin-bottom: 10px;
letter-spacing: 2px;
}

.timeline:before {
content: '';
.timeline::before {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 2px;
height: 100%;
content: '';
background-color: gray;
transform: translateX(-50%);
}
.timeline ul li {
width: 50%;
position: relative;
margin-bottom: 50px;
}

.timeline ul li:nth-child(odd) {
float: left;
clear: right;
border-radius: 20px 0 20px 20px;
transform: translateX(-60px);
border-radius: 20px 0px 20px 20px;
}

.timeline ul li:nth-child(even) {
float: right;
clear: left;
border-radius: 0 20px 20px;
transform: translateX(30px);
border-radius: 0px 20px 20px 20px;
}

.timeline ul li::before {
content: '';
position: absolute;
height: 20px;
top: -10px;
width: 20px;
border-radius: 50%;
height: 20px;
content: '';
background-color: gray;
top: -10px;
border-radius: 50%;
}

.timeline ul li:nth-child(odd)::before {
transform: translate(50%, -50%);
right: -40px;
transform: translate(50%, -50%);
}

.timeline ul li:nth-child(even)::before {
transform: translate(-50%, -50%);
left: -48px;
transform: translate(-50%, -50%);
}
.timeline_content .date {
position: absolute;
top: -30px;
}

.timeline ul li:hover::before {
background-color: aqua;
}

@media only screen and (max-width: 420px) {
@media only screen and (width <= 420px) {
.timeline_content h1 {
padding-right: 10px;
font-size: 20px;
line-height: 25px;
padding-right: 10px;
}

.timeline_content p {
Expand All @@ -123,7 +123,7 @@
}

.timeline_content .date {
font-size: 10px;
margin-bottom: 5px;
font-size: 10px;
}
}
83 changes: 83 additions & 0 deletions apps/react/src/challenges/grid-lights/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import classes from './styles.module.scss';
import { Leva, useControls } from 'leva';

function App() {
const [selections, setSelections] = useState<number[][]>([]);
const { gridSize } = useControls({ gridSize: { value: 3, min: 2, max: 4, step: 1 } });
const { delay } = useControls({ delay: { value: 300, min: 100, max: 700, step: 100 } });
const cellCount = gridSize * gridSize;

const isUndoing = useRef(false);

function onCellClick(e: React.MouseEvent<HTMLButtonElement>) {
const target = e.target as HTMLButtonElement;
const row = Number(target.dataset.row);
const col = Number(target.dataset.col);
setSelections(selections.concat([[row, col]]));
}

const undoSelections = useCallback(
async function () {
isUndoing.current = true;
for (let i = selections.length - 1; i >= 0; i--) {
await new Promise((resolve) => setTimeout(resolve, delay));
setSelections(selections.slice(0, i));
}
isUndoing.current = false;
},
[selections, delay]
);

useEffect(() => {
if (selections.length === cellCount) {
undoSelections();
}
}, [undoSelections, selections.length, cellCount]);

useEffect(() => {
setSelections([]);
}, [gridSize]);

return (
<div className={classes.wrapper}>
<Leva
collapsed
hideCopyButton={true}
titleBar={{ position: { x: 0, y: 40 }, filter: false, title: 'Config' }}
theme={{
colors: {
highlight1: 'white',
highlight2: 'white',
},
}}
/>

<p>
Click on cells to select them. Once all cells are selected, they will be unselected one by
one in the reverse order they were selected.
</p>

<div className={classes.grid} style={{ '--grid-size': gridSize } as React.CSSProperties}>
{Array.from({ length: gridSize }, (_, row) =>
Array.from({ length: gridSize }, (_, col) => {
const isSelected = selections.some((s) => s[0] === row && s[1] === col);
return (
<button
key={`${row}-${col}`}
type="button"
className={isSelected ? classes.selected : ''}
data-row={row}
data-col={col}
onClick={onCellClick}
disabled={isSelected || isUndoing.current}
/>
);
})
)}
</div>
</div>
);
}

export default App;
24 changes: 24 additions & 0 deletions apps/react/src/challenges/grid-lights/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
:root {
--size: min(90vw, 100vh - 10rem);
--grid-size: 3;
}

.wrapper {
text-align: center;
}

.grid {
display: inline-grid;
grid-template-columns: repeat(var(--grid-size), 1fr);
width: var(--size);
gap: calc(var(--size) / 20);

button {
border: 1px solid #000;
aspect-ratio: 1;
}
}

.selected {
background-color: green;
}
29 changes: 0 additions & 29 deletions apps/react/src/challenges/timeline/App.jsx

This file was deleted.

2 changes: 2 additions & 0 deletions apps/react/src/pages/Challenge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import AnalogClock from '@/challenges/analog-clock/analog-clock';
import AdvancedCounter from '@/challenges/advanced-counter/advanced-counter';
import NestedCheckbox from '@/challenges/nested-checkbox/App';
import MeetingCalendar from '@/challenges/meeting-calendar/App';
import GridLights from '@/challenges/grid-lights/App';

const reactChallengesMap = {
'transfer-list': <TransferListApp />,
Expand Down Expand Up @@ -132,6 +133,7 @@ const reactChallengesMap = {
'analog-clock': <AnalogClock />,
'nested-checkbox': <NestedCheckbox />,
'meeting-calendar': <MeetingCalendar />,
'grid-lights': <GridLights />,
};

function Challenge() {
Expand Down
11 changes: 11 additions & 0 deletions shared/data/content/css-challenges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,17 @@ const challenges: Map<string, IChallenge> = new Map([
isNew: true,
},
],
[
'timeline',
{
title: 'Timeline',
link: 'timeline/',
difficulty: EDifficulty.Medium,
developer: 'Vivek7038',
tags: [],
isNew: true,
},
],
]);

export const cssChallenges = sortChallengesByDifficulty(challenges);
Loading

0 comments on commit 47372cb

Please sign in to comment.