Skip to content

Commit

Permalink
fix(news): add image viewer shortcuts info
Browse files Browse the repository at this point in the history
  • Loading branch information
Robin-w151 committed Feb 21, 2025
1 parent 49f160c commit 9a8a33c
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 21 deletions.
94 changes: 79 additions & 15 deletions src/lib/components/news/story/content/StoryImageViewer.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<script lang="ts">
import Popover from '$lib/components/shared/content/Popover.svelte';
import PopoverContent from '$lib/components/shared/content/PopoverContent.svelte';
import { PAN_DISTANCE } from '$lib/configs/client';
import type { StoryImage } from '$lib/models/story';
import { onDestroy, onMount } from 'svelte';
import type { PanzoomObject } from '@panzoom/panzoom';
import { logger } from '$lib/utils/logger';
import { Panzoom } from '$lib/utils/panzoomModule';
import { defaultBackground, defaultGap, defaultPadding, defaultText } from '$lib/utils/styles';
import { isMacOsPlatform, isTouchDevice } from '$lib/utils/support';
import type { PanzoomObject } from '@panzoom/panzoom';
import { ChevronLeft, ChevronRight, QuestionMarkCircle, XMark } from '@steeze-ui/heroicons';
import { Icon } from '@steeze-ui/svelte-icon';
import { ChevronLeft, ChevronRight, XMark } from '@steeze-ui/heroicons';
import { logger } from '$lib/utils/logger';
import { PAN_DISTANCE } from '$lib/configs/client';
import { onDestroy, onMount } from 'svelte';
interface Props {
image: StoryImage;
Expand All @@ -29,6 +32,19 @@
let isNavigationEnabled = $derived(images.length > 1);
let viewerButtonClass = $derived(`${showControls ? 'visible' : 'invisible'}`);
const shortcuts: Array<{ key: string; description: string }> = [
{ key: 'ESC', description: 'Bildansicht beenden' },
{ key: '', description: 'Vorheriges Bild anzeigen' },
{ key: '', description: 'Nächstes Bild anzeigen' },
{ key: 'CTRL +', description: 'Heranzoomen' },
{ key: 'CTRL -', description: 'Herauszoomen' },
{ key: 'CTRL 0', description: 'Zoom zurücksetzen' },
{ key: 'CTRL ←', description: 'Nach links bewegen' },
{ key: 'CTRL →', description: 'Nach rechts bewegen' },
{ key: 'CTRL ↑', description: 'Nach oben bewegen' },
{ key: 'CTRL ↓', description: 'Nach unten bewegen' },
];
const containerClass = 'flex justify-center items-center fixed top-0 bottom-0 left-0 right-0 z-50';
const imageContainerClass = 'w-full h-full bg-black';
const imageClass = 'object-scale-down w-full h-full bg-transparent';
Expand Down Expand Up @@ -60,7 +76,7 @@
$effect(() => {
if (image) {
resetPanzoom();
resetPanzoom(false);
}
});
Expand Down Expand Up @@ -144,6 +160,10 @@
gotoPrevImage();
}
function handleHelpClick(event: Event): void {
event.stopPropagation();
}
function closeViewer(): void {
onClose?.();
}
Expand Down Expand Up @@ -186,6 +206,7 @@
maxScale: 10,
step: isTouch ? 1 : isMacOs ? 0.075 : 0.5,
contain: 'outside',
animate: true,
handleStartEvent: (event: Event) => {
event.preventDefault();
},
Expand All @@ -196,9 +217,9 @@
}
}
function resetPanzoom(): void {
function resetPanzoom(animate = true): void {
if (panzoom) {
panzoom.reset({ animate: false });
panzoom.reset({ animate });
}
}
Expand All @@ -217,7 +238,7 @@
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<div class={containerClass} role="region" onclick={handleContainerClick} onpointerdown={handleContainerPointerDown}>
<button
class="viewer-button z-10 !top-2 right-2 {viewerButtonClass}"
class="viewer-button z-10 top-2 right-2 {viewerButtonClass}"
title="Bild schließen"
bind:this={closeButtonRef}
onclick={handleCloseButtonClick}
Expand All @@ -229,7 +250,7 @@
{#if isNavigationEnabled}
{#if prevImage(image)}
<button
class="viewer-button z-10 left-2 {viewerButtonClass}"
class="viewer-button viewer-button-center z-10 left-2 {viewerButtonClass}"
title="Vorheriges Bild anzeigen"
onclick={handlePrevImageClick}
>
Expand All @@ -240,7 +261,7 @@
{/if}
{#if nextImage(image)}
<button
class="viewer-button z-10 right-2 {viewerButtonClass}"
class="viewer-button viewer-button-center z-10 right-2 {viewerButtonClass}"
title="Nächstes Bild anzeigen"
onclick={handleNextImageClick}
>
Expand All @@ -250,29 +271,60 @@
</button>
{/if}
{/if}
<Popover
containerClass="viewer-button bottom-2 right-2 {viewerButtonClass}"
placement="top-end"
openOnHover
delay={{ open: 250 }}
>
{#snippet anchorContent(props)}
<span class={viewerButtonCircleClass} {...props} onclick={handleHelpClick}>
<Icon src={QuestionMarkCircle} theme="outlined" class={viewerButtonIconClass} />
</span>
{/snippet}
{#snippet popoverContent()}
<PopoverContent>
<div class="flex flex-col {defaultGap} {defaultPadding} w-max {defaultText} {defaultBackground} rounded-lg">
<table>
<thead>
<tr>
<th class="p-2">Kombination</th>
<th class="p-2">Beschreibung</th>
</tr>
</thead>
<tbody>
{#each shortcuts as { key, description }}
<tr>
<td class="px-2 py-1 whitespace-nowrap">{key}</td>
<td class="px-2 py-1 whitespace-nowrap">{description}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</PopoverContent>
{/snippet}
</Popover>
<div class={imageContainerClass}>
<img class={imageClass} src={image.src} srcset={image.srcset} alt={image.alt} bind:this={imageRef} />
</div>
</div>

<style lang="postcss">
.viewer-button {
:global(.viewer-button) {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: calc(50% - 1.5rem);
width: 3rem;
height: 3rem;
@screen md {
top: calc(50% - 2rem);
width: 4rem;
height: 4rem;
}
@media (hover: hover) and (pointer: fine) {
top: calc(50% - 3rem);
width: 6rem;
height: 6rem;
Expand All @@ -284,4 +336,16 @@
}
}
}
.viewer-button-center {
top: calc(50% - 1.5rem);
@screen md {
top: calc(50% - 2rem);
}
@media (hover: hover) and (pointer: fine) {
top: calc(50% - 3rem);
}
}
</style>
26 changes: 20 additions & 6 deletions src/lib/components/shared/content/Popover.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
useDismiss,
useFloating,
useFocus,
useHover,
useInteractions,
useRole,
} from '@skeletonlabs/floating-ui-svelte';
Expand All @@ -25,8 +26,11 @@
disabled?: boolean | undefined;
placement?: Placement;
openOnFocus?: boolean;
openOnHover?: boolean;
openOnKeyboardClick?: boolean;
delay?: number | { open?: number; close?: number };
containerClass?: string | Array<string>;
anchorRegionClass?: string | Array<string>;
onVisibleChange?: (visible: boolean) => void;
anchorContent?: Snippet<[Record<string, unknown>]>;
buttonContent?: Snippet;
Expand All @@ -42,8 +46,11 @@
disabled,
placement = 'bottom',
openOnFocus = false,
openOnHover = false,
openOnKeyboardClick = true,
delay,
containerClass,
anchorRegionClass,
onVisibleChange,
anchorContent,
buttonContent,
Expand All @@ -70,11 +77,18 @@
const click = useClick(floating.context, { keyboardHandlers: openOnKeyboardClick });
const dismiss = useDismiss(floating.context);
const interactions = useInteractions(
[role, dismiss, click, openOnFocus ? useFocus(floating.context) : null].filter((props) => !!props),
[
role,
dismiss,
click,
openOnFocus ? useFocus(floating.context) : null,
openOnHover ? useHover(floating.context, { delay }) : null,
].filter((props) => !!props),
);
const dropdownContentClass = 'z-40';
let dropdownButtonClass = $derived(buttonClassFn({ btnType, size, iconOnly, round }));
const popoverContentClass = 'z-40';
let popoverButtonClass = $derived(buttonClassFn({ btnType, size, iconOnly, round }));
export function setOpen(newOpen: boolean) {
open = newOpen;
Expand All @@ -83,12 +97,12 @@

<div class={containerClass}>
{#if anchorContent}
<div class="w-full" bind:this={floating.elements.reference}>
<div class={anchorRegionClass} bind:this={floating.elements.reference}>
{@render anchorContent?.(interactions.getReferenceProps())}
</div>
{:else}
<button
class={dropdownButtonClass}
class={popoverButtonClass}
{disabled}
{title}
{...interactions.getReferenceProps()}
Expand All @@ -99,7 +113,7 @@
{/if}
{#if open}
<div
class={dropdownContentClass}
class={popoverContentClass}
data-testid="popover"
style={floating.floatingStyles}
{...interactions.getFloatingProps()}
Expand Down

0 comments on commit 9a8a33c

Please sign in to comment.