diff --git a/src/components/CustomCarousel.astro b/src/components/CustomCarousel.astro
index 5a7f8ab..2ccde3e 100644
--- a/src/components/CustomCarousel.astro
+++ b/src/components/CustomCarousel.astro
@@ -93,85 +93,4 @@ const limitByWidthClasses = "max-h-fit";
)}
-
+
\ No newline at end of file
diff --git a/src/components/Timeline.astro b/src/components/Timeline.astro
index 43a80b3..3a182cf 100644
--- a/src/components/Timeline.astro
+++ b/src/components/Timeline.astro
@@ -1,5 +1,6 @@
---
import type {timelineEntry} from "@interfaces/timeline.ts";
+
const timeline: timelineEntry[] = Astro.props.timeline || [];
---
@@ -30,51 +31,4 @@ const timeline: timelineEntry[] = Astro.props.timeline || [];
-
+
\ No newline at end of file
diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro
index 292a6ba..cddc3e8 100644
--- a/src/layouts/BaseLayout.astro
+++ b/src/layouts/BaseLayout.astro
@@ -13,9 +13,6 @@ const pageTitle = Astro.props.title ? `${Astro.props.title} - Corwin Perren` : "
{pageTitle}
-
-
-
diff --git a/src/scripts/components/custom-carousel.ts b/src/scripts/components/custom-carousel.ts
new file mode 100644
index 0000000..90f2cca
--- /dev/null
+++ b/src/scripts/components/custom-carousel.ts
@@ -0,0 +1,80 @@
+import {Carousel, type CarouselItem, type CarouselOptions, type IndicatorItem} from 'flowbite';
+
+class CustomCarousel extends HTMLElement {
+ _slide: boolean;
+ _items: CarouselItem[];
+ _options: CarouselOptions;
+ _carousel: Carousel;
+
+ constructor() {
+ super();
+
+ this._slide = this.getAttribute('data-custom-carousel') === 'slide';
+ this._items = this._getItems();
+ this._options = this._getOptions();
+ this._carousel = new Carousel(this, this._items, this._options);
+
+ if (this._slide && this._items.length > 0) this._carousel.cycle();
+
+ window.addEventListener("load", this._attachHandlers);
+
+ }
+
+ _getItems = (): CarouselItem[] => {
+ let customItems = this.querySelectorAll('[data-custom-carousel-item]') || [];
+
+ return Array.from(customItems).map(
+ (item, index): CarouselItem => {
+ return {el: item as HTMLElement, position: index}
+ }
+ )
+ }
+
+ _getOptions = (): CarouselOptions => {
+ let customIndicators = this.querySelectorAll('[data-custom-carousel-slide-to]') || [];
+
+ return {
+ defaultPosition: 0,
+ interval: this.dataset.customCarouselInterval ? Number(this.dataset.customCarouselInterval) : 8000,
+
+ indicators: {
+ activeClasses: 'border-2 border-caperren-green bg-black',
+ inactiveClasses: 'bg-caperren-green/40 hover:bg-caperren-green-light',
+ items: Array.from(customIndicators).map(
+ (item, index): IndicatorItem => {
+ return {
+ el: item as HTMLElement,
+ position: Number(item.getAttribute('data-custom-carousel-slide-to'))
+ }
+ }
+ )
+ }
+ }
+ }
+
+ _attachHandlers = (): void => {
+ // Controls
+ const carouselNextEl = this.querySelector(
+ '[data-custom-carousel-next]'
+ );
+ const carouselPrevEl = this.querySelector(
+ '[data-custom-carousel-prev]'
+ );
+
+ if (carouselNextEl) {
+ carouselNextEl.addEventListener('click', () => {
+ this._carousel.next();
+ });
+ }
+
+ if (carouselPrevEl) {
+ carouselPrevEl.addEventListener('click', () => {
+ this._carousel.prev();
+ });
+ }
+ }
+
+
+}
+
+customElements.define('custom-carousel', CustomCarousel)
\ No newline at end of file
diff --git a/src/scripts/components/timeline.ts b/src/scripts/components/timeline.ts
new file mode 100644
index 0000000..e1678f3
--- /dev/null
+++ b/src/scripts/components/timeline.ts
@@ -0,0 +1,44 @@
+import LeaderLine from "leader-line-new";
+
+class CustomTimeline extends HTMLElement {
+ _eventElements: Element[];
+
+ constructor() {
+ super();
+ this._eventElements = this._getNodeElements();
+
+
+ window.addEventListener("load", this._paintLeaderLines);
+
+ }
+
+ _getNodeElements = (): Element[] =>
+ Array.from(this.querySelectorAll('[data-timeline-node-index]')).sort(
+ (elementA, elementB) =>
+ Number(elementA.getAttribute('data-timeline-node-index')) - Number(elementB.getAttribute('data-timeline-node-index'))
+ );
+
+
+ _paintLeaderLines = () => {
+ let pairs = this._eventElements.map((entry, index) => {
+ if (index < this._eventElements.length - 1) {
+ return [entry, this._eventElements[index + 1]];
+ }
+ }).filter(pair => pair !== undefined)
+
+ pairs.forEach(pair => {
+ new LeaderLine({
+ start: pair[0],
+ end: pair[1],
+ color: '#10ac25',
+ size: 3,
+ startSocket: "right",
+ endSocket: "left",
+ startPlug: "square",
+ endPlug: "arrow3"
+ });
+ });
+ }
+}
+
+customElements.define('custom-timeline', CustomTimeline)
\ No newline at end of file
diff --git a/src/scripts/main.js b/src/scripts/main.js
index 0f3307e..e69de29 100644
--- a/src/scripts/main.js
+++ b/src/scripts/main.js
@@ -1 +0,0 @@
-import 'flowbite';
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 8441b7c..05765df 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,6 +6,7 @@
"@data/*": ["./src/data/*"],
"@interfaces/*": ["./src/interfaces/*"],
"@layouts/*": ["./src/layouts/*"],
+ "@scripts/*": ["./src/scripts/*"],
"@styles/*": ["./src/styles/*"]
}