diff --git a/docs/asset-manifest.json b/docs/asset-manifest.json index 0939544..bbc1ac7 100644 --- a/docs/asset-manifest.json +++ b/docs/asset-manifest.json @@ -1,14 +1,14 @@ { "files": { "main.css": "./static/css/main.f984a888.chunk.css", - "main.js": "./static/js/main.84410772.chunk.js", - "main.js.map": "./static/js/main.84410772.chunk.js.map", + "main.js": "./static/js/main.0640bcb2.chunk.js", + "main.js.map": "./static/js/main.0640bcb2.chunk.js.map", "runtime-main.js": "./static/js/runtime-main.7ce483fb.js", "runtime-main.js.map": "./static/js/runtime-main.7ce483fb.js.map", "static/js/2.9e4b6b4d.chunk.js": "./static/js/2.9e4b6b4d.chunk.js", "static/js/2.9e4b6b4d.chunk.js.map": "./static/js/2.9e4b6b4d.chunk.js.map", "index.html": "./index.html", - "precache-manifest.04d9ba919653cb0dc611b53d9527a267.js": "./precache-manifest.04d9ba919653cb0dc611b53d9527a267.js", + "precache-manifest.6997a9090c2a6212a007afc1c66b1781.js": "./precache-manifest.6997a9090c2a6212a007afc1c66b1781.js", "service-worker.js": "./service-worker.js", "static/css/main.f984a888.chunk.css.map": "./static/css/main.f984a888.chunk.css.map" }, @@ -16,6 +16,6 @@ "static/js/runtime-main.7ce483fb.js", "static/js/2.9e4b6b4d.chunk.js", "static/css/main.f984a888.chunk.css", - "static/js/main.84410772.chunk.js" + "static/js/main.0640bcb2.chunk.js" ] } \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 7104361..15480f8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1 +1 @@ -Qwixx App
\ No newline at end of file +Qwixx App
\ No newline at end of file diff --git a/docs/precache-manifest.6997a9090c2a6212a007afc1c66b1781.js b/docs/precache-manifest.6997a9090c2a6212a007afc1c66b1781.js new file mode 100644 index 0000000..9a58e02 --- /dev/null +++ b/docs/precache-manifest.6997a9090c2a6212a007afc1c66b1781.js @@ -0,0 +1,22 @@ +self.__precacheManifest = (self.__precacheManifest || []).concat([ + { + "revision": "2609b41d358fee71f9df31f5dc0cad17", + "url": "./index.html" + }, + { + "revision": "fe05544e4cd30852989d", + "url": "./static/css/main.f984a888.chunk.css" + }, + { + "revision": "368201668f4462647aaf", + "url": "./static/js/2.9e4b6b4d.chunk.js" + }, + { + "revision": "fe05544e4cd30852989d", + "url": "./static/js/main.0640bcb2.chunk.js" + }, + { + "revision": "77355447c501bfc3172a", + "url": "./static/js/runtime-main.7ce483fb.js" + } +]); \ No newline at end of file diff --git a/docs/service-worker.js b/docs/service-worker.js index cecdb97..b1552bf 100644 --- a/docs/service-worker.js +++ b/docs/service-worker.js @@ -14,7 +14,7 @@ importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); importScripts( - "./precache-manifest.04d9ba919653cb0dc611b53d9527a267.js" + "./precache-manifest.6997a9090c2a6212a007afc1c66b1781.js" ); self.addEventListener('message', (event) => { diff --git a/docs/static/js/main.0640bcb2.chunk.js b/docs/static/js/main.0640bcb2.chunk.js new file mode 100644 index 0000000..93cf4ea --- /dev/null +++ b/docs/static/js/main.0640bcb2.chunk.js @@ -0,0 +1,2 @@ +(this["webpackJsonpquixx-score-card"]=this["webpackJsonpquixx-score-card"]||[]).push([[0],{80:function(e,t,n){e.exports=n(95)},85:function(e,t,n){},95:function(e,t,n){"use strict";n.r(t);var a=n(0),r=n.n(a),o=n(9),i=n.n(o),l=n(137),c=n(136),s=(n(85),Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)));function g(e,t){navigator.serviceWorker.register(e).then((function(e){e.onupdatefound=function(){var n=e.installing;null!=n&&(n.onstatechange=function(){"installed"===n.state&&(navigator.serviceWorker.controller?(console.log("New content is available and will be used when all tabs for this page are closed. See https://bit.ly/CRA-PWA."),t&&t.onUpdate&&t.onUpdate(e)):(console.log("Content is cached for offline use."),t&&t.onSuccess&&t.onSuccess(e)))})}})).catch((function(e){console.error("Error during service worker registration:",e)}))}var d=n(71),u=n(14),m=n(30),h=n(60),p=n(61),b=n(72),f=n(70),k=n(47),w=n.n(k),v=n(121),S=n(138),y=n(123),C=n(124),E=n(125),x=n(126),O=n(139),R=n(127),N=n(122),j=n(128),D=n(129),A=n(130),I=n(131),W=n(132),G=n(133),B=n(134),L=n(97),T=n(4),z=n(68),H=n.n(z),J=n(67),U=n.n(J),Q=n(99);function F(e){var t=e.score,n=e.onClick,a=e.revealScore,o=e.color,i=e.reverse,l=void 0!==i&&i,c=e.row,s=e.showScore,g=Object(m.a)(c,2),d=g[0],h=g[1],p=function(e){return Object(Q.a)((function(t){var n=t.palette,a=n.blue,r=n.green,o=n.red,i=n.yellow;return{marks:{backgroundColor:t.palette[e].main,padding:t.spacing(),paddingLeft:"env(safe-area-inset-left)"},number:{cursor:"pointer",borderRadius:t.spacing(),marginLeft:t.spacing(),padding:t.spacing()},numberContent:{textAlign:"center",width:"100%"},liveNumber:{backgroundColor:t.palette[e].light,color:t.palette[e].main},disabledNumber:{backgroundColor:"transparent",color:"black"},disabledNumberContent:{textDecoration:"line-through"},x:{fontWeight:"bold",color:"black"},square:{borderRadius:t.spacing()},circle:Object(u.a)({borderRadius:t.spacing(20)},t.breakpoints.down("sm"),{padding:t.spacing()}),openLock:{fontSize:t.typography.fontSize,marginBottom:-4,transform:"rotate(45deg)"},lock:{fontSize:t.typography.fontSize,marginBottom:-4},scoreContent:{paddingRight:t.spacing(2)},block:{backgroundColor:"white",cursor:"pointer",padding:t.spacing(2),textAlign:"center"},blockRed:{borderColor:o.main,backgroundColor:o.light,color:o.light,paddingRight:"env(safe-area-inset-right)"},blockYellow:{borderColor:i.main,backgroundColor:i.light,color:i.light,paddingRight:"env(safe-area-inset-right)"},blockGreen:{borderColor:r.main,backgroundColor:r.light,color:r.light,paddingRight:"env(safe-area-inset-right)"},blockBlue:{borderColor:a.main,backgroundColor:a.light,color:a.light,paddingRight:"env(safe-area-inset-right)"},blackText:{color:"black"}}}))}(o)(),b=d.filter((function(e){return e})).length<5,f=o.charAt(0).toUpperCase()+o.slice(1);return r.a.createElement(v.a,{container:!0,direction:"row",justifyContent:"center",alignItems:"center"},r.a.createElement(v.a,{item:!0,xs:!0,key:"0",className:p.marks},r.a.createElement(v.a,{container:!0,direction:"row",justifyContent:"center",alignItems:"center"},d.map((function(e,t){var a=t+1===d.length;return t<10?r.a.createElement(v.a,{item:!0,xs:!0,key:o+t,className:Object(T.a)(p.number,p.square,h[t]&&!e?p.disabledNumber:p.liveNumber),onClick:function(){return n(o,t)}},r.a.createElement("div",{className:Object(T.a)(p.numberContent,h[t]&&!e&&p.disabledNumberContent)},e?r.a.createElement("span",{className:p.x},"X"):l?d.length-t:t+2)):r.a.createElement(v.a,{item:!0,xs:!0,key:o+t,className:Object(T.a)(p.number,p.square,h[t]&&!e?p.disabledNumber:p.liveNumber),onClick:function(){return n(o,t,a)}},r.a.createElement("div",{className:Object(T.a)(p.numberContent,!b&&h[t]&&!e&&p.disabledNumberContent)},e?a?r.a.createElement(U.a,{className:p.lock}):r.a.createElement("span",{className:p.x},"X"):a?r.a.createElement(H.a,{className:p.openLock}):l?d.length-t:t+2))})))),r.a.createElement(v.a,{item:!0,xs:1,key:"1",className:Object(T.a)(p.block,p["block".concat(f)],s&&p.blackText),onClick:function(){return a("show".concat(f))}},r.a.createElement("div",{className:p.scoreContent},t)))}var X=n(31),q=n(33),Y=Object(Q.a)((function(e){var t=e.palette,n=t.grey,a=t.blue,r=t.green,o=t.red,i=t.yellow;return{scoreContainer:{marginRight:e.spacing(2)},score:{backgroundColor:"white",border:"2px solid ".concat(e.palette.grey.main),borderRadius:e.spacing(),paddingLeft:e.spacing()/2,paddingRight:e.spacing()/2,fontSize:18},scoreTop:{borderBottom:"1px solid ".concat(e.palette.grey.main),textAlign:"center",padding:e.spacing(.75)},scoreBottom:{padding:e.spacing()/2,textAlign:"center"},stop:{color:"white",marginRight:e.spacing(2)},reset:{color:"white",marginRight:e.spacing(2)},history:{color:"white",marginRight:e.spacing(2)},minusIcon:{color:"white",marginLeft:e.spacing(30)},moves:{backgroundColor:"white",border:"1px solid ".concat(e.palette.grey.main),borderRadius:e.spacing()/2,cursor:"pointer",fontWeight:"bold",padding:"5px ".concat(e.spacing(1.9),"px")},movesEmpty:{visibility:"hidden"},movesRed:{backgroundColor:o.light},movesYellow:{backgroundColor:i.light},movesGreen:{backgroundColor:r.light},movesBlue:{backgroundColor:a.light},strike:{backgroundColor:"white",border:"1px solid ".concat(e.palette.grey.main),borderRadius:e.spacing()/2,cursor:"pointer",fontWeight:"bold",padding:"5px ".concat(e.spacing(1.9),"px")},strikeEmpty:{color:"White"},strikesContainer:{width:"100%"},strikesLabel:{fontSize:18,textAlign:"center"},strikesLabelX:{fontWeight:"bold",color:"red"},block:{backgroundColor:"white",border:"2px solid ".concat(n.dark),borderRadius:e.spacing(2),marginLeft:e.spacing(2),cursor:"pointer",flexGrow:1,flexShrink:0,padding:e.spacing(1),textAlign:"center",width:64},blockWhite:{color:"white"},blackText:{color:"black"},strikesScore:{marginRight:e.spacing(3)},totalScore:{marginRight:e.spacing(1.5)}}}));var M=function(e){var t=e.onClick,n=e.onClickUndo,a=e.onReset,o=e.onEndGame,i=e.onHistory,l=e.moves,c=e.strikes,s=e.showStrikes,g=e.showFinal,d=e.revealScore,u=e.strikesScore,m=e.totalScore,h=Y(),p=[2,3,4,5,6,7,8,9,10,11,12];return r.a.createElement(v.a,{container:!0,justify:"space-between",alignItems:"center",wrap:"nowrap",className:h.row},r.a.createElement(v.a,{item:!0,className:h.strikesContainer},r.a.createElement(v.a,{container:!0,spacing:1,justify:"space-around",alignItems:"center",wrap:"nowrap"},r.a.createElement(X.a,{icon:q.a,className:h.stop,onClick:o}),r.a.createElement(X.a,{icon:q.c,className:h.reset,onClick:a}),r.a.createElement(X.a,{icon:q.d,className:h.history,onClick:i}),r.a.createElement("div",{onClick:n,className:Object(T.a)(h.moves,0===l.length?h.movesEmpty:h["moves".concat(l[0][0][0].toUpperCase()+l[0][0].slice(1))])},l.length>0?(["red","yellow"].includes(l[0][0])?p:p.toReversed())[l[0][1]]:null),r.a.createElement(X.a,{icon:q.b,className:h.minusIcon}),c.map((function(e,n){return r.a.createElement(v.a,{item:!0,key:n,onClick:function(){return t(n)}},r.a.createElement("div",{className:Object(T.a)(h.strike,e?null:h.strikeEmpty)},"X"))})),r.a.createElement(v.a,{item:!0,className:Object(T.a)(h.block,h.blockWhite,h.strikesScore,s&&h.blackText),onClick:function(){return d("showStrikes")}},u),r.a.createElement(v.a,{item:!0,className:Object(T.a)(h.block,h.blockWhite,h.totalScore,g&&h.blackText),onClick:function(){return d("showFinal")}},m))))},V=[0,1,3,6,10,15,21,28,36,45,55,66,78],P={red:2,yellow:3,green:4,blue:5},$={moves:[],blue:[new Array(12).fill(!1),[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1,!0,!1]],blueScore:0,disabledDice:new Array(6).fill(!1),endGameDialogOpen:!1,historyDialogOpen:!1,green:[new Array(12).fill(!1),[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1,!0,!1]],greenScore:0,red:[new Array(12).fill(!1),[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1,!0,!1]],redScore:0,showBlue:!0,showFinal:!0,showGreen:!0,showRed:!0,showStrikes:!0,showYellow:!0,strikes:new Array(4).fill(!1),strikesScore:0,yellow:[new Array(12).fill(!1),[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1,!0,!1]],yellowScore:0},K=function(e){Object(b.a)(n,e);var t=Object(f.a)(n);function n(){var e;Object(h.a)(this,n);for(var a=arguments.length,r=new Array(a),o=0;o0&&l[0][0]===t&&l[0][1]===n?l.shift():l.unshift([t,n])),a&&(!s[n]&&!i[P[t]]||s[n]&&i[P[t]])&&e.toggleDisabled(t),s[n]=!s[n];var d=s.filter((function(e){return e})).length-(s[11]&&!s[10]?1:0),h=V[d];g=g.map((function(e,t){return t===s.length-2&&d<5||t1&&e.setState({endGameDialogOpen:!0})}},e.handleClickUndo=function(){var t=e.state.moves;e.handleClick(t[0][0],t[0][1],!1)},e.handleClickStrikes=function(t){var n=e.state.strikes;n[t]=!n[t];var a=5*n.filter((function(e){return e})).length;e.setState({strikes:n,strikesScore:a}),4==n.reduce((function(e,t){return e+t}),0)&&e.setState({endGameDialogOpen:!0})},e.handleReset=function(t,n){(n||window.confirm("Are you sure you want to reset the card?"))&&e.setState(w()($))},e.handleDelete=function(t,n){if(window.confirm("Are you sure you want to delete the record?")){var a=JSON.parse(localStorage.getItem("QuixxHistory"));a.splice(n,1),localStorage.setItem("QuixxHistory",JSON.stringify(a)),e.setState({historyDialogOpen:!0})}},e.handleView=function(e,t){window.alert("Stay tuned for this feature!")},e.toggleDisabled=function(t){var n=e.state.disabledDice,a=Object(m.a)(e.state[t],2),r=a[0],o=a[1],i=P[t];if(n[i]=!n[i],n[i])o=o.map((function(){return!0})),r[r.length-1]&&(o[o.length-1]=!1);else{var l=r.filter((function(e){return e})).length;o=o.map((function(e,t){return t>=r.length-2&&l<5||t {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl)\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import React from 'react';\nimport clsx from 'clsx';\nimport { Grid } from '@material-ui/core';\nimport OpenLockIcon from '@material-ui/icons/LockOpenOutlined';\nimport LockIcon from '@material-ui/icons/Lock';\nimport { makeStyles } from '@material-ui/styles';\n\nconst useStyles = (color) => makeStyles((theme) => {\n const { blue, green, red, yellow } = theme.palette;\n\n return ({\n marks: {\n backgroundColor: theme.palette[color].main,\n padding: theme.spacing(),\n paddingLeft: `env(safe-area-inset-left)`,\n },\n number: {\n cursor: 'pointer',\n borderRadius: theme.spacing(),\n marginLeft: theme.spacing(),\n padding: theme.spacing(),\n },\n numberContent: {\n textAlign: 'center',\n width: '100%',\n },\n liveNumber: {\n backgroundColor: theme.palette[color].light,\n color: theme.palette[color].main,\n },\n disabledNumber: {\n backgroundColor: 'transparent',\n color: 'black',\n },\n disabledNumberContent: {\n textDecoration: 'line-through',\n },\n x: {\n fontWeight: 'bold',\n color: 'black',\n },\n square: {\n borderRadius: theme.spacing(),\n },\n circle: {\n borderRadius: theme.spacing(20),\n [theme.breakpoints.down('sm')]: {\n padding: theme.spacing(),\n },\n },\n openLock: {\n fontSize: theme.typography.fontSize,\n marginBottom: -4,\n transform: 'rotate(45deg)',\n },\n lock: {\n fontSize: theme.typography.fontSize,\n marginBottom: -4,\n },\n scoreContent: {\n paddingRight: theme.spacing(2),\n },\n block: {\n backgroundColor: 'white',\n cursor: 'pointer',\n padding: theme.spacing(2),\n textAlign: 'center',\n },\n blockRed: {\n borderColor: red.main,\n backgroundColor: red.light,\n color: red.light,\n paddingRight: `env(safe-area-inset-right)`,\n },\n blockYellow: {\n borderColor: yellow.main,\n backgroundColor: yellow.light,\n color: yellow.light,\n paddingRight: `env(safe-area-inset-right)`,\n },\n blockGreen: {\n borderColor: green.main,\n backgroundColor: green.light,\n color: green.light,\n paddingRight: `env(safe-area-inset-right)`,\n },\n blockBlue: {\n borderColor: blue.main,\n backgroundColor: blue.light,\n color: blue.light,\n paddingRight: `env(safe-area-inset-right)`,\n },\n blackText: {\n color: 'black',\n },\n });\n});\n\nexport default function ColorRow(props) {\n const {\n score,\n onClick,\n revealScore,\n color,\n reverse = false,\n row,\n showScore,\n } = props;\n const [marks, disabled] = row;\n const classes = useStyles(color)();\n const fiveXLocked = marks.filter(value => value).length < 5;\n const capitalizedColor = color.charAt(0).toUpperCase() + color.slice(1);\n\n return (\n \n \n \n {marks.map((selected, i) => {\n const isLock = i + 1 === marks.length;\n\n return i < 10\n ? onClick(color, i)}\n >\n
\n {selected\n ? X\n : reverse\n ? marks.length - i\n : i + 2\n }\n
\n
\n : onClick(color, i, isLock)}\n >\n
\n {selected\n ? isLock\n ? \n : X\n : isLock\n ? \n : reverse\n ? marks.length - i\n : i + 2\n }\n
\n ;\n })}\n \n \n revealScore(`show${capitalizedColor}`)}\n >\n
{score}
\n
\n \n );\n}","import React from 'react';\nimport clsx from 'clsx';\nimport { Grid } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/styles';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faMinus, faRedo, faCircleStop, faTrophy } from '@fortawesome/free-solid-svg-icons';\n\nconst useStyles = makeStyles((theme) => {\n const { grey, blue, green, red, yellow } = theme.palette;\n\n return ({\n scoreContainer: {\n marginRight: theme.spacing(2),\n },\n score: {\n backgroundColor: 'white',\n border: `2px solid ${theme.palette.grey.main}`,\n borderRadius: theme.spacing(),\n paddingLeft: theme.spacing()/2,\n paddingRight: theme.spacing()/2,\n fontSize: 18,\n },\n scoreTop: {\n borderBottom: `1px solid ${theme.palette.grey.main}`,\n textAlign: 'center',\n padding: theme.spacing(0.75),\n },\n scoreBottom: {\n padding: theme.spacing()/2,\n textAlign: 'center',\n },\n stop: {\n color: 'white',\n marginRight: theme.spacing(2),\n },\n reset: {\n color: 'white',\n marginRight: theme.spacing(2),\n },\n history: {\n color: 'white',\n marginRight: theme.spacing(2),\n },\n minusIcon: {\n color: 'white',\n marginLeft: theme.spacing(30),\n },\n moves: {\n backgroundColor: 'white',\n border: `1px solid ${theme.palette.grey.main}`,\n borderRadius: theme.spacing()/2,\n cursor: 'pointer',\n fontWeight: 'bold',\n padding: `5px ${theme.spacing(1.9)}px`,\n },\n movesEmpty: {\n visibility: 'hidden',\n },\n movesRed: {\n backgroundColor: red.light,\n },\n movesYellow: {\n backgroundColor: yellow.light,\n },\n movesGreen: {\n backgroundColor: green.light,\n },\n movesBlue: {\n backgroundColor: blue.light,\n },\n strike: {\n backgroundColor: 'white',\n border: `1px solid ${theme.palette.grey.main}`,\n borderRadius: theme.spacing()/2,\n cursor: 'pointer',\n fontWeight: 'bold',\n padding: `5px ${theme.spacing(1.9)}px`,\n },\n strikeEmpty: {\n color: 'White',\n },\n strikesContainer: {\n width: \"100%\",\n },\n strikesLabel: {\n fontSize: 18,\n textAlign: 'center',\n },\n strikesLabelX: {\n fontWeight: 'bold',\n color: 'red',\n },\n block: {\n backgroundColor: 'white',\n border: `2px solid ${grey.dark}`,\n borderRadius: theme.spacing(2),\n marginLeft: theme.spacing(2),\n cursor: 'pointer',\n flexGrow: 1,\n flexShrink: 0,\n padding: theme.spacing(1),\n textAlign: 'center',\n width: 64\n },\n blockWhite: {\n color: 'white',\n },\n blackText: {\n color: 'black',\n },\n strikesScore: {\n marginRight: theme.spacing(3),\n },\n totalScore: {\n marginRight: theme.spacing(1.5),\n },\n });\n});\n\nfunction StrikesRow(props) {\n const { onClick, onClickUndo, onReset, onEndGame, onHistory, moves, strikes, showStrikes, showFinal, revealScore, strikesScore, totalScore } = props;\n const classes = useStyles();\n const numbers = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];\n\n return (\n \n \n \n \n \n \n
\n {moves.length > 0\n ? (['red', 'yellow'].includes(moves[0][0]) ? numbers : numbers.toReversed())[moves[0][1]]\n : null}\n
\n \n {strikes.map((strike, i) => (\n onClick(i)}\n >\n
\n X\n
\n
\n ))}\n revealScore('showStrikes')}\n >\n {strikesScore}\n \n revealScore('showFinal')}\n >\n {totalScore}\n \n
\n
\n
\n );\n}\n\nexport default StrikesRow;","import React, { Component } from 'react';\nimport cloneDeep from 'lodash.clonedeep';\nimport { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Grid, Paper, useMediaQuery, Table, TableContainer, TableHead, TableRow, TableBody, TableCell } from '@material-ui/core';\nimport { DeleteForever, Visibility } from '@material-ui/icons';\nimport { withStyles } from '@material-ui/styles';\nimport { useTheme } from '@material-ui/core/styles';\nimport ColorRow from './components/ColorRow';\nimport StrikesRow from './components/StrikesRow';\n\nconst scoring = [0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78];\nconst styles = (theme) => ({\n cardSubTitle: {\n color: theme.palette.grey.main,\n display: 'inline-block',\n fontWeight: 'bold',\n flexGrow: 1,\n },\n disclaimer: {\n textAlign: 'center',\n margin: `${theme.spacing(2)} auto`,\n fontSize: 14,\n },\n fiveXTop: {\n border: '1px solid',\n borderBottom: 0,\n borderTopLeftRadius: theme.spacing(2),\n borderTopRightRadius: theme.spacing(2),\n display: 'inline-block',\n float: 'right',\n fontSize: 14,\n marginRight: theme.spacing(0.75),\n textAlign: 'center',\n width: 130,\n },\n fiveXBottom: {\n border: '1px solid',\n borderBottomLeftRadius: theme.spacing(4),\n borderBottomRightRadius: theme.spacing(4),\n borderTop: 0,\n display: 'inline-block',\n float: 'right',\n height: theme.spacing(),\n marginBottom: theme.spacing(),\n marginRight: theme.spacing(0.75),\n marginTop: -theme.spacing(2),\n width: 130,\n },\n game: {\n backgroundColor: '#282c34',\n height: '100%',\n paddingBottom: `env(safe-area-inset-bottom)`,\n },\n});\n\nconst diceIndex = { red: 2, yellow: 3, green: 4, blue: 5 };\nconst blankState = {\n moves: [],\n blue: [\n new Array(12).fill(false),\n [false, false, false, false, false, false, false, false, false, false, true, false]\n ],\n blueScore: 0,\n disabledDice: new Array(6).fill(false),\n endGameDialogOpen: false,\n historyDialogOpen: false,\n green: [\n new Array(12).fill(false),\n [false, false, false, false, false, false, false, false, false, false, true, false]\n ],\n greenScore: 0,\n red: [\n new Array(12).fill(false),\n [false, false, false, false, false, false, false, false, false, false, true, false]\n ],\n redScore: 0,\n showBlue: true, \n showFinal: true,\n showGreen: true, \n showRed: true, \n showStrikes: true,\n showYellow: true,\n strikes: new Array(4).fill(false),\n strikesScore: 0,\n yellow: [\n new Array(12).fill(false),\n [false, false, false, false, false, false, false, false, false, false, true, false]\n ],\n yellowScore: 0,\n}\n\nclass QuixxScoreCard extends Component {\n state = cloneDeep(blankState);\n\n componentDidMount() {\n // if there is a saved state, reload it\n let savedState = localStorage.getItem('QwixxState');\n if (savedState) {\n savedState = JSON.parse(savedState);\n localStorage.removeItem('QwixxState');\n this.setState(savedState);\n }\n \n // save the state if the user navagates away or refreshes\n window.addEventListener('pagehide', () => {\n console.log('saving state');\n localStorage.setItem('QwixxState', JSON.stringify(this.state));\n });\n }\n \n /**\n * Handles clicks for the colored number rows\n * @param {String} color The color of the row\n * @param {Number} index The index of the clicked square\n * @param {Boolean} isLock Whether or not the square clicked is a lock\n */\n handleClick = (color, index, isLock) => {\n const { disabledDice, moves } = this.state;\n let [marks, disabled] = this.state[color];\n\n // if disabled do nothing\n if (disabled[index]) {\n return;\n }\n\n if (!isLock)\n {\n if (moves.length > 0 && moves[0][0] === color && moves[0][1] === index) {\n moves.shift();\n } else {\n moves.unshift([color, index]);\n }\n }\n\n // Disable a dice if a lock is marked\n // really means toggle the state of both lock and dice when they are in the state\n // this must be done before marks is modified\n if (isLock && (\n (!marks[index] && !disabledDice[diceIndex[color]]) ||\n (marks[index] && disabledDice[diceIndex[color]])\n )) {\n this.toggleDisabled(color);\n }\n\n // mark the square\n marks[index] = !marks[index];\n\n // calculate new score\n const numMarks = marks.filter(value => value).length - (marks[11] && !marks[10] ? 1 : 0);\n const score = scoring[numMarks];\n\n // disable all before the index and enable all after\n disabled = disabled.map((element, i) => {\n // Check lock section first, then check the rest\n return (i === marks.length - 2 && numMarks < 5) || i < marks.lastIndexOf(true);\n });\n\n this.setState({\n moves: moves,\n [color]: [marks, disabled],\n [`${color}Score`]: score,\n });\n\n // If two rows have become locked, end the game\n const { red, yellow, green, blue } = this.state;\n if ((red[0].toReversed()[0] + yellow[0].toReversed()[0] + green[0].toReversed()[0] + blue[0].toReversed()[0]) > 1) {\n this.setState({endGameDialogOpen: true});\n }\n }\n\n handleClickUndo = () => {\n const moves = this.state.moves;\n this.handleClick(moves[0][0], moves[0][1], false);\n }\n\n /**\n * Handles clicks for the strike row \n * @param {Number} index The index of the Strike that was clicked\n */\n handleClickStrikes = (index) => {\n const marks = this.state.strikes;\n\n // mark the square\n marks[index] = !marks[index];\n\n // calculate new score\n const score = marks.filter(value => value).length * 5;\n\n this.setState({\n strikes: marks,\n strikesScore: score,\n });\n\n // If all strikes have been used, end the game\n if (marks.reduce((a, b) => a + b, 0) == 4) {\n this.setState({endGameDialogOpen: true});\n }\n }\n\n handleReset = (e, skipConfirm) => {\n // TODO: Use proper dialog\n if (skipConfirm || window.confirm('Are you sure you want to reset the card?'))\n {\n this.setState(cloneDeep(blankState));\n }\n }\n\n handleDelete = (e, i) => {\n // TODO: Use proper dialog\n if (window.confirm('Are you sure you want to delete the record?'))\n {\n var history = JSON.parse(localStorage.getItem(\"QuixxHistory\"));\n history.splice(i, 1);\n localStorage.setItem(\"QuixxHistory\", JSON.stringify(history));\n // Cause the dialog to refresh\n this.setState({historyDialogOpen: true});\n }\n }\n\n handleView = (e, i) => {\n // TODO: Show the game state from the history\n window.alert(\"Stay tuned for this feature!\");\n }\n\n toggleDisabled = (color) => {\n const { disabledDice } = this.state;\n let [marks, disabled] = this.state[color];\n const index = diceIndex[color];\n\n // toggle the specified dice\n disabledDice[index] = !disabledDice[index];\n\n // disable or enable the entries of a row\n if (disabledDice[index]) {\n disabled = disabled.map(() => true);\n\n // don't disable the lock if it is marked\n if (marks[marks.length - 1]) {\n disabled[disabled.length - 1] = false;\n }\n } else {\n const numMarks = marks.filter(value => value).length;\n disabled = disabled.map((element, i) => {\n // Check lock section first, then check the rest\n return (i >= marks.length - 2 && numMarks < 5) || i < marks.lastIndexOf(true);\n });\n }\n\n this.setState({ \n disabledDice,\n [color]: [marks, disabled],\n });\n }\n\n render() {\n const { classes } = this.props;\n // const theme = useTheme();\n // const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));\n const {\n red,\n yellow,\n green,\n blue,\n blueScore = 0,\n greenScore = 0,\n redScore = 0,\n showBlue,\n showGreen,\n showRed,\n showYellow,\n showFinal,\n showStrikes,\n moves,\n strikes,\n strikesScore = 0,\n yellowScore = 0,\n endGameDialogOpen,\n historyDialogOpen,\n } = this.state;\n\n const qwixxHistory = JSON.parse(localStorage.getItem('QuixxHistory') || '[]');\n const scores = qwixxHistory.map((item) => item.score);\n const averageScore = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length);\n const highScore = Math.max(...scores);\n const winRate = 100 * qwixxHistory.map((item) => item.won).reduce((a, b) => a + b, 0) / qwixxHistory.length;\n\n const getTotalScore = () => redScore + yellowScore + greenScore + blueScore - strikesScore;\n \n const handleLoss = (e) => handleRecordScore(e, false);\n const handleWin = (e) => handleRecordScore(e, true);\n\n const handleEndGame = () => {\n this.setState({endGameDialogOpen: true});\n };\n \n const handleRecordScore = (e, won) => {\n this.setState({endGameDialogOpen: false});\n const scores = JSON.parse(localStorage.getItem(\"QuixxHistory\") || '[]');\n scores.push({\n 'date': (new Date()).toISOString(),\n 'score': getTotalScore(),\n 'won': won,\n 'state': cloneDeep(this.state),\n });\n localStorage.setItem('QuixxHistory', JSON.stringify(scores));\n this.handleReset(e, true);\n };\n\n return (\n \n this.setState({ [score]: !this.state[score] })}\n />\n this.setState({ [score]: !this.state[score] })}\n />\n this.setState({ [score]: !this.state[score] })}\n />\n this.setState({ [score]: !this.state[score] })}\n />\n this.setState({historyDialogOpen: true})}\n onClick={(i) => this.handleClickStrikes(i)}\n showFinal={showFinal}\n showStrikes={showStrikes}\n totalScore={getTotalScore()}\n strikesScore={-strikesScore}\n revealScore={(score) => this.setState({ [score]: !this.state[score] })}\n />\n\n this.setState({endGameDialogOpen: false})}\n aria-labelledby=\"responsive-dialog-title\"\n >\n Game Over\n \n \n Did you win?\n \n \n \n \n \n \n \n \n\n this.setState({historyDialogOpen: false})}\n aria-labelledby=\"responsive-dialog-title\"\n >\n \n History (Avg: {averageScore} | High: {highScore} | %Win: {winRate})\n \n \n \n \n \n \n \n Date\n Score\n Win?\n View\n Delete\n \n \n \n {qwixxHistory.map((row, i) => (\n \n \n {new Date(row.date).toLocaleDateString()} {new Date(row.date).toLocaleTimeString()}\n \n {row.score}\n {row.won ? 'X' : ''}\n this.handleView(e, i)} />\n this.handleDelete(e, i)} />\n \n ))}\n \n
\n
\n
\n
\n \n \n \n \n \n );\n }\n}\n\nexport default withStyles(styles)(React.memo(QuixxScoreCard));\n","import { createMuiTheme } from '@material-ui/core/styles';\nimport { blue, green, grey, red, yellow } from '@material-ui/core/colors';\n\nexport default createMuiTheme({\n typography: {\n fontSize: 28,\n },\n palette: {\n grey: {\n light: grey[200],\n main: grey[500],\n dark: grey[700],\n darker: grey[900],\n }, \n blue: {\n light: blue[100],\n main: blue[700],\n },\n green: {\n light: green[100],\n main: green[700],\n },\n red: {\n light: red[100],\n main: red[700],\n },\n yellow: {\n light: yellow[100],\n main: yellow[700],\n }\n }\n});","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport CssBaseline from '@material-ui/core/CssBaseline';\nimport { ThemeProvider } from '@material-ui/core/styles';\n\nimport './index.css';\nimport * as serviceWorker from './serviceWorker';\n\nimport App from './App';\nimport Theme from './Theme';\n\nReactDOM.render(\n \n \n \n \n, document.getElementById('root'));\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.register();\n"],"sourceRoot":""} \ No newline at end of file