From 37991e220e33bfb86e14084c7772e1524cdaef9c Mon Sep 17 00:00:00 2001 From: Bogdan Abaev Date: Fri, 5 Jul 2024 16:12:41 -0700 Subject: [PATCH] use Icons.getCSSIcon instead of getDOMElement (#4338) - getDOMElement relied on React.renderToStaticMarkup, which is react 18 was moved to a different file than the one exposed with react-dom-server. To not add another file just for that one function, replace getDOMElement with getCSSIcon. - getDOMElement was mainly used for a few remaining png icons that were not replaced with svg. For those few icons, just record which background-url should be set when the module loads and add it in getCSSIcon if applicable. Alternatively, background-image setting could be moved into a stylesheet? - a few hardcoded twisty svgs in icons.jsx are not used anywhere (they would be fetched via IconTwisty), so those are removed --- chrome/content/zotero/components/icons.jsx | 95 ++----------------- .../zotero/components/progressQueueTable.jsx | 8 +- .../zotero/components/virtualized-table.jsx | 4 +- chrome/content/zotero/itemTree.jsx | 6 +- .../zotero/preferences/preferences_sync.jsx | 1 - 5 files changed, 17 insertions(+), 97 deletions(-) diff --git a/chrome/content/zotero/components/icons.jsx b/chrome/content/zotero/components/icons.jsx index 2e52995bd5..2e24bdb1b5 100644 --- a/chrome/content/zotero/components/icons.jsx +++ b/chrome/content/zotero/components/icons.jsx @@ -1,8 +1,6 @@ 'use strict'; const React = require('react'); -const { renderToStaticMarkup } = require('react-dom-server'); -const { PureComponent } = React; const { element, string, object } = require('prop-types'); const Icon = (props) => { @@ -51,6 +49,8 @@ CSSItemTypeIcon.propTypes = { module.exports = { Icon, CSSIcon, CSSItemTypeIcon }; +// Icons cache for a few remaining png icons till they are replaced +let legacyIconsCache = {}; function i(name, svgOrSrc, hasHiDPI = true) { if (typeof svgOrSrc == 'string' && hasHiDPI && window.devicePixelRatio >= 1.25) { @@ -59,99 +59,15 @@ function i(name, svgOrSrc, hasHiDPI = true) { parts[parts.length - 2] = parts[parts.length - 2] + '@2x'; svgOrSrc = parts.join('.'); } - - const icon = class extends PureComponent { - render() { - let props = Object.assign({}, this.props); - props.name = name.toLowerCase(); - - if (typeof svgOrSrc == 'string') { - if (!("style" in props)) props.style = {}; - props.style.backgroundImage = `url(${svgOrSrc})`; - props.className = props.className || ""; - props.className += " icon-bg"; - // We use css background-image. - // This is a performance optimization for fast-scrolling trees. - // If we use img elements they are slow to render - // and produce pop-in when fast-scrolling. - return ( - - ); - } - - return ( - {svgOrSrc} - ) - } - } - - icon.propTypes = { - className: string - } - - icon.displayName = `Icon${name}` - - module.exports[icon.displayName] = icon + legacyIconsCache[`Icon${name}`] = svgOrSrc; } /* eslint-disable max-len */ - -i('Twisty', ( - /* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - - - -)); i('Cross', "chrome://zotero/skin/cross.png"); i('Tick', "chrome://zotero/skin/tick.png"); i('ArrowRefresh', "chrome://zotero/skin/arrow_refresh.png"); -if (Zotero.isMac) { - i('Twisty', ( - /* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - - - - )); -} - -if (Zotero.isWin) { - i('Twisty', ( - /* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - - - - )); -} - -let domElementCache = {}; - -/** - * Returns a DOM element for the icon class - * - * To be used in itemTree where rendering is done without react - * for performance reasons - * @param {String} icon - * @returns {Element} - */ -module.exports.getDOMElement = function (icon) { - if (domElementCache[icon]) return domElementCache[icon].cloneNode(true); - if (!module.exports[icon]) { - Zotero.debug(`Attempting to get non-existant icon ${icon}`); - return ""; - } - let div = document.createElement('div'); - div.innerHTML = renderToStaticMarkup(React.createElement(module.exports[icon])); - domElementCache[icon] = div.firstChild; - return domElementCache[icon].cloneNode(true); -}; let cssIconsCache = new Map(); @@ -161,6 +77,11 @@ module.exports.getCSSIcon = function (key) { iconEl.classList.add('icon'); iconEl.classList.add('icon-css'); iconEl.classList.add(`icon-${key}`); + // Temporarily set background image for a few remaining png icons + if (legacyIconsCache[key]) { + iconEl.style.backgroundImage = `url(${legacyIconsCache[key]})`; + iconEl.classList.add("icon-bg"); + } cssIconsCache.set(key, iconEl); } diff --git a/chrome/content/zotero/components/progressQueueTable.jsx b/chrome/content/zotero/components/progressQueueTable.jsx index dfa6c66ef2..44d1de72db 100644 --- a/chrome/content/zotero/components/progressQueueTable.jsx +++ b/chrome/content/zotero/components/progressQueueTable.jsx @@ -24,7 +24,7 @@ */ import React, { memo, useCallback, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; -import { getDOMElement } from 'components/icons'; +import { getCSSIcon } from 'components/icons'; import VirtualizedTable, { renderCell } from 'components/virtualized-table'; import { nextHTMLID, noop } from './utils'; @@ -32,13 +32,13 @@ import { nextHTMLID, noop } from './utils'; function getImageByStatus(status) { if (status === Zotero.ProgressQueue.ROW_PROCESSING) { - return getDOMElement('IconArrowRefresh'); + return getCSSIcon('IconArrowRefresh'); } else if (status === Zotero.ProgressQueue.ROW_FAILED) { - return getDOMElement('IconCross'); + return getCSSIcon('IconCross'); } else if (status === Zotero.ProgressQueue.ROW_SUCCEEDED) { - return getDOMElement('IconTick'); + return getCSSIcon('IconTick'); } return document.createElement('span'); } diff --git a/chrome/content/zotero/components/virtualized-table.jsx b/chrome/content/zotero/components/virtualized-table.jsx index a1f59c8324..c7d7476ae6 100644 --- a/chrome/content/zotero/components/virtualized-table.jsx +++ b/chrome/content/zotero/components/virtualized-table.jsx @@ -30,7 +30,7 @@ const PropTypes = require('prop-types'); const cx = require('classnames'); const WindowedList = require('./windowed-list'); const Draggable = require('./draggable'); -const { CSSIcon, getDOMElement } = require('components/icons'); +const { CSSIcon, getCSSIcon } = require('components/icons'); const { Zotero_Tooltip } = require('./tooltip'); const TYPING_TIMEOUT = 1000; @@ -1751,7 +1751,7 @@ function renderCheckboxCell(index, data, column, dir = null) { span.setAttribute('role', 'checkbox'); span.setAttribute('aria-checked', data); if (data) { - span.appendChild(getDOMElement('IconTick')); + span.appendChild(getCSSIcon('IconTick')); } return span; } diff --git a/chrome/content/zotero/itemTree.jsx b/chrome/content/zotero/itemTree.jsx index 3503bac4a0..03269fc63f 100644 --- a/chrome/content/zotero/itemTree.jsx +++ b/chrome/content/zotero/itemTree.jsx @@ -31,7 +31,7 @@ const LibraryTree = require('./libraryTree'); const VirtualizedTable = require('components/virtualized-table'); const { renderCell, formatColumnName } = VirtualizedTable; const Icons = require('components/icons'); -const { getDOMElement, getCSSIcon, getCSSItemTypeIcon } = Icons; +const { getCSSIcon, getCSSItemTypeIcon } = Icons; const { COLUMNS } = require("zotero/itemTreeColumns"); const { Cc, Ci, Cu, ChromeUtils } = require('chrome'); const { OS } = ChromeUtils.importESModule("chrome://zotero/content/osfile.mjs"); @@ -2796,7 +2796,7 @@ var ItemTree = class ItemTree extends LibraryTree { let retracted = ""; let retractedAriaLabel = ""; if (Zotero.Retractions.isRetracted(item)) { - retracted = getDOMElement('IconCross'); + retracted = getCSSIcon("IconCross"); retracted.classList.add("retracted"); retractedAriaLabel = Zotero.getString('retraction.banner'); } @@ -2916,7 +2916,7 @@ var ItemTree = class ItemTree extends LibraryTree { } //else if (type == 'none') { // if (item.getField('url') || item.getField('DOI')) { - // icon = getDOMElement('IconLink'); + // icon = getCSSIcon('IconLink'); // ariaLabel = Zotero.getString('pane.item.attachments.hasLink'); // icon.classList.add('cell-icon'); // } diff --git a/chrome/content/zotero/preferences/preferences_sync.jsx b/chrome/content/zotero/preferences/preferences_sync.jsx index 1cc4d25520..d9dc868429 100644 --- a/chrome/content/zotero/preferences/preferences_sync.jsx +++ b/chrome/content/zotero/preferences/preferences_sync.jsx @@ -30,7 +30,6 @@ Components.utils.import("resource://zotero/config.js"); var React = require('react'); var ReactDOM = require('react-dom'); var VirtualizedTable = require('components/virtualized-table'); -var { getDOMElement } = require('components/icons'); var { renderCell } = VirtualizedTable; Zotero_Preferences.Sync = {