Skip to content

Commit 5cb40de

Browse files
committed
add featured packages slider
1 parent ca04d4b commit 5cb40de

13 files changed

Lines changed: 209 additions & 29 deletions

File tree

config/app.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@
9292
// 'cacheTime' => '+1 year'
9393
],
9494

95+
'Packages' => [
96+
'featured' => [
97+
'markstory/asset_compress',
98+
'josegonzalez/cakephp-upload',
99+
'dereuromark/cakephp-queue',
100+
'lordsimal/cakephp-sentry',
101+
],
102+
],
103+
95104
/*
96105
* Configure the cache adapters.
97106
*/

package-lock.json

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
{
22
"type": "module",
33
"devDependencies": {
4-
"tailwindcss": "^4.0.0",
54
"@tailwindcss/vite": "^4.1.4",
5+
"tailwindcss": "^4.0.0",
66
"vite": "^8.0.0"
77
},
88
"scripts": {
99
"dev": "vite build --watch",
1010
"build": "vite build"
11+
},
12+
"dependencies": {
13+
"swiper": "^12.1.3"
1114
}
1215
}

phpunit.xml.dist

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd">
99
<php>
1010
<ini name="memory_limit" value="-1"/>
11-
<ini name="apc.enable_cli" value="1"/>
1211
</php>
1312

1413
<!-- Add any additional test suites you want to run here -->

resources/css/style.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
@import "tailwindcss";
22
@import "./fonts.css";
3+
@import "swiper/css";
4+
@import "swiper/css/navigation";
35

