Skip to content

Commit 5e1efa3

Browse files
Features pages (#162)
* wip * fix partner icon * add groupby to composable * directory component update * features page updates * types * fixed groupnmae * add features to prerender * add thumbnail * add muted bg * left align feature pages * more tweaks * tweak color --------- Co-authored-by: Alexandre Chopin <[email protected]>
1 parent c785e27 commit 5e1efa3

File tree

12 files changed

+337
-27
lines changed

12 files changed

+337
-27
lines changed

components/Base/Button.vue

+10-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface BaseButtonProps {
1010
target?: '_blank' | '_self' | '_parent' | '_top';
1111
outline?: boolean;
1212
block?: boolean;
13+
disabled?: boolean;
1314
}
1415
1516
const props = withDefaults(defineProps<BaseButtonProps>(), {
@@ -60,6 +61,9 @@ const { theme } = useTheme();
6061
`color-${color}`,
6162
`theme-${theme}`,
6263
{ 'icon-only': isIconOnly, outline, 'size-block': block },
64+
{
65+
disabled,
66+
},
6367
]"
6468
v-bind="buttonProps"
6569
>
@@ -242,10 +246,15 @@ const { theme } = useTheme();
242246
}
243247
244248
.size-x-large.icon-only {
245-
padding: var(--space-2);
249+
padding: var(--space-3);
246250
}
247251
248252
.size-block {
249253
width: 100%;
250254
}
255+
256+
.disabled {
257+
pointer-events: none;
258+
opacity: 0.5;
259+
}
251260
</style>

components/Base/Icon.vue

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const filledIcons = [
6565
'monitoring',
6666
'online_prediction',
6767
'password',
68+
'partner_exchange',
6869
'post_add',
6970
'public',
7071
'published_with_changes',

components/Block/Directory.vue

+72-18
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const props = defineProps<BlockProps>();
1010
const { data: block } = await useAsyncData(props.uuid, () =>
1111
$directus.request(
1212
$readItem('block_directory', props.uuid, {
13-
fields: ['style', 'grid', 'collection', 'filter', 'title_size'],
13+
fields: ['style', 'grid', 'collection', 'filter', 'title_size', 'group_by'],
1414
}),
1515
),
1616
);
@@ -73,6 +73,19 @@ const dirConfig = computed(() => {
7373
href: (item: Project) => `/built-with-directus/${item.slug}`,
7474
},
7575
};
76+
} else if (context.collection === 'features') {
77+
return {
78+
searchFields: ['title', 'description'],
79+
facetFields: ['module'],
80+
fieldMapping: {
81+
title: 'title',
82+
image: 'thumbnail',
83+
description: 'description',
84+
href: (item: any) => `/features/${item.slug}`,
85+
module: 'module',
86+
},
87+
groupBy: 'module',
88+
};
7689
}
7790
});
7891
@@ -82,6 +95,7 @@ const { searchQuery, selectedFacets, facets, filteredItems, isFilterActive, clea
8295
searchFields: unref(dirConfig)?.searchFields ?? [],
8396
facetFields: unref(dirConfig)?.facetFields ?? [],
8497
fieldMapping: unref(dirConfig)?.fieldMapping ?? undefined,
98+
groupBy: unref(dirConfig)?.groupBy ?? undefined,
8599
});
86100
87101
// Mobile filter state
@@ -130,23 +144,52 @@ const toggleFilter = () => {
130144
</div>
131145
</aside>
132146
<main>
133-
<BaseCardGroup v-auto-animate :grid="block.grid">
134-
<BaseCard
135-
v-for="card in filteredItems"
136-
:key="card.title"
137-
:title="card.title"
138-
:image="card.image ?? undefined"
139-
:media-style="block.style"
140-
:description="card.description ?? undefined"
141-
:description-avatar="card.avatar ?? undefined"
142-
layout="vertical"
143-
:to="card.href"
144-
:badge="card.badge ?? undefined"
145-
:title-size="block.title_size"
146-
/>
147-
</BaseCardGroup>
148-
149-
<p v-if="filteredItems?.length === 0">No items were found. Try changing the search criteria.</p>
147+
<template v-if="Array.isArray(filteredItems)">
148+
<BaseCardGroup v-auto-animate :grid="block.grid">
149+
<BaseCard
150+
v-for="card in filteredItems"
151+
:key="card.title"
152+
:title="card.title"
153+
:image="card.image ?? undefined"
154+
:media-style="block.style"
155+
:description="card.description ?? undefined"
156+
:description-avatar="card.avatar ?? undefined"
157+
layout="vertical"
158+
:to="card.href"
159+
:badge="card.badge ?? undefined"
160+
:title-size="block.title_size"
161+
/>
162+
</BaseCardGroup>
163+
</template>
164+
<template v-else>
165+
<div v-for="(group, groupName) in filteredItems" :key="groupName" class="group-container">
166+
<h2 v-if="groupName" class="group-title">{{ formatTitle(groupName as string) }}</h2>
167+
<BaseCardGroup v-auto-animate :grid="block.grid">
168+
<BaseCard
169+
v-for="card in group"
170+
:key="card.title"
171+
:title="card.title"
172+
:image="card.image ?? undefined"
173+
:media-style="block.style"
174+
:description="card.description ?? undefined"
175+
:description-avatar="card.avatar ?? undefined"
176+
layout="vertical"
177+
:to="card.href"
178+
:badge="card.badge ?? undefined"
179+
:title-size="block.title_size"
180+
/>
181+
</BaseCardGroup>
182+
</div>
183+
</template>
184+
185+
<p
186+
v-if="
187+
(Array.isArray(filteredItems) && filteredItems.length === 0) ||
188+
(!Array.isArray(filteredItems) && Object.keys(filteredItems).length === 0)
189+
"
190+
>
191+
No items were found. Try changing the search criteria.
192+
</p>
150193
<!-- @TODO: Pagination -->
151194
</main>
152195
</div>
@@ -231,4 +274,15 @@ const toggleFilter = () => {
231274
justify-self: end;
232275
}
233276
}
277+
278+
.group-container {
279+
margin-bottom: var(--space-8);
280+
}
281+
282+
.group-title {
283+
font-size: var(--text-lg);
284+
font-weight: var(--weight-bold);
285+
margin-bottom: var(--space-4);
286+
border-bottom: 1px solid var(--gray-200);
287+
}
234288
</style>

