This commit is contained in:
Theophyl Hoguet committed 2023-03-10 16:55:23 +01:00
1 parent 4d5e096e11
commit da9d5e6782
17 files changed
+341 -209

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="icon" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PDL L3 - Galerie</title>
</head>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#e4e6ea;}.cls-2{fill:#ffb85a;}.cls-3{opacity:0.12;}.cls-4{fill:#2b374f;}</style></defs><title>magic-wand-filled</title><path class="cls-1" d="M20.28,491.72h0a25.6,25.6,0,0,1,0-36.2L288.91,186.88l36.21,36.21L56.48,491.72A25.6,25.6,0,0,1,20.28,491.72Z"/><path class="cls-2" d="M445.95,213.51l-59.07-1.28-33.79,48.13a12.81,12.81,0,0,1-22.66-3.46l-18.18-57.15-57.15-18.18a12.81,12.81,0,0,1-3.46-22.66l48.13-33.79-1.28-59.07A12.82,12.82,0,0,1,319,55.55L366.78,91.4,423.1,72.71A12.78,12.78,0,0,1,439.29,88.9L420.6,145.22,456.45,193A12.82,12.82,0,0,1,445.95,213.51Z"/><g class="cls-3"><path d="M445.95,213.51l-59.07-1.28-33.79,48.13a12.81,12.81,0,0,1-22.66-3.46l-18.18-57.15,124-124a12.75,12.75,0,0,1,3.07,13.12L420.6,145.22,456.45,193A12.82,12.82,0,0,1,445.95,213.51Z"/></g><path class="cls-4" d="M434.87,142.92l16.57-50a25.6,25.6,0,0,0-32.35-32.35l-50,16.57L326.66,45.32a25.6,25.6,0,0,0-41,21l1.12,52.2-42.54,29.9a25.6,25.6,0,0,0,7,45.34l9.62,3.06L11.22,446.46a38.46,38.46,0,0,0,0,54.31h0a38.45,38.45,0,0,0,54.31,0L315.15,251.15l3.06,9.62a25.45,25.45,0,0,0,20.51,17.54,26.06,26.06,0,0,0,4,.3,25.43,25.43,0,0,0,20.88-10.89l29.9-42.54,52.2,1.12a25.6,25.6,0,0,0,21-41ZM47.44,482.67a12.8,12.8,0,1,1-18.11-18.1l259-259,1.8.57,15.74,15.74.57,1.79ZM387.18,199.44a12.81,12.81,0,0,0-10.75,5.44L342.61,253,324.41,195.9a12.81,12.81,0,0,0-8.31-8.31L259,169.39l48.12-33.82a12.8,12.8,0,0,0,5.44-10.75l-1.27-59,47.77,35.83a12.79,12.79,0,0,0,11.71,1.91l56.39-18.77,0,.08-18.68,56.36a12.81,12.81,0,0,0,1.91,11.71l35.84,47.78Z"/><circle class="cls-4" cx="486.4" cy="121.6" r="12.8"/><circle class="cls-4" cx="492.8" cy="19.2" r="19.2"/><circle class="cls-4" cx="364.79" cy="12.8" r="12.8"/><circle class="cls-4" cx="422.4" cy="281.61" r="19.2"/><circle class="cls-4" cx="223.99" cy="89.6" r="12.8"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+102 -51
View File
@@ -1,60 +1,111 @@
<script setup lang="ts">
</script>
<template>
<div id="app">
<nav>
<ul>
<li>
<router-link to="/">Home</router-link>
</li>
<li>
<router-link to="/gallery">Gallery</router-link>
</li>
<li>
<router-link to="/upload">Upload</router-link>
</li>
</ul>
</nav>
<div>
<h1>PDL - L3</h1>
<router-view />
</div>
<div v-for="route in routes" class="button">
<a :href="'#' + (routes[route] || '/')"> {{ route }}</a>
</div>
<div>
<label>Files
<input id="fileUpload" type="file" @change="handleFilesUpload($event as InputFileEvent)" />
</label>
<button :class="sent ? 'green' : 'normal'" @click="submitImage()">{{ sent ? 'Sent' : 'Submit' }}</button>
</div>
<component :is="currentView" :key="sent" :images="images" @delete="deleteFile" />
</template>
<style>
#app {
<script setup lang="ts">
import Home from './components/Home.vue'
import Gallery from './components/Gallery.vue';
import NotFound from './components/NotFound.vue'
import { defineComponent } from 'vue'
import { api } from './http-api'
import { ImageType, ImageClass } from './image';
</script>
<script lang="ts">
const routes: { [key: string]: string } = {
'/': "Home",
'Gallery': "Gallery"
}
interface InputFileEvent extends Event {
target: HTMLInputElement;
}
export default defineComponent({
components: {
Home,
Gallery,
NotFound
},
data() {
return {
images: new Map<Number, ImageType>(),
currentPath: window.location.hash,
sent: false,
file: null as FileList | null
}
},
computed: {
currentView(): string {
return routes[this.currentPath.slice(1) || '/'] || "notFound";
}
},
methods: {
async submitImage() {
if (this.sent == true || this.file == null)
return;
let formData = new FormData();
formData.append('file', this.file[0]);
await api.createImage(formData);
this.sent = true;
(document.getElementById('fileUpload') as HTMLInputElement).value = "";
this.updateImageList(100);
},
handleFilesUpload(event: InputFileEvent) {
this.file = event.target.files;
this.sent = false;
},
async updateImageList(sleep: number) {
setTimeout(() => {
api.getImageList().then((newImages) => {
const newImageList = new Map<number, ImageType>();
for (const image of newImages.values()) {
newImageList.set(image.id, image);
}
this.images = newImageList; console.log(this.images);
})
}, sleep);
},
deleteFile(id: number) {
api.deleteImage(id).then(() => {
this.updateImageList(100);
})
}
},
mounted() {
this.updateImageList(0);
window.addEventListener('hashchange', () => {
this.currentPath = window.location.hash;
});
}
})
</script>
<style scoped>
.button {
display: inline-block;
color: #444;
border: 1px solid #CCC;
background: #DDD;
box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2);
cursor: pointer;
vertical-align: middle;
max-width: 100px;
padding: 5px;
text-align: center;
font-family: Arial, Helvetica, sans-serif;
color: #2c3e50;
margin: 5px;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
li {
float: left;
}
li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
.error {
.button:active {
color: red;
font-weight: bold;
list-style-type: none;
margin-top: 1em;
box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);
}
.green {
color: green;
}
</style>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

