Word count picker added, Share/contact links added

This commit is contained in:
Gene Mecija 2020-02-12 17:48:34 -08:00
parent 1117989502
commit 70b0c4b3ee
16 changed files with 394 additions and 54 deletions

View file

@ -1,11 +1,13 @@
import React, { useContext } from "react"
import { GameClockContext } from "../contexts/gameClockContext"
import { ChallengeContext } from "../contexts/challengeContext"
import { WordListPickerContext } from "../contexts/wordListPickerContext"
export default (function ChallengeComplete(props) {
const {gameClockTime} = useContext(GameClockContext)
const {setChallengeState} = useContext(ChallengeContext)
const {wordListCount, wordListCategory, metadata} = useContext(WordListPickerContext)
function _continue() {
setChallengeState('ready')
@ -19,7 +21,9 @@ export default (function ChallengeComplete(props) {
return (
<div id="challengeComplete" className="notify">
<span id="notify-title">Challenge Complete</span>
<span id="message">Challenge completed in {time}!</span>
<span id="message">You completed <b>{wordListCount}</b> words<br />
from the <b>{metadata[wordListCategory]['name']}</b> word list<br />
in <b>{time}</b>!</span>
<button id="continue" onClick={_continue}>Continue</button>
</div>

View file

@ -2,7 +2,33 @@ import React from "react"
export default (function Footer() {
const contactLinks = {
'email': {
name: 'Email',
icon: "ri-mail-line",
link: 'mailto:gene@genemecija.com?subject='+encodeURIComponent('Hello, Gene!')
},
'github': {
name: 'GitHub',
icon: 'ri-github-fill',
link: 'https://github.com/genemecija'
},
'twitter': {
name: 'Twitter',
icon: 'ri-twitter-fill',
link:'https://twitter.com/genemecija'
}
}
function handleClick(event) {
window.open(contactLinks[event.target.id]['link'])
}
return (
<div id="footer">Created by Gene Mecija &nbsp;<a href='https://github.com/genemecija/learn-morse-code'>View on Github</a>&nbsp;•&nbsp;<a href='https://twitter.com/genemecija'>Twitter</a></div>
<div id="footer">
App by Gene Mecija &nbsp;
Code:&nbsp;<span id="contact-icons"><i id="github" onClick={handleClick} className={contactLinks['github']['icon']}></i></span>&nbsp;
Say Hi!&nbsp;<span id="contact-icons"><i id="twitter" onClick={handleClick} className={contactLinks['twitter']['icon']}></i>&nbsp;<i id="email" onClick={handleClick} className={contactLinks['email']['icon']}></i></span>
</div>
)
})

View file

@ -2,9 +2,73 @@ import React from "react"
export default (function Header () {
const shareLinks = {
'twitter': {
name: 'Twitter',
icon: 'ri-twitter-fill',
link:'https://twitter.com/intent/tweet?text=Check%20out%20this%20site%20that%20helps%20you%20learn%20Morse%20Code%3A%20https%3A//learnmorsecode.com%20%40genemecija%20%23morse%20%23morsecode'
},
'facebook': {
name: 'Facebook',
icon: 'ri-facebook-box-fill',
link:'https://www.facebook.com/sharer/sharer.php?u=https%3A//learnmorsecode.com'
},
'email': {
name: 'Email',
icon: "ri-mail-line",
link: 'mailto:?subject='+encodeURIComponent('Check out this site that helps you learn Morse code! https://learnmorsecode.com')
}
}
function PopupCenter(url, title, w, h) {
// Credit: http://www.xtf.dk/2011/08/center-new-popup-window-even-on.html
// Fixes dual-screen position Most browsers Firefox
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screen.left;
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screen.top;
const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : window.screen.width;
let height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : window.screen.height;
const left = ((width / 2) - (w / 2)) + dualScreenLeft;
const top = ((height / 2) - (h / 2)) + dualScreenTop;
const newWindow = window.open(url, title, 'scrollbars=yes, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left);
// Puts focus on the newWindow
if (window.focus) {
newWindow.focus();
}
}
function handleClick(event) {
let link = event.target.id
let url = shareLinks[link]['link']
let title = 'Share'
let width = '900'
let height = '500'
if (link === 'email') {
width = '150'
height = '150'
}
PopupCenter(url, title, width, height)
}
let contacts = Object.keys(shareLinks).map((contact, index) => {
return (
<i id={contact} key={index} onClick={handleClick} className={shareLinks[contact]['icon']}></i>
)
})
return (
<div id="header">
Learn Morse Code
<div id="title">
Learn Morse Code
</div>
<div id="social-links">
Share: <span id="share-icons">{contacts}</span>
</div>
</div>
)
})

View file

@ -1,7 +1,7 @@
import React from "react"
import useMorsePlayer from "../hooks/useMorsePlayer"
import straight_key from "../images/straight_key.jpg"
import electronic_key from "../images/electronic_key.jpg"
import straight_key from "../media/images/straight_key.jpg"
import electronic_key from "../media/images/electronic_key.jpg"
export default React.memo(function Info() {

View file

@ -0,0 +1,33 @@
import React, {useContext} from "react"
import { WordListPickerContext } from "../contexts/wordListPickerContext";
import { WordFeederContext } from "../contexts/wordFeederContext";
export default React.memo(function WordCountPicker(props) {
const {setWordListCount, wordListCountMax} = useContext(WordListPickerContext)
const {resetFeeder} = useContext(WordFeederContext)
function handleChange(e) {
resetFeeder()
setWordListCount(e.target.value)
}
// Create Options for Select Input
let options = []
for (let i = 0; i < wordListCountMax; i++) {
options.push(<option value={i+1} key={i}>{i+1}</option>)
}
return (
<div id='word-count' className='mode-picker'>
<div id='title'>
Challenge Word Count: <span id="range">(1-{wordListCountMax})</span>
</div>
<div id="input">
<select id="wordCount-picker" defaultValue={wordListCountMax} onChange={handleChange}>
{options}
</select>
</div>
</div>
)
})

View file

@ -1,10 +1,11 @@
import React, {useContext} from "react"
import { WordListPickerContext } from "../contexts/wordListPickerContext";
import { WordFeederContext } from "../contexts/wordFeederContext";
import WordCountPicker from "./WordCountPicker";
export default React.memo(function WordListPicker() {
const {wordListCategory, setWordListCategory} = useContext(WordListPickerContext)
const {wordListCategory, setWordListCategory, metadata} = useContext(WordListPickerContext)
const {resetFeeder, setOrder} = useContext(WordFeederContext)
const orderOpts = ['sequential', 'random']
@ -26,17 +27,8 @@ export default React.memo(function WordListPicker() {
}
}
let wordLists = ['alphabet', 'numbers', 'boys', 'girls', 'startrek', 'common100', 'test', 'short']
const metadata = {
'alphabet': {name: 'Alphabet', description: 'Each letter of the alphabet', count: 26},
'numbers': {name: 'Numbers', description: '0-9', count: 10},
'boys': {name: 'Boys Names', description: 'Top 20 Boys Names', count: 20},
'girls': {name: 'Girls Names', description: 'Top 20 Girls Names', count: 20},
'startrek': {name: 'Star Trek', description: 'Word list from the Star Trek universe', count: 20},
'common100': {name: 'Common 100', description: '100 most common words', count: 100},
'test': {name: 'Test List', description: 'A test list', count: 5},
'short': {name: 'Short List', description: 'A short list', count: 1}
}
let wordLists = Object.keys(metadata)
let options = wordLists.map((wl, index) => (<option value={wl} key={index}>{metadata[wl]['name']}</option>))
return (
@ -66,6 +58,8 @@ export default React.memo(function WordListPicker() {
</div>
</div>
<WordCountPicker />
<div id="wordlist-description" className="mode-picker">
<div id="title">
Description:
@ -74,14 +68,14 @@ export default React.memo(function WordListPicker() {
{metadata[wordListCategory]['description']}
</div>
</div>
<div id="wordlist-count" className="mode-picker">
{/* <div id="wordlist-count" className="mode-picker">
<div id="title">
# of List Items:
</div>
<div id="info">
{metadata[wordListCategory]['count']}
</div>
</div>
</div> */}
</div>
)
})

View file

@ -12,10 +12,13 @@ const WordListPickerContext = React.createContext()
function WordListPickerContextProvider(props) {
const [wordListCategory, setWordListCategory] = useState('alphabet')
const [wordListCount, setWordListCount] = useState(10)
let wordList = []
const testList = ['gene', 'anya', 'ali', 'liam', 'last']
const short = ['gene']
if (wordListCategory === 'alphabet') {
wordList = alphabet.words
} else if (wordListCategory === 'numbers') {
@ -34,6 +37,19 @@ function WordListPickerContextProvider(props) {
wordList = short
}
const wordListCountMax = wordList.length
const metadata = {
'alphabet': {name: 'Alphabet', description: 'All letters of the alphabet'},
'numbers': {name: 'Numbers', description: '0-9'},
'boys': {name: 'Boys Names', description: 'Popular Boys Names'},
'girls': {name: 'Girls Names', description: 'Popular Girls Names'},
'startrek': {name: 'Star Trek', description: 'Star Trek universe'},
'common100': {name: 'Common Words', description: '100 Most Common Words'},
'test': {name: 'Test List', description: 'A test list'},
'short': {name: 'Short List', description: 'A short list'}
}
function randomize(arr) {
let array = [...arr]
@ -56,7 +72,16 @@ function WordListPickerContextProvider(props) {
}
return (
<WordListPickerContext.Provider value={{wordList: wordList, wordListShuffled: randomize(wordList), wordListCategory: wordListCategory, setWordListCategory: setWordListCategory}}>
<WordListPickerContext.Provider value={{
wordList: wordList.slice(0,wordListCount),
wordListShuffled: randomize(wordList).slice(0,wordListCount),
wordListCategory: wordListCategory,
setWordListCategory: setWordListCategory,
metadata: metadata,
wordListCount: wordListCount,
setWordListCount: setWordListCount,
wordListCountMax: wordListCountMax
}}>
{props.children}
</WordListPickerContext.Provider>
)

View file

@ -48,15 +48,76 @@ html, body {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
padding-left: 15px;
background: #333;
font-family: "Roboto", sans-serif;
font-size: 2.5em;
color: #dab820;
color: #eee;
z-index: 1000;
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.45);
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.45);
font-size: 2.5em;
}
#header #title {
font-weight: bold;
text-transform: uppercase;
}
#header #social-links {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: space-evenly;
-ms-flex-pack: space-evenly;
justify-content: space-evenly;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
font-size: 1rem;
font-weight: bold;
text-transform: uppercase;
color: #999;
margin-right: 10px;
}
#header #social-links i {
color: #ccc;
padding-left: 5px;
padding-right: 5px;
font-size: 2rem;
}
#header #social-links i:hover {
color: goldenrod;
}
#header #social-links div {
height: auto;
}
#header #social-links div img {
width: 40px;
height: 40px;
opacity: 30%;
}
#header #social-links div img:hover {
-webkit-animation-name: socialLinkHover;
animation-name: socialLinkHover;
-webkit-animation-duration: 150ms;
animation-duration: 150ms;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
-webkit-animation-timing-function: ease-in-out;
animation-timing-function: ease-in-out;
}
#header #social-links div#twitter img {
-webkit-filter: invert(90%);
filter: invert(90%);
}
#main-content {
@ -471,6 +532,9 @@ html, body {
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background: #333;
font-family: "Roboto", sans-serif;
font-size: 1em;
@ -478,18 +542,13 @@ html, body {
z-index: 1000;
}
#footer a {
color: #eee;
text-decoration: none;
#footer i {
font-size: 1.3em;
color: #ccc;
}
#footer a:visited {
color: #eee;
}
#footer a:hover {
#footer i:hover {
color: gold;
text-decoration: underline;
}
h2 {
@ -1496,16 +1555,25 @@ i[class*="ri-"] {
}
#header {
width: 100vw;
font-size: 1.5em;
font-size: 1.3em;
height: 1.5em;
min-height: 1.5em;
max-height: 1.5em;
padding-left: 5px;
}
#header #social-links {
margin-right: 0px;
font-size: 1rem;
}
#header #social-links i {
font-size: 1.5rem;
padding-left: 0px;
}
#root #main-content {
height: calc(100vh - 4.1em);
}
#root #main-content .sidebar#left {
top: 2.2em;
top: 2em;
width: 100vw;
min-width: 40%;
max-width: 100vw;

File diff suppressed because one or more lines are too long

View file

@ -21,6 +21,7 @@
"t",
"u",
"v",
"w",
"x",
"y",
"z"

View file

@ -17,6 +17,38 @@
"William",
"Daniel",
"Jayden",
"Oliver"
"Oliver",
"Carter",
"Sebastian",
"Joseph",
"David",
"Gabriel",
"Julian",
"Jackson",
"Anthony",
"Dylan",
"Wyatt",
"Grayson",
"Isaiah",
"Christopher",
"Joshua",
"Christian",
"Andrew",
"Samuel",
"Mateo",
"Jaxon",
"Josiah",
"John",
"Luke",
"Ryan",
"Nathan",
"Isaac",
"Owen",
"Henry",
"Levi",
"Aaron",
"Caleb",
"Jeremiah",
"Landon"
]
}