46
@theme {
57
--font-raleway: "Raleway", sans-serif;
@@ -37,3 +39,25 @@
3739
[data-is-php-filter] + .ss-main {
3840
--ss-primary-color: var(--color-blue-500);
3941
}
42+
43+
@layer components {
44+
.featured-packages-slider-shell {
45+
@apply -mx-3 overflow-hidden px-3 pb-4;
46+
}
47+
48+
.featured-packages-slider {
49+
@apply overflow-visible;
50+
}
51+
52+
.featured-packages-slider .swiper-slide {
53+
@apply h-auto py-2;
54+
}
55+
56+
.featured-packages-slider-button {
57+
@apply inline-flex h-11 w-11 items-center justify-center rounded-full border border-slate-200 bg-white text-lg text-slate-700 shadow-sm transition hover:border-cake-red hover:text-cake-red disabled:cursor-not-allowed disabled:opacity-40;
58+
}
59+
60+
.featured-packages-slider-button::after {
61+
content: none;
62+
}
63+
}

resources/js/app.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import Swiper from 'swiper'
2+
import { Autoplay, Navigation } from 'swiper/modules'
3+
4+
const initializeSelects = () => {
5+
if (typeof window.SlimSelect !== 'function') {
6+
return
7+
}
8+
9+
const selects = document.querySelectorAll('select')
10+
11+
selects.forEach((element) => {
12+
const placeholder = element.getAttribute('data-placeholder')
13+
14+
new window.SlimSelect({
15+
select: element,
16+
settings: {
17+
placeholderText: placeholder,
18+
},
19+
})
20+
})
21+
}
22+
23+
const initializeFeaturedPackagesSlider = () => {
24+
const slider = document.querySelector('[data-featured-packages-slider]')
25+
26+
if (!slider) {
27+
return
28+
}
29+
30+
new Swiper(slider, {
31+
modules: [Autoplay, Navigation],
32+
loop: true,
33+
slidesPerView: 1,
34+
spaceBetween: 24,
35+
grabCursor: true,
36+
// autoplay: {
37+
// delay: 4000,
38+
// disableOnInteraction: false,
39+
// pauseOnMouseEnter: true,
40+
// },
41+
navigation: {
42+
nextEl: '[data-featured-packages-next]',
43+
prevEl: '[data-featured-packages-prev]',
44+
},
45+
breakpoints: {
46+
768: {
47+
slidesPerView: 2,
48+
},
49+
1280: {
50+
slidesPerView: 3,
51+
},
52+
},
53+
})
54+
}
55+
56+
initializeSelects()
57+
initializeFeaturedPackagesSlider()

src/Controller/PackagesController.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace App\Controller;
55

6+
use Cake\Core\Configure;
67
use Cake\ORM\Query\SelectQuery;
78

89
/**
@@ -28,11 +29,36 @@ public function index()
2829
));
2930
}
3031

32+
$featuredPackageNames = array_values(array_filter((array)Configure::read('Packages.featured', [])));
33+
$featuredPackages = [];
34+
35+
if ($featuredPackageNames !== []) {
36+
$featuredPackages = $this->Packages
37+
->find()
38+
->contain(['Tags' => function (SelectQuery $q) {
39+
return $q->orderByDesc('Tags.label');
40+
}])
41+
->where(['Packages.package IN' => $featuredPackageNames])
42+
->all()
43+
->indexBy('package')
44+
->toArray();
45+
46+
$featuredPackages = array_values(array_filter(
47+
array_map(
48+
static fn (string $packageName) => $featuredPackages[$packageName] ?? null,
49+
$featuredPackageNames,
50+
),
51+
));
52+
}
53+
3154
$query = $this->Packages
3255
->find('search', search: $this->request->getQueryParams())
3356
->contain(['Tags' => function (SelectQuery $q) {
3457
return $q->orderByDesc('Tags.label');
3558
}]);
59+
if ($featuredPackageNames !== []) {
60+
$query->where(['Packages.package NOT IN' => $featuredPackageNames]);
61+
}
3662
$packages = $this->paginate($query, ['limit' => 21]);
3763

3864
$cakephpTags = $this->Packages->Tags->find('list', keyField: 'slug')
@@ -44,6 +70,6 @@ public function index()
4470
->orderByAsc('label')
4571
->toArray();
4672

47-
$this->set(compact('packages', 'cakephpTags', 'phpTags'));
73+
$this->set(compact('featuredPackages', 'packages', 'cakephpTags', 'phpTags'));
4874
}
4975
}

templates/Packages/index.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
<?php
22
/**
33
* @var \App\View\AppView $this
4+
* @var iterable<\App\Model\Entity\Package> $featuredPackages
45
* @var iterable<\App\Model\Entity\Package> $packages
56
* @var iterable<\Tags\Model\Entity\Tag> $cakephpTags
67
* @var iterable<\Tags\Model\Entity\Tag> $phpTags
78
*/
89
?>
910
<div class="packages index content">
11+
<?php $featuredPackageNames = array_column((array)$featuredPackages, 'package'); ?>
1012
<div class="px-4 py-8 sm:px-6 lg:px-8">
1113
<div class="mb-8 flex flex-col gap-5 xl:flex-row xl:items-end xl:justify-between">
1214
<div>
@@ -43,9 +45,41 @@
4345
</div>
4446
</div>
4547

48+
<?php if ($featuredPackages) : ?>
49+
<section class="mb-8">
50+
<div class="mb-5 flex items-end justify-between gap-4">
51+
<div>
52+
<p class="text-sm font-medium uppercase tracking-[0.2em] text-cake-red"><?= __('Featured') ?></p>
53+
</div>
54+
<div class="hidden items-center gap-3 md:flex">
55+
<button type="button" class="featured-packages-slider-button" data-featured-packages-prev aria-label="<?= __('Previous featured package') ?>">
56+
&larr;
57+
</button>
58+
<button type="button" class="featured-packages-slider-button" data-featured-packages-next aria-label="<?= __('Next featured package') ?>">
59+
&rarr;
60+
</button>
61+
</div>
62+
</div>
63+
<div class="featured-packages-slider-shell">
64+
<div class="featured-packages-slider swiper" data-featured-packages-slider>
65+
<div class="swiper-wrapper">
66+
<?php foreach ($featuredPackages as $package) : ?>
67+
<div class="swiper-slide">
68+
<?= $this->element('Packages/package-tile', ['package' => $package, 'isFeatured' => true]) ?>
69+
</div>
70+
<?php endforeach; ?>
71+
</div>
72+
</div>
73+
</div>
74+
</section>
75+
<?php endif; ?>
76+
4677
<div class="grid gap-6 md:grid-cols-2 xl:grid-cols-3">
4778
<?php foreach ($packages as $package) : ?>
48-
<?= $this->element('Packages/package-tile', ['package' => $package]) ?>
79+
<?= $this->element('Packages/package-tile', [
80+
'package' => $package,
81+
'isFeatured' => in_array($package->package, $featuredPackageNames, true),
82+
]) ?>
4983
<?php endforeach; ?>
5084
</div>
5185

templates/element/Packages/package-tile.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,25 @@
22
/**
33
* @var \App\View\AppView $this
44
* @var \App\Model\Entity\Package $package
5+
* @var bool $isFeatured
56
*/
7+
$isFeatured = $isFeatured ?? false;
68
?>
79
<article class="group flex h-full flex-col overflow-hidden rounded-3xl border border-slate-200 bg-white shadow-sm transition hover:-translate-y-1 hover:shadow-lg">
810
<a class="block border-b border-slate-200 bg-slate-50 px-5 py-4 transition group-hover:bg-white"
911
target="_blank" rel="noopener noreferrer" href="<?= h($package->repo_url) ?>">
1012
<div class="flex items-start justify-between gap-4">
1113
<div class="min-w-0">
12-
<h2 class="truncate text-lg font-semibold text-slate-950 transition group-hover:text-cake-red">
13-
<?= h($package->package) ?>
14-
</h2>
14+
<div class="flex flex-wrap items-center gap-2">
15+
<h2 class="truncate text-lg font-semibold text-slate-950 transition group-hover:text-cake-red">
16+
<?= h($package->package) ?>
17+
</h2>
18+
<?php if ($isFeatured) : ?>
19+
<span class="shrink-0 rounded-full bg-cake-red px-2.5 py-1 text-xs font-semibold uppercase tracking-wide text-white">
20+
<?= __('Featured') ?>
21+
</span>
22+
<?php endif; ?>
23+
</div>
1524
</div>
1625
<span class="shrink-0 rounded-full border border-slate-200 bg-white px-2.5 py-1 text-xs font-medium text-slate-600">
1726
<?= __('Plugin') ?>

templates/layout/default.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
<script src="https://unpkg.com/slim-select@latest/dist/slimselect.js"></script>
3333
<link href="https://unpkg.com/slim-select@latest/dist/slimselect.css" rel="stylesheet">
34+
<?= $this->Html->script('app.js', ['type' => 'module']) ?>
3435

3536
<?= $this->fetch('meta') ?>
3637
<?= $this->fetch('css') ?>
@@ -54,17 +55,5 @@
5455
<?= $this->Flash->render() ?>
5556
<?= $this->fetch('content') ?>
5657
</main>
57-
<script>
58-
const selects = document.querySelectorAll('select');
59-
selects.forEach((elem) => {
60-
let placeholder = elem.getAttribute('data-placeholder');
61-
new SlimSelect({
62-
select: elem,
63-
settings: {
64-
placeholderText: placeholder,
65-
}
66-
})
67-
});
68-
</script>
6958
</body>
7059
</html>

0 commit comments

Comments
 (0)