feat: initial uploads and single study space view
Co-Authored-By: Alex Ling <al443@ic.ac.uk>
This commit is contained in:
11
src/lib/assets/camera.svg
Normal file
11
src/lib/assets/camera.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="38" height="38" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_117_282)">
|
||||
<path d="M36.4168 30.0833C36.4168 30.9232 36.0832 31.7286 35.4893 32.3225C34.8955 32.9164 34.09 33.25 33.2502 33.25H4.75016C3.91031 33.25 3.10486 32.9164 2.51099 32.3225C1.91713 31.7286 1.5835 30.9232 1.5835 30.0833V12.6667C1.5835 11.8268 1.91713 11.0214 2.51099 10.4275C3.10486 9.83363 3.91031 9.5 4.75016 9.5H11.0835L14.2502 4.75H23.7502L26.9168 9.5H33.2502C34.09 9.5 34.8955 9.83363 35.4893 10.4275C36.0832 11.0214 36.4168 11.8268 36.4168 12.6667V30.0833Z" stroke="#49BD85" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M19.0002 26.9167C22.498 26.9167 25.3335 24.0811 25.3335 20.5833C25.3335 17.0855 22.498 14.25 19.0002 14.25C15.5024 14.25 12.6668 17.0855 12.6668 20.5833C12.6668 24.0811 15.5024 26.9167 19.0002 26.9167Z" stroke="#49BD85" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_117_282">
|
||||
<rect width="38" height="38" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
3
src/lib/assets/cross.svg
Normal file
3
src/lib/assets/cross.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="68" height="68" viewBox="0 0 68 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M43.8404 24.0416L24.0414 43.8406M24.0414 24.0416L43.8404 43.8406" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 259 B |
30
src/lib/components/Button.svelte
Normal file
30
src/lib/components/Button.svelte
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from "svelte";
|
||||
|
||||
interface Props {
|
||||
onclick?: (event: MouseEvent) => void;
|
||||
disabled?: boolean;
|
||||
type?: "button" | "submit" | "reset";
|
||||
children?: Snippet;
|
||||
}
|
||||
const { children, ...rest }: Props = $props();
|
||||
</script>
|
||||
|
||||
<button {...rest}>
|
||||
{@render children?.()}
|
||||
</button>
|
||||
|
||||
<style>
|
||||
button {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
background: linear-gradient(-83deg, #3fb095, #49bd85);
|
||||
box-shadow: 0rem 0rem 0.5rem #182125;
|
||||
color: #eaffeb;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:focus {
|
||||
outline: 2px solid #007bff;
|
||||
}
|
||||
</style>
|
||||
45
src/lib/components/Navbar.svelte
Normal file
45
src/lib/components/Navbar.svelte
Normal file
@@ -0,0 +1,45 @@
|
||||
<script lang="ts">
|
||||
import logoUrl from "$lib/assets/logo.svg";
|
||||
import type { Snippet } from "svelte";
|
||||
|
||||
interface Props {
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
const { children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
<a href="/" class="logo">
|
||||
<img src={logoUrl} alt="logo" />
|
||||
</a>
|
||||
<div class="rightButton">{@render children?.()}</div>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
display: flex;
|
||||
position: sticky;
|
||||
width: 100%;
|
||||
height: 4rem;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(-77deg, #2e4653, #3a5b56);
|
||||
box-shadow: 0rem 0rem 0.5rem #182125;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
.logo img {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.rightButton {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -5,17 +5,18 @@
|
||||
alt: string;
|
||||
imgSrc: string;
|
||||
description?: Snippet;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
const { alt, imgSrc, description }: Props = $props();
|
||||
const { alt, imgSrc, description, href }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="card">
|
||||
<a class="card" {href}>
|
||||
<img src={imgSrc} {alt} />
|
||||
<div class="description">
|
||||
{@render description?.()}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
|
||||
64
src/lib/components/inputs/Image.svelte
Normal file
64
src/lib/components/inputs/Image.svelte
Normal file
@@ -0,0 +1,64 @@
|
||||
<script lang="ts">
|
||||
import cameraUrl from "$lib/assets/camera.svg";
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
height?: string;
|
||||
minHeight?: string;
|
||||
files?: FileList;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
let { name, height, minHeight, files = $bindable(), ...rest }: Props = $props();
|
||||
</script>
|
||||
|
||||
<label
|
||||
for={name}
|
||||
style="height: {height || 'auto'}; min-height: {minHeight || 'auto'};"
|
||||
class:no-bg={files && files.length > 0}
|
||||
>
|
||||
{#if files && files.length > 0}
|
||||
<img src={URL.createObjectURL(files[0])} alt="uploaded study space" class="preview" />
|
||||
{:else}
|
||||
<div class="message">
|
||||
<img src={cameraUrl} class="icon" alt="camera icon" />
|
||||
<span>Click to upload a photo</span>
|
||||
</div>
|
||||
{/if}
|
||||
<input type="file" id={name} {name} accept=".png, .jpg, .jpeg, .svg" {...rest} bind:files />
|
||||
</label>
|
||||
|
||||
<style>
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: #eaffeb;
|
||||
cursor: pointer;
|
||||
}
|
||||
label.no-bg {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
label input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #49bd85;
|
||||
}
|
||||
.preview {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
.message .icon {
|
||||
width: 2rem;
|
||||
}
|
||||
</style>
|
||||
29
src/lib/components/inputs/Text.svelte
Normal file
29
src/lib/components/inputs/Text.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
name: string;
|
||||
value?: string | null;
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
let { value = $bindable(), name, ...rest }: Props = $props();
|
||||
</script>
|
||||
|
||||
<input type="text" id={name} {name} bind:value {...rest} />
|
||||
|
||||
<style>
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 2px solid #eaffeb;
|
||||
background: none;
|
||||
color: #eaffeb;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
32
src/lib/components/inputs/Textarea.svelte
Normal file
32
src/lib/components/inputs/Textarea.svelte
Normal file
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
name: string;
|
||||
value?: string | null;
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
rows?: number;
|
||||
cols?: number;
|
||||
}
|
||||
|
||||
let { value = $bindable(), name, ...rest }: Props = $props();
|
||||
</script>
|
||||
|
||||
<textarea id={name} {name} {...rest}></textarea>
|
||||
|
||||
<style>
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
resize: vertical;
|
||||
border: 2px solid #eaffeb;
|
||||
background: none;
|
||||
color: #eaffeb;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
textarea:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user