651 lines
17 KiB
TypeScript
651 lines
17 KiB
TypeScript
|
|
import {
|
||
|
|
DeesElement,
|
||
|
|
css,
|
||
|
|
cssManager,
|
||
|
|
customElement,
|
||
|
|
html,
|
||
|
|
property,
|
||
|
|
state,
|
||
|
|
type TemplateResult,
|
||
|
|
} from '@design.estate/dees-element';
|
||
|
|
|
||
|
|
import { mobileComponentStyles } from '../../00componentstyles.js';
|
||
|
|
import '../dees-mobile-icon/dees-mobile-icon.js';
|
||
|
|
import { demoFunc } from './dees-mobile-gallery.demo.js';
|
||
|
|
|
||
|
|
export interface IGalleryItem {
|
||
|
|
id: string;
|
||
|
|
url: string;
|
||
|
|
thumbnailUrl?: string;
|
||
|
|
filename?: string;
|
||
|
|
mimeType?: string;
|
||
|
|
metadata?: Record<string, any>;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface IGalleryConfig {
|
||
|
|
showFilename?: boolean;
|
||
|
|
showActions?: boolean;
|
||
|
|
allowDelete?: boolean;
|
||
|
|
allowDownload?: boolean;
|
||
|
|
allowShare?: boolean;
|
||
|
|
startIndex?: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
declare global {
|
||
|
|
interface HTMLElementTagNameMap {
|
||
|
|
'dees-mobile-gallery': DeesMobileGallery;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@customElement('dees-mobile-gallery')
|
||
|
|
export class DeesMobileGallery extends DeesElement {
|
||
|
|
public static demo = demoFunc;
|
||
|
|
|
||
|
|
@property({ type: Array })
|
||
|
|
accessor items: IGalleryItem[] = [];
|
||
|
|
|
||
|
|
@property({ type: Object })
|
||
|
|
accessor config: IGalleryConfig = {};
|
||
|
|
|
||
|
|
@state()
|
||
|
|
accessor currentIndex: number = 0;
|
||
|
|
|
||
|
|
@state()
|
||
|
|
accessor isLoading: boolean = true;
|
||
|
|
|
||
|
|
@state()
|
||
|
|
accessor showThumbnails: boolean = false;
|
||
|
|
|
||
|
|
// Touch/swipe state
|
||
|
|
private touchStartX: number = 0;
|
||
|
|
private touchStartY: number = 0;
|
||
|
|
private touchDeltaX: number = 0;
|
||
|
|
private isSwiping: boolean = false;
|
||
|
|
private swipeThreshold: number = 50;
|
||
|
|
|
||
|
|
public static styles = [
|
||
|
|
cssManager.defaultStyles,
|
||
|
|
mobileComponentStyles,
|
||
|
|
css`
|
||
|
|
:host {
|
||
|
|
position: fixed;
|
||
|
|
inset: 0;
|
||
|
|
z-index: var(--dees-z-modal, 500);
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
background: rgba(0, 0, 0, 0);
|
||
|
|
animation: fadeInGallery 0.2s ease-out forwards;
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes fadeInGallery {
|
||
|
|
to {
|
||
|
|
background: rgba(0, 0, 0, 0.95);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.gallery-header {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: space-between;
|
||
|
|
padding: var(--dees-space-md);
|
||
|
|
padding-top: calc(var(--dees-space-md) + env(safe-area-inset-top, 0px));
|
||
|
|
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), transparent);
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
left: 0;
|
||
|
|
right: 0;
|
||
|
|
z-index: 10;
|
||
|
|
}
|
||
|
|
|
||
|
|
.header-left {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: var(--dees-space-sm);
|
||
|
|
flex: 1;
|
||
|
|
min-width: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.close-button {
|
||
|
|
width: 40px;
|
||
|
|
height: 40px;
|
||
|
|
border: none;
|
||
|
|
background: rgba(255, 255, 255, 0.1);
|
||
|
|
border-radius: var(--dees-radius-full);
|
||
|
|
color: white;
|
||
|
|
cursor: pointer;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
transition: background var(--dees-transition-fast);
|
||
|
|
flex-shrink: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.close-button:hover {
|
||
|
|
background: rgba(255, 255, 255, 0.2);
|
||
|
|
}
|
||
|
|
|
||
|
|
.filename {
|
||
|
|
color: white;
|
||
|
|
font-size: 0.875rem;
|
||
|
|
font-weight: 500;
|
||
|
|
overflow: hidden;
|
||
|
|
text-overflow: ellipsis;
|
||
|
|
white-space: nowrap;
|
||
|
|
}
|
||
|
|
|
||
|
|
.counter {
|
||
|
|
color: rgba(255, 255, 255, 0.7);
|
||
|
|
font-size: 0.875rem;
|
||
|
|
flex-shrink: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.gallery-content {
|
||
|
|
flex: 1;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
position: relative;
|
||
|
|
overflow: hidden;
|
||
|
|
touch-action: pan-y pinch-zoom;
|
||
|
|
}
|
||
|
|
|
||
|
|
.image-container {
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
transition: transform 0.3s ease-out;
|
||
|
|
}
|
||
|
|
|
||
|
|
.image-container.swiping {
|
||
|
|
transition: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.gallery-image {
|
||
|
|
max-width: 100%;
|
||
|
|
max-height: 100%;
|
||
|
|
object-fit: contain;
|
||
|
|
opacity: 0;
|
||
|
|
transition: opacity 0.2s ease-out;
|
||
|
|
}
|
||
|
|
|
||
|
|
.gallery-image.loaded {
|
||
|
|
opacity: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
.loading-spinner {
|
||
|
|
position: absolute;
|
||
|
|
width: 40px;
|
||
|
|
height: 40px;
|
||
|
|
border: 3px solid rgba(255, 255, 255, 0.2);
|
||
|
|
border-top-color: white;
|
||
|
|
border-radius: 50%;
|
||
|
|
animation: spin 0.8s linear infinite;
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes spin {
|
||
|
|
to {
|
||
|
|
transform: rotate(360deg);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-button {
|
||
|
|
position: absolute;
|
||
|
|
top: 50%;
|
||
|
|
transform: translateY(-50%);
|
||
|
|
width: 48px;
|
||
|
|
height: 48px;
|
||
|
|
border: none;
|
||
|
|
background: rgba(255, 255, 255, 0.1);
|
||
|
|
border-radius: var(--dees-radius-full);
|
||
|
|
color: white;
|
||
|
|
cursor: pointer;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
transition: background var(--dees-transition-fast), opacity var(--dees-transition-fast);
|
||
|
|
z-index: 5;
|
||
|
|
opacity: 0.7;
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-button:hover {
|
||
|
|
background: rgba(255, 255, 255, 0.2);
|
||
|
|
opacity: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-button.prev {
|
||
|
|
left: var(--dees-space-md);
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-button.next {
|
||
|
|
right: var(--dees-space-md);
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-button:disabled {
|
||
|
|
opacity: 0.3;
|
||
|
|
cursor: not-allowed;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Hide nav buttons on mobile - use swipe instead */
|
||
|
|
@media (max-width: 640px) {
|
||
|
|
.nav-button {
|
||
|
|
display: none;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.gallery-footer {
|
||
|
|
padding: var(--dees-space-md);
|
||
|
|
padding-bottom: calc(var(--dees-space-md) + env(safe-area-inset-bottom, 0px));
|
||
|
|
background: linear-gradient(to top, rgba(0, 0, 0, 0.5), transparent);
|
||
|
|
position: absolute;
|
||
|
|
bottom: 0;
|
||
|
|
left: 0;
|
||
|
|
right: 0;
|
||
|
|
z-index: 10;
|
||
|
|
}
|
||
|
|
|
||
|
|
.actions {
|
||
|
|
display: flex;
|
||
|
|
justify-content: center;
|
||
|
|
gap: var(--dees-space-lg);
|
||
|
|
}
|
||
|
|
|
||
|
|
.action-button {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
align-items: center;
|
||
|
|
gap: var(--dees-space-xs);
|
||
|
|
border: none;
|
||
|
|
background: none;
|
||
|
|
color: white;
|
||
|
|
cursor: pointer;
|
||
|
|
padding: var(--dees-space-sm);
|
||
|
|
border-radius: var(--dees-radius-md);
|
||
|
|
transition: background var(--dees-transition-fast);
|
||
|
|
min-width: 64px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.action-button:hover {
|
||
|
|
background: rgba(255, 255, 255, 0.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.action-button .icon {
|
||
|
|
width: 40px;
|
||
|
|
height: 40px;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
background: rgba(255, 255, 255, 0.15);
|
||
|
|
border-radius: var(--dees-radius-full);
|
||
|
|
}
|
||
|
|
|
||
|
|
.action-button .label {
|
||
|
|
font-size: 0.75rem;
|
||
|
|
opacity: 0.9;
|
||
|
|
}
|
||
|
|
|
||
|
|
.action-button.danger .icon {
|
||
|
|
background: rgba(220, 38, 38, 0.3);
|
||
|
|
}
|
||
|
|
|
||
|
|
.action-button.danger {
|
||
|
|
color: #fca5a5;
|
||
|
|
}
|
||
|
|
|
||
|
|
.thumbnails {
|
||
|
|
display: flex;
|
||
|
|
gap: var(--dees-space-xs);
|
||
|
|
justify-content: center;
|
||
|
|
margin-bottom: var(--dees-space-md);
|
||
|
|
overflow-x: auto;
|
||
|
|
padding: 0 var(--dees-space-md);
|
||
|
|
-webkit-overflow-scrolling: touch;
|
||
|
|
}
|
||
|
|
|
||
|
|
.thumbnail {
|
||
|
|
width: 48px;
|
||
|
|
height: 48px;
|
||
|
|
border-radius: var(--dees-radius-sm);
|
||
|
|
object-fit: cover;
|
||
|
|
cursor: pointer;
|
||
|
|
opacity: 0.5;
|
||
|
|
transition: opacity var(--dees-transition-fast), transform var(--dees-transition-fast);
|
||
|
|
flex-shrink: 0;
|
||
|
|
border: 2px solid transparent;
|
||
|
|
}
|
||
|
|
|
||
|
|
.thumbnail:hover {
|
||
|
|
opacity: 0.8;
|
||
|
|
}
|
||
|
|
|
||
|
|
.thumbnail.active {
|
||
|
|
opacity: 1;
|
||
|
|
border-color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pdf-preview {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
gap: var(--dees-space-md);
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pdf-icon {
|
||
|
|
width: 80px;
|
||
|
|
height: 80px;
|
||
|
|
background: rgba(255, 255, 255, 0.1);
|
||
|
|
border-radius: var(--dees-radius-lg);
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pdf-text {
|
||
|
|
font-size: 0.875rem;
|
||
|
|
opacity: 0.7;
|
||
|
|
}
|
||
|
|
`,
|
||
|
|
];
|
||
|
|
|
||
|
|
async connectedCallback() {
|
||
|
|
await super.connectedCallback();
|
||
|
|
this.currentIndex = this.config.startIndex ?? 0;
|
||
|
|
document.addEventListener('keydown', this.handleKeydown);
|
||
|
|
}
|
||
|
|
|
||
|
|
async disconnectedCallback() {
|
||
|
|
await super.disconnectedCallback();
|
||
|
|
document.removeEventListener('keydown', this.handleKeydown);
|
||
|
|
}
|
||
|
|
|
||
|
|
private handleKeydown = (e: KeyboardEvent) => {
|
||
|
|
switch (e.key) {
|
||
|
|
case 'Escape':
|
||
|
|
this.handleClose();
|
||
|
|
break;
|
||
|
|
case 'ArrowLeft':
|
||
|
|
this.goToPrevious();
|
||
|
|
break;
|
||
|
|
case 'ArrowRight':
|
||
|
|
this.goToNext();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
private handleClose() {
|
||
|
|
this.dispatchEvent(new CustomEvent('close', {
|
||
|
|
bubbles: true,
|
||
|
|
composed: true,
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
private handleTouchStart = (e: TouchEvent) => {
|
||
|
|
this.touchStartX = e.touches[0].clientX;
|
||
|
|
this.touchStartY = e.touches[0].clientY;
|
||
|
|
this.touchDeltaX = 0;
|
||
|
|
this.isSwiping = false;
|
||
|
|
};
|
||
|
|
|
||
|
|
private handleTouchMove = (e: TouchEvent) => {
|
||
|
|
const deltaX = e.touches[0].clientX - this.touchStartX;
|
||
|
|
const deltaY = e.touches[0].clientY - this.touchStartY;
|
||
|
|
|
||
|
|
// Only swipe horizontally if horizontal movement is greater than vertical
|
||
|
|
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 10) {
|
||
|
|
this.isSwiping = true;
|
||
|
|
this.touchDeltaX = deltaX;
|
||
|
|
this.requestUpdate();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
private handleTouchEnd = () => {
|
||
|
|
if (this.isSwiping) {
|
||
|
|
if (this.touchDeltaX > this.swipeThreshold) {
|
||
|
|
this.goToPrevious();
|
||
|
|
} else if (this.touchDeltaX < -this.swipeThreshold) {
|
||
|
|
this.goToNext();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
this.isSwiping = false;
|
||
|
|
this.touchDeltaX = 0;
|
||
|
|
this.requestUpdate();
|
||
|
|
};
|
||
|
|
|
||
|
|
private goToPrevious() {
|
||
|
|
if (this.currentIndex > 0) {
|
||
|
|
this.currentIndex--;
|
||
|
|
this.isLoading = true;
|
||
|
|
this.dispatchChangeEvent();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private goToNext() {
|
||
|
|
if (this.currentIndex < this.items.length - 1) {
|
||
|
|
this.currentIndex++;
|
||
|
|
this.isLoading = true;
|
||
|
|
this.dispatchChangeEvent();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private goToIndex(index: number) {
|
||
|
|
if (index >= 0 && index < this.items.length && index !== this.currentIndex) {
|
||
|
|
this.currentIndex = index;
|
||
|
|
this.isLoading = true;
|
||
|
|
this.dispatchChangeEvent();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private dispatchChangeEvent() {
|
||
|
|
this.dispatchEvent(new CustomEvent('change', {
|
||
|
|
detail: {
|
||
|
|
index: this.currentIndex,
|
||
|
|
item: this.items[this.currentIndex],
|
||
|
|
},
|
||
|
|
bubbles: true,
|
||
|
|
composed: true,
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
private handleImageLoad() {
|
||
|
|
this.isLoading = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
private handleDownload() {
|
||
|
|
const item = this.items[this.currentIndex];
|
||
|
|
this.dispatchEvent(new CustomEvent('download', {
|
||
|
|
detail: item,
|
||
|
|
bubbles: true,
|
||
|
|
composed: true,
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
private handleDelete() {
|
||
|
|
const item = this.items[this.currentIndex];
|
||
|
|
this.dispatchEvent(new CustomEvent('delete', {
|
||
|
|
detail: item,
|
||
|
|
bubbles: true,
|
||
|
|
composed: true,
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
private handleShare() {
|
||
|
|
const item = this.items[this.currentIndex];
|
||
|
|
this.dispatchEvent(new CustomEvent('share', {
|
||
|
|
detail: item,
|
||
|
|
bubbles: true,
|
||
|
|
composed: true,
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
private isPdf(item: IGalleryItem): boolean {
|
||
|
|
return item.mimeType?.toLowerCase() === 'application/pdf' ||
|
||
|
|
item.filename?.toLowerCase().endsWith('.pdf') ||
|
||
|
|
item.url.toLowerCase().endsWith('.pdf');
|
||
|
|
}
|
||
|
|
|
||
|
|
public render(): TemplateResult {
|
||
|
|
const currentItem = this.items[this.currentIndex];
|
||
|
|
const showFilename = this.config.showFilename ?? true;
|
||
|
|
const showActions = this.config.showActions ?? true;
|
||
|
|
const allowDelete = this.config.allowDelete ?? false;
|
||
|
|
const allowDownload = this.config.allowDownload ?? true;
|
||
|
|
const allowShare = this.config.allowShare ?? false;
|
||
|
|
const showThumbnails = this.items.length > 1;
|
||
|
|
|
||
|
|
const swipeTransform = this.isSwiping ? `translateX(${this.touchDeltaX}px)` : '';
|
||
|
|
|
||
|
|
return html`
|
||
|
|
<div class="gallery-header">
|
||
|
|
<div class="header-left">
|
||
|
|
<button class="close-button" @click=${this.handleClose}>
|
||
|
|
<dees-mobile-icon icon="x" size="24"></dees-mobile-icon>
|
||
|
|
</button>
|
||
|
|
${showFilename && currentItem?.filename ? html`
|
||
|
|
<span class="filename">${currentItem.filename}</span>
|
||
|
|
` : ''}
|
||
|
|
</div>
|
||
|
|
${this.items.length > 1 ? html`
|
||
|
|
<span class="counter">${this.currentIndex + 1} / ${this.items.length}</span>
|
||
|
|
` : ''}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div
|
||
|
|
class="gallery-content"
|
||
|
|
@touchstart=${this.handleTouchStart}
|
||
|
|
@touchmove=${this.handleTouchMove}
|
||
|
|
@touchend=${this.handleTouchEnd}
|
||
|
|
>
|
||
|
|
${this.isLoading ? html`<div class="loading-spinner"></div>` : ''}
|
||
|
|
|
||
|
|
<div
|
||
|
|
class="image-container ${this.isSwiping ? 'swiping' : ''}"
|
||
|
|
style=${swipeTransform ? `transform: ${swipeTransform}` : ''}
|
||
|
|
>
|
||
|
|
${currentItem ? (
|
||
|
|
this.isPdf(currentItem) ? html`
|
||
|
|
<div class="pdf-preview">
|
||
|
|
<div class="pdf-icon">
|
||
|
|
<dees-mobile-icon icon="file-text" size="48" color="white"></dees-mobile-icon>
|
||
|
|
</div>
|
||
|
|
<span class="pdf-text">${currentItem.filename || 'PDF Document'}</span>
|
||
|
|
</div>
|
||
|
|
` : html`
|
||
|
|
<img
|
||
|
|
class="gallery-image ${!this.isLoading ? 'loaded' : ''}"
|
||
|
|
src=${currentItem.url}
|
||
|
|
alt=${currentItem.filename || ''}
|
||
|
|
@load=${this.handleImageLoad}
|
||
|
|
/>
|
||
|
|
`
|
||
|
|
) : ''}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
${this.items.length > 1 ? html`
|
||
|
|
<button
|
||
|
|
class="nav-button prev"
|
||
|
|
@click=${this.goToPrevious}
|
||
|
|
?disabled=${this.currentIndex === 0}
|
||
|
|
>
|
||
|
|
<dees-mobile-icon icon="chevron-left" size="28"></dees-mobile-icon>
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
class="nav-button next"
|
||
|
|
@click=${this.goToNext}
|
||
|
|
?disabled=${this.currentIndex === this.items.length - 1}
|
||
|
|
>
|
||
|
|
<dees-mobile-icon icon="chevron-right" size="28"></dees-mobile-icon>
|
||
|
|
</button>
|
||
|
|
` : ''}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="gallery-footer">
|
||
|
|
${showThumbnails ? html`
|
||
|
|
<div class="thumbnails">
|
||
|
|
${this.items.map((item, index) => html`
|
||
|
|
<img
|
||
|
|
class="thumbnail ${index === this.currentIndex ? 'active' : ''}"
|
||
|
|
src=${item.thumbnailUrl || item.url}
|
||
|
|
alt=""
|
||
|
|
@click=${() => this.goToIndex(index)}
|
||
|
|
/>
|
||
|
|
`)}
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
|
||
|
|
${showActions ? html`
|
||
|
|
<div class="actions">
|
||
|
|
${allowDownload ? html`
|
||
|
|
<button class="action-button" @click=${this.handleDownload}>
|
||
|
|
<div class="icon">
|
||
|
|
<dees-mobile-icon icon="download" size="22"></dees-mobile-icon>
|
||
|
|
</div>
|
||
|
|
<span class="label">Download</span>
|
||
|
|
</button>
|
||
|
|
` : ''}
|
||
|
|
|
||
|
|
${allowShare ? html`
|
||
|
|
<button class="action-button" @click=${this.handleShare}>
|
||
|
|
<div class="icon">
|
||
|
|
<dees-mobile-icon icon="share" size="22"></dees-mobile-icon>
|
||
|
|
</div>
|
||
|
|
<span class="label">Share</span>
|
||
|
|
</button>
|
||
|
|
` : ''}
|
||
|
|
|
||
|
|
${allowDelete ? html`
|
||
|
|
<button class="action-button danger" @click=${this.handleDelete}>
|
||
|
|
<div class="icon">
|
||
|
|
<dees-mobile-icon icon="trash-2" size="22"></dees-mobile-icon>
|
||
|
|
</div>
|
||
|
|
<span class="label">Delete</span>
|
||
|
|
</button>
|
||
|
|
` : ''}
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Static factory method to show the gallery
|
||
|
|
*/
|
||
|
|
public static async show(
|
||
|
|
items: IGalleryItem[],
|
||
|
|
config: IGalleryConfig = {}
|
||
|
|
): Promise<{ action: 'close' | 'delete' | 'download' | 'share'; item?: IGalleryItem }> {
|
||
|
|
return new Promise((resolve) => {
|
||
|
|
const gallery = document.createElement('dees-mobile-gallery') as DeesMobileGallery;
|
||
|
|
gallery.items = items;
|
||
|
|
gallery.config = config;
|
||
|
|
|
||
|
|
const cleanup = () => {
|
||
|
|
gallery.remove();
|
||
|
|
};
|
||
|
|
|
||
|
|
gallery.addEventListener('close', () => {
|
||
|
|
cleanup();
|
||
|
|
resolve({ action: 'close' });
|
||
|
|
});
|
||
|
|
|
||
|
|
gallery.addEventListener('delete', (e: CustomEvent) => {
|
||
|
|
cleanup();
|
||
|
|
resolve({ action: 'delete', item: e.detail });
|
||
|
|
});
|
||
|
|
|
||
|
|
gallery.addEventListener('download', (e: CustomEvent) => {
|
||
|
|
// Don't close on download - user might want to download multiple
|
||
|
|
resolve({ action: 'download', item: e.detail });
|
||
|
|
});
|
||
|
|
|
||
|
|
gallery.addEventListener('share', (e: CustomEvent) => {
|
||
|
|
resolve({ action: 'share', item: e.detail });
|
||
|
|
});
|
||
|
|
|
||
|
|
document.body.appendChild(gallery);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|