+35 -19
View File
@@ -1,25 +1,41 @@
<template>
<div class="flex" v-if="images.keys != null">
<Image v-for="[id, image] in images" :key="id" :id="id" />
</div>
<h1 v-else>No image found</h1>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { api } from '@/http-api';
import { defineComponent } from 'vue'
import { ImageType } from '@/image'
import Image from './Image.vue';
import Image from '@/components/Image.vue'
</script>
<script lang="ts">
const imageList = ref<ImageType[]>([]);
api.getImageList()
.then((data) => {
imageList.value = data;
})
.catch(e => {
console.log(e.message);
});
export default defineComponent({
props: {
images: {
type: Map<number, ImageType>,
required: true
}
}
})
</script>
<template>
<div>
<h3>Gallery</h3>
<Image v-for="image in imageList" :id="image.id" />
</div>
</template>
<style scoped>
.flex {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
<style scoped></style>
.flex>img {
margin: 5px;
}
img {
max-width: 25%;
max-height: 25%;
}
</style>
+55 -28
View File
@@ -1,35 +1,62 @@
<script setup lang="ts">
import { ref } from 'vue';
import router from "@/router";
import { api } from '@/http-api';
import { ImageType } from '@/image'
const selectedId = ref(-1);
const imageList = ref<ImageType[]>([]);
getImageList();
function getImageList() {
api.getImageList().then((data) => {
imageList.value = data;
}).catch(e => {
console.log(e.message);
});
}
function showImage() {
router.push({ name: 'image', params: { id: selectedId.value } })
}
</script>
<template>
<div>
<h3>Choose an image</h3>
<div class="flex" v-if="images.keys != null">
<div>
<select v-model="selectedId" @change="showImage">
<option v-for="image in imageList" :value="image.id" :key="image.id">{{ image.name }}</option>
<select v-model="imageSelectedId" style="width: min-content;margin: 15px;">
<option v-for="[id, image] in images" :key="id" :value="id">{{ image.name }}</option>
</select>
<button @click="$emit('delete', imageSelectedId)">Delete</button>
</div>
<Image :id="imageSelectedId"></Image>
</div>
<h1 v-else>No images found</h1>
</template>
<script setup lang="ts">
import Image from '@/components/Image.vue'
import { defineComponent } from 'vue'
import { ImageType } from '@/image'
</script>
<script lang="ts">
export default defineComponent({
emits: {
delete(id: number) {
return id >= 0;
}
},
props: {
images: {
type: Map<number, ImageType>,
required: true
}
},
data() {
return {
imageSelectedId: 0 as number
}
},
mounted() {
this.imageSelectedId = this.images.keys().next().value;
console.log(this.imageSelectedId);
},
watch: {
images() {
this.imageSelectedId = this.images.keys().next().value;
console.log(this.imageSelectedId);
},
imageSelectedId() {
console.log(this.imageSelectedId);
}
}
})
</script>
<style scoped>
.flex {
display: flex;
flex-direction: column;
align-items: center;
}
<style scoped></style>
img {
max-width: 90%;
max-height: 60%;
}
</style>
+35 -24
View File
@@ -1,31 +1,42 @@
<script setup lang="ts">
import { defineProps } from 'vue';
import { defineComponent } from 'vue';
import { api } from '@/http-api';
const props = defineProps<{ id: number }>()
api.getImage(props.id)
.then((data: Blob) => {
const reader = new window.FileReader();
reader.readAsDataURL(data);
reader.onload = () => {
const galleryElt = document.getElementById("gallery-" + props.id);
if (galleryElt !== null) {
const imgElt = document.createElement("img");
galleryElt.appendChild(imgElt);
if (imgElt !== null && reader.result as string) {
imgElt.setAttribute("src", (reader.result as string));
}
}
};
})
.catch(e => {
console.log(e.message);
});
</script>
<script lang="ts">
export default defineComponent({
props: {
id: {
type: Number,
required: true
}
},
methods: {
async getImage(id: number) {
const data = await api.getImage(id);
const reader = new window.FileReader();
reader.readAsDataURL(data);
reader.onload = () => {
const imgElt = document.getElementById("img-" + id) as HTMLImageElement;
if (imgElt !== null) {
if (reader.result as string) {
imgElt.setAttribute("src", (reader.result as string));
}
}
};
}
},
mounted() {
this.getImage(this.id);
},
watch: {
id() {
this.getImage(this.id);
}
}
})
</script>
<template>
<figure :id="'gallery-' + id"></figure>
<img :id="'img-' + id" />
</template>
<style></style>
+13
View File
@@ -0,0 +1,13 @@
<template>
<div>
<h1>404 NOT FOUND</h1>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
-40
View File
@@ -1,40 +0,0 @@
<script setup lang="ts">
import { ref } from 'vue';
import { api } from '@/http-api';
const target = ref<HTMLInputElement>();
function submitFile() {
if (target.value !== null && target.value !== undefined && target.value.files !== null) {
const file = target.value.files[0];
if (file === undefined)
return;
let formData = new FormData();
formData.append("file", file);
api.createImage(formData).then(() => {
if (target.value !== undefined)
target.value.value = '';
}).catch(e => {
console.log(e.message);
});
}
}
function handleFileUpload(event: Event) {
target.value = (event.target as HTMLInputElement);
}
</script>
<template>
<div>
<h3>Upload an image</h3>
<div>
<input type="file" id="file" ref="file" @change="handleFileUpload" />
</div>
<div>
<button @click="submitFile">Submit</button>
</div>
</div>
</template>
<style scoped></style>
-8
View File
@@ -1,8 +0,0 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
+9
View File
@@ -1,4 +1,13 @@
export interface ImageType {
id: number;
name: string;
}
export class ImageClass implements ImageType {
id: number
name: string
constructor(id: number, title: string) {
this.id = id
this.name = title
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router';
createApp(App).use(router).mount('#app')
createApp(App).mount('#app')
-36
View File
@@ -1,36 +0,0 @@
import { createWebHistory, createRouter } from "vue-router";
import { RouteRecordRaw } from "vue-router";
const routes: Array<RouteRecordRaw> = [
{
path: "/",
name: "home",
component: () => import("./components/Home.vue"),
props: true
},
{
path: "/gallery",
name: "gallery",
component: () => import("./components/Gallery.vue"),
props: true
},
{
path: "/image/:id",
name: "image",
component: () => import("./components/Image.vue"),
props: ({ params }) => ({ id: Number(params.id) || 0 })
},
{
path: "/upload",
name: "upload",
component: () => import("./components/Upload.vue"),
props: true
}
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
+86
View File
@@ -0,0 +1,86 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />