Skip to content

Commit b452419

Browse files
committed
feat(ktl-2781): implement filtering for cases
1 parent 7245477 commit b452419

15 files changed

+341
-215
lines changed

blocks/case-studies/card/case-studies-card.module.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
background: #fff;
88
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
99
border: 1px solid rgba(0,0,0,0.12);
10-
max-width: 500px;
1110
box-sizing: border-box;
1211
}
1312

blocks/case-studies/card/case-studies-card.tsx

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import React from 'react';
22
import cn from 'classnames';
33
import styles from './case-studies-card.module.css';
44
import { AndroidIcon, AppleIcon, ServerIcon, ComputerIcon, GlobusIcon } from '@rescui/icons';
5-
import { CaseStudyItem, CaseStudyType, isExternalCaseStudy, Platform } from '../case-studies';
6-
5+
import { CaseItem, CaseType, isExternalCase, CasePlatform } from '../case-studies';
76

87
/**
98
* Resolve asset path from YAML:
@@ -38,30 +37,30 @@ const mdToHtml = (md: string) => {
3837
return withLinks;
3938
};
4039

41-
const badgeText: Record<CaseStudyType, string> = {
40+
const badgeText: Record<CaseType, string> = {
4241
'multiplatform': 'Kotlin Multiplatform',
4342
'server-side': 'Server-side'
4443
};
4544

46-
const badgeClass: Record<CaseStudyType, string> = {
45+
const badgeClass: Record<CaseType, string> = {
4746
'multiplatform': 'badgeMultiplatform',
4847
'server-side': 'badgeServerSide'
4948
};
5049

5150
// Platform icon path builder. If you keep icons in (for example) /images/platforms/*.svg,
5251
// they’ll be resolved automatically by key. If an icon is missing, we still render the label.
53-
const getPlatformIcon = (p: Platform) => {
52+
const getPlatformIcon = (p: CasePlatform) => {
5453
switch (p) {
5554
case 'android':
56-
return <AndroidIcon/>;
55+
return <AndroidIcon />;
5756
case 'ios':
58-
return <AppleIcon/>;
57+
return <AppleIcon />;
5958
case 'desktop':
60-
return <ComputerIcon/>;
59+
return <ComputerIcon />;
6160
case 'frontend':
62-
return <GlobusIcon/>;
61+
return <GlobusIcon />;
6362
case 'backend':
64-
return <ServerIcon/>;
63+
return <ServerIcon />;
6564
case 'compose-multiplatform':
6665
return <img className={styles.platformIcon} src={'/images/platforms/compose-multiplatform.svg'}
6766
alt="Compose Multiplatform icon"
@@ -71,12 +70,11 @@ const getPlatformIcon = (p: Platform) => {
7170
}
7271
};
7372

74-
type Props = {
75-
item: CaseStudyItem;
73+
type Props = CaseItem &{
7674
className?: string;
7775
};
7876

79-
export const CaseStudyCard: React.FC<Props> = ({ item, className }) => {
77+
export const CaseStudyCard: React.FC<Props> = ({ className, ...item }) => {
8078
const logos = item.logo ?? [];
8179
const logoSrc1 = resolveAssetPath(logos[0]);
8280
const logoSrc2 = resolveAssetPath(logos[1]);
@@ -159,7 +157,7 @@ export const CaseStudyCard: React.FC<Props> = ({ item, className }) => {
159157
})}
160158
</div>
161159
)}
162-
{(isExternalCaseStudy(item)) ? (
160+
{(isExternalCase(item)) ? (
163161
<div className={styles.actions}>
164162
{item.externalLink && (
165163
<a
@@ -218,14 +216,10 @@ function hideBrokenIcon(img: HTMLImageElement) {
218216
/**
219217
* Humanize platform name for label.
220218
*/
221-
function humanizePlatform(p: Platform): string {
219+
function humanizePlatform(p: CasePlatform): string {
222220
switch (p) {
223221
case 'compose-multiplatform':
224222
return 'Compose Multiplatform';
225-
case 'frontend':
226-
return 'Frontend';
227-
case 'backend':
228-
return 'Backend';
229223
case 'ios':
230224
return 'iOS';
231225
default:
Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
1-
export type CaseStudyType = 'multiplatform' | 'server-side';
1+
export type CaseType = 'multiplatform' | 'server-side';
22

3-
type CaseStudyDestination = 'internal' | 'external';
3+
export type CaseTypeSwitch = 'all' | CaseType;
44

5-
export type Platform =
6-
| 'android'
7-
| 'ios'
8-
| 'desktop'
9-
| 'frontend'
10-
| 'backend'
11-
| 'compose-multiplatform';
5+
type CaseDestination = 'internal' | 'external';
6+
7+
export const Platforms = [
8+
'android',
9+
'ios',
10+
'desktop',
11+
'frontend',
12+
'backend',
13+
] as const;
14+
15+
export const PlatformNames: Record<typeof Platforms[number], string> = {
16+
"android": "Android",
17+
"ios": "iOS",
18+
"desktop": "Desktop",
19+
"frontend": "Frontend",
20+
"backend": "Backend",
21+
}
22+
23+
export type CasePlatform = typeof Platforms[number] | 'compose-multiplatform';
1224

1325
type Signature = {
1426
line1: string;
@@ -27,36 +39,36 @@ type ImageMedia = {
2739

2840
type Media = YoutubeMedia | ImageMedia;
2941

30-
interface CaseStudyItemBase {
42+
interface CaseItemBase {
3143
id: string;
32-
type: CaseStudyType;
44+
type: CaseType;
3345
description: string;
34-
destination: CaseStudyDestination;
46+
destination: CaseDestination;
3547
logo?: string[];
3648
signature?: Signature;
37-
platforms?: Platform[];
49+
platforms?: CasePlatform[];
3850
media?: Media;
3951
featuredOnMainPage?: boolean;
4052
slug?: string;
4153
externalLinkText?: string;
4254
}
4355

44-
export interface ExternalDestinationCaseStudyItem extends CaseStudyItemBase {
56+
export interface ExternalDestinationCaseItem extends CaseItemBase {
4557
destination: 'external';
4658
// required when destination === 'external'
4759
externalLink: string;
4860
}
4961

50-
export interface InternalDestinationCaseStudyItem extends CaseStudyItemBase {
62+
export interface InternalDestinationCaseItem extends CaseItemBase {
5163
destination: 'internal';
5264
// required when destination === 'internal'
5365
pageContentPath: string;
5466
}
5567

56-
export type CaseStudyItem =
57-
| ExternalDestinationCaseStudyItem
58-
| InternalDestinationCaseStudyItem;
68+
export type CaseItem =
69+
| ExternalDestinationCaseItem
70+
| InternalDestinationCaseItem;
5971

60-
export function isExternalCaseStudy(item: CaseStudyItem): item is ExternalDestinationCaseStudyItem {
72+
export function isExternalCase(item: CaseItem): item is ExternalDestinationCaseItem {
6173
return item.destination === 'external';
6274
}
Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,42 @@
1-
.wrapper {
2-
padding: 32px 0;
3-
color: #ffffff; /* ensure white text */
4-
}
5-
6-
.title {
7-
margin: 0 0 16px 0;
8-
color: inherit;
9-
}
10-
11-
.inner {
1+
.content {
2+
padding: 24px 0 72px;
123
display: grid;
13-
grid-template-columns: 1fr;
14-
gap: 24px;
4+
grid-template-columns: 1fr 1fr;
5+
grid-column-gap: 80px;
6+
grid-template-areas:
7+
"group1 group1"
8+
"group2 group3";
9+
@media (width <= 1000px) {
10+
display: block;
11+
grid-template-columns: unset;
12+
grid-template-areas: unset;
13+
}
1514
}
1615

17-
.group {
18-
background: #0f0f0f;
19-
border-radius: 12px;
20-
padding: 16px;
16+
.group:first-child {
17+
grid-area: group1;
2118
}
2219

23-
.groupType {
24-
/* will span both columns on desktop */
20+
.group:not(:last-child) {
21+
margin-bottom: 32px;
2522
}
2623

2724
.groupTitle {
28-
margin: 0 0 12px 0;
29-
color: inherit;
25+
margin: 0 0 8px;
3026
}
3127

3228
.checkboxes {
3329
display: flex;
34-
flex-wrap: wrap;
35-
gap: 8px 16px;
3630
align-items: center;
31+
gap: 8px 24px;
3732
}
3833

39-
.switcherSmall {
40-
}
34+
.checkbox {
35+
display: flex;
36+
align-items: center;
4137

42-
@media (min-width: 768px) {
43-
.inner {
44-
grid-template-columns: repeat(2, 1fr);
45-
}
46-
.groupType {
47-
grid-column: 1 / -1;
38+
& span + span {
39+
display: flex;
40+
gap: 8px;
4841
}
4942
}

0 commit comments

Comments
 (0)