The goal is to create a replica of the GitHub grid using a spreadsheet from Google Drive as the database.
Let's create a component in React called GitHubGrid.js
:
jsximport React, { useState, useEffect } from 'react'; import DrawGrid from './DrawGrid.js'; import GetDataFromDrive from './GetDataFromDrive.js'; import SortMonths from './SortMonths.js' import '../Styles/GitHubGrid.css'; const GitHubGrid = () => { // 🕒 Declaring new date: from one year ago until today const today = new Date(); const lastYear = new Date(today.getFullYear() - 1, today.getMonth(), today.getDate()); // 🗄️ Getting data from Google Drive => Change [YOUR_KEY] for yours const url = 'https://spreadsheets.google.com/feeds/list/[YOUR_KEY]/od6/public/values?alt=json'; const data = GetDataFromDrive(url); // 🖼️ Drawing squares const grid = DrawGrid(lastYear, today, data); const months = SortMonths(); return ( <div className = 'Status'> <p>Last year habits:</p> <div className = 'Contribution'> <div className = 'Contribution-Wrap'> <div className = 'Months'> {months} </div> <div className = 'Days-Squares'> <div className = 'Squares'> {grid} </div> </div> </div> </div> </div> ); } export default GitHubGrid;
GitHubGrid.js does 3 actions:
It requires to import GetDataFromDrive.js
, SortMonths.js
and DrawGrid.js
(we'll see these components later).
The spreadsheet allows us to track our habits:
The key of the document is (you can see it in the URL of the picture above):
"1rtxEO8zm1Wea2WUPbzLnnWgWt3oaSVQ7s7kyLspjgQw"
You need to change this key for yours when you create the url variable in GitHubGrid.js. Also, it's important to set the document as "public" to fetch the data from the app:
The function to fetch the data:
jsximport React, { useState, useEffect } from 'react'; const GetDataFromDrive = (url) => { const [data, setData] = useState(null); useEffect( () => { fetch(url).then(resp => resp.json()).then(data => setData(data.feed.entry)) }, [] ); return data; } export default GetDataFromDrive;
GitHub displays the current month as the last one on the right of the page. So, we replicate this behavior:
jsximport React, { useState, useEffect } from 'react'; const SortMonths = () => { const [months, setMonths] = useState(null); useEffect( () => { var sorted = []; var today = new Date(); var months = [<div>Jan</div>, <div>Feb</div>, <div>Mar</div>, <div>Apr</div>, <div>May</div>, <div>Jun</div>, <div>Jul</div>, <div>Aug</div>, <div>Sep</div>, <div>Oct</div>, <div>Nov</div>, <div>Dec</div>]; for(var i = 0; i < 12; i ++) sorted[i] = months[(today.getMonth() + i + 1) % 12]; setMonths(sorted); }, []) return months; } export default SortMonths;
Now it's time to draw the grid:
jsximport React, { useState, useEffect } from 'react'; const DrawGrid = (From, to, data) => { const [squares, setSquares] = useState(null); useEffect( () => { const array = []; if(data){ for(var today = From; today <= to; today.setDate(today.getDate() + 1)) { var nHabits = 0; var classname; data.map(data => { var year = parseInt(data['gsx$date'].$t.substring(6, 10)); var month = parseInt(data['gsx$date'].$t.substring(3, 5)); var day = parseInt(data['gsx$date'].$t.substring(0, 2)); if(year === today.getFullYear() && month === (today.getMonth() + 1) && day === today.getDate()){ data['gsx$code'].$t === 'Yes' ? nHabits ++ : nHabits; data['gsx$swim'].$t === 'Yes' ? nHabits ++ : nHabits; data['gsx$gym'].$t === 'Yes' ? nHabits ++ : nHabits; data['gsx$meditation'].$t === 'Yes' ? nHabits ++ : nHabits; } classname = `Square Square-${nHabits}`; }); array.push(<div className = {classname}> <div className = {'Invisible ' + classname}>{nHabits} habits, {today.getDate() + '/' + (today.getMonth() + 1) + '/' + today.getFullYear()}</div> </div>); } setSquares(array); } }, [data]) return squares; } export default DrawGrid;
Last, the css:
css.Status{ margin: auto; max-width: 1000px; } .Contribution{ display: flex; flex-direction: column; font-size: x-small; margin: auto; } .Contribution-Wrap{ display: flex; flex-direction: column; font-size: x-small; right: 0; } .Months{ display: flex; justify-content: space-between; overflow: hidden; width: 620px; } .Days{ display: flex; flex-direction: column; justify-content: space-between; } .Days-Squares{ display: flex; height: 90px; overflow: hidden; width: 620px; } .Squares{ display: flex; flex-direction: column; flex-wrap: wrap; } .Square{ align-items: center; display: flex; justify-content: center; height: 8px; margin: 1.87px; width: 8px; } .Square:hover .Invisible{ cursor: default; display: flex; } .Square-1{ background: #e5faff; } .Square-2{ background: #ccf6ff; } .Square-3{ background: #99edff; } .Square-4{ background: #007e99; } .Invisible{ background: rgba(1, 1, 1, 0.7); border-radius: 5px; display: none; height: 20px; margin-top: -15px; padding: 5px; position: absolute; width: 150px; }
Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.