Questionnaire in app + editable readme

This commit is contained in:
Ibitoye Rotimi Best
2021-05-14 08:04:35 +03:00
parent 618644b663
commit c614e691ed
11 changed files with 2639 additions and 708 deletions
+1999 -678
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -12,6 +12,7 @@
"@tailwindcss/forms": "^0.3.2",
"compression": "^1.7.1",
"marked": "^1.2.7",
"node-sass": "^6.0.0",
"polka": "next",
"sirv": "^1.0.0"
},
@@ -35,9 +36,10 @@
"rollup-plugin-svelte": "^6.0.0",
"rollup-plugin-terser": "^7.0.0",
"sapper": "^0.28.0",
"sass": "^1.32.13",
"svelte": "^3.17.3",
"svelte-awesome": "^2.3.1",
"svelte-preprocess": "^4.3.0",
"svelte-preprocess": "^4.7.3",
"tailwindcss": "^2.1.2",
"typescript": "^4.1.3"
}
@@ -1 +1,151 @@
<div>Home workl</div>
<script>
import RadioQuestion from "../../../RadioQuestion/index.svelte";
import PrimaryButton from "../../../PrimaryButton/index.svelte";
let answers = {};
let currentQuestionIndex = 0;
let currentQuestion = {};
const questions = [
{
name: "react-founder",
type: "radio",
title: "Who is the creator of React.js",
options: [
{
label: "Dan Abrahmov",
value: "danabrahmov",
},
{
label: "Google",
value: "google",
},
{
label: "Facebook",
value: "Facebook",
},
{
label: "Traversy Media",
value: "traversymedia",
},
],
},
{
name: "vue-founder",
type: "radio",
title: "Who is the creator of Vue.js",
options: [
{
label: "Evan Vue",
value: "evan-vue",
},
{
label: "Mark Zukerberg",
value: "mark",
},
{
label: "Prince Charles",
value: "prince-charles",
},
{
label: "Bill Gates",
value: "gates-bill",
},
],
},
{
name: "svelte-founder",
type: "radio",
title: "Who is the creator of Svelte.js",
options: [
{
label: "Hillary Svelte",
value: "danabrahmov",
},
{
label: "Mircosoft",
value: "microsoft",
},
{
label: "Elevate",
value: "elevate",
},
{
label: "Coding Train",
value: "coding-train",
},
],
},
{
name: "angular-founder",
type: "radio",
title: "Who is the creator of Angular.js",
options: [
{
label: "Laryy page",
value: "larry-page",
},
{
label: "Google",
value: "google",
},
{
label: "Sales Force",
value: "sales-force",
},
],
},
];
function handleStart() {
currentQuestionIndex += 1;
}
function onSubmit(name, value) {
if (!value) return;
answers = {
...answers,
[name]: value,
};
currentQuestionIndex += 1;
}
$: currentQuestion = questions[currentQuestionIndex - 1];
function onPrevious() {
currentQuestionIndex -= 1;
}
</script>
{#if currentQuestionIndex === 0}
<div>
<h2>Home task 1</h2>
<p>
You will be to answer 10 questions, it isn't timed so you can take your
time to answer. You can also continue from where you left off, you don't
need to worry cause everything is automatically syncronized in the cloud.
</p>
<PrimaryButton
onClick={handleStart}
label="Start"
className="my-5"
type="button"
/>
</div>
{:else if !currentQuestion}
<pre>
<code>{JSON.stringify(answers)}</code>
</pre>
{:else}
<RadioQuestion
title={currentQuestionIndex + ". " + currentQuestion.title}
name={currentQuestion.name}
options={currentQuestion.options}
defaultValue={answers[currentQuestion.name] || false}
{onSubmit}
{onPrevious}
disablePreviousButton={currentQuestionIndex === 1}
isLast={currentQuestionIndex === questions.length}
/>
{/if}
@@ -1,19 +1,94 @@
<script>
import marked from "marked";
import PrimaryButton from "../../../PrimaryButton/index.svelte";
import readme from "../readme.js";
let isWriteMode = true;
export let value = readme;
export let placeholder = ``;
const activeClassName = "border-b-2 border-blue-700";
const handleTabClick = (tab) => (event) => {
isWriteMode = tab === 1;
};
</script>
<div class="root">
<div class="flex justify-center">
<button
on:click={handleTabClick(1)}
class="focus:outline-none p-2 {isWriteMode && `${activeClassName}`}"
>
Write
</button>
<button
on:click={handleTabClick(2)}
class="focus:outline-none p-2 {!isWriteMode && `${activeClassName}`}"
>
Preview
</button>
</div>
<div class="m-2 mb-0 p-0">
{#if isWriteMode}
<textarea
bind:value
{placeholder}
class="border border-gray rounded-md p-2"
/>
{:else}
<article class="preview prose prose-sm sm:prose p-2">
{@html marked(value)}
</article>
{/if}
</div>
{#if !isWriteMode}
<hr />
{/if}
<div class="flex justify-between items-center px-2 pb-1">
<div class="flex items-center text-sm">
<svg
class="octicon mr-2"
height="16"
viewBox="0 0 16 16"
version="1.1"
width="16"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"
/>
</svg>
<span>Styling with Markdown is supported</span>
</div>
<PrimaryButton label="Answer" />
</div>
</div>
<!-- {@html marked(readme, {
breaks: true,
gfm: true,
})} -->
<style>
.root {
width: 100%;
min-height: 200px;
}
<iframe
title="Lecture"
src="https://docs.google.com/presentation/d/e/2PACX-1vTi_ATSrNBL-FMP7oDSi2DtXU7L8yzL0KAocNc-k7EqXaMM-oCbnbVmQjHYP0QKAw3r-QZayNfx5EpM/embed?start=false&loop=false&delayms=3000"
frameborder="0"
width="100%"
height="569"
allowfullscreen="true"
mozallowfullscreen="true"
webkitallowfullscreen="true" />
textarea {
width: 100%;
height: 500px;
}
.preview {
min-height: 150px;
}
.octicon {
display: inline-block;
vertical-align: text-top;
fill: currentColor;
}
</style>
@@ -0,0 +1,94 @@
<script>
import marked from "marked";
import PrimaryButton from "../../../../PrimaryButton/index.svelte";
import readme from "../../readme.js";
let isWriteMode = true;
export let value = readme;
export let placeholder = ``;
const activeClassName = "border-b-2 border-blue-700";
const handleTabClick = (tab) => (event) => {
isWriteMode = tab === 1;
};
</script>
<div class="root">
<div class="flex justify-center">
<button
on:click={handleTabClick(1)}
class="focus:outline-none p-2 {isWriteMode && `${activeClassName}`}"
>
Write
</button>
<button
on:click={handleTabClick(2)}
class="focus:outline-none p-2 {!isWriteMode && `${activeClassName}`}"
>
Preview
</button>
</div>
<div class="m-2 mb-0 p-0">
{#if isWriteMode}
<textarea
bind:value
{placeholder}
class="border border-gray rounded-md p-2"
/>
{:else}
<article class="preview prose prose-sm sm:prose p-2">
{@html marked(value)}
</article>
{/if}
</div>
{#if !isWriteMode}
<hr />
{/if}
<div class="flex justify-between items-center px-2 pb-1">
<div class="flex items-center text-sm">
<svg
class="octicon mr-2"
height="16"
viewBox="0 0 16 16"
version="1.1"
width="16"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"
/>
</svg>
<span>Styling with Markdown is supported</span>
</div>
<PrimaryButton label="Answer" />
</div>
</div>
<!-- {@html marked(readme, {
breaks: true,
gfm: true,
})} -->
<style>
.root {
width: 100%;
min-height: 200px;
}
textarea {
width: 100%;
height: 500px;
}
.preview {
min-height: 150px;
}
.octicon {
display: inline-block;
vertical-align: text-top;
fill: currentColor;
}
</style>
@@ -0,0 +1,29 @@
<script>
import marked from "marked";
import PageNav from "../../PageNav/index.svelte";
const readme = `
# Introduction
Welcome to this course, we are glad you made it here
## What you'l learn
1. Build powerful, fast, user-friendly and reactive web apps.
2. Provide amazing user experiences by leveraging the power of JavaScript with ease.
3. Apply for high-paid jobs or work as a freelancer in one the most-demanded sectors you can find in web dev right now.
4. Learn all about React Hooks and React Components.
`;
</script>
<PageNav title="Overview" disableSticky={true} />
<div class="body">
{@html marked(readme, { breaks: true, gfm: true })}
</div>
<style>
.body {
max-width: 1000px;
margin: 10px;
}
</style>
@@ -2,7 +2,9 @@
import PageNav from "../../../PageNav/index.svelte";
import Classwork from "./Classwork.svelte";
import Homework from "./Homework.svelte";
import Readme from "./Readme.svelte";
// import Readme from "./Readme.svelte";
import ReadmeInEditMode from "./Readme/EditMode.svelte";
import Switcher from "../../../Switcher/index.svelte";
export let path;
export let tab;
@@ -29,11 +31,15 @@
href: `${path}?tab=hometasks`,
},
]}
/>
>
<div slot="widget">
<Switcher />
</div>
</PageNav>
<div class="course">
{#if tab === "lectures"}
<Readme />
<ReadmeInEditMode />
{:else if tab === "classwork"}
<Classwork />
{:else if tab === "hometasks"}
+21 -16
View File
@@ -1,22 +1,27 @@
export default `
<p align="center">
<a href="https://nextjs.org">
<img src="https://assets.vercel.com/image/upload/v1607554385/repositories/next-js/next-logo.png" height="128">
<h1 align="center">React.js</h1>
</a>
</p>
export default `# 1. Soft Skills
## Getting Started
## Notes
Here I can write some notes to my students
Visit <a aria-label="next.js learn" href="https://nextjs.org/learn">https://nextjs.org/learn</a> to get started with Next.js.
## Slide
<iframe
title="Lecture"
src="https://docs.google.com/presentation/d/e/2PACX-1vTi_ATSrNBL-FMP7oDSi2DtXU7L8yzL0KAocNc-k7EqXaMM-oCbnbVmQjHYP0QKAw3r-QZayNfx5EpM/embed?start=false&loop=false&delayms=3000"
frameborder="0"
width="100%"
height="569"
allowfullscreen="true"
mozallowfullscreen="true"
webkitallowfullscreen="true"></iframe>
## Documentation
Visit [https://nextjs.org/docs](https://nextjs.org/docs) to view the full documentation.
## Who is using Next.js?
Next.js is used by the world's leading companies. Check out the [Next.js Showcase](https://nextjs.org/showcase) to learn more.
## Video
<iframe
width="100%"
height="315"
src="https://www.youtube.com/embed/8pLZkbN9h_s"
title="YouTube video player"
frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
webkitallowfullscreen="true"></iframe>
## Community
@@ -1,10 +1,16 @@
<script>
export let label;
export let className = "";
export let onClick = () => {};
export let name = "";
export let type;
</script>
<button
class="bg-blue-700 hover:bg-blue-900 text-white font-bold py-1 px-3 rounded {className}"
on:click={onClick}
{name}
{type}
>
{label}
</button>
+61
View File
@@ -0,0 +1,61 @@
<script>
import PrimaryButton from "../PrimaryButton/index.svelte";
export let title = "";
export let name = "";
export let options = [];
export let onSubmit = () => {};
export let onPrevious = () => {};
export let defaultValue = "";
export let disablePreviousButton = false;
export let isLast = false;
function getRadioVal(form, name) {
let val;
const radios = form.elements[name];
for (let i = 0, len = radios.length; i < len; i++) {
if (radios[i].checked) {
val = radios[i].value;
break;
}
}
return val;
}
function handleFormSubmit(event) {
event.preventDefault();
const value = getRadioVal(event.target, name);
onSubmit(name, value);
event.target.reset();
}
function handlePrevious(event) {
event.preventDefault();
onPrevious();
}
</script>
<form on:submit={handleFormSubmit}>
<h2>{title}</h2>
<div class="ml-4">
{#each options as option}
<div>
<input
type="radio"
{name}
value={option.value}
checked={option.value === defaultValue}
/>
<label for={option.value}>{option.label}</label>
</div>
{/each}
</div>
<div class="mt-3">
{#if disablePreviousButton === false}
<PrimaryButton onClick={handlePrevious} label="Previous" />
{/if}
<PrimaryButton type="submit" label={isLast ? "Finish" : "Next"} {name} />
</div>
</form>
+182
View File
@@ -0,0 +1,182 @@
<script>
export let checked = false;
function onChange() {
checked = !checked;
}
</script>
<div class="neumorphism-toggle">
<input type="checkbox" id="neumorphism" {checked} {onChange} />
<label for="neumorphism">
<div class="switch">
<div class="dot" />
</div>
<span>Neumorphism</span>
</label>
<span>... not allowed to toggle again :(</span>
</div>
<style lang="scss">
.neumorphism-toggle {
position: relative;
transition: transform 0.3s;
transform: scale(var(--scale, 1)) translateZ(0);
&:active {
--scale: 0.96;
}
input {
display: none;
& + label {
background: #fff;
border-radius: 9px;
padding: 16px 0 16px 20px;
min-width: 208px;
display: block;
cursor: pointer;
position: relative;
box-shadow: -12px -12px 24px var(--light-shadow, transparent),
12px 12px 24px var(--shadow, transparent);
transition: box-shadow 0.4s;
&:before {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border-radius: inherit;
background: linear-gradient(160deg, #f1f4ff, #f9faff);
opacity: var(--gradient, 0);
transition: opacity 0.4s;
}
.switch {
position: relative;
display: inline-block;
z-index: 1;
vertical-align: top;
height: 22px;
width: 40px;
border-radius: 11px;
background: #eceffc;
&:before {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border-radius: inherit;
background: linear-gradient(90deg, #4f97ff, #275efe);
opacity: var(--gradient, 0);
transition: opacity 0.4s;
}
.dot {
background: #d1d6ee;
position: absolute;
width: 24px;
height: 24px;
border-radius: 50%;
left: -1px;
top: -1px;
transform: translateX(var(--offset, 0));
transition: transform 0.4s, box-shadow 0.4s;
box-shadow: -4px -4px 8px var(--light-shadow-2, transparent),
4px 4px 8px var(--shadow, transparent);
&:before {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border-radius: inherit;
background: linear-gradient(160deg, #f1f4ff, #f9faff);
opacity: var(--gradient, 0);
transition: opacity 0.4s;
}
}
}
span {
line-height: 22px;
font-size: 16px;
color: var(--text, #646b8c);
font-weight: 500;
display: inline-block;
vertical-align: top;
z-index: 1;
position: relative;
margin-left: 12px;
transition: color 0.4s;
}
& + span {
text-align: center;
display: block;
position: absolute;
left: 0;
right: 0;
top: 100%;
opacity: 0;
font-size: 12px;
font-weight: 500;
color: #a6accd;
transform: translateY(4px);
transition: opacity 0.4s, transform 0.4s;
}
}
&:not(:checked) {
& + label {
pointer-events: none;
& + span {
opacity: 1;
transform: translateY(12px);
}
}
}
&:checked {
& + label {
--offset: 18px;
--text: #404660;
--gradient: 1;
--shadow: rgba(0, 6, 39, 0.1);
--light-shadow: rgba(255, 255, 255, 0.8);
--light-shadow-2: rgba(255, 255, 255, 0.1);
}
}
}
}
html {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
}
* {
box-sizing: inherit;
&:before,
&:after {
box-sizing: inherit;
}
}
// dribbble & twitter
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: "Inter", "Inter UI", Arial;
background: #f6f8ff;
.twitter {
position: fixed;
display: block;
right: 16px;
bottom: 16px;
svg {
width: 32px;
height: 32px;
fill: #1da1f2;
}
}
}
</style>