zwischenstand

This commit is contained in:
2025-10-02 09:14:58 +00:00
parent f3dc0dc9bd
commit b22d5d5103
8 changed files with 140 additions and 185 deletions

View File

@@ -5,7 +5,6 @@
import Content from "./routes/Content.svelte"
import Notifications from "./lib/components/widgets/Notifications.svelte"
import SSRSkip from "./lib/components/SSRSkip.svelte"
import DateModal from "./lib/components/widgets/DateModal.svelte"
import { baseURL } from "./config"
import { isMobile, location, openModal } from "./lib/store"
@@ -106,7 +105,6 @@
</div>
<Notifications />
<DateModal />
<SSRSkip />
<style lang="less" global>

View File

@@ -1,51 +0,0 @@
<script lang="ts">
import { actionApproval } from "../../store"
import Modal from "../Modal.svelte"
</script>
{#if $actionApproval}
<Modal
show={true}
size="sm"
on:close={() => {
actionApproval.set(null)
}}
>
<svelte:fragment slot="title">{$actionApproval.modalTitle}</svelte:fragment>
<p>
{$actionApproval.modalText}
</p>
<div
slot="footer"
class="action-approval-footer"
>
<button
class="btn cta primary"
on:click={() => {
actionApproval.set(null)
}}
>
Nein
</button>
<button
class="btn cta secondary"
on:click={() => {
$actionApproval.callback()
actionApproval.set(null)
}}>Ja</button
>
</div>
</Modal>
{/if}
<style lang="less">
.action-approval-footer {
display: flex;
justify-content: space-between;
}
h2 {
color: var(--text-invers-100);
padding-bottom: 1px;
}
</style>

View File

@@ -1,50 +0,0 @@
<script lang="ts"></script>
<div id="dateModal"></div>
<style
lang="less"
global
>
#dateModal {
position: absolute;
z-index: 9999;
background-color: white;
display: none;
}
#dateDeleteValidationModal {
position: absolute;
z-index: 9999;
background-color: white;
display: none;
flex-direction: column;
gap: 10px;
padding: 12px;
width: 236px;
.question {
padding: 0px;
margin: 0px;
font-size: 12px;
}
.bottom {
display: flex;
justify-content: space-between;
.cancel,
.delete {
padding: 0px;
cursor: pointer;
background-color: transparent;
border: none;
font-size: 12px;
}
.cancel {
color: var(--stategrey);
}
.delete {
color: var(--renzrot);
}
}
}
</style>

View File

@@ -1,5 +1,4 @@
<script lang="ts">
//check with matchMedia api if the slot should be rendered or not.
import { onMount } from "svelte"
export let query: string

View File

