mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
feat(processing-folders): file badges lead with the category icon
The badge showed only label-level icons, while the sidebar speaks in categories — so a card tagged manual + user-guide gave no visual cue that it lives under Operations. The file's categories (the families its labels roll up into) now lead the badge, each wearing its family icon in the same cycled accent as its sidebar group, with the labels' icons following. The hover names the group first and the specifics after: 'Operations — Manual, User guide'.
This commit is contained in:
@@ -3936,6 +3936,7 @@ backToMyFiles = "Back to My Files"
|
||||
breadcrumbs = "Folder path"
|
||||
cancel = "Cancel"
|
||||
categoryHint = "Categories: {{labels}}"
|
||||
categoryHintGrouped = "{{families}} — {{labels}}"
|
||||
classification = "Classification"
|
||||
clearSelection = "Clear selection"
|
||||
closeDetails = "Close details"
|
||||
|
||||
@@ -2,29 +2,41 @@ import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tooltip } from "@mantine/core";
|
||||
import { LocalIcon } from "@app/components/shared/LocalIcon";
|
||||
import { useLabelBadges } from "@app/components/shared/fileSidebarGrouping";
|
||||
import {
|
||||
useFamilyBadges,
|
||||
useLabelBadges,
|
||||
} from "@app/components/shared/fileSidebarGrouping";
|
||||
|
||||
/** At most this many label icons on a card; the hover names every label. */
|
||||
/** At most this many icons on a card; the hover names everything. */
|
||||
const MAX_ICONS = 3;
|
||||
|
||||
/**
|
||||
* A classified file's categories, worn as the classification vocabulary's own
|
||||
* icons in the sidebar's own category accents — no text, names on hover.
|
||||
* Renders nothing for an unclassified file (or in builds without
|
||||
* classification): absence of the badge IS the "no category" state.
|
||||
* Category (family) icons lead, the labels follow, so the badge names the
|
||||
* sidebar group first and the specifics after. Renders nothing for an
|
||||
* unclassified file (or in builds without classification): absence of the
|
||||
* badge IS the "no category" state.
|
||||
*/
|
||||
export function FileCategoryBadge({ labels }: { labels?: string[] | null }) {
|
||||
const { t } = useTranslation();
|
||||
const badges = useLabelBadges(labels);
|
||||
const families = useFamilyBadges(labels);
|
||||
const labelBadges = useLabelBadges(labels);
|
||||
const badges = [...families, ...labelBadges];
|
||||
if (badges.length === 0) return null;
|
||||
const hover =
|
||||
families.length > 0
|
||||
? t("filesPage.categoryHintGrouped", {
|
||||
families: families.map((badge) => badge.name).join(", "),
|
||||
labels: labelBadges.map((badge) => badge.name).join(", "),
|
||||
defaultValue: "{{families}} — {{labels}}",
|
||||
})
|
||||
: t("filesPage.categoryHint", {
|
||||
labels: labelBadges.map((badge) => badge.name).join(", "),
|
||||
defaultValue: "Categories: {{labels}}",
|
||||
});
|
||||
return (
|
||||
<Tooltip
|
||||
label={t("filesPage.categoryHint", {
|
||||
labels: badges.map((badge) => badge.name).join(", "),
|
||||
defaultValue: "Categories: {{labels}}",
|
||||
})}
|
||||
withinPortal
|
||||
>
|
||||
<Tooltip label={hover} withinPortal>
|
||||
<span
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
|
||||
@@ -43,6 +43,14 @@ export function useLabelBadges(_labels?: string[] | null): LabelBadge[] {
|
||||
return NO_BADGES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Badge descriptors for the categories (label families) a file's labels roll
|
||||
* up into — the same identities the sidebar groups by. Core has none.
|
||||
*/
|
||||
export function useFamilyBadges(_labels?: string[] | null): LabelBadge[] {
|
||||
return NO_BADGES;
|
||||
}
|
||||
|
||||
/** One category (label family) as a files-page filter offers it. */
|
||||
export interface CategoryFilterOption {
|
||||
id: string;
|
||||
|
||||
@@ -145,6 +145,39 @@ export function useCategoryFilterOptions(): CategoryFilterOption[] {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Badge descriptors for the categories a file's labels roll up into: each
|
||||
* visible family's own icon, wearing the same cycled accent its sidebar group
|
||||
* does. Deduped and in sidebar display order; labels only under hidden
|
||||
* categories contribute nothing (their files read as "Other").
|
||||
*/
|
||||
export function useFamilyBadges(labels?: string[] | null): LabelBadge[] {
|
||||
const { t } = useTranslation();
|
||||
const categories = useSyncExternalStore(
|
||||
subscribeSidebarCategories,
|
||||
getSidebarCategories,
|
||||
);
|
||||
return useMemo(() => {
|
||||
if (!labels || labels.length === 0) return [];
|
||||
const carried = new Set(labels);
|
||||
return categories
|
||||
.filter((category) => !category.hidden)
|
||||
.sort((a, b) =>
|
||||
a.name.localeCompare(b.name, undefined, { sensitivity: "base" }),
|
||||
)
|
||||
.map((category, index) => ({ category, index }))
|
||||
.filter(({ category }) =>
|
||||
category.labelKeys.some((key) => carried.has(key)),
|
||||
)
|
||||
.map(({ category, index }) => ({
|
||||
id: category.id,
|
||||
name: category.name,
|
||||
icon: category.icon,
|
||||
color: accentCycleColor(index),
|
||||
}));
|
||||
}, [labels, categories, t]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Badge descriptors for a file's labels: each label's own icon from the
|
||||
* classification vocabulary, coloured with the accent its category cycles to
|
||||
|
||||
Reference in New Issue
Block a user