Skip to content

Commit

Permalink
Sort selected tags first
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexGustafsson committed Jan 12, 2025
1 parent 7e43ebd commit e2bce19
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 47 deletions.
36 changes: 19 additions & 17 deletions web/components/ImageCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { JSX } from 'react'

import { NavLink, useNavigate } from 'react-router-dom'
import { TagsByName, sortTags } from '../tags'
import { TagsByName, compareTags } from '../tags'
import { formatRelativeTimeTo } from '../time'
import { Badge } from './Badge'
import { ImageLogo } from './ImageLogo'
Expand Down Expand Up @@ -82,22 +82,24 @@ export function ImageCard({
)}
<p className="text-sm mt-2">{description}</p>
<div className="flex flex-wrap gap-2 mt-4">
{tags.toSorted(sortTags).map((x) => (
<Badge
key={x}
label={x}
color={TagsByName[x]?.color}
className="hover:opacity-90"
// It's illegal to nest anchors in HTML, so unfortunately we need
// to use onClick here
onClick={(e) => {
e.metaKey || e.ctrlKey
? openTab(`/?tag=${encodeURIComponent(x)}`)
: navigate(`/?tag=${encodeURIComponent(x)}`)
e.preventDefault()
}}
/>
))}
{tags
.toSorted((a, b) => compareTags(a, b))
.map((x) => (
<Badge
key={x}
label={x}
color={TagsByName[x]?.color}
className="hover:opacity-90"
// It's illegal to nest anchors in HTML, so unfortunately we need
// to use onClick here
onClick={(e) => {
e.metaKey || e.ctrlKey
? openTab(`/?tag=${encodeURIComponent(x)}`)
: navigate(`/?tag=${encodeURIComponent(x)}`)
e.preventDefault()
}}
/>
))}
</div>
</div>
</div>
Expand Down
67 changes: 39 additions & 28 deletions web/components/TagSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type JSX, type PropsWithChildren, useRef, useState } from 'react'

import { type Tag, sortTags } from '../tags'
import { type Tag, compareTags } from '../tags'
import { Badge } from './Badge'

const IOS = [
Expand Down Expand Up @@ -39,11 +39,13 @@ export function TagSelect({
)
}
>
{tags.toSorted(sortTags).map((x) => (
<option key={x.name} value={x.name}>
{x.name}
</option>
))}
{tags
.toSorted((a, b) => compareTags(a.name, b.name))
.map((x) => (
<option key={x.name} value={x.name}>
{x.name}
</option>
))}
</select>
<svg
role="img"
Expand Down Expand Up @@ -97,28 +99,37 @@ export function TagSelect({
{isOpen && (
<div className="absolute group-hover:visible -top-4 -left-4 p-2 z-50 text-black dark:text-[#dddddd]">
<div className="flex max-h-64 overflow-y-auto flex-col gap-y-2 py-2 px-3 pr-6 bg-white dark:bg-[#292929] border-solid border-[1px] border-[#d0d0d0]/95 dark:border-[#505050] rounded-lg w-max shadow">
{tags.toSorted(sortTags).map((x) => (
<label key={x.name} className="cursor-pointer">
<input
type="checkbox"
checked={filter.includes(x.name)}
onChange={(e) =>
onChange((current) =>
e.target.checked
? [...current, x.name]
: current.filter((y) => y !== x.name)
)
}
className="scale-125 cursor-pointer"
/>
<Badge
title={x.description}
label={x.name}
color={x.color}
className="ml-2"
/>
</label>
))}
{tags
.toSorted((a, b) =>
compareTags(
a.name,
b.name,
filter.includes(a.name),
filter.includes(b.name)
)
)
.map((x) => (
<label key={x.name} className="cursor-pointer">
<input
type="checkbox"
checked={filter.includes(x.name)}
onChange={(e) =>
onChange((current) =>
e.target.checked
? [...current, x.name]
: current.filter((y) => y !== x.name)
)
}
className="scale-125 cursor-pointer"
/>
<Badge
title={x.description}
label={x.name}
color={x.color}
className="ml-2"
/>
</label>
))}
</div>
</div>
)}
Expand Down
16 changes: 14 additions & 2 deletions web/tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,20 @@ export const TagsByName: Record<string, Tag> = Object.fromEntries(
Tags.map((x) => [x.name, x])
)

/** Sort tags lexically, putting prefixed tags last. */
export function sortTags(a: Tag | string, b: Tag | string): number {
/** Sort tags lexically, putting prefixed tags last, selected tags first. */
export function compareTags(
a: string,
b: string,
aSelected?: boolean,
bSelected?: boolean
): number {
// Prioritize selected tags
if (aSelected === true && bSelected === false) {
return -1
} else if (aSelected === false && bSelected === true) {
return 1
}

const aString = typeof a === 'string' ? a : a.name

Check failure on line 140 in web/tags.ts

View workflow job for this annotation

GitHub Actions / Build web

Property 'name' does not exist on type 'never'.
const bString = typeof b === 'string' ? b : b.name

Check failure on line 141 in web/tags.ts

View workflow job for this annotation

GitHub Actions / Build web

Property 'name' does not exist on type 'never'.

Expand Down

0 comments on commit e2bce19

Please sign in to comment.