From db4af960407d95594f714e02814967aa323e8416 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 26 Dec 2025 20:42:19 +0200 Subject: [PATCH] feat(note_icon): filter by icon pack --- .../src/translations/en/translation.json | 3 +- apps/client/src/widgets/note_icon.css | 11 ++---- apps/client/src/widgets/note_icon.tsx | 37 ++++++++++++++++++- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 07b1b8e12..921c7bdb6 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -766,7 +766,8 @@ "note_icon": { "change_note_icon": "Change note icon", "search": "Search:", - "reset-default": "Reset to default icon" + "reset-default": "Reset to default icon", + "filter-none": "All icons" }, "basic_properties": { "note_type": "Note type", diff --git a/apps/client/src/widgets/note_icon.css b/apps/client/src/widgets/note_icon.css index e715d5699..8a739626d 100644 --- a/apps/client/src/widgets/note_icon.css +++ b/apps/client/src/widgets/note_icon.css @@ -32,17 +32,14 @@ div.note-icon-widget { } .note-icon-widget .filter-row { - padding-top: 10px; - padding-bottom: 10px; - padding-inline-end: 20px; + padding: 10px; display: flex; - align-items: baseline; + align-items: center; + gap: 1em; } .note-icon-widget .filter-row span { display: block; - padding-inline-start: 15px; - padding-inline-end: 15px; font-weight: bold; } @@ -111,4 +108,4 @@ body.experimental-feature-new-layout { transition: background 200ms ease-out; } } -} \ No newline at end of file +} diff --git a/apps/client/src/widgets/note_icon.tsx b/apps/client/src/widgets/note_icon.tsx index f26ce5369..65c6f1ebc 100644 --- a/apps/client/src/widgets/note_icon.tsx +++ b/apps/client/src/widgets/note_icon.tsx @@ -7,10 +7,13 @@ import FNote from "../entities/fnote"; import attributes from "../services/attributes"; import server from "../services/server"; import type { Icon } from "./icon_list"; +import ActionButton from "./react/ActionButton"; import Button from "./react/Button"; import Dropdown from "./react/Dropdown"; +import { FormDropdownDivider, FormListItem } from "./react/FormList"; import FormTextBox from "./react/FormTextBox"; import { useNoteContext, useNoteLabel } from "./react/hooks"; +import Icon from "./react/Icon"; interface IconToCountCache { iconClassToCountMap: Record; @@ -44,6 +47,7 @@ export default function NoteIcon() { buttonClassName={`note-icon tn-focusable-button ${icon ?? "bx bx-empty"}`} hideToggleArrow disabled={viewScope?.viewMode !== "default"} + dropdownOptions={{ autoClose: "outside" }} > { note && } @@ -54,6 +58,7 @@ function NoteIconList({ note }: { note: FNote }) { const searchBoxRef = useRef(null); const [ search, setSearch ] = useState(); const [ iconData, setIconData ] = useState(); + const [ filterByPrefix, setFilterByIconPack ] = useState(null); useEffect(() => { async function loadIcons() { @@ -71,8 +76,14 @@ function NoteIconList({ note }: { note: FNote }) { }))).flat() ]; const processedSearch = search?.trim()?.toLowerCase(); - if (processedSearch) { + if (processedSearch || filterByPrefix !== null) { icons = icons.filter((icon) => { + if (filterByPrefix) { + if (!icon.className?.startsWith(`${filterByPrefix} `)) { + return false; + } + } + if (processedSearch) { if (!icon.name.includes(processedSearch) && !icon.term?.find((t) => t.includes(processedSearch))) { @@ -102,7 +113,7 @@ function NoteIconList({ note }: { note: FNote }) { } loadIcons(); - }, [ search ]); + }, [ search, filterByPrefix ]); return ( <> @@ -115,6 +126,28 @@ function NoteIconList({ note }: { note: FNote }) { currentValue={search} onChange={setSearch} autoFocus /> + + + setFilterByIconPack(null)} + >{t("note_icon.filter-none")} + + + {glob.iconRegistry.sources.map(({ prefix, name }) => ( + setFilterByIconPack(prefix)} + checked={filterByPrefix === prefix} + >{name} + ))} +