components/PageSection.vue

+4
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ const { height: headerHeight } = useHeaderHeight();
209209
background-size: cover;
210210
}
211211
212+
.bg-colorful-muted {
213+
background: linear-gradient(to bottom, #fe97dc15, #745eff15);
214+
}
215+
212216
.bg-pristine-white + .bg-pristine-white-lines {
213217
border-block-start: 1px solid var(--gray-200);
214218
}

composables/useDirectory.ts

+22-6
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ interface UseDirectoryOptions {
1919
searchFields: string[];
2020
facetFields: string[];
2121
fieldMapping?: FieldMapping | undefined;
22+
groupBy?: string;
2223
}
2324

24-
export function useDirectory({ items, searchFields, facetFields, fieldMapping = {} }: UseDirectoryOptions) {
25+
export function useDirectory({ items, searchFields, facetFields, fieldMapping = {}, groupBy }: UseDirectoryOptions) {
2526
const route = useRoute();
2627
const router = useRouter();
27-
2828
const searchQuery = ref('');
2929
const selectedFacets = ref<Record<string, string[]>>(Object.fromEntries(facetFields.map((field) => [field, []])));
3030

@@ -52,7 +52,6 @@ export function useDirectory({ items, searchFields, facetFields, fieldMapping =
5252

5353
const readFromURL = () => {
5454
const { q, ...facetParams } = route.query;
55-
5655
searchQuery.value = (q as string) || '';
5756

5857
Object.keys(selectedFacets.value).forEach((field) => {
@@ -62,7 +61,6 @@ export function useDirectory({ items, searchFields, facetFields, fieldMapping =
6261
};
6362

6463
readFromURL();
65-
6664
watch([searchQuery, selectedFacets], updateURL, { deep: true });
6765

6866
const facets = computed(() => {
@@ -115,16 +113,34 @@ export function useDirectory({ items, searchFields, facetFields, fieldMapping =
115113
}, {} as DirectoryItem);
116114
};
117115

116+
const groupItems = (items: DirectoryItem[]) => {
117+
if (!groupBy) return items;
118+
119+
return items.reduce(
120+
(acc, item) => {
121+
const groupValue = item[groupBy];
122+
123+
if (!acc[groupValue]) {
124+
acc[groupValue] = [];
125+
}
126+
127+
acc[groupValue].push(item);
128+
return acc;
129+
},
130+
{} as Record<string, DirectoryItem[]>,
131+
);
132+
};
133+
118134
const filteredItems = computed(() => {
119135
let result = items;
120-
121136
result = filterItemsByFacets(result);
122137

123138
if (searchQuery.value) {
124139
result = fuse.search(searchQuery.value).map((res) => res.item);
125140
}
126141

127-
return result.map(applyFieldMapping);
142+
const mappedResult = result.map(applyFieldMapping);
143+
return groupBy ? groupItems(mappedResult) : mappedResult;
128144
});
129145

130146
const isFilterActive = computed(() => {

modules/prerender.ts

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export default defineNuxtModule({
5757

5858
const agencyPartners = await directus.request(readItems('agency_partners', { fields: ['slug'], limit: -1 }));
5959

60+
const features = await directus.request(readItems('features', { fields: ['slug'], limit: -1 }));
61+
6062
const shows = await directusTv.request(readItems('shows', { fields: ['slug'], limit: -1 }));
6163

6264
const episodes = await directusTv.request(
@@ -68,6 +70,7 @@ export default defineNuxtModule({
6870
permalinks.push(...team.map((member) => `/team/${member.slug}`));
6971
permalinks.push(...projects.map((project) => `/built-with-directus/${project.slug}`));
7072
permalinks.push(...agencyPartners.map((partner) => `/agency-directory/${partner.slug}`));
73+
permalinks.push(...features.map((feature) => `/features/${feature.slug}`));
7174
permalinks.push(...shows.map((show) => `/tv/${show.slug}`));
7275
permalinks.push(...episodes.map((ep) => `/tv/${ep.season.show.slug}/${ep.slug}`));
7376

0 commit comments

Comments
 (0)