From 49a78f12af13c61fd92246b2ab6791f632e5dde5 Mon Sep 17 00:00:00 2001 From: Matt Wang Date: Mon, 16 Dec 2019 17:53:21 -0500 Subject: [PATCH] Light Mode (#139) This is a PR that implements a site-wide light mode and dark mode. Here's a summary of the PR: * implements a `themify()` SASS mixin and a `$themes` object to generate themed SASS classes (based on parent class specificity) (@emmyc) * adds themed variants to the editor/output (@emmyc) and to the profile panel + sketches pages (@malsf21) * creates a `` custom component that adds visual decoration to a simple textbox (@emmyc, @fan-matt) * adds the `` component to the profile page to toggle the user's preference (@emmyc) * slightly modify's the Profile Panel's name input (@malsf21) * creates Redux handlers for the user's theme state (@fan-matt) * saves user's state to document-wide cookies (@malsf21) * writes Jest/Enzyme tests for the `` component (@malsf21) Big thanks to @emmyc for taking the lead on this PR, and @fan-matt for helping me correct my small mistakes! --- README.md | 10 +- package-lock.json | 789 +++++++++--------- src/actions/uiActions.js | 5 + src/components/Main.js | 20 +- src/components/Output/OutputContainer.js | 5 +- src/components/Output/Python.js | 40 +- .../Sketches/containers/SketchesContainer.js | 5 +- .../TextEditor/components/TextEditor.js | 17 +- .../containers/TextEditorContainer.js | 8 +- src/components/common/ProfilePanel.js | 20 +- src/components/common/Switch.js | 49 ++ src/components/common/Switch.test.js | 51 ++ .../containers/DropdownButtonContainer.js | 5 +- .../containers/ProfilePanelContainer.js | 7 +- src/components/containers/MainContainer.js | 3 + src/lib/cookies.js | 77 ++ src/lib/index.js | 8 +- src/reducers/uiReducer.js | 5 +- src/styles/CustomCM.scss | 140 ++-- src/styles/Editor.scss | 57 +- src/styles/Footer.scss | 4 +- src/styles/Panel.scss | 58 +- src/styles/SketchBox.scss | 14 +- src/styles/Sketches.scss | 14 +- src/styles/Switch.scss | 57 ++ src/styles/variables.scss | 87 +- 26 files changed, 1024 insertions(+), 531 deletions(-) create mode 100644 src/components/common/Switch.js create mode 100644 src/components/common/Switch.test.js create mode 100644 src/lib/cookies.js create mode 100644 src/styles/Switch.scss diff --git a/README.md b/README.md index 01694129..89dbe00b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## ACM Teach LA's Editor Frontend -[![Build Status](https://travis-ci.org/uclaacm/TeachLAFrontend.svg?branch=master)](https://travis-ci.org/uclaacm/TeachLAFrontend) +[![Build Status](https://travis-ci.org/uclaacm/TeachLAFrontend.svg?branch=master)](https://travis-ci.org/uclaacm/TeachLAFrontend) [![Netlify Status](https://api.netlify.com/api/v1/badges/15895bed-2a7e-4a27-aa63-633a0cd645f1/deploy-status)](https://app.netlify.com/sites/sleepy-franklin-7a3e4c/deploys) This repository holds the frontend code for the ACM Teach LA online editor! Teach LA uses the editor to help teach LA students about Python, Web Development, and expose them to computer science! @@ -58,7 +58,7 @@ The client should now be automatically opened in your browser; however, you can ## Notes for Developers: -* every time you pull from master, make sure to run `npm install` - it's likely that some dependency has changed! -* `lint-staged` and `husky` auto-prettify some JS code on save - don't be spooked! -* Travis CI auto-builds branches and PRs - make sure that `npm run test` `npm run prod_build` pass, or your changes for-sure won't work! -* Netlify auto-deploys PRs, branches, and production deploys using the contents of `npm run prod_build`! +- every time you pull from master, make sure to run `npm install` - it's likely that some dependency has changed! +- `lint-staged` and `husky` auto-prettify some JS code on save - don't be spooked! +- Travis CI auto-builds branches and PRs - make sure that `npm run test` `npm run prod_build` pass, or your changes for-sure won't work! +- Netlify auto-deploys PRs, branches, and production deploys using the contents of `npm run prod_build`! diff --git a/package-lock.json b/package-lock.json index 77cb3f91..c47da7d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1944,6 +1944,11 @@ "strip-ansi": "^5.0.0" }, "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", @@ -2919,9 +2924,19 @@ "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" }, "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", + "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "requires": { + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + } + } }, "ansi-html": { "version": "0.0.7", @@ -2951,16 +2966,6 @@ "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } } }, "aproba": { @@ -3754,12 +3759,30 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "requires": { - "fill-range": "^7.0.1" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "brorand": { @@ -3853,9 +3876,9 @@ } }, "browserslist": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.1.tgz", - "integrity": "sha512-X/lIDboA5bvFg9SOhHN7OBgHHlaZQWcwTXBLBGgrB8+6Iy1dL0wmUfHRe7OdETRTuD2d6f5JPa7iTEcbVyVf6Q==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.2.tgz", + "integrity": "sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA==", "requires": { "caniuse-lite": "^1.0.30001015", "electron-to-chromium": "^1.3.322", @@ -4158,42 +4181,6 @@ "upath": "^1.1.1" }, "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, "fsevents": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", @@ -4694,22 +4681,10 @@ } } }, - "is-number": { + "normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" } } }, @@ -4787,11 +4762,11 @@ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" }, "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "requires": { - "restore-cursor": "^2.0.0" + "restore-cursor": "^3.1.0" } }, "cli-truncate": { @@ -5390,6 +5365,11 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" } } }, @@ -7225,9 +7205,9 @@ "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" }, "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", + "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", "requires": { "escape-string-regexp": "^1.0.5" } @@ -7255,12 +7235,24 @@ "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==" }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "requires": { - "to-regex-range": "^5.0.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "finalhandler": { @@ -7600,9 +7592,9 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, "get-own-enumerable-property-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz", - "integrity": "sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz", + "integrity": "sha512-09/VS4iek66Dh2bctjRkowueRJbY1JDGR1L/zRxO1Qk8Uxs6PnqaNSqalpizPT+CDjre3hnEsuzvhgomz9qYrA==" }, "get-stdin": { "version": "4.0.1", @@ -7752,19 +7744,23 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true + "resolved": false, + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "resolved": false, + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "aproba": { "version": "1.2.0", - "bundled": true + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "are-we-there-yet": { "version": "1.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -7772,11 +7768,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": false, + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7784,57 +7782,69 @@ }, "chownr": { "version": "1.1.3", - "bundled": true + "resolved": false, + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" }, "code-point-at": { "version": "1.1.0", - "bundled": true + "resolved": false, + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "concat-map": { "version": "0.0.1", - "bundled": true + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "resolved": false, + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "core-util-is": { "version": "1.0.2", - "bundled": true + "resolved": false, + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "debug": { "version": "3.2.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "requires": { "ms": "^2.1.1" } }, "deep-extend": { "version": "0.6.0", - "bundled": true + "resolved": false, + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, "delegates": { "version": "1.0.0", - "bundled": true + "resolved": false, + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "detect-libc": { "version": "1.0.3", - "bundled": true + "resolved": false, + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, "fs-minipass": { "version": "1.2.7", - "bundled": true, + "resolved": false, + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "requires": { "minipass": "^2.6.0" } }, "fs.realpath": { "version": "1.0.0", - "bundled": true + "resolved": false, + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -7848,7 +7858,8 @@ }, "glob": { "version": "7.1.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -7860,25 +7871,29 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true + "resolved": false, + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "iconv-lite": { "version": "0.4.24", - "bundled": true, + "resolved": false, + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "requires": { "safer-buffer": ">= 2.1.2 < 3" } }, "ignore-walk": { "version": "3.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "requires": { "minimatch": "^3.0.4" } }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -7886,37 +7901,44 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "resolved": false, + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.5", - "bundled": true + "resolved": false, + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "bundled": true + "resolved": false, + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.0", - "bundled": true + "resolved": false, + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "minipass": { "version": "2.9.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7924,31 +7946,36 @@ }, "minizlib": { "version": "1.3.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "requires": { "minipass": "^2.9.0" } }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" }, "dependencies": { "minimist": { "version": "0.0.8", - "bundled": true + "resolved": false, + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } }, "ms": { "version": "2.1.2", - "bundled": true + "resolved": false, + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "needle": { "version": "2.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", "requires": { "debug": "^3.2.6", "iconv-lite": "^0.4.4", @@ -7957,7 +7984,8 @@ }, "node-pre-gyp": { "version": "0.14.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", "requires": { "detect-libc": "^1.0.2", "mkdirp": "^0.5.1", @@ -7973,7 +8001,8 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "requires": { "abbrev": "1", "osenv": "^0.1.4" @@ -7981,11 +8010,13 @@ }, "npm-bundled": { "version": "1.0.6", - "bundled": true + "resolved": false, + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==" }, "npm-packlist": { "version": "1.4.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==", "requires": { "ignore-walk": "^3.0.1", "npm-bundled": "^1.0.1" @@ -7993,7 +8024,8 @@ }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -8003,30 +8035,36 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "resolved": false, + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-assign": { "version": "4.1.1", - "bundled": true + "resolved": false, + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "bundled": true + "resolved": false, + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-tmpdir": { "version": "1.0.2", - "bundled": true + "resolved": false, + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -8034,11 +8072,13 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true + "resolved": false, + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "process-nextick-args": { "version": "2.0.1", - "bundled": true + "resolved": false, + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "protobufjs": { "version": "5.0.3", @@ -8053,7 +8093,8 @@ }, "rc": { "version": "1.2.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -8063,7 +8104,8 @@ }, "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -8076,38 +8118,46 @@ }, "rimraf": { "version": "2.7.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "requires": { "glob": "^7.1.3" } }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "resolved": false, + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", - "bundled": true + "resolved": false, + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sax": { "version": "1.2.4", - "bundled": true + "resolved": false, + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { "version": "5.7.1", - "bundled": true + "resolved": false, + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "set-blocking": { "version": "2.0.0", - "bundled": true + "resolved": false, + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "signal-exit": { "version": "3.0.2", - "bundled": true + "resolved": false, + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8116,25 +8166,29 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" } }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "bundled": true + "resolved": false, + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "tar": { "version": "4.4.13", - "bundled": true, + "resolved": false, + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", @@ -8147,22 +8201,26 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true + "resolved": false, + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "wide-align": { "version": "1.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "requires": { "string-width": "^1.0.2 || 2" } }, "wrappy": { "version": "1.0.2", - "bundled": true + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "yallist": { "version": "3.1.1", - "bundled": true + "resolved": false, + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } }, @@ -8279,24 +8337,6 @@ "kind-of": "^4.0.0" }, "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -8832,14 +8872,6 @@ "through": "^2.3.6" }, "dependencies": { - "ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", - "requires": { - "type-fest": "^0.8.1" - } - }, "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", @@ -8863,49 +8895,11 @@ "supports-color": "^5.3.0" } }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "figures": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", - "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", @@ -8948,11 +8942,6 @@ "requires": { "has-flag": "^3.0.0" } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" } } }, @@ -9136,10 +9125,12 @@ } }, "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + } }, "is-number-object": { "version": "1.0.3", @@ -10924,14 +10915,6 @@ "strip-ansi": "^5.0.0" }, "dependencies": { - "ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", - "requires": { - "type-fest": "^0.8.1" - } - }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", @@ -10984,11 +10967,6 @@ "requires": { "has-flag": "^3.0.0" } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" } } }, @@ -11006,6 +10984,11 @@ "string-length": "^2.0.0" }, "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -11313,6 +11296,15 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -11378,9 +11370,9 @@ } }, "fast-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.0.tgz", - "integrity": "sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.1.tgz", + "integrity": "sha512-nTCREpBY8w8r+boyFYAx21iL6faSsQynliPHM4Uf56SbkyohCNxpVPEH9xrF5TXKy+IsjkPUHDKiUkzBVRXn9g==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -11390,6 +11382,15 @@ "micromatch": "^4.0.2" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "get-stream": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", @@ -11421,10 +11422,10 @@ "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", "dev": true }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-path-inside": { @@ -11449,10 +11450,10 @@ "picomatch": "^2.0.5" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "npm-run-path": { @@ -11464,30 +11465,12 @@ "path-key": "^3.0.0" } }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, "p-finally": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", "dev": true }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -11539,6 +11522,15 @@ "has-flag": "^3.0.0" } }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -11656,6 +11648,49 @@ "supports-color": "^5.3.0" } }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -11875,18 +11910,58 @@ "wrap-ansi": "^3.0.1" }, "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -12042,13 +12117,6 @@ "map-age-cleaner": "^0.1.1", "mimic-fn": "^2.0.0", "p-is-promise": "^2.0.0" - }, - "dependencies": { - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - } } }, "memory-fs": { @@ -12132,85 +12200,10 @@ "to-regex": "^3.0.2" }, "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } } } }, @@ -12242,9 +12235,9 @@ } }, "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "mini-create-react-context": { "version": "0.3.2", @@ -12709,9 +12702,12 @@ } }, "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } }, "normalize-range": { "version": "0.1.2", @@ -12961,11 +12957,11 @@ } }, "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "^2.1.0" } }, "open": { @@ -13300,9 +13296,9 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", - "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", + "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==", "dev": true }, "pify": { @@ -14827,6 +14823,11 @@ "text-table": "0.2.0" }, "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -14860,6 +14861,22 @@ "supports-color": "^5.3.0" } }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -14893,11 +14910,33 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -15607,11 +15646,11 @@ } }, "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "requires": { - "onetime": "^2.0.0", + "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, @@ -17151,12 +17190,12 @@ } }, "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "requires": { - "is-number": "^7.0.0" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" } }, "toidentifier": { @@ -17207,9 +17246,9 @@ "integrity": "sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA==" }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" }, "tsutils": { "version": "3.17.1", diff --git a/src/actions/uiActions.js b/src/actions/uiActions.js index 63e857ae..8d841ea3 100644 --- a/src/actions/uiActions.js +++ b/src/actions/uiActions.js @@ -12,3 +12,8 @@ export const SET_PANEL = "SET_PANEL"; export function setPanel(value) { return { type: SET_PANEL, value }; } + +export const SET_THEME = "SET_THEME"; +export function setTheme(theme) { + return { type: SET_THEME, theme }; +} diff --git a/src/components/Main.js b/src/components/Main.js index 1d20a25d..d30cc2c0 100644 --- a/src/components/Main.js +++ b/src/components/Main.js @@ -4,6 +4,7 @@ import OutputContainer from "./Output/OutputContainer.js"; import TextEditorContainer from "./TextEditor/containers/TextEditorContainer.js"; import DropdownButtonContainer from "./common/containers/DropdownButtonContainer"; import * as fetch from "../lib/fetch.js"; +import * as cookies from "../lib/cookies.js"; import SketchesPageContainer from "./Sketches/containers/SketchesContainer"; import "styles/Main.scss"; import ProfilePanelContainer from "./common/containers/ProfilePanelContainer"; @@ -15,6 +16,7 @@ import CodeDownloader from "../util/languages/CodeDownloader"; import { PANEL_SIZE } from "../constants"; import "codemirror/lib/codemirror.css"; import "codemirror/theme/material.css"; +import "codemirror/theme/duotone-light.css"; import "styles/CustomCM.scss"; import "styles/Resizer.scss"; import "styles/Editor.scss"; @@ -34,6 +36,9 @@ class Main extends React.Component { redirect: this.props.listOfPrograms.length === 0 ? "/sketches" : "", pane1Style: { transition: "width .5s ease" }, }; + + // Set theme from cookies (yum) + this.props.setTheme(cookies.getThemeFromCookie()); } //==============React Lifecycle Functions Start===================// @@ -47,6 +52,12 @@ class Main extends React.Component { } } + onThemeChange = () => { + let newTheme = this.props.theme === "dark" ? "light" : "dark"; + cookies.setThemeCookie(newTheme); + this.props.setTheme(newTheme); + }; + resetSaveText = () => { this.setState({ saveText: "Save", @@ -139,6 +150,7 @@ class Main extends React.Component { screenHeight={this.props.screenHeight} screenWidth={this.props.screenWidth} handleDownload={this.handleDownload} + theme={this.props.theme} /> ); @@ -178,8 +190,12 @@ class Main extends React.Component { }; return ( -
- +
+
{this.renderContent()}
diff --git a/src/components/Output/OutputContainer.js b/src/components/Output/OutputContainer.js index c55ebd89..ed2155ab 100644 --- a/src/components/Output/OutputContainer.js +++ b/src/components/Output/OutputContainer.js @@ -18,9 +18,6 @@ const mapDispatchToProps = dispatch => { }; }; -const OutputContainer = connect( - mapStateToProps, - mapDispatchToProps, -)(Output); +const OutputContainer = connect(mapStateToProps, mapDispatchToProps)(Output); export default OutputContainer; diff --git a/src/components/Output/Python.js b/src/components/Output/Python.js index 7d0451f7..00c33246 100644 --- a/src/components/Output/Python.js +++ b/src/components/Output/Python.js @@ -3,27 +3,25 @@ const getPythonSrcDocHead = () => ` - `; diff --git a/src/components/Sketches/containers/SketchesContainer.js b/src/components/Sketches/containers/SketchesContainer.js index 71f5c9ba..1e3f7486 100644 --- a/src/components/Sketches/containers/SketchesContainer.js +++ b/src/components/Sketches/containers/SketchesContainer.js @@ -33,9 +33,6 @@ const mapDispatchToProps = dispatch => { }; }; -const SketchesContainer = connect( - mapStateToProps, - mapDispatchToProps, -)(Sketches); +const SketchesContainer = connect(mapStateToProps, mapDispatchToProps)(Sketches); export default SketchesContainer; diff --git a/src/components/TextEditor/components/TextEditor.js b/src/components/TextEditor/components/TextEditor.js index 55270ab4..c8fce92a 100644 --- a/src/components/TextEditor/components/TextEditor.js +++ b/src/components/TextEditor/components/TextEditor.js @@ -96,6 +96,21 @@ class TextEditor extends React.Component { this.setState({ currentLine: line }); }; + /** + * returns a theme string for the CodeMirror editor, based off of the app's current theme + * @param {string} theme - the app's current theme + * @returns {string} the codemirror theme - see https://codemirror.net/demo/theme.html for more info + */ + + getCMTheme = theme => { + switch (theme) { + case "light": + return "duotone-light"; + case "dark": + default: + return "material"; + } + }; renderDropdown = () => ; renderBanner = () => { @@ -131,7 +146,7 @@ class TextEditor extends React.Component { //json required by CodeMirror const options = { mode: CODEMIRROR_CONVERSIONS[this.props.language], - theme: "material", //requires lots of CSS tuning to get a theme to work, be wary of changing + theme: this.getCMTheme(this.props.theme), lineNumbers: true, //text editor has line numbers lineWrapping: true, //text editor does not overflow in the x direction, uses word wrap (NOTE: it's like MO Word wrapping, so words are not cut in the middle, if a word overlaps, the whole word is brought to the next line) indentWithTabs: true, diff --git a/src/components/TextEditor/containers/TextEditorContainer.js b/src/components/TextEditor/containers/TextEditorContainer.js index 8e9da71f..024e4b21 100644 --- a/src/components/TextEditor/containers/TextEditorContainer.js +++ b/src/components/TextEditor/containers/TextEditorContainer.js @@ -3,7 +3,7 @@ import TextEditor from "../components/TextEditor"; import { connect } from "react-redux"; import { setProgramCode, setProgramDirty } from "../../../actions/programsActions.js"; -const mapStateToProps = state => { +const mapStateToProps = (state, ownProps) => { const { uid, mostRecentProgram } = state.userData; //program data should be an object representing the most recent program @@ -14,6 +14,7 @@ const mapStateToProps = state => { return { ...programData, mostRecentProgram, + theme: ownProps.theme, uid, }; }; @@ -29,9 +30,6 @@ const mapDispatchToProps = (dispatch, ownProps) => { }; }; -const TextEditorContainer = connect( - mapStateToProps, - mapDispatchToProps, -)(TextEditor); +const TextEditorContainer = connect(mapStateToProps, mapDispatchToProps)(TextEditor); export default TextEditorContainer; diff --git a/src/components/common/ProfilePanel.js b/src/components/common/ProfilePanel.js index 843bd93a..a71895e5 100644 --- a/src/components/common/ProfilePanel.js +++ b/src/components/common/ProfilePanel.js @@ -15,9 +15,12 @@ import { faBook } from "@fortawesome/free-solid-svg-icons"; import { faEdit } from "@fortawesome/free-solid-svg-icons"; import { faPencilAlt } from "@fortawesome/free-solid-svg-icons"; import { faSignOutAlt } from "@fortawesome/free-solid-svg-icons"; +import { faMoon } from "@fortawesome/free-solid-svg-icons"; +import { faSun } from "@fortawesome/free-solid-svg-icons"; import { faTimes } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import "styles/Panel.scss"; +import Switch from "./Switch"; import Footer from "./Footer"; /**--------Props-------- @@ -248,7 +251,6 @@ class ProfilePanel extends React.Component { renderButtons = () => { let panelButtons = []; - switch (this.props.contentType) { case "sketches": panelButtons.push(this.renderEditorButton()); @@ -265,6 +267,21 @@ class ProfilePanel extends React.Component { return
{panelButtons}
; }; + renderThemeSwitch = () => { + let onToggle = on => { + this.props.onThemeChange(on ? "light" : "dark"); + }; + + return ( + } + offImg={} + /> + ); + }; + renderContent = () => (
{this.renderPanelImage()} @@ -272,6 +289,7 @@ class ProfilePanel extends React.Component { {this.renderName()} {this.renderErrorMessage(this.state.displayNameMessage)} {this.renderButtons()} + {this.renderThemeSwitch()}
); diff --git a/src/components/common/Switch.js b/src/components/common/Switch.js new file mode 100644 index 00000000..b80d0fe2 --- /dev/null +++ b/src/components/common/Switch.js @@ -0,0 +1,49 @@ +import React from "react"; + +import "styles/Switch.scss"; + +/** + * Generic switch component. Props are as follows: + * @param {boolean} [on] - whether the switch is set to the "on" position or not; optional prop, defaults to false + * @param {function} onToggle - function to be called when switch is toggled. Has a single parameter (bool) that holds whether the switch is in the "on" position (true) or not (false) + * @param {JSX} onImg (optional): (JSX) element to be displayed on switch body when set to "on" + * @param {JSX} offImg (optional): (JSX) element to be displayed on switch body when set to "off" + */ +export default class Switch extends React.Component { + constructor(props) { + super(props); + + this.state = { + on: !this.props.on ? false : this.props.on, + }; + } + + componentDidUpdate(prevProps) { + if (this.props.on !== prevProps.on) { + this.setState({ + on: this.props.on, + }); + } + } + + onSwitchChange = () => { + this.props.onToggle(!this.state.on); + this.setState({ + on: !this.state.on, + }); + }; + + render() { + let switchedClass = this.state.on ? " switch-on" : ""; + + return ( + + ); + } +} diff --git a/src/components/common/Switch.test.js b/src/components/common/Switch.test.js new file mode 100644 index 00000000..4eb7e63a --- /dev/null +++ b/src/components/common/Switch.test.js @@ -0,0 +1,51 @@ +import React from "react"; +import { shallow } from "enzyme"; +import Switch from "components/common/Switch.js"; + +const switchOnClass = ".switch-body.switch-on"; // there has to be a better way of doing this +const switchCheckboxClass = ".switch-input"; + +describe("Switch", () => { + it("smoke test", () => { + const component = shallow(); + expect(component.exists()).toBe(true); + }); + it("handles on prop properly", () => { + const componentDefault = shallow(); + expect(componentDefault.find(switchOnClass)).toHaveLength(0); + + const componentFalse = shallow(); + expect(componentFalse.find(switchOnClass)).toHaveLength(0); + + const componentTrue = shallow(); + expect(componentTrue.find(switchOnClass)).toHaveLength(1); + }); + it("triggers the onToggle function when clicked", () => { + const clickFn = jest.fn(val => val); + const component = shallow(); + expect(component.find(switchOnClass)).toHaveLength(0); + + component.find(switchCheckboxClass).simulate("change", { target: { checked: true } }); + expect(clickFn.mock.calls.length).toBe(1); + expect(clickFn.mock.calls[0][0]).toBe(true); + + component.find(switchCheckboxClass).simulate("change", { target: { checked: false } }); + expect(clickFn.mock.calls.length).toBe(2); + expect(clickFn.mock.calls[1][0]).toBe(false); + }); + it("handles the onImg and offImg props properly", () => { + const clickFn = jest.fn(val => val); + const onText = ; + const offText = ; + const component = shallow( + , + ); + + expect(component.find("#off-test")).toHaveLength(1); + expect(component.find("#on-test")).toHaveLength(0); + + component.find(switchCheckboxClass).simulate("change", { target: { checked: true } }); + expect(component.find("#off-test")).toHaveLength(0); + expect(component.find("#on-test")).toHaveLength(1); + }); +}); diff --git a/src/components/common/containers/DropdownButtonContainer.js b/src/components/common/containers/DropdownButtonContainer.js index 920091b4..875a0e63 100644 --- a/src/components/common/containers/DropdownButtonContainer.js +++ b/src/components/common/containers/DropdownButtonContainer.js @@ -33,9 +33,6 @@ const mapDispatchToProps = dispatch => { }; }; -const DropdownButtonContainer = connect( - mapStateToProps, - mapDispatchToProps, -)(DropdownButton); +const DropdownButtonContainer = connect(mapStateToProps, mapDispatchToProps)(DropdownButton); export default DropdownButtonContainer; diff --git a/src/components/common/containers/ProfilePanelContainer.js b/src/components/common/containers/ProfilePanelContainer.js index 962fb2e6..b40a62e5 100644 --- a/src/components/common/containers/ProfilePanelContainer.js +++ b/src/components/common/containers/ProfilePanelContainer.js @@ -11,11 +11,13 @@ const mapStateToProps = (state, ownProps) => { photoName: state.userData.photoName || DEFAULT_PHOTO_NAME, screenHeight: state.ui.screenHeight, left: state.ui.panelOpen ? OPEN_PANEL_LEFT : CLOSED_PANEL_LEFT, + theme: ownProps.theme, }; }; const mapDispatchToProps = (dispatch, ownProps) => { return { + onThemeChange: ownProps.onThemeChange, collectUserPhoto: () => {}, setDisplayName: name => dispatch(setDisplayName(name)), setPhotoName: name => dispatch(setPhotoName(name)), @@ -25,9 +27,6 @@ const mapDispatchToProps = (dispatch, ownProps) => { }; }; -const ProfilePanelContainer = connect( - mapStateToProps, - mapDispatchToProps, -)(ProfilePanel); +const ProfilePanelContainer = connect(mapStateToProps, mapDispatchToProps)(ProfilePanel); export default ProfilePanelContainer; diff --git a/src/components/containers/MainContainer.js b/src/components/containers/MainContainer.js index c1047f14..1ad79f51 100644 --- a/src/components/containers/MainContainer.js +++ b/src/components/containers/MainContainer.js @@ -4,6 +4,7 @@ import { setOutput } from "../../actions/outputActions.js"; import { setMostRecentProgram } from "../../actions/userDataActions.js"; import { setProgramDirty } from "../../actions/programsActions.js"; import { togglePanel } from "../../actions/uiActions.js"; +import { setTheme } from "../../actions/uiActions.js"; import { CLOSED_PANEL_LEFT, OPEN_PANEL_LEFT, PANEL_SIZE } from "../../constants"; const mapStateToProps = state => { @@ -32,6 +33,7 @@ const mapStateToProps = state => { left: (state.ui.panelOpen ? OPEN_PANEL_LEFT : CLOSED_PANEL_LEFT) + PANEL_SIZE, name, language, + theme: state.ui.theme, }; }; @@ -41,6 +43,7 @@ const mapDispatchToProps = dispatch => { runCode: (code, language) => dispatch(setOutput(code, language)), cleanCode: program => dispatch(setProgramDirty(program, false)), togglePanel: () => dispatch(togglePanel()), + setTheme: theme => dispatch(setTheme(theme)), }; }; diff --git a/src/lib/cookies.js b/src/lib/cookies.js new file mode 100644 index 00000000..a43cc03b --- /dev/null +++ b/src/lib/cookies.js @@ -0,0 +1,77 @@ +/** + * an internal helper function to get a cookie from the document. + * almost word-for-word copied from https://www.w3schools.com/js/js_cookies.asp + * @param {string} cname - the key value for the requested cookie + * @return {string} the value of the requested key, "" for none set + */ + +function getCookie(cname) { + let name = cname + "="; + let decodedCookie = decodeURIComponent(document.cookie); + let ca = decodedCookie.split(";"); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) === " ") { + c = c.substring(1); + } + if (c.indexOf(name) === 0) { + return c.substring(name.length, c.length); + } + } + return ""; +} + +/** + * an internal helpder function to set a cookie in the document + * almost word-for-word copied from https://www.w3schools.com/js/js_cookies.asp + * @param {string} cname - the key value for the requested cookie + * @param {string} cvalue - the value to set the cookie to + * @param {number} exdays - the number of days until the cookie expires + */ + +function setCookie(cname, cvalue, exdays) { + let d = new Date(); + d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000); + let expires = "expires=" + d.toUTCString(); + document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; +} + +/** + * exported function that gets the current app theme from the theme cookie; + * if none is set, returns dark and sets the cookie to dark + * @returns {string} the current theme, or dark if the cookie doesn't exist + */ + +export const getThemeFromCookie = () => { + let theme = getCookie("theme"); + if (theme !== "") { + return theme; + } else { + setCookie("theme", "dark", 365); + return "dark"; + } +}; + +/** + * exported function that sets the theme cookie to the input, with a one-year expiry date + * @param {string} theme theme string to store in the cookie + */ + +export const setThemeCookie = theme => { + setCookie("theme", theme, 365); +}; + +/** + * exported function that toggles the theme cookie to flip-flop its value from light to dark + * if none is set, assumes that the user is currently in dark and switches to light + * @returns {string} returns the current value of the theme cookie + */ + +export const toggleCookie = () => { + if (getThemeFromCookie() === "light") { + setCookie("theme", "dark", 365); + return "dark"; + } + setCookie("theme", "light", 365); + return "light"; +}; diff --git a/src/lib/index.js b/src/lib/index.js index 5e8c7be8..1b84bca2 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -1,7 +1,9 @@ -import * as fetch from './fetch.js' -import * as helpers from './fetch.js' +import * as cookies from "./cookies.js"; +import * as fetch from "./fetch.js"; +import * as helpers from "./fetch.js"; export default { + ...cookies, ...fetch, ...helpers, -} \ No newline at end of file +}; diff --git a/src/reducers/uiReducer.js b/src/reducers/uiReducer.js index d139cf5b..848f06c7 100644 --- a/src/reducers/uiReducer.js +++ b/src/reducers/uiReducer.js @@ -1,9 +1,10 @@ -import { SCREEN_RESIZE, TOGGLE_PANEL, SET_PANEL } from "../actions/uiActions"; +import { SCREEN_RESIZE, TOGGLE_PANEL, SET_PANEL, SET_THEME } from "../actions/uiActions"; const initialState = { screenWidth: typeof window === "object" ? window.innerWidth : null, screenHeight: typeof window === "object" ? window.innerHeight : null, panelOpen: false, + theme: "dark", }; function uiReducer(state = initialState, action) { @@ -14,6 +15,8 @@ function uiReducer(state = initialState, action) { return Object.assign({}, state, { panelOpen: !state.panelOpen }); case SET_PANEL: return Object.assign({}, state, { panelOpen: action.value }); + case SET_THEME: + return Object.assign({}, state, { theme: action.theme }); default: return state; } diff --git a/src/styles/CustomCM.scss b/src/styles/CustomCM.scss index c3a0383a..63b05b5b 100644 --- a/src/styles/CustomCM.scss +++ b/src/styles/CustomCM.scss @@ -1,86 +1,113 @@ -@import 'styles/variables.scss'; +@import "styles/variables.scss"; .CodeMirror-lines { cursor: default; } -.cm-s-material.CodeMirror { - box-sizing: border-box; - max-width: inherit; - max-height: inherit; - min-height: inherit; - background-color: rgba(38, 50, 56, 0); - font-family: "Ubuntu Mono", monospace; - color: white; - font-size: 18px; - height: 100%; -} - -.CodeMirror pre { - padding-left: 5px; - padding-right: 4px; -} - -.cm-s-material .CodeMirror-gutters { - background-color: $theme-dark; -} -.cm-s-material .CodeMirror-guttermarker, -.cm-s-material .CodeMirror-guttermarker-subtle, -.cm-s-material .CodeMirror-linenumber { - color: white; -} - -.cm-s-material .CodeMirror-guttermarker, -.cm-s-material .CodeMirror-guttermarker-subtle, -.cm-s-material .CodeMirror-linenumber { - text-align: left; - padding-left: 15px; -} - /* comments, e.g. // # /* */ -.cm-s-material .cm-comment { - color: white; +.cm-comment { + @include themify($themes) { + color: themed("comment"); + } } /* import for while in etc */ -.cm-s-material span.cm-keyword { - color: #deb0ff; +span.cm-keyword { + @include themify($themes) { + color: themed("keyword"); + } } /*e.g. var *blah*, blah will be colored */ -.cm-s-material span.cm-def { - color: #47e2d5; +span.cm-def { + @include themify($themes) { + color: themed("def"); + } } /*e.g. var *blah*, blah will be colored */ -.cm-s-material span.cm-variable { - color: #f5e83c; +span.cm-variable { + @include themify($themes) { + color: themed("variable"); + } } /*for python: print, range, all built-in functions */ -.cm-s-material .cm-builtin { - color: #ffb36f; +.cm-builtin { + @include themify($themes) { + color: themed("builtin"); + } } /* 1 2 -11 etc */ -.cm-s-material .cm-number { - color: #f69797; +.cm-number { + @include themify($themes) { + color: themed("number"); + } } /* + - > < etc */ -.cm-s-material .cm-operator { - color: rgba(233, 237, 237, 1); +.cm-operator { + @include themify($themes) { + color: themed("operator"); + } +} +.cm-property { + @include themify($themes) { + color: themed("property"); + } +} +.CodeMirror { + box-sizing: border-box; + max-width: inherit; + max-height: inherit; + min-height: inherit; + font-family: "Ubuntu Mono", monospace; + font-size: 18px; + height: 100%; + @include themify($themes) { + background-color: themed("backgroundColor"); + color: themed("color"); + } +} +.CodeMirror .CodeMirror pre { + padding-left: 5px; + padding-right: 4px; +} + +.CodeMirror-gutter { + @include themify($themes) { + background-color: themed("backgroundColor"); + color: themed("color"); + } + white-space: normal; + height: 100%; + display: inline-block; + vertical-align: top; + margin-bottom: -30px; + width: 41px; } -/* e.g. turtle.*Blah*(), Blah will be colored */ -.cm-s-material .cm-property { - color: #80cbae; +.CodeMirror-linenumber { + width: 23px; + color: #fff; + text-align: left; + padding-left: 15px; + @include themify($themes) { + color: themed("color"); + } } -.cm-s-material .CodeMirror-cursor { - border-left: 1px solid #f4ee94; + +// a strange CSS way to create our floating cursor. not great, not awful. + +.CodeMirror-cursor { + border-top: none !important; + border-bottom: none !important; + border-left: 1px solid #A38B41 !important; + border-right: 0.5px solid #A38B41 !important; } -.cm-s-material .CodeMirror-cursor::after { +.CodeMirror-cursor::after { position: absolute; margin-top: 26px; left: -10px; @@ -96,11 +123,10 @@ } /* required for safari because it places the ::after element differently than chrome/fiefox; taken from https://stackoverflow.com/questions/16348489/is-there-a-css-hack-for-safari-only-not-chrome */ -@media not all and (min-resolution:.001dpcm) { - .cm-s-material .CodeMirror-cursor::after { +@media not all and (min-resolution: 0.001dpcm) { + .CodeMirror-cursor::after { margin-top: 2px; } - } .CodeMirror-scroll { overflow: hidden !important; diff --git a/src/styles/Editor.scss b/src/styles/Editor.scss index a1cbe6cb..3ffd875e 100644 --- a/src/styles/Editor.scss +++ b/src/styles/Editor.scss @@ -1,10 +1,11 @@ @import "styles/variables.scss"; - +.root { + transition: all 0.5s ease; +} .editor { display: flex; flex-direction: row; align-items: flex-start; - background-color: $theme-dark; justify-content: flex-start; position: absolute; transition: all 0.5s ease; @@ -18,7 +19,9 @@ align-items: flex-start; max-height: 100vh; overflow-y: auto; - background-color: $theme-dark; + @include themify($themes) { + background-color: themed("backgroundColor"); + } } .code-section-banner { @@ -30,23 +33,21 @@ justify-content: flex-start; width: 100%; height: 67px; - border-bottom: 1px white solid; + @include themify($themes) { + border-bottom: 1px solid themed("color"); + } } .editor-expand-panel-arrow { - color: #dddcdf; + @include themify($themes) { + color: themed("color"); + } padding: 1rem; cursor: pointer; flex: 0 1 auto; width: 46px; } -.editor-expand-panel-arrow-hidden { - // padding: 1rem; - // flex: 0 1 auto; - // width: 46px; -} - .editor-language-dropdown { flex: 0 1 auto; } @@ -60,7 +61,9 @@ } .editor-output { - background-color: $theme-tertiary; + @include themify($themes) { + background-color: themed("outputBackground"); + } height: 100%; } @@ -72,37 +75,53 @@ align-items: center; justify-content: flex-start; width: 100%; - border-bottom: 1px white solid; + @include themify($themes) { + border-bottom: 1px solid themed("color"); + } } .editor-output-iframe { width: 100%; height: 100%; - background-color: white; + @include themify($themes) { + background-color: themed("outputBackground"); + } + border: none; display: flex; } .text-editor-container { width: 100%; - margin-top: 10px; + padding-top: 10px; top: 61px; overflow: auto; + @include themify($themes) { + background-color: themed("backgroundColor"); + } } .btn-language-dropdown { + transition: none; display: flex; align-items: center; font-size: 24px; - color: white; + @include themify($themes) { + color: themed("color"); + background-color: themed("backgroundColor"); + } border: 0px; } .btn-language-dropdown:hover { - color: white; + @include themify($themes) { + color: themed("color"); + } } .selected-line { - background-color: rgba(255, 255, 255, 0.1); + @include themify($themes) { + background-color: themed("selectedLine"); + } } .text-editor-container::-webkit-scrollbar { @@ -122,4 +141,4 @@ /* Handle on hover */ .text-editor-container::-webkit-scrollbar-thumb:hover { background: #2f2d33; -} \ No newline at end of file +} diff --git a/src/styles/Footer.scss b/src/styles/Footer.scss index 282e84bf..3593f613 100644 --- a/src/styles/Footer.scss +++ b/src/styles/Footer.scss @@ -6,9 +6,11 @@ width: 100%; bottom: 0px; height: 8vh; /*calc used to do arithmetic*/ - background-color: $theme-light; text-align: center; align-items: center; + @include themify($themes) { + background-color: themed("accentBackground"); + } } .footer-image { diff --git a/src/styles/Panel.scss b/src/styles/Panel.scss index 766cf5f5..ff4c62e9 100644 --- a/src/styles/Panel.scss +++ b/src/styles/Panel.scss @@ -1,9 +1,8 @@ -@import 'styles/variables.scss'; +@import "styles/variables.scss"; .panel { display: flex; position: absolute; - background-color: $theme-light; flex-direction: column; align-items: center; justify-content: flex-start; @@ -11,10 +10,16 @@ margin: 0; padding: 0; transition: left 0.5s ease; + @include themify($themes) { + color: themed("accentColor"); + background-color: themed("accentBackground"); + } } .panel-collapse-button { - color: $off-white; + @include themify($themes) { + color: themed("accentColor"); + } padding-right: 15px; flex: 0 0 auto; font-size: 1.25rem; @@ -74,10 +79,12 @@ font-size: 1.25rem; border: none; background-color: transparent; - color: $off-white; position: absolute; right: -15px; bottom: -15px; + @include themify($themes) { + color: themed("accentColor"); + } } .gallery { @@ -124,7 +131,9 @@ .edit-icon-image { font-size: 1.25rem; - color: $off-white; + @include themify($themes) { + color: themed("accentColor"); + } position: absolute; border: none; right: -30px; @@ -146,12 +155,14 @@ padding: 20px 0 5px; text-align: center; font-size: 2rem; - font-weight: bold; - color: $off-white; word-wrap: break-word; - background-color: $theme-dark; + background: none; align-items: baseline; border: none; + @include themify($themes) { + color: themed("accentColor"); + border-bottom: 1px solid themed("accentColor"); + } } .profile-input-error { @@ -169,7 +180,9 @@ padding: 20px 0 20px; font-size: 2rem; word-wrap: break-word; - color: white; + @include themify($themes) { + color: themed("accentColor"); + } position: relative; font-family: $font-family-heading; } @@ -184,8 +197,6 @@ } .panel-button { - background-color: $theme-dark; - border-color: $theme-dark; text-align: left; font-size: 2em !important; /* reactstrap also has a !important somewhere, so we need to do this */ /* this next block of CSS properly aligns button content */ @@ -193,12 +204,20 @@ flex-direction: row; justify-content: flex-start; align-items: center; + @include themify($themes) { + color: themed("color"); + border-color: themed("backgroundColor"); + background-color: themed("backgroundColor"); + } } -.panel-button:hover, .panel-button:active { - color: $theme-dark !important; - border-color: $off-white !important; - background-color: $off-white !important; +.panel-button:hover, +.panel-button:active { + @include themify($themes) { + color: themed("backgroundColor"); + border-color: themed("color"); + background-color: themed("color"); + } } .panel-button-text { @@ -225,3 +244,12 @@ .panel-content::-webkit-scrollbar-thumb:hover { background: #fff; } + +// Theme Switch Images +.icon-dark { + color: #ffffff; +} + +.icon-light { + color: $theme-dark; +} \ No newline at end of file diff --git a/src/styles/SketchBox.scss b/src/styles/SketchBox.scss index 63862662..8fccc194 100644 --- a/src/styles/SketchBox.scss +++ b/src/styles/SketchBox.scss @@ -15,12 +15,16 @@ justify-content: flex-start; align-items: center; width: 200px; - background-color: $off-white; border-radius: 5px; color: $theme-dark; margin: 0px 10px; cursor: pointer; flex: 0 0 auto; + @include themify($themes) { + color: themed("cardColor"); + background-color: themed("cardBackground"); + border: themed("cardBorder") + } } .sketch-box-body { @@ -36,15 +40,19 @@ } .sketch-divider { - border-top: 1px solid $theme-dark; width: 100%; margin: 0; padding: 0; + @include themify($themes){ + border-top: 1px solid themed("cardDivider"); + } } .sketch-button-divider { width: 1px; - border: 0.5px solid $theme-dark; + @include themify($themes){ + border: 0.5px solid themed("cardDivider"); + } } .sketch-name { diff --git a/src/styles/Sketches.scss b/src/styles/Sketches.scss index 408bf806..9551176a 100644 --- a/src/styles/Sketches.scss +++ b/src/styles/Sketches.scss @@ -3,7 +3,10 @@ .sketches-container { flex-direction: column; align-items: flex-start; - background-color: $theme-dark; + @include themify($themes) { + background-color: themed("backgroundColor"); + color: themed("color"); + } justify-content: flex-start; position: absolute; transition: all 0.5s ease; @@ -11,7 +14,6 @@ } .sketches-header-text { - color: $off-white; font-weight: bold; padding: 1rem; cursor: pointer; @@ -26,7 +28,9 @@ flex-direction: row; justify-content: flex-start; align-items: center; - border-bottom: 2px solid $off-white; + @include themify($themes) { + border-bottom: 1px solid themed("color"); + } } .sketches-grid { @@ -51,8 +55,10 @@ .no-sketches-container { margin-top: 3em; text-align: center; - color: $off-white; width: 100%; height: 100%; padding: 1em; + @include themify($themes) { + color: themed("color"); + } } \ No newline at end of file diff --git a/src/styles/Switch.scss b/src/styles/Switch.scss new file mode 100644 index 00000000..9372b2cb --- /dev/null +++ b/src/styles/Switch.scss @@ -0,0 +1,57 @@ +@import "styles/variables.scss"; + +$switch-transition-time: 0.3s; + +.switch { + padding: 0.5rem 1rem; + position: relative; + display: block; + width: 100px; + height: 30px; + border-radius: 6px; + cursor: pointer; + box-sizing: content-box; + font-family: inherit; +} + +.switch-input { + padding: 0px; + position: absolute; + top: 0; + left: 0; + opacity: 0; + box-sizing: content-box; +} + +.switch-body { + position: relative; + display: flex; + align-items: center; + justify-content: flex-end; + padding: 10px; + height: inherit; + font-size: 1rem; + background: $white; + border-radius: inherit; + transition: all $switch-transition-time; + &.switch-on { + justify-content: flex-start !important; + background: $theme-dark; + } +} + +.switch-handle { + position: absolute; + top: 8px; + + width: 30px; + height: 30px; + border-radius: 6px; + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); + background: $theme-dark; + transition: all $switch-transition-time; + &.switch-on { + margin-left: 70px; + background: white; + } +} diff --git a/src/styles/variables.scss b/src/styles/variables.scss index fbeb1244..ec46d988 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -11,11 +11,94 @@ $gray-purple: #585166; $off-white: #dddcdf; $select-cyan: #83dadd; $error-red: #f34d4d; +$teachla-green: #aed643; +$light-lavender: #8485cc; +$white: #fcfcfc; // "Theme" Colours - specifically designed to allow for hot-swapping later - $theme-light: $light-purple; $theme-dark: $dark-purple; $theme-tertiary: $gray-purple; $theme-highlight: $select-cyan; -$theme-error: $error-red; \ No newline at end of file +$theme-error: $error-red; + +//Theme variables +/* To change colors according to theme: +ex) + .class{ + @include themify($themes) { + background-color: themed("backgroundColor"); + } + } +*/ + +$themes: ( + light: ( + color: $theme-dark !important, + backgroundColor: $white !important, + outputBackground: #e6e6fa, + accentColor: $white, + accentBackground: $light-lavender, + /* Card variables */ cardColor: $theme-dark, + cardBackground: $white, + cardBorder: 1px solid $light-lavender, + cardDivider: $light-lavender, + /* these next colors are all for the editor - check out CustomCM.scss for more info! */ comment: + $theme-dark, + keyword: #9715f3, + def: #0052ff, + variable: #f07178, + builtin: #ff7800, + number: #fd1f44, + operator: $theme-dark, + property: #056d45, + cursor: #f4ee94, + selectedLine: $off-white, + ), + dark: ( + color: white !important, + backgroundColor: $theme-dark !important, + outputBackground: $theme-tertiary, + accentColor: white, + accentBackground: $theme-light, + /* Card variables */ cardColor: $theme-dark, + cardBackground: $off-white, + cardBorder: none, + cardDivider: $theme-dark, + /* these next colors are all for the editor - check out CustomCM.scss for more info! */ comment: + white, + keyword: #deb0ff, + def: #47e2d5, + variable: #f5e83c, + builtin: #ffb36f, + number: #f69797, + operator: rgba(233, 237, 237, 1), + property: #80cbae, + cursor: #f4ee94, + selectedLine: rgba(255, 255, 255, 0.1), + ), +); + +@mixin themify($themes: $themes) { + @each $theme, $map in $themes { + .theme-#{$theme} & { + $theme-map: () !global; + @each $key, $submap in $map { + $value: map-get(map-get($themes, $theme), "#{$key}"); + $theme-map: map-merge( + $theme-map, + ( + $key: $value, + ) + ) !global; + } + + @content; + $theme-map: null !global; + } + } +} + +@function themed($key) { + @return map-get($theme-map, $key); +}