@@ -1,13 +1,9 @@
<script
context="module"
lang="ts"
>
<script module lang="ts">
import pDebounce from "p-debounce"
import { getDBEntries, getDBEntry } from "../../../api"
// TODO: check ssr
const medialibCache: { [id: string]: MedialibEntry } ={}
import { apiBaseURL } from "../../configs/config"
import { getCachedEntries, getCachedEntry } from "../../api"
const medialibCache: { [id: string]: MedialibEntry } = {}
let loadQueue: string[] = []
export async function loadMedialibEntry(id: string): Promise<MedialibEntry> {
@@ -21,7 +17,7 @@
if (loadQueue.length) {
const _ids = loadQueue
loadQueue = []
const entries = await getDBEntries("medialib", { _id: { $in: _ids } })
const entries = await getCachedEntries("medialib", { _id: { $in: _ids } })
entries.forEach((entry) => {
medialibCache[entry.id] = entry
})
@@ -32,14 +28,24 @@
</script>
<script lang="ts">
import { apiBaseURL } from "../../../config"
import { apiBaseOverride } from "../../store"
let {
id,
noPlaceholder,
loading,
childrenSnippet,
loadingSnippet,
notFoundSnippet,
}: {
id: string
noPlaceholder?: boolean
loading?: boolean
loadingSnippet: ({ entry, src }: { entry: MedialibEntry; src: string }) => any
childrenSnippet: ({ entry, src }: { entry: MedialibEntry; src: string }) => any
notFoundSnippet: () => any
} = $props()
export let id: string,
noPlaceholder: boolean = false,
loading = true
let entry: MedialibEntry, fileSrc: string
let entry: MedialibEntry = $state(),
fileSrc: string = $state()
async function loadFile() {
loading = true
@@ -47,33 +53,31 @@
fileSrc = null
try {
entry =
typeof window !== "undefined" ? await loadMedialibEntry(id) : await getDBEntry("medialib", { _id: id })
if (entry?.file?.src)
fileSrc = ($apiBaseOverride ? $apiBaseOverride : apiBaseURL) + "medialib/" + id + "/" + entry.file.src
typeof window !== "undefined"
? await loadMedialibEntry(id)
: await getCachedEntry("medialib", { _id: id })
if (entry?.file?.src) {
fileSrc = `${apiBaseURL}${window?.location.host.includes("tibi") ? "_/renz_shop_2024/" : ""}medialib/${id}/${entry.file.src}`
}
} catch (e) {
console.error(e)
}
loading = false
}
$: if (id) loadFile()
$effect(() => {
if (id) loadFile()
})
</script>
{#if id}
{#if loading}
{#if !noPlaceholder}
<slot
name="loading"
entry={entry}
src={fileSrc}
/>
{@render loadingSnippet({ entry, src: fileSrc })}
{/if}
{:else if entry}
<slot
entry={entry}
src={fileSrc}
/>
{@render childrenSnippet({ entry, src: fileSrc })}
{:else if !noPlaceholder}
<slot name="not-found" />
{@render notFoundSnippet()}
{/if}
{/if}

View File

@@ -1,83 +1,138 @@
<script lang="ts">
import { onDestroy } from "svelte"
import MedialibFile from "./MedialibFile.svelte"
export let id: string,
filter: null | "xs" | "s" | "m" | "l" | "xl" | "xxl" = null,
minWidth: number = null,
noPlaceholder: boolean = false
interface Props {
id: string
filter?:
| null
| "xs"
| "s"
| "m"
| "l"
| "xl"
| "xxl"
| "xs-webp"
| "s-webp"
| "m-webp"
| "l-webp"
| "xl-webp"
| "xxl-webp"
minWidth?: number
noPlaceholder?: boolean
style?: string
widthMultiplier?: number
lazy?: boolean
}
let imgElement: HTMLImageElement
let {
id,
filter,
minWidth = null,
noPlaceholder = false,
style,
widthMultiplier = 1,
lazy = false,
}: Props = $props()
let imgElement: HTMLImageElement = $state()
function getSrcWithFilter(_imgElement: HTMLImageElement, entry: MedialibEntry, src: string) {
if (typeof window === "undefined") {
return src + `?filter=${filter || "xs"}`
return src + `?filter=${filter || "xs-webp"}`
}
let internalFilter = filter
if (imgElement) {
if (!entry.file?.type?.match(/^image\/(png|jpe?g)/)) return src // no filter for svg
if (!filter) {
if (!entry.file?.type?.match(/^image\/(png|jpe?g|webp)/)) return src // no filter for svg
if (!internalFilter) {
let imgWidth = _imgElement.width
if (widthMultiplier) {
imgWidth *= widthMultiplier
}
// get the width of the image element
const width = minWidth
? _imgElement.width < minWidth
? minWidth
: _imgElement.width
: _imgElement.width
const width = minWidth ? (imgWidth < minWidth ? minWidth : imgWidth) : imgWidth
switch (true) {
case width <= 90:
filter = "xs"
internalFilter = "xs-webp"
break
case width <= 300:
filter = "s"
internalFilter = "s-webp"
break
case width <= 600:
filter = "m"
internalFilter = "m-webp"
break
case width <= 1200:
filter = "l"
internalFilter = "l-webp"
break
case width <= 2000:
filter = "xl"
internalFilter = "xl-webp"
break
case width <= 4000:
filter = "xxl"
internalFilter = "xxl-webp"
break
}
}
return src + (filter ? `?filter=${filter}` : "")
return src + (internalFilter ? `?filter=${internalFilter}` : "")
} else {
// placeholder
return "/assets/img/placeholder-image.png"
}
}
let resizeObserver: ResizeObserver
function initResizeObserver(node: HTMLImageElement, { entry, src }: { entry: MedialibEntry; src: string }) {
if (!filter && typeof ResizeObserver !== "undefined") {
resizeObserver?.disconnect()
resizeObserver = new ResizeObserver(() => {
const oldWidth = parseInt(node.getAttribute("data-width")) || 0
const newWidth = node.width
if (oldWidth >= newWidth) return
const newSrc = getSrcWithFilter(imgElement, entry, src)
if (newSrc !== node.getAttribute("src")) {
node.setAttribute("src", newSrc)
}
node.setAttribute("data-width", newWidth.toString())
})
resizeObserver.observe(node)
}
return {
destroy() {
resizeObserver?.disconnect()
},
}
}
onDestroy(() => {
resizeObserver?.disconnect()
})
let properties = {}
if (lazy) {
properties = { loading: "lazy" }
}
</script>
{#if id}
<MedialibFile
id={id}
let:entry
let:src
noPlaceholder={noPlaceholder}
>
<MedialibFile {id} {noPlaceholder}>
{#snippet childrenSnippet({ entry, src }: { entry: MedialibEntry; src: string })}
<img
{style}
bind:this={imgElement}
src={getSrcWithFilter(imgElement, entry, src)}
alt={entry.alt || 'icon'}
data-entry-id={id}
/>
<img
slot="loading"
let:entry
let:src
src="/assets/img/placeholder-image.png"
alt="placeholder"
data-entry-id={id}
data-entry={JSON.stringify(entry)}
data-entry-src={src}
/>
<img
slot="not-found"
src="/assets/img/placeholder-image.png"
alt="not found"
alt={entry.alt || "icon"}
data-entry-id={id}
use:initResizeObserver={{ entry, src }}
loading="lazy"
{...properties}
/>
{/snippet}
{#snippet loadingSnippet({ entry, src }: { entry: MedialibEntry; src: string })}{/snippet}
{#snippet notFoundSnippet()}{/snippet}
</MedialibFile>
{/if}

View File

@@ -20,7 +20,7 @@
class="container"
class:inModal={force}
>
{#each $notifications as n, i (n)}
{#each $notifications as n (n)}
{#if !$openModal || ($openModal && force)}
<!-- svelte-ignore a11y-no-noninteractive-element-to-interactive-role -->
<li

View File

@@ -2,8 +2,8 @@
import { getCachedEntry } from "../api"
import NotFound from "./NotFound.svelte"
export let location: LocationStore | undefined
export let id: string | undefined
export let location: LocationStore | undefined = undefined
export let id: string | null = null
let loading = true
let contentEntry: ContentEntry | null = null