forked from mastodon/joinmastodon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAppsGrid.tsx
106 lines (101 loc) · 4.05 KB
/
AppsGrid.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { FormattedMessage, useIntl } from "react-intl"
import { AppCard } from "../components/AppCard"
import classNames from "classnames"
import { useState } from "react"
import SelectMenu from "../components/SelectMenu"
import { sortBy as _sortBy } from "lodash"
import type { appsList } from "../data/apps"
export type AppsGridProps = {
apps: appsList
}
/** Renders AppCards as a grid, with sorting and filtering options */
export const AppsGrid = ({ apps }: AppsGridProps) => {
const intl = useIntl()
const [activeCategory, setActiveCategory] = useState("all")
/** normalizing the apps dictionary as an array */
const allApps = Object.entries(apps)
.map(([category, apps]) =>
apps.map(({ name, icon, url, paid, released_on }) => ({
name,
icon,
url,
category,
paid: paid ?? false,
released_on: new Date(released_on) ?? null,
}))
)
.flat()
//prettier-ignore
const sortOptions = [
{ value: "date_added", label: intl.formatMessage({ id: "sorting.recently_added", defaultMessage: "Recently Added" }) },
{ value: "paid", label: intl.formatMessage({ id: "sorting.free", defaultMessage: "Free" }) },
{ value: "name", label: intl.formatMessage({ id: "sorting.name", defaultMessage: "Alphabetical" }) },
]
const [sortOption, setSortOption] = useState(sortOptions[0].value)
const filteredApps = allApps.filter(
({ category }) => category === activeCategory || activeCategory === "all"
)
const sortedAndFilteredApps = _sortBy(filteredApps, sortOption)
//prettier-ignore
const categories = [
{ key: "all", label: intl.formatMessage({ id: "browse_apps.all", defaultMessage: "All" }) },
{ key: "android", label: intl.formatMessage({ id: "browse_apps.android", defaultMessage: "Android" }) },
{ key: "ios", label: intl.formatMessage({ id: "browse_apps.ios", defaultMessage: "iOS" }) },
{ key: "web", label: intl.formatMessage({ id: "browse_apps.web", defaultMessage: "Web" }) },
{ key: "sailfish", label: intl.formatMessage({ id: "browse_apps.sailfish", defaultMessage: "SailfishOS" }) },
{ key: "desktop", label: intl.formatMessage({ id: "browse_apps.desktop", defaultMessage: "Desktop" }) },
]
return (
<div>
<div>
<h2 className="h4 mb-8">
<FormattedMessage
id="browse_apps.title2"
defaultMessage="Browse third-party apps"
/>
</h2>
<div className="-mx-gutter pis-gutter mb-6 overflow-x-auto">
<div className="flex flex-wrap gap-gutter md:flex-nowrap">
{categories.map((category) => (
<label
key={category.key}
className={classNames(
"b3 block cursor-pointer whitespace-nowrap rounded-md border-2 p-4 text-center !font-semibold transition-all md:w-full",
category.key === activeCategory
? "border-blurple-500 bg-blurple-500 text-white hover:border-blurple-600 hover:bg-blurple-600 focus-visible-within:border-blurple-600 focus-visible-within:bg-blurple-600"
: "border-blurple-500 bg-white text-blurple-500 hover:border-blurple-600 hover:text-blurple-600"
)}
>
<input
className="sr-only"
type="radio"
name="apps-selection"
id=""
value={category.key}
onChange={(e) => setActiveCategory(e.target.value)}
/>
{category.label}
</label>
))}
</div>
</div>
</div>
<div className="my-8">
<SelectMenu
label={
<FormattedMessage id="sorting.sort_by" defaultMessage="Sort" />
}
value={sortOption}
onChange={(v) => {
setSortOption(v)
}}
options={sortOptions}
/>
</div>
<div className="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-4">
{sortedAndFilteredApps.map(AppCard)}
</div>
</div>
)
}
export default AppsGrid