Skip to content

Commit f4d3776

Browse files
authored
Merge pull request #8 from DataLinx/main
Merge from main
2 parents c6126c4 + 330c775 commit f4d3776

11 files changed

Lines changed: 531 additions & 4 deletions

File tree

.github/workflows/linter.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ jobs:
2222
- name: Checkout code
2323
uses: actions/checkout@v4
2424
with:
25-
ref: ${{ github.head_ref }}
25+
# Checkout the actual branch, not a specific commit
26+
ref: ${{ github.head_ref || github.ref_name }}
27+
# Fetch the full history to avoid shallow clone issues
28+
fetch-depth: 0
2629

2730
- name: Run Laravel Pint
2831
uses: aglipanci/laravel-pint-action@latest

.github/workflows/test-runner.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
- name: Checkout code
4242
uses: actions/checkout@v4
4343
with:
44-
ref: ${{ github.head_ref }}
44+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
4545

4646
- name: Validate composer.json and composer.lock
4747
run: composer validate --strict

composer.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
"spatie/laravel-package-tools": "^1.19"
4949
},
5050
"require-dev": {
51-
"eclipsephp/catalogue-plugin": "dev-main",
5251
"laravel/pint": "^1.21",
5352
"orchestra/testbench": "^10.1",
5453
"pestphp/pest": "^3.7",

resources/dist/slider-column.css

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
[x-cloak] { display: none !important; }
2+
3+
.image-preview-lightbox-overlay {
4+
position: fixed;
5+
top: 0;
6+
left: 0;
7+
right: 0;
8+
bottom: 0;
9+
z-index: 9999 !important;
10+
background-color: rgba(0, 0, 0, 0.7);
11+
backdrop-filter: blur(10px);
12+
display: flex;
13+
align-items: center;
14+
justify-content: center;
15+
padding: 80px;
16+
}
17+
18+
.image-preview-lightbox-container {
19+
position: relative;
20+
max-width: 90vw;
21+
max-height: 90vh;
22+
display: flex;
23+
align-items: center;
24+
justify-content: center;
25+
z-index: 9999 !important;
26+
}
27+
28+
.image-preview-lightbox-close {
29+
position: absolute;
30+
top: -50px;
31+
right: 0;
32+
color: white;
33+
background: none;
34+
border: none;
35+
cursor: pointer;
36+
padding: 10px;
37+
opacity: 0.8;
38+
transition: opacity 0.2s;
39+
}
40+
41+
.image-preview-lightbox-close:hover {
42+
opacity: 1;
43+
}
44+
45+
.image-preview-lightbox-close svg {
46+
width: 32px;
47+
height: 32px;
48+
}
49+
50+
.image-preview-lightbox-image-wrapper {
51+
position: relative;
52+
display: flex;
53+
align-items: center;
54+
justify-content: center;
55+
background-color: #1f2937;
56+
border-radius: 8px;
57+
overflow: hidden;
58+
max-width: 90vw;
59+
max-height: 85vh;
60+
}
61+
62+
.image-preview-lightbox-image {
63+
max-width: 100%;
64+
max-height: 85vh;
65+
width: auto;
66+
height: auto;
67+
object-fit: contain;
68+
display: block;
69+
}
70+
71+
.image-preview-lightbox-nav {
72+
position: absolute;
73+
top: 50%;
74+
transform: translateY(-50%);
75+
background-color: rgba(255, 255, 255, 0.1);
76+
color: white;
77+
border: none;
78+
border-radius: 50%;
79+
width: 48px;
80+
height: 48px;
81+
display: flex;
82+
align-items: center;
83+
justify-content: center;
84+
cursor: pointer;
85+
transition: background-color 0.2s;
86+
}
87+
88+
.image-preview-lightbox-nav:hover {
89+
background-color: rgba(255, 255, 255, 0.2);
90+
}
91+
92+
.image-preview-lightbox-nav.prev {
93+
left: -60px;
94+
}
95+
96+
.image-preview-lightbox-nav.next {
97+
right: -60px;
98+
}
99+
100+
.image-preview-lightbox-nav svg {
101+
width: 24px;
102+
height: 24px;
103+
}
104+
105+
.image-preview-lightbox-info {
106+
position: absolute;
107+
bottom: 0;
108+
left: 0;
109+
right: 0;
110+
background: linear-gradient(to top, rgba(0, 0, 0, 0.9), transparent);
111+
padding: 24px;
112+
color: white;
113+
border-radius: 0 0 8px 8px;
114+
}
115+
116+
.image-preview-lightbox-title {
117+
font-size: 18px;
118+
font-weight: 600;
119+
margin: 0 0 8px 0;
120+
}
121+
122+
.image-preview-lightbox-link {
123+
margin: 8px 0 0 0;
124+
}

resources/dist/slider-column.js

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
window.imagePreviewLightbox = function() {
2+
return {
3+
isOpen: false,
4+
currentIndex: 0,
5+
images: [],
6+
currentImage: {
7+
url: '',
8+
title: '',
9+
link: '',
10+
filename: ''
11+
},
12+
13+
getDisplayName() {
14+
return this.currentImage.title || this.currentImage.filename || '';
15+
},
16+
17+
init() {
18+
const self = this;
19+
document.addEventListener('click', function(e) {
20+
if (e.target.classList.contains('image-preview-trigger')) {
21+
e.preventDefault();
22+
e.stopPropagation();
23+
self.openFromTable(e.target);
24+
}
25+
}, true);
26+
document.addEventListener('keydown', (e) => {
27+
if (this.isOpen) {
28+
if (e.key === 'ArrowLeft') {
29+
e.preventDefault();
30+
this.previous();
31+
} else if (e.key === 'ArrowRight') {
32+
e.preventDefault();
33+
this.next();
34+
}
35+
}
36+
});
37+
},
38+
39+
openFromTable(imageElement) {
40+
const allRows = document.querySelectorAll('tbody tr');
41+
this.images = [];
42+
let clickedIndex = 0;
43+
44+
const imageGrid = [];
45+
let maxColumns = 0;
46+
47+
allRows.forEach((row) => {
48+
const rowImages = row.querySelectorAll('.fi-ta-image .image-preview-trigger');
49+
const rowImageArray = Array.from(rowImages);
50+
imageGrid.push(rowImageArray);
51+
maxColumns = Math.max(maxColumns, rowImageArray.length);
52+
});
53+
54+
let clickedRowIndex = -1;
55+
let clickedColIndex = -1;
56+
57+
imageGrid.forEach((rowImages, rowIndex) => {
58+
rowImages.forEach((img, colIndex) => {
59+
if (img === imageElement) {
60+
clickedRowIndex = rowIndex;
61+
clickedColIndex = colIndex;
62+
}
63+
});
64+
});
65+
66+
imageGrid.forEach((rowImages, rowIndex) => {
67+
rowImages.forEach((img, colIndex) => {
68+
this.addImageToCollection(img, imageElement, () => {
69+
if (rowIndex === clickedRowIndex && colIndex === clickedColIndex) {
70+
clickedIndex = this.images.length - 1;
71+
}
72+
});
73+
});
74+
});
75+
76+
if (this.images.length > 0) {
77+
this.currentIndex = clickedIndex;
78+
this.updateCurrentImage();
79+
this.open();
80+
}
81+
},
82+
83+
addImageToCollection(img, imageElement, onMatch) {
84+
const configData = img.dataset.lightboxConfig;
85+
86+
if (configData) {
87+
try {
88+
const lightboxData = JSON.parse(configData);
89+
const matchingImageData = lightboxData.find(data =>
90+
img.src.includes(data.url) || data.url.includes(img.src) || data.url === img.src
91+
);
92+
93+
if (matchingImageData) {
94+
this.images.push({
95+
url: img.src,
96+
title: matchingImageData.title || '',
97+
link: matchingImageData.link || '',
98+
filename: img.alt || ''
99+
});
100+
101+
if (img === imageElement) {
102+
onMatch();
103+
}
104+
return;
105+
}
106+
} catch (e) {
107+
console.error('Error parsing lightbox config:', e);
108+
}
109+
}
110+
111+
const imageData = {
112+
url: img.src,
113+
title: '',
114+
link: '',
115+
filename: img.alt || ''
116+
};
117+
118+
this.images.push(imageData);
119+
120+
if (img === imageElement) {
121+
onMatch();
122+
}
123+
},
124+
125+
open() {
126+
this.isOpen = true;
127+
document.body.style.overflow = 'hidden';
128+
},
129+
130+
close() {
131+
this.isOpen = false;
132+
document.body.style.overflow = '';
133+
},
134+
135+
next() {
136+
if (this.images.length > 0) {
137+
this.currentIndex = (this.currentIndex + 1) % this.images.length;
138+
this.updateCurrentImage();
139+
}
140+
},
141+
142+
previous() {
143+
if (this.images.length > 0) {
144+
this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
145+
this.updateCurrentImage();
146+
}
147+
},
148+
149+
updateCurrentImage() {
150+
if (this.images[this.currentIndex]) {
151+
this.currentImage = this.images[this.currentIndex];
152+
}
153+
}
154+
};
155+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
@php
2+
$centerX = $width / 2;
3+
$centerY = $height / 2;
4+
$area = $width * $height;
5+
$averageDimension = sqrt($area);
6+
$fontSize = max(20, min(64, $averageDimension / 2.5));
7+
$aspectRatio = max($width, $height) / min($width, $height);
8+
if ($aspectRatio > 3) {
9+
$fontSize *= 0.9;
10+
}
11+
12+
$fontSize = round($fontSize, 1);
13+
@endphp
14+
<svg width="{{ $width }}px" height="{{ $height }}px" viewBox="0 0 {{ $width }} {{ $height }}"
15+
fill="none" xmlns="http://www.w3.org/2000/svg">
16+
<rect width="{{ $width }}" height="{{ $height }}" fill="#EFF1F3" />
17+
@if ($text)
18+
<text x="{{ $centerX }}" y="{{ $centerY }}" text-anchor="middle" dominant-baseline="central"
19+
fill="#687787" font-family="Arial, sans-serif" font-size="{{ $fontSize }}" font-weight="400">
20+
{{ $text }}
21+
</text>
22+
@else
23+
@php
24+
$scale = min($width, $height) / 120;
25+
$iconWidth = 120 * $scale;
26+
$iconHeight = 120 * $scale;
27+
$offsetX = ($width - $iconWidth) / 2;
28+
$offsetY = ($height - $iconHeight) / 2;
29+
@endphp
30+
<g transform="translate({{ $offsetX }}, {{ $offsetY }}) scale({{ $scale }})">
31+
<path fill-rule="evenodd" clip-rule="evenodd"
32+
d="M33.2503 38.4816C33.2603 37.0472 34.4199 35.8864 35.8543 35.875H83.1463C84.5848 35.875 85.7503 37.0431 85.7503 38.4816V80.5184C85.7403 81.9528 84.5807 83.1136 83.1463 83.125H35.8543C34.4158 83.1236 33.2503 81.957 33.2503 80.5184V38.4816ZM80.5006 41.1251H38.5006V77.8751L62.8921 53.4783C63.9172 52.4536 65.5788 52.4536 66.6039 53.4783L80.5006 67.4013V41.1251ZM43.75 51.6249C43.75 54.5244 46.1005 56.8749 49 56.8749C51.8995 56.8749 54.25 54.5244 54.25 51.6249C54.25 48.7254 51.8995 46.3749 49 46.3749C46.1005 46.3749 43.75 48.7254 43.75 51.6249Z"
33+
fill="#687787" />
34+
</g>
35+
@endif
36+
</svg>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<div x-data="imagePreviewLightbox()" x-init="init()">
2+
<div x-show="isOpen"
3+
x-transition:enter="transition ease-out duration-300"
4+
x-transition:enter-start="opacity-0"
5+
x-transition:enter-end="opacity-100"
6+
x-transition:leave="transition ease-in duration-200"
7+
x-transition:leave-start="opacity-100"
8+
x-transition:leave-end="opacity-0"
9+
@click="close()"
10+
@keydown.escape.window="close()"
11+
class="image-preview-lightbox-overlay"
12+
x-cloak>
13+
14+
<div class="image-preview-lightbox-container" @click.stop>
15+
<button type="button" @click.stop="close()" class="image-preview-lightbox-close">
16+
<x-filament::icon icon="heroicon-o-x-mark" />
17+
</button>
18+
19+
<div class="image-preview-lightbox-image-wrapper">
20+
<img :src="currentImage.url"
21+
:alt="getDisplayName()"
22+
class="image-preview-lightbox-image">
23+
<div class="image-preview-lightbox-info" x-show="currentImage.title || currentImage.link">
24+
<p class="image-preview-lightbox-title" x-text="currentImage.title" x-show="currentImage.title"></p>
25+
<p class="image-preview-lightbox-link" x-show="currentImage.link">
26+
<a :href="currentImage.link" target="_blank" class="text-blue-400 hover:text-blue-300">
27+
<x-filament::icon icon="heroicon-o-arrow-top-right-on-square" class="w-5 h-5" />
28+
</a>
29+
</p>
30+
</div>
31+
</div>
32+
<template x-if="images && images.length > 1">
33+
<div>
34+
<button type="button" @click.stop.prevent="previous()" class="image-preview-lightbox-nav prev">
35+
<x-filament::icon icon="heroicon-o-chevron-left" />
36+
</button>
37+
38+
<button type="button" @click.stop.prevent="next()" class="image-preview-lightbox-nav next">
39+
<x-filament::icon icon="heroicon-o-chevron-right" />
40+
</button>
41+
</div>
42+
</template>
43+
</div>
44+
</div>
45+
</div>

0 commit comments

Comments
 (0)