Initial commit
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
<script lang="ts">
|
||||
import Modal from "../../../Modal.svelte"
|
||||
import { apiBaseURL } from "../../../../../config"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import UploadImage from "./UploadImageWidget.svelte"
|
||||
import Icon from "../../Icon.svelte"
|
||||
import { mdiDeleteOutline } from "@mdi/js"
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
export let images: MedialibEntry[] = []
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
show="{true}"
|
||||
size="xl"
|
||||
on:close="{() => {
|
||||
dispatch('closeImageLibrary')
|
||||
}}"
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<img
|
||||
src="../../../../../../media/solar_camera-outline.svg"
|
||||
alt="icon"
|
||||
/>
|
||||
FOTOS
|
||||
</svelte:fragment>
|
||||
<div class="images">
|
||||
<UploadImage on:uploadImage="{() => dispatch('uploadImage')}" />
|
||||
{#each images as image}
|
||||
<div class="image">
|
||||
<div class="image-wrapper">
|
||||
<img
|
||||
src="{`${apiBaseURL}medialib/${image.id}/${image.file.src}`}"
|
||||
alt="Image Preview"
|
||||
class="image"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<button
|
||||
class="removeElement"
|
||||
on:click|preventDefault|stopPropagation="{() => {
|
||||
dispatch('removeImage', image.id)
|
||||
}}"
|
||||
>
|
||||
<Icon
|
||||
path="{mdiDeleteOutline}"
|
||||
size="1rem"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<style lang="less">
|
||||
@import "../../../../assets/css/variables.less";
|
||||
h3.label {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
align-items: center;
|
||||
}
|
||||
.images {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 12rem);
|
||||
gap: 6px;
|
||||
|
||||
.image {
|
||||
width: 12rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.image-wrapper {
|
||||
height: 12rem;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
background-color: var(--bg-100);
|
||||
width: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: unset;
|
||||
aspect-ratio: 1/1;
|
||||
object-fit: cover;
|
||||
border-radius: 12px;
|
||||
}
|
||||
@media @mobile {
|
||||
width: min(200px, 40vw);
|
||||
min-width: 50%;
|
||||
height: 50%;
|
||||
aspect-ratio: unset;
|
||||
img {
|
||||
aspect-ratio: unset;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
position: relative;
|
||||
.removeElement {
|
||||
top: 0.6rem;
|
||||
right: 0.6rem;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
border-radius: 48px;
|
||||
background: var(--bg-100);
|
||||
img {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
}
|
||||
border-radius: 12px;
|
||||
img {
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
@media @mobile {
|
||||
flex-direction: row;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media @mobile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,114 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import ImagePreview from "./imagePreview.svelte"
|
||||
|
||||
export let imageData: MedialibEntry[] = [],
|
||||
disabled: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const handleClick = (e: Event) => {
|
||||
if (!disabled) dispatch("openImageModal")
|
||||
}
|
||||
let innerWidth = 0
|
||||
$: isMobile = innerWidth < 1700
|
||||
$: amountOfVisibleImages = isMobile ? 2 : 12
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth="{innerWidth}" />
|
||||
<div class="image-preview-row">
|
||||
{#each imageData.slice(0, amountOfVisibleImages) as image}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:click|stopPropagation|preventDefault="{handleClick}"
|
||||
class="image-wrapper"
|
||||
>
|
||||
<ImagePreview image="{image}" />
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
{#if imageData.length > amountOfVisibleImages}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="image-wrapper"
|
||||
on:click|stopPropagation|preventDefault="{handleClick}"
|
||||
>
|
||||
<ImagePreview image="{imageData[amountOfVisibleImages]}" />
|
||||
{#if imageData.length > amountOfVisibleImages + 1}
|
||||
<div class="overlay">+{imageData.length - amountOfVisibleImages}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if !disabled}
|
||||
<button
|
||||
class="editImages"
|
||||
disabled="{disabled}"
|
||||
on:click|stopPropagation|preventDefault="{() => dispatch('uploadImage')}"
|
||||
>
|
||||
<img
|
||||
src="../../../../../../media/solar_pen-outline.svg"
|
||||
alt="icon"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="editImages"
|
||||
disabled="{disabled}"
|
||||
on:click|stopPropagation|preventDefault="{handleClick}"
|
||||
>
|
||||
<img
|
||||
src="../../../../../../media/solar_album-outline.svg"
|
||||
alt="icon"
|
||||
/>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
.image-preview-row {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.image-wrapper,
|
||||
.editImages {
|
||||
height: 2.7rem;
|
||||
width: 2.7rem;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
font-family: Inter;
|
||||
font-size: 0.5rem;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.editImages {
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--text-invers-100);
|
||||
img {
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,299 @@
|
||||
<script lang="ts">
|
||||
import Modal from "../../../Modal.svelte"
|
||||
import Icon from "../../Icon.svelte"
|
||||
import { mdiCloudUploadOutline, mdiDeleteOutline, mdiRefresh } from "@mdi/js"
|
||||
import { isMobile, newNotification } from "../../../../store"
|
||||
import LoadingWrapper from "../../LoadingWrapper.svelte"
|
||||
import { openCameraInput, openFileInput } from "./helper"
|
||||
import { apiBaseURL } from "../../../../../config"
|
||||
import { postDBEntry } from "../../../../../api"
|
||||
|
||||
export let imagesData: MedialibEntry[] = []
|
||||
export let showImageUploadModal: boolean
|
||||
|
||||
let sessionImages: MedialibEntry[] = []
|
||||
let modalStates = {
|
||||
isUploading: false,
|
||||
}
|
||||
|
||||
function readInput(input: HTMLInputElement) {
|
||||
return Array.from(input.files).map(
|
||||
(file) =>
|
||||
new Promise<MedialibEntry>(async (resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
|
||||
reader.onload = async (e: ProgressEvent<FileReader>) => {
|
||||
try {
|
||||
const fileData = e.target?.result
|
||||
|
||||
if (fileData) {
|
||||
const entry = await postDBEntry("medialib", {
|
||||
file: {
|
||||
path: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
src: typeof fileData === "string" ? fileData : "",
|
||||
},
|
||||
title: "Beweisfoto",
|
||||
alt: "Beweisfoto",
|
||||
type: "returnOrderFoto",
|
||||
})
|
||||
|
||||
resolve(entry.data)
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const handleFileChange = async (event: Event) => {
|
||||
const input = event.target as HTMLInputElement
|
||||
|
||||
modalStates.isUploading = true
|
||||
newNotification({
|
||||
html: "Bilder werden hochgeladen...",
|
||||
class: "info",
|
||||
})
|
||||
if (input.files) {
|
||||
const filePromises = readInput(input)
|
||||
|
||||
try {
|
||||
let images = await Promise.all(filePromises)
|
||||
if (!sessionImages || sessionImages.length === 0) sessionImages = []
|
||||
sessionImages.push(...images)
|
||||
sessionImages = [...sessionImages]
|
||||
imagesData = [...imagesData, ...images]
|
||||
} catch (error) {
|
||||
console.error("Error uploading files:", error)
|
||||
newNotification({
|
||||
html: "Fehler beim Hochladen der Dateien",
|
||||
class: "error",
|
||||
})
|
||||
} finally {
|
||||
modalStates.isUploading = false
|
||||
}
|
||||
} else {
|
||||
modalStates.isUploading = false
|
||||
}
|
||||
}
|
||||
|
||||
const replaceImage = async (index: number) => {
|
||||
loadingIndex = index
|
||||
|
||||
// Upload the new image
|
||||
openFileInput(async (event) => {
|
||||
const input = event.target as HTMLInputElement
|
||||
const filePromises = readInput(input)
|
||||
try {
|
||||
const newImage = await Promise.all(filePromises)
|
||||
const imgDataIndx = imagesData.findIndex((img) => img.id === sessionImages[index].id)
|
||||
imagesData[imgDataIndx] = newImage[0] // Replace the image in the imagesData array
|
||||
imagesData = [...imagesData] // Update imagesData to reflect the change
|
||||
sessionImages[index] = newImage[0] // Replace the image in the session array
|
||||
sessionImages = [...sessionImages] // Update sessionImages to reflect the change
|
||||
|
||||
newNotification({
|
||||
html: "Bild wurde ersetzt",
|
||||
class: "success",
|
||||
})
|
||||
loadingIndex = -1
|
||||
} catch (error) {
|
||||
console.error("Error replacing file:", error)
|
||||
newNotification({
|
||||
html: "Fehler beim Ersetzen der Datei",
|
||||
class: "error",
|
||||
})
|
||||
loadingIndex = -1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const deleteImage = async (index: number) => {
|
||||
const imgDataIndx = imagesData.findIndex((img) => img.id === sessionImages[index].id)
|
||||
imagesData.splice(imgDataIndx, 1) // Remove the image from the imagesData array
|
||||
imagesData = [...imagesData] // Update imagesData
|
||||
sessionImages.splice(index, 1) // Remove the image from the session array
|
||||
sessionImages = [...sessionImages] // Update sessionImages
|
||||
|
||||
newNotification({
|
||||
html: "Bild wurde entfernt",
|
||||
class: "success",
|
||||
})
|
||||
}
|
||||
|
||||
let gotTriggered = false
|
||||
$: if (showImageUploadModal && !gotTriggered) {
|
||||
gotTriggered = true
|
||||
openCameraInput(handleFileChange)
|
||||
}
|
||||
let loadingIndex = -1
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:show="{showImageUploadModal}"
|
||||
size="md"
|
||||
on:close="{() => {
|
||||
showImageUploadModal = false
|
||||
}}"
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<Icon
|
||||
path="{mdiCloudUploadOutline}"
|
||||
size="2.4rem"
|
||||
color="#2f4858"
|
||||
/>
|
||||
Bilder hochladen oder aufnehmen
|
||||
</svelte:fragment>
|
||||
|
||||
<p class="content-modal">
|
||||
Bitte wählen Sie die Bilder aus, die Sie hochladen oder aufnehmen möchten. Sie können mehrere Bilder
|
||||
gleichzeitig auswählen. Auf dem Computer müssen Sie die Bilder aus Ihrer Bibliothek auswählen. Auf einem
|
||||
Smartphone können Sie die Kamera verwenden, um Bilder aufzunehmen.
|
||||
</p>
|
||||
|
||||
<div class="image-preview upload">
|
||||
{#each sessionImages as image, index}
|
||||
<LoadingWrapper active="{loadingIndex === index}">
|
||||
<div class="image-container">
|
||||
{#if image}
|
||||
<div class="img-wrapper">
|
||||
<img
|
||||
src="{`${apiBaseURL}medialib/${image.id}/${image.file.src}`}"
|
||||
alt="Image Preview"
|
||||
class="image"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div class="actionRow">
|
||||
<button
|
||||
class="replace-button"
|
||||
on:click|preventDefault|stopPropagation="{() => replaceImage(index)}"
|
||||
>
|
||||
<Icon
|
||||
path="{mdiRefresh}"
|
||||
size="1rem"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="delete-button"
|
||||
on:click|preventDefault|stopPropagation="{() => deleteImage(index)}"
|
||||
>
|
||||
<Icon
|
||||
path="{mdiDeleteOutline}"
|
||||
size="1rem"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</LoadingWrapper>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div slot="footer">
|
||||
<LoadingWrapper
|
||||
active="{modalStates.isUploading}"
|
||||
styles="display: flex; justify-content: space-between;"
|
||||
>
|
||||
<button
|
||||
class="cta primary"
|
||||
on:click|preventDefault|stopPropagation="{() => openFileInput(handleFileChange)}"
|
||||
>
|
||||
<Icon
|
||||
path="{mdiCloudUploadOutline}"
|
||||
size="1.2rem"
|
||||
/>
|
||||
|
||||
<span> {$isMobile ? "Auswählen" : "Bilder auswählen"}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="cta primary"
|
||||
on:click|preventDefault|stopPropagation="{() => openFileInput(handleFileChange)}"
|
||||
>
|
||||
<Icon
|
||||
path="{mdiCloudUploadOutline}"
|
||||
size="1.2rem"
|
||||
/>
|
||||
<span> {$isMobile ? "Aufnehmen" : "Bilder Aufnehmen "}</span>
|
||||
</button>
|
||||
</LoadingWrapper>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<style lang="less">
|
||||
@import "../../../../assets/css/variables.less";
|
||||
|
||||
h3.label {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:global .upload.image-preview {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
margin-top: 1.2rem;
|
||||
& > div {
|
||||
width: fit-content !important;
|
||||
}
|
||||
}
|
||||
|
||||
.cta.primary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.img-wrapper {
|
||||
width: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
|
||||
max-width: 300px;
|
||||
@media @mobile {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
object-fit: cover;
|
||||
border-radius: 12px;
|
||||
}
|
||||
@media @mobile {
|
||||
width: min(200px, 40vw);
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
aspect-ratio: unset;
|
||||
img {
|
||||
aspect-ratio: unset;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
position: relative;
|
||||
|
||||
.actionRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0.6rem;
|
||||
left: 0.6rem;
|
||||
right: 0.6rem;
|
||||
button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
border-radius: 48px;
|
||||
color: white;
|
||||
background: var(--bg-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,154 @@
|
||||
<script lang="ts">
|
||||
import { getCachedEntries } from "../../../../../api"
|
||||
|
||||
import ImageFieldLibrary from "./ImageFieldLibrary.svelte"
|
||||
import ImagePreviewRow from "./ImagePreviewRow.svelte"
|
||||
import ImageUpload from "./ImageUpload.svelte"
|
||||
|
||||
export let mediaEntries: string[],
|
||||
disabled: boolean = false
|
||||
|
||||
let imagesData: MedialibEntry[]
|
||||
let loading = true
|
||||
|
||||
if (mediaEntries?.length > 0) {
|
||||
getCachedEntries("medialib", {
|
||||
_id: {
|
||||
$in: mediaEntries,
|
||||
},
|
||||
}).then((res) => {
|
||||
imagesData = res
|
||||
loading = false
|
||||
})
|
||||
} else {
|
||||
imagesData = []
|
||||
loading = false
|
||||
}
|
||||
let modalStates = {
|
||||
showImageUploadModal: false,
|
||||
}
|
||||
|
||||
const handleImageClick = () => {
|
||||
modalStates.showImageUploadModal = true
|
||||
}
|
||||
$: {
|
||||
const newmediaEntriess = imagesData?.map((image) => image.id)
|
||||
if (JSON.stringify(newmediaEntriess) !== JSON.stringify(mediaEntries) && !loading) {
|
||||
if (newmediaEntriess.length == 0 && !mediaEntries) {
|
||||
} else mediaEntries = newmediaEntriess
|
||||
}
|
||||
}
|
||||
|
||||
let showFileLibrary = false
|
||||
let reopenImageLibrary = false
|
||||
$: if (!modalStates.showImageUploadModal && reopenImageLibrary && noModalVisible) {
|
||||
reopenImageLibrary = false
|
||||
showFileLibrary = true
|
||||
}
|
||||
let noModalVisible = false
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="image-row {imagesData?.length == 0 ? 'smallOne' : ''}"
|
||||
class:disabled="{disabled}"
|
||||
on:click|stopPropagation|preventDefault="{() => {
|
||||
if (imagesData?.length == 0) handleImageClick()
|
||||
}}"
|
||||
>
|
||||
{#if !disabled}
|
||||
<h3
|
||||
class="label"
|
||||
style="{imagesData?.length > 0 ? 'padding-bottom: 6px;' : ''}"
|
||||
>
|
||||
<img
|
||||
src="../../../../../../media/solar_camera-outline.svg"
|
||||
alt="icon"
|
||||
/>
|
||||
<p>FOTOS</p>
|
||||
</h3>{/if}
|
||||
{#if imagesData?.length === 0}
|
||||
<button
|
||||
on:click|preventDefault
|
||||
class="editImages"
|
||||
>
|
||||
<img
|
||||
src="../../../../../../media/solar_album-outline.svg"
|
||||
alt="icon"
|
||||
/>
|
||||
</button>
|
||||
{:else}
|
||||
<ImagePreviewRow
|
||||
imageData="{imagesData}"
|
||||
disabled="{disabled}"
|
||||
on:uploadImage="{handleImageClick}"
|
||||
on:openImageModal="{() => {
|
||||
showFileLibrary = true
|
||||
}}"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{#if showFileLibrary}
|
||||
<ImageFieldLibrary
|
||||
bind:images="{imagesData}"
|
||||
on:removeImage="{(e) => {
|
||||
imagesData = imagesData?.filter((image) => image.id !== e.detail)
|
||||
}}"
|
||||
on:closeImageLibrary="{() => {
|
||||
showFileLibrary = false
|
||||
}}"
|
||||
on:uploadImage="{() => {
|
||||
showFileLibrary = false
|
||||
reopenImageLibrary = true
|
||||
handleImageClick()
|
||||
}}"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if imagesData && modalStates.showImageUploadModal}
|
||||
<ImageUpload
|
||||
bind:imagesData="{imagesData}"
|
||||
bind:showImageUploadModal="{modalStates.showImageUploadModal}"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<style lang="less">
|
||||
.image-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.6rem;
|
||||
padding: 0.6rem;
|
||||
|
||||
border-bottom: 1px solid var(--text-invers-100);
|
||||
&.disabled {
|
||||
padding: 0px;
|
||||
border: none;
|
||||
}
|
||||
background: white;
|
||||
&.smallOne {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 0px 0.6rem;
|
||||
cursor: pointer;
|
||||
.label {
|
||||
min-height: 54px;
|
||||
}
|
||||
}
|
||||
}
|
||||
h3.label {
|
||||
display: flex;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
gap: 0.6rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:global .modalMap {
|
||||
width: 100%;
|
||||
.mapwrapper {
|
||||
height: 400px !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte"
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="uploadImage"
|
||||
on:click|preventDefault|stopPropagation="{() => {
|
||||
dispatch('uploadImage')
|
||||
}}"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="25"
|
||||
viewBox="0 0 24 25"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M11.47 15.97C11.6106 15.8295 11.8012 15.7507 12 15.7507C12.1988 15.7507 12.3894 15.8295 12.53 15.97L14.53 17.97C14.6037 18.0387 14.6628 18.1215 14.7038 18.2135C14.7448 18.3055 14.7668 18.4048 14.7686 18.5055C14.7704 18.6062 14.7518 18.7062 14.7141 18.7996C14.6764 18.893 14.6203 18.9778 14.549 19.049C14.4778 19.1203 14.393 19.1764 14.2996 19.2141C14.2062 19.2518 14.1062 19.2704 14.0055 19.2686C13.9048 19.2668 13.8055 19.2448 13.7135 19.2038C13.6215 19.1628 13.5387 19.1037 13.47 19.03L12.75 18.31V22.5C12.75 22.6989 12.671 22.8897 12.5303 23.0303C12.3897 23.171 12.1989 23.25 12 23.25C11.8011 23.25 11.6103 23.171 11.4697 23.0303C11.329 22.8897 11.25 22.6989 11.25 22.5V18.31L10.53 19.03C10.4613 19.1037 10.3785 19.1628 10.2865 19.2038C10.1945 19.2448 10.0952 19.2668 9.99452 19.2686C9.89382 19.2704 9.79379 19.2518 9.7004 19.2141C9.60701 19.1764 9.52218 19.1203 9.45096 19.049C9.37974 18.9778 9.3236 18.893 9.28588 18.7996C9.24816 18.7062 9.22963 18.6062 9.23141 18.5055C9.23319 18.4048 9.25523 18.3055 9.29622 18.2135C9.33721 18.1215 9.39631 18.0387 9.47 17.97L11.47 15.97Z"
|
||||
fill="#26292C"></path>
|
||||
<path
|
||||
d="M12.476 4.25C9.726 4.25 7.512 6.45 7.512 9.147C7.512 9.609 7.577 10.056 7.697 10.478C8.194 10.622 8.66 10.838 9.08 11.118C9.16639 11.1703 9.24132 11.2396 9.3003 11.3216C9.35927 11.4036 9.40108 11.4966 9.4232 11.5952C9.44533 11.6938 9.44732 11.7958 9.42906 11.8951C9.41079 11.9944 9.37265 12.0891 9.31692 12.1733C9.26118 12.2576 9.18901 12.3297 9.10472 12.3853C9.02044 12.441 8.92578 12.4791 8.82642 12.4972C8.72706 12.5154 8.62506 12.5133 8.52652 12.4911C8.42799 12.4689 8.33495 12.427 8.253 12.368C7.66992 11.9817 6.98546 11.7767 6.286 11.779C4.325 11.779 2.75 13.349 2.75 15.265C2.75 17.181 4.325 18.75 6.286 18.75C6.48491 18.75 6.67568 18.829 6.81633 18.9697C6.95698 19.1103 7.036 19.3011 7.036 19.5C7.036 19.6989 6.95698 19.8897 6.81633 20.0303C6.67568 20.171 6.48491 20.25 6.286 20.25C3.513 20.25 1.25 18.026 1.25 15.265C1.25 12.56 3.42 10.372 6.114 10.282C6.04614 9.90748 6.012 9.52762 6.012 9.147C6.012 5.606 8.914 2.75 12.476 2.75C15.634 2.75 18.272 4.994 18.831 7.971C21.131 8.948 22.75 11.209 22.75 13.853C22.75 16.927 20.562 19.484 17.657 20.106C17.4625 20.1476 17.2594 20.1103 17.0924 20.0022C16.9254 19.8941 16.8081 19.724 16.7665 19.5295C16.7249 19.335 16.7622 19.1319 16.8703 18.9649C16.9784 18.7979 17.1485 18.6806 17.343 18.639C19.583 18.159 21.25 16.193 21.25 13.853C21.25 11.716 19.86 9.891 17.912 9.225C17.3886 9.04599 16.8392 8.95476 16.286 8.955C15.703 8.955 15.146 9.055 14.628 9.235C14.4414 9.29593 14.2383 9.28126 14.0624 9.19415C13.8865 9.10704 13.7517 8.95443 13.6871 8.76909C13.6224 8.58374 13.633 8.38043 13.7166 8.2028C13.8001 8.02516 13.95 7.88737 14.134 7.819C15.1039 7.48042 16.1401 7.37591 17.158 7.514C16.8085 6.55419 16.1712 5.72564 15.3333 5.14147C14.4953 4.5573 13.4975 4.24597 12.476 4.25Z"
|
||||
fill="#26292C"></path>
|
||||
</svg>
|
||||
<p>Bilder hochladen</p>
|
||||
</button>
|
||||
|
||||
<style lang="less">
|
||||
@import "../../../../assets/css/variables.less";
|
||||
.uploadImage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.6rem;
|
||||
border-radius: 12px;
|
||||
border: 2px solid var(--text-invers-100);
|
||||
p {
|
||||
color: var(--text-invers-100);
|
||||
}
|
||||
width: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,41 @@
|
||||
export const openFileInput = (callback: (e: Event) => Promise<void>) => {
|
||||
const input = document.createElement("input")
|
||||
input.type = "file"
|
||||
input.accept = "image/*"
|
||||
input.multiple = true
|
||||
input.onchange = async (e: Event) => {
|
||||
await callback(e)
|
||||
setTimeout(() => {
|
||||
input.remove() // Delay the removal slightly
|
||||
}, 100)
|
||||
}
|
||||
input.click()
|
||||
}
|
||||
|
||||
export const openCameraInput = (callback: (e: Event) => Promise<void>) => {
|
||||
const input = document.createElement("input")
|
||||
input.type = "file"
|
||||
input.accept = "image/*"
|
||||
input.capture = "environment"
|
||||
|
||||
input.style.position = "absolute"
|
||||
input.style.left = "-9999px" // Hide the input off-screen
|
||||
document.body.appendChild(input)
|
||||
|
||||
const handleChange = async (e: Event) => {
|
||||
console.log("HandleChange triggered") // Log to check if this triggers
|
||||
e.stopPropagation()
|
||||
await callback(e)
|
||||
|
||||
setTimeout(() => {
|
||||
input.value = "" // Reset input
|
||||
input.remove() // Remove element
|
||||
}, 1000) // Delay removal
|
||||
}
|
||||
|
||||
// Try using 'input' event listener as an alternative
|
||||
input.addEventListener("input", handleChange)
|
||||
|
||||
input.focus()
|
||||
input.click()
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { apiBaseURL } from "../../../../../config"
|
||||
|
||||
export let image: MedialibEntry
|
||||
</script>
|
||||
|
||||
<img
|
||||
src="{`${apiBaseURL}medialib/${image.id}/${image.file.src}`}"
|
||||
alt="Image Preview"
|
||||
class="image"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
<style lang="less">
|
||||
.image {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user