View file

@ -17,8 +17,38 @@
"Sofia",
"Scarlett",
"Aria",
"Elizabeth"
"Elizabeth",
"Camila",
"Layla",
"Ella",
"Chloe",
"Zoey",
"Penelope",
"Skylar",
"Grace",
"Mila",
"Lillian",
"Aaliyah",
"Lily",
"Paisley",
"Bella",
"Brooklyn",
"Savannah",
"Luna",
"Natalie",
"Ellie",
"Leah",
"Audrey",
"Ariana",
"Aurora",
"Zoe",
"Hannah",
"Violet",
"Samantha",
"Nora",
"Nevaeh",
"Serenity",
"Gabriella",
"Hailey"
]
}
}

View file

@ -3,13 +3,16 @@
"dilithium",
"borg",
"replicator",
"picard",
"vulcan",
"ensign",
"phaser",
"kirk",
"warbird",
"ferengi",
"hypospray",
"tribble",
"sisko",
"starfleet",
"engage",
"holodeck",
@ -18,7 +21,12 @@
"romulan",
"quadrant",
"tricorder",
"georgiou",
"futile",
"klingon"
"klingon",
"janeway",
"delta",
"assimilate",
"energize"
]
}

View file

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View file

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -51,14 +51,61 @@ html, body {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 15px;
background: #333;
font-family: $main-font;
font-size: 2.5em;
color: rgb(218, 184, 32);
color: $main-bg-color-light;
z-index: 1000;
box-shadow: 0px 2px 2px rgba(0,0,0,0.45);
font-size: 2.5em;
#title {
font-weight: bold;
text-transform: uppercase;
}
#social-links {
display: flex;
justify-content: space-evenly;
align-items: center;
font-size: 1rem;
font-weight: bold;
text-transform: uppercase;
color: #999;
margin-right: 10px;
// border: 1px solid red;
i {
color: #ccc;
padding-left: 5px;
padding-right: 5px;
font-size: 2rem;
&:hover {
color: goldenrod;
}
}
// border: 1px solid orange;
div {
// border: 1px solid red;
height: auto;
img {
width: 40px;
height: 40px;
opacity: 30%;
// filter: contrast(100%)
&:hover {
animation-name: socialLinkHover;
animation-duration: 150ms;
animation-fill-mode: forwards;
animation-timing-function: ease-in-out;
}
}
&#twitter img {
filter: invert(90%)
}
}
}
}
#main-content {
// border: 1px solid red;
@ -385,22 +432,20 @@ html, body {
padding: 0.3em;
display: flex;
justify-content: center;
align-items: center;
background: #333;
font-family: $main-font;
font-size: 1em;
// font-weight: bold;
color: $main-bg-color-light;
z-index: 1000;
a {
color: $main-bg-color-light;
text-decoration: none;
}
a:visited {
color: $main-bg-color-light;
}
a:hover {
color: gold;
text-decoration: underline;
i {
font-size: 1.3em;
color: #ccc;
&:hover {
color: gold;
}
}
}
h2 {
@ -1246,17 +1291,27 @@ $morse-button-color: rgba(112, 138, 144,0.5);
}
#header {
width: 100vw;
font-size: 1.5em;
font-size: 1.3em;
height: 1.5em;
min-height: 1.5em;
max-height: 1.5em;
padding-left: 5px;
#social-links {
margin-right: 0px;
font-size: 1rem;
i {
font-size: 1.5rem;
padding-left: 0px;
}
}
}
#root {
#main-content {
height: calc(100vh - 4.1em);
.sidebar#left {
top: 2.2em;
top: 2em;
width: 100vw;
min-width: 40%;
max-width: 100vw;