diff --git a/src/lightbox/index.html b/src/lightbox/index.html
new file mode 100644
index 0000000..516c6e9
--- /dev/null
+++ b/src/lightbox/index.html
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+ Web Component: Lightbox Gallery
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lightbox/index.ts b/src/lightbox/index.ts
new file mode 100644
index 0000000..030c582
--- /dev/null
+++ b/src/lightbox/index.ts
@@ -0,0 +1,32 @@
+/**
+ * Styles.
+ */
+import './style.scss';
+
+/**
+ * Components
+ */
+import { TPLightboxElement } from './tp-lightbox';
+import { TPLightboxTriggerElement } from './tp-lightbox-trigger';
+import { TPLightboxGalleryElement } from './tp-lightbox-gallery';
+import { TPLightboxSliderTrackElement } from './tp-lightbox-slider-track';
+import { TPLightboxSliderElement } from './tp-lightbox-slider';
+import { TPLightboxSlideElement } from './tp-lightbox-slide';
+import { TPLightboxNavElement } from './tp-lightbox-nav';
+import { TPLightboxNavButtonElement } from './tp-lightbox-nav-button';
+import { TPLightboxCounterElement } from './tp-lightbox-counter';
+import { TPLightboxCloseElement } from './tp-lightbox-close';
+
+/**
+ * Register Components
+ */
+customElements.define( 'tp-lightbox', TPLightboxElement );
+customElements.define( 'tp-lightbox-trigger', TPLightboxTriggerElement );
+customElements.define( 'tp-lightbox-gallery', TPLightboxGalleryElement );
+customElements.define( 'tp-lightbox-slider-track', TPLightboxSliderTrackElement );
+customElements.define( 'tp-lightbox-slider', TPLightboxSliderElement );
+customElements.define( 'tp-lightbox-slide', TPLightboxSlideElement );
+customElements.define( 'tp-lightbox-nav', TPLightboxNavElement );
+customElements.define( 'tp-lightbox-nav-button', TPLightboxNavButtonElement );
+customElements.define( 'tp-lightbox-counter', TPLightboxCounterElement );
+customElements.define( 'tp-lightbox-close', TPLightboxCloseElement );
diff --git a/src/lightbox/style.scss b/src/lightbox/style.scss
new file mode 100644
index 0000000..1d5ae01
--- /dev/null
+++ b/src/lightbox/style.scss
@@ -0,0 +1,34 @@
+tp-lightbox {
+
+ @keyframes fade-in {
+ to {
+ opacity: 1;
+ }
+ }
+
+ &-gallery {
+ display: none;
+
+ &[open] {
+ display: block;
+ position: fixed;
+ inset: 0;
+ background-color: rgba(0, 0, 0, 0.9);
+ }
+ }
+
+ &-close {
+ display: block;
+ position: absolute;
+ inset: 5px 10px auto auto;
+ height: fit-content;
+ width: fit-content;
+
+ &::before {
+ content: "\00d7";
+ color: #fff;
+ font-size: 2.5rem;
+ font-weight: 400;
+ }
+ }
+}
diff --git a/src/lightbox/tp-lightbox-close.ts b/src/lightbox/tp-lightbox-close.ts
new file mode 100644
index 0000000..c1fadcc
--- /dev/null
+++ b/src/lightbox/tp-lightbox-close.ts
@@ -0,0 +1,20 @@
+import { TPLightboxGalleryElement } from './tp-lightbox-gallery';
+
+/**
+ * TP Lightbox Close
+ */
+export class TPLightboxCloseElement extends HTMLElement {
+ /**
+ * Connected Callback
+ */
+ connectedCallback() {
+ this.addEventListener( 'click', this.handleClick.bind( this ) );
+ }
+
+ /**
+ * Handle Click
+ */
+ handleClick() {
+ this.closest( 'tp-lightbox-gallery' )?.close();
+ }
+}
diff --git a/src/lightbox/tp-lightbox-counter.ts b/src/lightbox/tp-lightbox-counter.ts
new file mode 100644
index 0000000..2262190
--- /dev/null
+++ b/src/lightbox/tp-lightbox-counter.ts
@@ -0,0 +1,6 @@
+/**
+ * TP Lightbox Counter
+ */
+export class TPLightboxCounterElement extends HTMLElement {
+
+}
diff --git a/src/lightbox/tp-lightbox-gallery.ts b/src/lightbox/tp-lightbox-gallery.ts
new file mode 100644
index 0000000..c40ef80
--- /dev/null
+++ b/src/lightbox/tp-lightbox-gallery.ts
@@ -0,0 +1,90 @@
+/**
+ * TP Lightbox Gallery
+ */
+export class TPLightboxGalleryElement extends HTMLElement {
+ /**
+ * Properties
+ */
+ private galleryID: string | null;
+ private numSlides: number;
+ private currentSlide: number;
+
+ /**
+ * Constructor
+ */
+ constructor() {
+ super();
+
+ this.galleryID = this.getAttribute( 'gallery-id' );
+ this.numSlides = 0;
+ this.currentSlide = 0;
+ }
+
+ /**
+ * Connected callback
+ */
+ connectedCallback() {
+ const innerElements = [
+ document.createElement( 'tp-lightbox-counter' ),
+ document.createElement( 'tp-lightbox-close' ),
+ document.createElement( 'tp-lightbox-slider' ),
+ ];
+
+ if ( null !== this.galleryID ) {
+ const prevBtn = document.createElement( 'tp-lightbox-nav-button' );
+ const nextBtn = document.createElement( 'tp-lightbox-nav-button' );
+ prevBtn.setAttribute( 'direction', 'prev' );
+ nextBtn.setAttribute( 'direction', 'next' );
+
+ innerElements.push( prevBtn, nextBtn );
+ }
+
+ innerElements.forEach( ( innerElement ) => this.appendChild( innerElement ) );
+
+ this.addEventListener( 'click', this.handleClick.bind( this ) );
+ }
+
+ /**
+ * Adds slide
+ *
+ * @param { HTMLElement | undefined } slideContent
+ */
+ addSlide( slideContent: HTMLElement | undefined ): number | undefined {
+ if ( ! slideContent ) {
+ return undefined;
+ }
+
+ const slideElement = document.createElement( 'tp-lightbox-slide' );
+ slideElement.appendChild( slideContent );
+
+ this.querySelector( 'tp-lightbox-slider' )?.appendChild( slideElement );
+
+ this.numSlides++;
+
+ return this.numSlides;
+ }
+
+ // Open the lightbox gallery.
+ open( slideIndex: number | undefined ) {
+ if ( ! slideIndex ) {
+ return;
+ }
+
+ this.currentSlide = slideIndex;
+ this.setAttribute( 'current-slide', this.currentSlide.toString() );
+ this.setAttribute( 'open', '' );
+ this.dispatchEvent( new CustomEvent( 'open', { bubbles: false } ) );
+ }
+
+ // Close the lightbox gallery.
+ close() {
+ this.removeAttribute( 'open' );
+ this.dispatchEvent( new CustomEvent( 'close', { bubbles: false } ) );
+ }
+
+ handleClick( e: Event ) {
+ if ( e.target === this ) {
+ this.close();
+ }
+ }
+}
diff --git a/src/lightbox/tp-lightbox-nav-button.ts b/src/lightbox/tp-lightbox-nav-button.ts
new file mode 100644
index 0000000..54b0b8c
--- /dev/null
+++ b/src/lightbox/tp-lightbox-nav-button.ts
@@ -0,0 +1,6 @@
+/**
+ * TP Lightbox Nav Button
+ */
+export class TPLightboxNavButtonElement extends HTMLElement {
+
+}
diff --git a/src/lightbox/tp-lightbox-nav.ts b/src/lightbox/tp-lightbox-nav.ts
new file mode 100644
index 0000000..9c606c4
--- /dev/null
+++ b/src/lightbox/tp-lightbox-nav.ts
@@ -0,0 +1,6 @@
+/**
+ * TP Lightbox Nav
+ */
+export class TPLightboxNavElement extends HTMLElement {
+
+}
diff --git a/src/lightbox/tp-lightbox-slide.ts b/src/lightbox/tp-lightbox-slide.ts
new file mode 100644
index 0000000..4e11123
--- /dev/null
+++ b/src/lightbox/tp-lightbox-slide.ts
@@ -0,0 +1,6 @@
+/**
+ * TP Lightbox Slide
+ */
+export class TPLightboxSlideElement extends HTMLElement {
+
+}
diff --git a/src/lightbox/tp-lightbox-slider-track.ts b/src/lightbox/tp-lightbox-slider-track.ts
new file mode 100644
index 0000000..5a69c6f
--- /dev/null
+++ b/src/lightbox/tp-lightbox-slider-track.ts
@@ -0,0 +1,6 @@
+/**
+ * TP Lightbox Slider Track
+ */
+export class TPLightboxSliderTrackElement extends HTMLElement {
+
+}
diff --git a/src/lightbox/tp-lightbox-slider.ts b/src/lightbox/tp-lightbox-slider.ts
new file mode 100644
index 0000000..dbe259f
--- /dev/null
+++ b/src/lightbox/tp-lightbox-slider.ts
@@ -0,0 +1,38 @@
+import { TPLightboxGalleryElement } from './tp-lightbox-gallery';
+import { TPLightboxTriggerElement } from './tp-lightbox-trigger';
+
+/**
+ * TP Lightbox Slider
+ */
+export class TPLightboxSliderElement extends HTMLElement {
+ /**
+ * Connected callback
+ */
+ connectedCallback() {
+ const gallery = this.closest( 'tp-lightbox-gallery' ) as TPLightboxGalleryElement;
+
+ if ( ! gallery ) {
+ return;
+ }
+
+ const galleryID = gallery.getAttribute( 'gallery-id' );
+
+ let triggerQuery = 'tp-lightbox-trigger';
+
+ if ( ! galleryID ) {
+ triggerQuery += ':not([gallery-id])';
+ } else {
+ triggerQuery += `[gallery-id="${ galleryID }"]`;
+ }
+
+ const triggers = Array.from( document.querySelectorAll( triggerQuery ) );
+
+ triggers.forEach( ( trigger ) => {
+ const slideIndex = gallery.addSlide( trigger.lightboxContentTemplate?.content.cloneNode( true ) as HTMLElement );
+
+ if ( slideIndex ) {
+ trigger.setAttribute( 'slide-index', slideIndex.toString() );
+ }
+ } );
+ }
+}
diff --git a/src/lightbox/tp-lightbox-trigger.ts b/src/lightbox/tp-lightbox-trigger.ts
new file mode 100644
index 0000000..e15ad82
--- /dev/null
+++ b/src/lightbox/tp-lightbox-trigger.ts
@@ -0,0 +1,112 @@
+import { TPLightboxGalleryElement } from './tp-lightbox-gallery';
+
+/**
+ * TP Lightbox Trigger
+ */
+export class TPLightboxTriggerElement extends HTMLElement {
+ /**
+ * Properties
+ */
+ private galleryID: string | null;
+ private slideIndex: number | undefined;
+ lightboxContentTemplate: HTMLTemplateElement | null;
+
+ /**
+ * Constructor
+ */
+ constructor() {
+ super();
+
+ this.galleryID = null;
+ this.lightboxContentTemplate = null;
+ }
+
+ /**
+ * Get observed attributes.
+ *
+ * @return {Array} List of observed attributes.
+ */
+ static get observedAttributes(): string[] {
+ return [ 'slide-index' ];
+ }
+
+ /**
+ * Attribute changed callback.
+ *
+ * @param {string} name Attribute name.
+ * @param {string} oldValue Old value.
+ * @param {string} newValue New value.
+ */
+ attributeChangedCallback( name: string = '', oldValue: string = '', newValue: string = '' ): void {
+ if ( 'slide-index' === name && oldValue !== newValue && '' !== newValue ) {
+ this.slideIndex = Number( newValue );
+ }
+ }
+
+ /**
+ * Connected Callback
+ */
+ connectedCallback(): void {
+ // Initialize fields
+ this.galleryID = this.getAttribute( 'gallery-id' );
+ this.lightboxContentTemplate = this.querySelector( 'template' );
+
+ // Check if there is a template for the content.
+ if ( ! this.lightboxContentTemplate ) {
+ return; // Not found, bail.
+ }
+
+ // Events
+ this.addEventListener( 'click', this.handleClick.bind( this ) );
+ }
+
+ /**
+ * Event: Handles click event.
+ */
+ handleClick() {
+ let galleryQuery = 'tp-lightbox-gallery';
+
+ if ( ! this.galleryID ) {
+ galleryQuery += ':not([gallery-id])';
+ } else {
+ galleryQuery += `[gallery-id="${ this.galleryID }"]`;
+ }
+
+ let tpLightboxGallery = document.querySelector( galleryQuery ) as TPLightboxGalleryElement;
+
+ if ( ! tpLightboxGallery ) {
+ tpLightboxGallery = this.initializeLightbox();
+
+ const slideIndexAttribute = this.getAttribute( 'slide-index' );
+
+ if ( slideIndexAttribute ) {
+ this.slideIndex = Number( slideIndexAttribute );
+ }
+ }
+
+ tpLightboxGallery.open( this.slideIndex );
+ }
+
+ /**
+ * Initializes the global TPLightboxElement
+ */
+ private initializeLightbox(): TPLightboxGalleryElement {
+ let tpLightbox = document.querySelector( 'tp-lightbox' );
+
+ if ( ! tpLightbox ) {
+ tpLightbox = document.createElement( 'tp-lightbox' );
+ document.body.appendChild( tpLightbox );
+ }
+
+ const tpLightboxGallery = document.createElement( 'tp-lightbox-gallery' ) as TPLightboxGalleryElement;
+
+ // There is no gallery id, put it in the default one.
+ if ( this.galleryID ) {
+ tpLightboxGallery.setAttribute( 'gallery-id', this.galleryID );
+ }
+
+ tpLightbox.appendChild( tpLightboxGallery );
+
+ return tpLightboxGallery;
+ }
+}
diff --git a/src/lightbox/tp-lightbox.ts b/src/lightbox/tp-lightbox.ts
new file mode 100644
index 0000000..b80b751
--- /dev/null
+++ b/src/lightbox/tp-lightbox.ts
@@ -0,0 +1,6 @@
+/**
+ * TP Lightbox
+ */
+export class TPLightboxElement extends HTMLElement {
+
+}
diff --git a/webpack.config.js b/webpack.config.js
index e75ea95..ab3c473 100755
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -49,12 +49,12 @@ class DeclarationBundlerPlugin {
const line = lines[ i ];
if (
line !== '' &&
- line.indexOf('export =' ) === -1 &&
+ line.indexOf( 'export =' ) === -1 &&
! ( /import ([a-z0-9A-Z_-]+) = require\(/ ).test( line ) &&
- ( ! this.excludedReferences || line.indexOf(' line.indexOf( reference ) !== -1 ) )
+ ( ! this.excludedReferences || line.indexOf( ' line.indexOf( reference ) !== -1 ) )
) {
- if ( line.indexOf('declare ' ) !== -1 ) {
- lines[ i ] = line.replace('declare ', '' );
+ if ( line.indexOf( 'declare ' ) !== -1 ) {
+ lines[ i ] = line.replace( 'declare ', '' );
}
lines[ i ] = lines[ i ];
} else {
@@ -82,6 +82,7 @@ module.exports = ( env ) => {
form: './src/form/index.ts',
accordion: './src/accordion/index.ts',
'multi-select': './src/multi-select/index.ts',
+ lightbox: './src/lightbox/index.ts',
},
module: {
rules: [