Complete questionnaire builder

This commit is contained in:
rotimi-best
2021-06-26 21:32:42 +03:00
parent deb445b694
commit 445858f4bb
14 changed files with 269 additions and 254 deletions
+40 -35
View File
@@ -48,64 +48,41 @@
});
</script>
<style lang="scss">
.root {
height: 95vh;
position: sticky;
top: 0;
border-left: 1px solid var(--border-color);
display: flex;
flex-direction: row;
& .apps {
padding-top: 20px;
display: flex;
flex-direction: column;
align-items: center;
width: 60px;
border-right: 1px solid var(--border-color);
height: 100%;
}
& .app {
width: 300px;
transition: all 1s ease-out;
position: relative;
}
}
</style>
<div class="root">
<div class="apps">
<IconButton
toolTipProps={{ title: 'Live Chat', hotkeys: ['Ctrl', '1'] }}
toolTipProps={{ title: "Live Chat", hotkeys: ["Ctrl", "1"] }}
value={APPS_CONSTANTS.APPS.LIVE_CHAT}
onClick={handleAppClick}
selected={APPS_CONSTANTS.APPS.LIVE_CHAT === selectedApp}>
selected={APPS_CONSTANTS.APPS.LIVE_CHAT === selectedApp}
>
<SendAlt24 class="carbon-icon" />
</IconButton>
<IconButton
toolTipProps={{ title: 'QandA', hotkeys: ['Ctrl', '2'] }}
toolTipProps={{ title: "QandA", hotkeys: ["Ctrl", "2"] }}
value={APPS_CONSTANTS.APPS.QANDA}
onClick={handleAppClick}
selected={APPS_CONSTANTS.APPS.QANDA === selectedApp}>
selected={APPS_CONSTANTS.APPS.QANDA === selectedApp}
>
<Forum24 class="carbon-icon" />
</IconButton>
<IconButton
toolTipProps={{ title: 'Notes', hotkeys: ['Ctrl', '3'] }}
toolTipProps={{ title: "Notes", hotkeys: ["Ctrl", "3"] }}
value={APPS_CONSTANTS.APPS.NOTES}
onClick={handleAppClick}
selected={APPS_CONSTANTS.APPS.NOTES === selectedApp}>
selected={APPS_CONSTANTS.APPS.NOTES === selectedApp}
>
<AlignBoxTopLeft24 class="carbon-icon" />
</IconButton>
<IconButton
toolTipProps={{ title: 'Poll', hotkeys: ['Ctrl', '4'] }}
toolTipProps={{ title: "Poll", hotkeys: ["Ctrl", "4"] }}
value={APPS_CONSTANTS.APPS.POLL}
onClick={handleAppClick}
selected={APPS_CONSTANTS.APPS.POLL === selectedApp}>
selected={APPS_CONSTANTS.APPS.POLL === selectedApp}
>
<ChartPie24 class="carbon-icon" />
</IconButton>
</div>
@@ -124,3 +101,31 @@
</div>
{/if}
</div>
<style lang="scss">
.root {
height: 95vh;
position: sticky;
top: 0;
border-left: 1px solid var(--border-color);
display: flex;
flex-direction: row;
z-index: 1;
& .apps {
padding-top: 20px;
display: flex;
flex-direction: column;
align-items: center;
width: 60px;
border-right: 1px solid var(--border-color);
height: 100%;
}
& .app {
width: 300px;
transition: all 1s ease-out;
position: relative;
}
}
</style>
+2 -1
View File
@@ -3,8 +3,9 @@
import IconButton from "../../IconButton/index.svelte";
export let onClick;
export let contained;
</script>
<IconButton {onClick} title="Close">
<IconButton {onClick} {contained} title="Close">
<Close24 class="carbon-icon" title="Close" />
</IconButton>
@@ -3,38 +3,34 @@
import AddFilled24 from "carbon-icons-svelte/lib/AddFilled24";
import TextField from "../../../../Form/TextField.svelte";
import TextArea from "../../../../Form/TextArea.svelte";
import Checkbox from "../../../../Form/Checkbox.svelte";
import RadioItem from "../../../../Form/RadioItem.svelte";
import IconButton from "../../../../IconButton/index.svelte";
import Select from "../../../../Form/Select.svelte";
import PrimaryButton from "../../../../PrimaryButton/index.svelte";
import { VARIANTS } from "../../../../PrimaryButton/constants";
import QuestionContainer from "../../../../QuestionContainer/index.svelte";
import {
QUESTION_TYPE,
QUESTION_TYPES,
QUESTION_TEMPLATE,
} from "../../../../Question/constants";
// import EditContent from "../../../../EditContent/index.svelte";
// import readme from "../../readme.js";
// let value = readme;
let questions = [
{
id: 1,
title: "",
type: QUESTION_TYPE.RADIO,
options: [
{
id: 1,
value: "",
},
],
},
];
let task = {
title: "",
description: null,
};
let questions = [];
function handleAddOption(id) {
function handleAddOption(questionId) {
return () => {
questions = questions.map((question) => {
if (question.id === id) {
if (question.id === questionId) {
return {
...question,
options: [
@@ -73,78 +69,108 @@
questions = [
...questions,
{
...QUESTION_TEMPLATE,
id: questions.length + 1,
title: "",
type: QUESTION_TYPE.RADIO,
options: [
{
id: 1,
value: "",
},
],
},
];
}
function handleRemoveQuestion(questionId) {
return () => {
questions = questions.filter((q) => q.id !== questionId);
};
}
</script>
{#each questions as question}
<div
class="border-2 border-gray border-r-2 rounded-md px-4 py-2 mb-4 w-3/5 inline-block"
>
<div class="flex">
<div class="mr-2">
<TextField placeholder="Question" bind:value={question.title} />
</div>
<Select bind:value={question.type} options={QUESTION_TYPES} />
</div>
<div class="flex flex-col">
{#each question.options as option}
{#if QUESTION_TYPE.RADIO === question.type}
<RadioItem
isEditable={true}
name={question.title || "radio-name"}
bind:label={option.value}
>
<div slot="iconbutton">
<IconButton
value={option.id}
onClick={handleOptionRemove(question.id, option.id)}
>
<Delete24 class="carbon-icon" />
</IconButton>
</div>
</RadioItem>
{/if}
{#if QUESTION_TYPE.CHECKBOX === question.type}
<Checkbox
isEditable={true}
name={question || "checkbox-name"}
bind:label={option.value}
>
<div slot="iconbutton">
<IconButton
value={option.id}
onClick={handleOptionRemove(question.id)}
>
<Delete24 class="carbon-icon" />
</IconButton>
</div>
</Checkbox>
{/if}
{/each}
</div>
<div class="flex items-center">
<IconButton onClick={handleAddOption(question.id)}>
<AddFilled24 class="carbon-icon" />
</IconButton>
<p class="italic">Add option</p>
</div>
<div class="root relative">
<div class="head flex bg-white items-center justify-between sticky right-0">
<h3>Home task</h3>
<PrimaryButton
variant={VARIANTS.OUTLINED}
onClick={handleAddQuestion}
label="Add Question"
/>
</div>
{/each}
<div class="flex justify-end mt-2">
<PrimaryButton onClick={handleAddQuestion} label="Add Question" />
<QuestionContainer isTitle={true}>
<TextField placeholder="Title" bind:value={task.title} />
<TextArea
placeholder="Description and Rules"
bind:value={task.description}
/>
</QuestionContainer>
{#each questions as question}
<QuestionContainer onClose={handleRemoveQuestion(question.id)}>
<div class="flex justify-between">
<div class="mr-2 w-3/5">
<TextField placeholder="Question" bind:value={question.title} />
</div>
<Select bind:value={question.type} options={QUESTION_TYPES} />
</div>
<div class="flex flex-col">
{#each question.options as option}
{#if QUESTION_TYPE.RADIO === question.type}
<RadioItem
isEditable={true}
name={question.title || "radio-name"}
bind:label={option.value}
>
<div slot="iconbutton">
<IconButton
value={option.id}
onClick={handleOptionRemove(question.id, option.id)}
>
<Delete24 class="carbon-icon" />
</IconButton>
</div>
</RadioItem>
{/if}
{#if QUESTION_TYPE.CHECKBOX === question.type}
<Checkbox
isEditable={true}
name={question || "checkbox-name"}
bind:label={option.value}
>
<div slot="iconbutton">
<IconButton
value={option.id}
onClick={handleOptionRemove(question.id)}
>
<Delete24 class="carbon-icon" />
</IconButton>
</div>
</Checkbox>
{/if}
{#if QUESTION_TYPE.TEXTAREA === question.type}
<TextArea bind:value={option.value} />
{/if}
{/each}
</div>
{#if QUESTION_TYPE.TEXTAREA !== question.type}
<div class="flex items-center">
<IconButton onClick={handleAddOption(question.id)}>
<AddFilled24 class="carbon-icon" />
</IconButton>
<p class="italic">Add option</p>
</div>
{/if}
</QuestionContainer>
{/each}
</div>
<style>
.root {
max-width: 700px;
}
.head {
top: 63px;
z-index: 2;
}
</style>
@@ -1,94 +0,0 @@
<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>
@@ -49,7 +49,7 @@
</div>
</PageNav>
<div class="course">
<div class="course relative">
{#if tab === "lectures"}
<Readme {mode} />
{:else if tab === "classwork"}
+6 -2
View File
@@ -1,15 +1,19 @@
<script>
export let label;
export let placeholder;
export let placeholder = "Write your answer...";
export let value;
</script>
<label class="block">
<span class="text-gray-700">{label}</span>
{#if label}
<span class="text-gray-700">{label}</span>
{/if}
<textarea
class="form-textarea mt-1 block w-full h-24"
rows="3"
{placeholder}
bind:value
/>
<slot name="iconbutton" />
</label>
+6 -1
View File
@@ -3,6 +3,7 @@
export let onClick;
export let disabled = false;
export let selected = false;
export let contained = false;
export let value;
export let toolTipProps = {
title: "",
@@ -16,7 +17,7 @@
<ToolTip title={toolTipProps.title} hotkeys={toolTipProps.hotkeys}>
<button
class={`root ${selected && "active"}`}
class="root {selected && 'active'} {contained && 'contained'}"
{disabled}
on:click={handleClick}
>
@@ -49,4 +50,8 @@
background-color: rgba(0, 0, 0, 0.04);
}
}
.contained {
background-color: rgba(0, 0, 0, 0.04);
}
</style>
+24 -21
View File
@@ -12,12 +12,36 @@
Array.isArray(navItems) && navItems.length > 4 ? "bring-down" : "";
</script>
<div
class="header flex items-center justify-between {!disableSticky &&
'sticky'} {dynamicRootClass}"
style={overidableStyle}
>
{#if !!title}
<h4 class="title">{title}</h4>
{/if}
{#if Array.isArray(navItems) && navItems.length}
<div class="flex justify-evenly items-center">
{#each navItems as item}
<a class="mr-5 text-sm {item.isActive && 'active'}" href={item.href}>
{item.label}
</a>
{/each}
{#if addButtonHref}
<NewButton href={addButtonHref} label={addButtonLabel} />
{/if}
</div>
{/if}
<slot name="widget" />
</div>
<style>
.header {
border-bottom: 1px solid var(--border-color);
padding: 15px;
min-height: 61px;
background-color: #fff;
z-index: 1;
}
.header.sticky {
@@ -66,24 +90,3 @@
}
}
</style>
<div
class="header flex items-center justify-between mb-2 {!disableSticky && 'sticky'} {dynamicRootClass}"
style={overidableStyle}>
{#if !!title}
<h4 class="title">{title}</h4>
{/if}
{#if Array.isArray(navItems) && navItems.length}
<div class="flex justify-evenly items-center">
{#each navItems as item}
<a class="mr-5 text-sm {item.isActive && 'active'}" href={item.href}>
{item.label}
</a>
{/each}
{#if addButtonHref}
<NewButton href={addButtonHref} label={addButtonLabel} />
{/if}
</div>
{/if}
<slot name="widget" />
</div>
+10
View File
@@ -0,0 +1,10 @@
export const VARIANTS = {
CONTAINED: "CONTAINED",
OUTLINED: "OUTLINED",
};
export const VARIANTS_CLASS = {
[VARIANTS.CONTAINED]: "bg-blue-700 hover:bg-blue-900 text-white font-bold",
[VARIANTS.OUTLINED]:
"border border-blue-700 hover:border-blue-700 hover:bg-blue-200 text-black",
};
+5 -1
View File
@@ -1,13 +1,17 @@
<script>
import { VARIANTS, VARIANTS_CLASS } from "./constants";
export let label;
export let className = "";
export let onClick = () => {};
export let name = "";
export let type;
export let variant = VARIANTS.CONTAINED;
const variantClass = VARIANTS_CLASS[variant];
</script>
<button
class="bg-blue-700 hover:bg-blue-900 text-white font-bold py-1 px-3 rounded {className}"
class="h-auto {variantClass} py-1 px-3 rounded {className} transition delay-150 duration-300 ease-in-out"
on:click={onClick}
{name}
{type}
+12
View File
@@ -18,3 +18,15 @@ export const QUESTION_TYPES = [
text: "Paragraph",
},
];
export const QUESTION_TEMPLATE = {
id: 1,
title: "",
type: QUESTION_TYPE.RADIO,
options: [
{
id: 1,
value: "",
},
],
};
@@ -0,0 +1,39 @@
<script>
import CloseButton from "../Buttons/Close/index.svelte";
export let isTitle = false;
export let onClose;
</script>
<div
class="{isTitle
? 'border'
: 'border-2'} border-gray border-r-2 rounded-md mb-6 relative root"
>
{#if isTitle}
<div class="title absolute bg-blue-700" />
{/if}
{#if onClose}
<div class="close-button hidden absolute -top-6 -right-6">
<CloseButton onClick={onClose} contained={true} />
</div>
{/if}
<div class="px-4 {isTitle ? 'pt-4' : 'pt-2'} pb-3">
<slot />
</div>
</div>
<style>
.root:hover .close-button {
display: block;
}
.title {
color: rgba(255, 255, 255, 1);
border-top-left-radius: 8px;
border-top-right-radius: 8px;
height: 10px;
left: -1px;
top: -1px;
width: calc(100% + 2px);
}
</style>
+17 -17
View File
@@ -3,6 +3,22 @@
export let hotkeys = [];
</script>
{#if !!title}
<div class="tooltip">
<span class="tooltiptext z-20">
{title}
{#if Array.isArray(hotkeys) && hotkeys.length}
<span class="shortcut">
{#each hotkeys as hotkey}<span>{hotkey}</span>{/each}
</span>
{/if}
</span>
<slot />
</div>
{:else}
<slot />
{/if}
<style>
.tooltip {
position: relative;
@@ -26,7 +42,7 @@
/* Position the tooltip */
position: absolute;
z-index: 1;
z-index: 2;
/* top: 100%;
left: 0; */
top: 20%;
@@ -46,19 +62,3 @@
margin-left: 5px;
}
</style>
{#if !!title}
<div class="tooltip">
<span class="tooltiptext">
{title}
{#if Array.isArray(hotkeys) && hotkeys.length}
<span class="shortcut">
{#each hotkeys as hotkey}<span>{hotkey}</span>{/each}
</span>
{/if}
</span>
<slot />
</div>
{:else}
<slot />
{/if}