Do 100 things, and track it with this ugly ass tracker.
Created
July 12, 2022 05:53
-
-
Save weirdyang/b393eacaa33443c168d930d703a1f394 to your computer and use it in GitHub Desktop.
Do π― things β
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <div class="modal-overlay close" id="modal-overlay"></div> | |
| <main class=""> | |
| <section class="controls"> | |
| <button class="reset-button button button-reset"> | |
| reset | |
| </button> | |
| <button class="export-button button button-export"> | |
| export</button> | |
| <button class="theme-button button button-theme">π</button> | |
| </section> | |
| <section class="cells"> | |
| </section> | |
| </main> | |
| <article class="card modal" id="modal"> | |
| <section class="card-header"> | |
| <h1 class="title">The Thing</h1> | |
| <p class="subtitle muted"></p> | |
| </section> | |
| <section class="card-content"> | |
| <div class="card-content-item"> | |
| <div class="item-text-description"> | |
| <textarea class="item-text-content" cols="120" rows="8" style="max-width:100%;">Doggo ipsum puggorino doge porgo. I am bekom fat wow such tempt waggy wags shoober, such treat super chub. Ruff long water shoob blop, heck. ruff most angery pupper I have ever seen lotsa pats. Bork shoob smol borking doggo with a long snoot for pats much ruin diet vvv I am bekom fat heckin tungg, smol borking doggo with a long snoot for pats clouds super chub clouds mlem. you are doing me a frighten. Heckin good boys and girls heckin good boys super chub maximum borkdrive blep vvv, pats doggorino tungg long woofer. | |
| Doggo tungg corgo boofers long water shoob, doge many pats long doggo ruff shooberino, shoob long doggo boofers. Shibe blop corgo such treat, bork. Waggy wags mlem clouds, noodle horse. Doing me a frighten what a nice floof boof you are doing me a frighten boofers, wrinkler very taste wow shibe he made many woofs, lotsa pats long doggo pupper. Waggy wags super chub thicc, length boy. Aqua doggo you are doing me a frighten corgo shibe, heckin good boys. Mlem I am bekom fat shibe porgo dat tungg tho vvv, long woofer very good spot vvv clouds. Thicc extremely cuuuuuute borking doggo long bois doggo, boof puggo shoob, tungg such treat very hand that feed shibe. | |
| Much ruin diet tungg heck you are doing me the shock, vvv. mlem. Sub woofer shibe big ol pupper blep h*ck clouds, dat tungg tho borkdrive tungg waggy wags. Super chub pupperino heck puggo maximum borkdrive, shoob you are doing me a frighten heck. Dat tungg tho big ol borkdrive most angery pupper I have ever seen, tungg. Heck lotsa pats h*ck shooberino, long bois. Boofers blop shooberino much ruin diet ruff such treat puggorino many pats heckin shoober, heckin angery woofer floofs h*ck many pats bork big ol long water shoob. Blep big ol pupper very jealous pupper snoot blop, heckin angery woofer wrinkler. Doggorino long bois floofs wow very biscit, boofers.</textarea> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="card-footer"> | |
| <button class="done-button button button-primary">Edit</button> | |
| <button class="save-button button button-link">Save</button> | |
| <button class="close-button button button-danger-borderless">Close</button> | |
| </section> | |
| </article> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const cells = 100; | |
| const app = { data: [], dark: false }; | |
| const modalOverlay = document.querySelector("#modal-overlay"); | |
| const main = document.querySelector("main"); | |
| const storageKey = "hundo"; | |
| const themeKey = "theme"; | |
| const button = document.querySelector(".done-button"); | |
| const save = document.querySelector(".save-button"); | |
| const modal = document.getElementById("modal"); | |
| const info = document.querySelector(".subtitle"); | |
| const cellContainer = document.querySelector(".cells"); | |
| const theme = document.querySelector(".theme-button"); | |
| const exportButton = document.querySelector(".export-button"); | |
| const reset = document.querySelector('.reset-button'); | |
| const truncate = (str, n=140) => (str.length > n) ? str.substr(0, n-1) + '...' : str; | |
| const hideModal = () => { | |
| modal.style.display = "none"; | |
| modalOverlay.classList.toggle("close"); | |
| clearInfo(); | |
| }; | |
| const showModal = () => { | |
| modal.style.display = "block"; | |
| modalOverlay.classList.toggle("close"); | |
| }; | |
| const setUpModal = (modal) => { | |
| const close = document.querySelector(".close-button"); | |
| // When the user clicks on <span> (x), close the modal | |
| close.addEventListener("click", () => { | |
| hideModal(); | |
| }); | |
| // When the user clicks anywhere outside of the modal, close it | |
| modalOverlay.addEventListener("click", () => { | |
| hideModal(); | |
| }); | |
| }; | |
| const checkAndParse = (key, prop) => { | |
| const data = localStorage.getItem(key); | |
| if (!data) { | |
| return app[prop]; | |
| } | |
| return JSON.parse(data); | |
| }; | |
| const toggleDark = () => { | |
| main.classList.toggle("dark"); | |
| modal.classList.toggle("dark"); | |
| theme.innerText = app.dark ? `π` : `βοΈ`; | |
| }; | |
| const downloadFile = (jsonStr, fileName) => { | |
| let element = document.createElement("a"); | |
| element.setAttribute( | |
| "href", | |
| "data:text/plain;charset=utf-8," + encodeURIComponent(jsonStr) | |
| ); | |
| element.setAttribute("download", fileName); | |
| element.style.display = "none"; | |
| document.body.appendChild(element); | |
| element.click(); | |
| document.body.removeChild(element); | |
| }; | |
| const setUpListeners = () => { | |
| reset.addEventListener('click', () => { | |
| resetData(); | |
| }) | |
| exportButton.addEventListener("click", () => { | |
| const data = JSON.stringify(app); | |
| downloadFile(data, `${new Date().toISOString().slice(0,10)}.json`) | |
| }); | |
| save.addEventListener("click", () => { | |
| const id = modal.dataset.cell; | |
| const data = modal.querySelector(".item-text-content").value; | |
| app.data[id].text = data; | |
| const text = data ?? "The thing"; | |
| const cellText = document.querySelector(`.cell-text-${id}`); | |
| cellText.innerText = truncate(text); | |
| saveToLocal(storageKey, app.data); | |
| updateInfo(); | |
| }); | |
| theme.addEventListener("click", () => { | |
| toggleDark(); | |
| app.dark = !app.dark; | |
| saveToLocal(themeKey, app.dark); | |
| }); | |
| button.addEventListener("click", () => { | |
| const data = modal.dataset.cell; | |
| app.data[data].filled = !app.data[data].filled; | |
| toggleDone(app.data[data].filled, button, data); | |
| saveToLocal(storageKey, app.data); | |
| updateInfo(); | |
| }); | |
| }; | |
| const updateInfo = () => { | |
| info.innerText = `Updated: ${new Date().toLocaleTimeString()}`; | |
| }; | |
| const clearInfo = () => { | |
| info.innerText = ``; | |
| }; | |
| const toggleDone = (done, button, id) => { | |
| button.innerText = done ? "undo" : "done"; | |
| document.getElementById(id).classList.toggle("done"); | |
| }; | |
| const saveToLocal = (storageKey, data) => { | |
| localStorage.setItem(storageKey, JSON.stringify(data)); | |
| }; | |
| const createCells = (cells) => { | |
| for (let i = 0; i < cells; i++) { | |
| const cell = document.createElement("article"); | |
| cell.classList.add("cell"); | |
| cell.id = i; | |
| const cellNum = document.createElement("section"); | |
| cellNum.classList.add('cell-num'); | |
| cellNum.innerText = i + 1; | |
| cell.appendChild(cellNum); | |
| const cellText = document.createElement('section'); | |
| cellText.classList.add('cell-text', `cell-text-${i}`); | |
| cell.appendChild(cellText); | |
| if (app.data[i]) { | |
| const info = app.data[i]; | |
| if (info.filled) { | |
| cell.classList.toggle("done"); | |
| } | |
| const text = info.text ?? "The thing"; | |
| cellText.innerText = truncate(text); | |
| } else { | |
| app.data.push({ | |
| id: i, | |
| text: "the thing", | |
| filled: false | |
| }); | |
| } | |
| cell.addEventListener("click", () => { | |
| modal.setAttribute("data-cell", i); | |
| modalOverlay.classList.toggle("close"); | |
| modal.style.display = "block"; | |
| modal.querySelector(".item-text-content").value = app.data[i].text; | |
| button.innerText = app.data[i].filled ? "undo" : "done"; | |
| }); | |
| cellContainer.appendChild(cell); | |
| } | |
| }; | |
| const resetData = () => { | |
| localStorage.removeItem(storageKey); | |
| localStorage.removeItem(themeKey); | |
| resetUpApp(); | |
| } | |
| const resetUpApp = () => { | |
| app.data = []; | |
| app.dark = false; | |
| modal.classList.remove('dark'); | |
| main.classList.remove('dark'); | |
| cellContainer.innerHTML = ""; | |
| createCells(cells); | |
| localStorage.setItem(storageKey, JSON.stringify(app.data)); | |
| } | |
| document.addEventListener("DOMContentLoaded", () => { | |
| setUpModal(modal); | |
| app.data = checkAndParse(storageKey, 'data'); | |
| app.dark = checkAndParse(themeKey,'dark'); | |
| if (app.dark) { | |
| toggleDark(); | |
| } | |
| createCells(cells); | |
| setUpListeners(); | |
| localStorage.setItem(storageKey, JSON.stringify(app.data)); | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @import "https://fonts.googleapis.com/css?family=Prompt:400,700"; | |
| :root { | |
| --primary: #00d1b2; | |
| --primary-invert: #fff; | |
| --base-hue: 171; | |
| --primary-light: hsl(var(--base-hue), 100%, 96%); | |
| --primary-med: hsl(var(--base-hue), 100%, 75%); | |
| --primary-dark: #00947e; | |
| --peach: hsl(25, 88%, 76%); | |
| --link: hsl(25, 88%, 76%); | |
| --danger: #dc1c13; | |
| --shadow-base: 0 0 0; | |
| --shadow-1: rgb(var(--shadow-base) / 26%); | |
| --shadow-2: rgb(var(--shadow-base) / 28%); | |
| --shadow-3: rgb(var(--shadow-base) / 22%); | |
| background: var(--primary-light); | |
| font-family: "Prompt", sans-serif; | |
| text-overflow: ellipsis; | |
| } | |
| $color-bg-darkest: #13141b; | |
| $color-bg-darker: #1b1e27; | |
| $color-bg-dark: #232837; | |
| $color-bg-med: #2f3646; | |
| $color-bg-light: #455066; | |
| $color-bg-lighter: #5b6882; | |
| $color-text-dark: #72809b; | |
| $color-text-med-dark: #919db5; | |
| $color-text-med: #a0aabe; | |
| $color-text-med-light: #d9dce1; | |
| $color-text-light: #f0f1f6; | |
| $color-text-lighter: #fff; | |
| .dark { | |
| --primary-light: #{$color-bg-dark}; | |
| --primary-dark: var(--primary); | |
| --link: #{$color-bg-lighter}; | |
| .cell { | |
| color: #{$color-text-light}; | |
| background: var(--link); | |
| &.done { | |
| color: #{$color-text-lighter}; | |
| } | |
| } | |
| &.modal { | |
| h1 { | |
| color: #{$color-text-light}; | |
| } | |
| .subtitle { | |
| color: #{$color-text-light}; | |
| } | |
| --link: hsl(25, 88%, 76%); | |
| .button-danger-borderless { | |
| background: var(--danger); | |
| color: #{$color-text-light}; | |
| &:hover { | |
| filter: brightness(1.4); | |
| } | |
| } | |
| } | |
| .controls { | |
| background: var(--primary-light); | |
| } | |
| } | |
| .wrapper { | |
| display: grid; | |
| grid-template-rows: auto 1fr; | |
| } | |
| .controls { | |
| display: flex; | |
| justify-content: flex-end; | |
| padding: 1rem; | |
| padding-left: 3rem; | |
| padding-right: 3rem; | |
| .export-button { | |
| margin-right: auto; | |
| } | |
| .reset-button { | |
| margin-right: auto; | |
| } | |
| } | |
| @mixin styled-scrollbar { | |
| overflow-y: auto; | |
| overflow-x: hidden; | |
| &::-webkit-scrollbar { | |
| width: 3em; | |
| } | |
| &::-webkit-scrollbar-track { | |
| background-color: var(--shadow-3); | |
| opacity: 0.2; | |
| } | |
| &::-webkit-scrollbar-thumb { | |
| background-color: var(--shadow-1); | |
| outline: 0.25rem var(--shadow-1); | |
| } | |
| } | |
| @mixin hide-scrollbar { | |
| overflow-x: hidden; | |
| overflow-y: auto; | |
| scrollbar-width: none; /* Firefox */ | |
| -ms-overflow-style: none; /* Internet Explorer 10+ */ | |
| &::-webkit-scrollbar { | |
| /* WebKit */ | |
| width: 0; | |
| height: 0; | |
| } | |
| } | |
| @mixin light-shadow { | |
| box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15), 0 2px 2px rgba(0, 0, 0, 0.15), | |
| 0 4px 4px rgba(0, 0, 0, 0.15), 0 8px 8px rgba(0, 0, 0, 0.15); | |
| } | |
| html, | |
| body { | |
| box-sizing: border-box; | |
| height: 100%; | |
| width: 100%; | |
| } | |
| main { | |
| background: var(--primary-light); | |
| padding: 1rem; | |
| grid-gap: 1rem; | |
| display: grid; | |
| .cells { | |
| display: grid; | |
| place-items: center; | |
| grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); | |
| grid-template-rows: minmax(150px, 15%); | |
| } | |
| .cell { | |
| border-radius: 10px; | |
| display: flex; | |
| flex-direction: column; | |
| flex: 1 1 150px; | |
| margin: 1rem; | |
| padding: 1rem; | |
| height: 150px; | |
| width: 150px; | |
| opacity: 0.9; | |
| background: var(--primary-med); | |
| @include light-shadow; | |
| &:hover { | |
| transform: scale(1.04); | |
| } | |
| .cell-text { | |
| margin-top: 0.25rem; | |
| } | |
| &.done { | |
| background: var(--primary-dark); | |
| color: var(--primary-light); | |
| } | |
| } | |
| } | |
| $border-width: 1.25px; | |
| .button:disabled { | |
| cursor: not-allowed; | |
| pointer-events: all !important; | |
| background: inherit !important; | |
| filter: brightness(0.5) !important; | |
| color: rgb(0 0 0 / 20%); | |
| &:hover { | |
| background: inherit !important; | |
| filter: brightness(0.5) !important; | |
| } | |
| } | |
| @mixin color-button($color, $surface) { | |
| border: $border-width solid $surface; | |
| background: $color; | |
| color: $surface; | |
| &:hover { | |
| border: $border-width solid $color; | |
| color: $color; | |
| background: transparent; | |
| } | |
| } | |
| .button { | |
| text-transform: Capitalize; | |
| font-size: 1em; | |
| background-color: #fff; | |
| border-color: #dbdbdb; | |
| border-width: 1px; | |
| color: #363636; | |
| cursor: pointer; | |
| justify-content: center; | |
| padding-bottom: calc(0.5em - 1px); | |
| padding-left: 1em; | |
| padding-right: 1em; | |
| padding-top: calc(0.5em - 1px); | |
| text-align: center; | |
| white-space: nowrap; | |
| border-radius: 0.25rem; | |
| margin: 0; | |
| font-family: Consolas, monaco, monospace; | |
| &-reset { | |
| @include color-button(var(--danger), white); | |
| } | |
| &-export { | |
| @include color-button(var(--primary-med), black); | |
| } | |
| &-theme { | |
| @include color-button(var(--primary-light), black); | |
| border:none; | |
| &:hover { | |
| filter: brightness(1.4); | |
| border: none; | |
| } | |
| } | |
| &-primary { | |
| @include color-button(var(--primary), black); | |
| } | |
| &-link { | |
| @include color-button(var(--link), black); | |
| } | |
| &-danger-borderless { | |
| border: 1px solid var(--danger); | |
| color: var(--danger); | |
| &:hover { | |
| background: var(--danger); | |
| color: #fff; | |
| } | |
| } | |
| &-link-flat { | |
| border: $border-width solid var(--link); | |
| color: var(--link); | |
| &:hover { | |
| background: var(--link); | |
| color: #fff; | |
| } | |
| } | |
| } | |
| .modal.card { | |
| display: none; | |
| background: var(--primary-light); | |
| } | |
| .modal { | |
| /* This way it could be display flex or grid or whatever also. */ | |
| display: block; | |
| /* Probably need media queries here */ | |
| width: 600px; | |
| max-width: 100%; | |
| max-height: 100%; | |
| position: fixed; | |
| z-index: 100; | |
| left: 50%; | |
| top: 35%; | |
| /* Use this for centering if unknown width/height */ | |
| transform: translate(-50%, -35%); | |
| /* If known, negative margins are probably better (less chance of blurry text). */ | |
| /* margin: -200px 0 0 -200px; */ | |
| background: var(--primary-light); | |
| @include light-shadow; | |
| ul { | |
| margin: 10px 0 10px 30px; | |
| } | |
| li, | |
| p { | |
| margin: 0 0 10px 0; | |
| } | |
| h1 { | |
| font-weight: 700; | |
| font-size: 2em; | |
| } | |
| } | |
| .close { | |
| display: none; | |
| } | |
| .modal-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: 50; | |
| background: rgba(0, 0, 0, 0.6); | |
| } | |
| $card-width: 315px; | |
| $breakpoint-narrow: 350px; | |
| .card { | |
| display: grid; | |
| padding: 1rem 2rem; | |
| grid-template-row: auto 1fr auto; | |
| max-width: 75vw; | |
| min-width: 315px; | |
| gap: 1rem; | |
| border: 1px solid black; | |
| border-radius: 2rem; | |
| background: #fff; | |
| box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15), 0 2px 2px rgba(0, 0, 0, 0.15), | |
| 0 4px 4px rgba(0, 0, 0, 0.15), 0 8px 8px rgba(0, 0, 0, 0.15); | |
| @media screen and (max-width: 375) { | |
| padding: 1rem 1.25rem; | |
| width: 70vw; | |
| } | |
| } | |
| .title { | |
| margin-top: 0; | |
| color: #4e4b4b; | |
| } | |
| .title + .subtitle { | |
| margin-bottom: 0; | |
| } | |
| .subtitle { | |
| font-size: 0.75rem; | |
| margin-top: 1rem; | |
| margin-left: 0.25rem!important; | |
| } | |
| .muted { | |
| opacity: 0.75; | |
| } | |
| .brand-tag { | |
| border-radius: 0.375rem; | |
| border: 1px solid black; | |
| padding: 0.25rem 0.5rem; | |
| margin: 0.25rem 0; | |
| background: var(--primary-light); | |
| font-weight: 300; | |
| &.category { | |
| text-transform: capitalize; | |
| background: var(--link); | |
| } | |
| } | |
| .card-header { | |
| display: flex; | |
| margin-top: 0.25rem; | |
| min-height: 75px; | |
| align-items: flex-start; | |
| flex-direction: column; | |
| } | |
| .card-image { | |
| display: flex; | |
| flex-direction: row; | |
| justify-content: center; | |
| img { | |
| max-width: $card-width - 16px - 16px; | |
| margin: auto; | |
| border-radius: 0.25rem; | |
| padding: 0; | |
| border: 1px solid rgb(0 0 0 / 20%); | |
| } | |
| } | |
| .card-image + .card-content { | |
| } | |
| .card-content { | |
| padding: 0.25rem; | |
| &:hover { | |
| .description { | |
| box-shadow: 0 0 0 1px var(--shadow-3); | |
| border-radius: 0.25rem; | |
| } | |
| } | |
| &:last-child { | |
| margin-bottom: 0.75rem; | |
| } | |
| p { | |
| font-weight: 200; | |
| } | |
| .card-content-item { | |
| &:not(last-child) { | |
| margin-bottom: 1rem; | |
| } | |
| @media screen and (min-width: 400px) { | |
| display: grid; | |
| grid-template-columns: auto 1fr; | |
| } | |
| .item-icon { | |
| margin-right: 0; | |
| padding-right: 0; | |
| @media screen and (max-width: $breakpoint-narrow) { | |
| display: none; | |
| } | |
| } | |
| .item-text { | |
| @media screen and (max-width: $breakpoint-narrow) { | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: space-between; | |
| } | |
| word-break: break-word; | |
| text-overflow: ellipsis; | |
| &-header { | |
| text-transform: capitalize; | |
| font-weight: 600; | |
| } | |
| &.description { | |
| @include hide-scrollbar; | |
| display: flex; | |
| width: 100%; | |
| max-width: 580px; | |
| } | |
| } | |
| } | |
| } | |
| .card-footer { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 1rem; | |
| margin-top: auto; | |
| } | |
| textarea { | |
| max-width: 100%; | |
| width: 100%; | |
| font-size: 1em; | |
| flex: 1; | |
| display: block; | |
| padding: 0.25rem; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment