feat(note_icon): use grid virtualization for listing high performance

This commit is contained in:
Elian Doran 2025-12-28 19:45:30 +02:00
parent cbb7b4ffea
commit 8f21c0b34a
No known key found for this signature in database
2 changed files with 45 additions and 20 deletions

View File

@ -3,9 +3,10 @@ import "./note_icon.css";
import { IconRegistry } from "@triliumnext/commons";
import { Dropdown as BootstrapDropdown } from "bootstrap";
import clsx from "clsx";
import { t, use } from "i18next";
import { RefObject } from "preact";
import { t } from "i18next";
import { CSSProperties, RefObject } from "preact";
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
import { CellComponentProps, Grid } from "react-window";
import FNote from "../entities/fnote";
import attributes from "../services/attributes";
@ -132,13 +133,16 @@ function NoteIconList({ note, dropdownRef }: {
}}
>
{filteredIcons.length ? (
(filteredIcons ?? []).map(({ id, terms, iconPack }) => (
<span
key={id}
class={clsx(id, "tn-icon")}
title={t("note_icon.icon_tooltip", { name: terms?.[0] ?? id, iconPack })}
/>
))
<Grid
columnCount={12}
columnWidth={48}
rowCount={Math.ceil(filteredIcons.length / 12)}
rowHeight={48}
cellComponent={IconItemCell}
cellProps={{
filteredIcons
}}
/>
) : (
<div class="no-results">{t("note_icon.no_results")}</div>
)}
@ -147,6 +151,22 @@ function NoteIconList({ note, dropdownRef }: {
);
}
function IconItemCell({ rowIndex, columnIndex, style, filteredIcons }: CellComponentProps<{
filteredIcons: IconWithName[];
}>): React.JSX.Element {
const iconIndex = rowIndex * 12 + columnIndex;
const iconData = filteredIcons[iconIndex];
const { id, terms, iconPack } = iconData;
return (
<span
key={id}
class={clsx(id, "tn-icon")}
title={t("note_icon.icon_tooltip", { name: terms?.[0] ?? id, iconPack })}
style={style as CSSProperties}
/>
);
}
function IconFilterContent({ filterByPrefix, setFilterByPrefix }: {
filterByPrefix: string | null;
setFilterByPrefix: (value: string | null) => void;
@ -212,7 +232,7 @@ function useFilteredIcons(allIcons: IconWithName[] | undefined, search: string |
if (processedSearch || filterByPrefix !== null) {
icons = icons.filter((icon) => {
if (filterByPrefix) {
if (!icon.id?.startsWith(`${filterByPrefix}-`)) {
if (!icon.id?.startsWith(`${filterByPrefix} `)) {
return false;
}
}

25
pnpm-lock.yaml generated
View File

@ -292,6 +292,9 @@ importers:
react-i18next:
specifier: 16.5.0
version: 16.5.0(i18next@25.7.3(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)
react-window:
specifier: 2.2.3
version: 2.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
reveal.js:
specifier: 5.2.1
version: 5.2.1
@ -483,11 +486,7 @@ importers:
specifier: 11.3.3
version: 11.3.3
apps/icon-pack-builder:
devDependencies:
'@phosphor-icons/web':
specifier: 2.1.2
version: 2.1.2
apps/icon-pack-builder: {}
apps/server:
dependencies:
@ -4035,9 +4034,6 @@ packages:
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
engines: {node: '>= 10.0.0'}
'@phosphor-icons/web@2.1.2':
resolution: {integrity: sha512-rPAR9o/bEcp4Cw4DEeZHXf+nlGCMNGkNDRizYHM47NLxz9vvEHp/Tt6FMK1NcWadzw/pFDPnRBGi/ofRya958A==}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@ -12010,6 +12006,12 @@ packages:
peerDependencies:
react: ^18.0.0 || ^19.0.0
react-window@2.2.3:
resolution: {integrity: sha512-gTRqQYC8ojbiXyd9duYFiSn2TJw0ROXCgYjenOvNKITWzK0m0eCvkUsEUM08xvydkMh7ncp+LE0uS3DeNGZxnQ==}
peerDependencies:
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
react@16.14.0:
resolution: {integrity: sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==}
engines: {node: '>=0.10.0'}
@ -18460,8 +18462,6 @@ snapshots:
'@parcel/watcher-win32-x64': 2.5.1
optional: true
'@phosphor-icons/web@2.1.2': {}
'@pkgjs/parseargs@0.11.0':
optional: true
@ -28213,6 +28213,11 @@ snapshots:
prop-types: 15.8.1
react: 19.2.3
react-window@2.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
react@16.14.0:
dependencies:
loose-envify: 1.4.0