From c9efc33d9c6ea739e535315d6281111575bec086 Mon Sep 17 00:00:00 2001 From: Tom Najdek Date: Tue, 15 Jul 2025 03:21:07 +0200 Subject: [PATCH] Re-implement "Create Parent Dialog" without using React for 7.0 (#5381) * Fixed an issue where the window size did not match the content * Replaced the semi-transparent progress bar with a spinner * Fixed a problem that allowed triggering a search while one was already in progress * Reduced code complexity --- .../components/createParent/createParent.jsx | 91 -------------- chrome/content/zotero/createParentDialog.js | 113 +++++++++--------- .../content/zotero/createParentDialog.xhtml | 14 ++- scss/components/_createParent.scss | 27 +---- scss/linux/_createParent.scss | 2 +- scss/mac/_createParent.scss | 2 +- scss/win/_createParent.scss | 2 +- test/tests/zoteroPaneTest.js | 2 +- 8 files changed, 75 insertions(+), 178 deletions(-) delete mode 100644 chrome/content/zotero/components/createParent/createParent.jsx diff --git a/chrome/content/zotero/components/createParent/createParent.jsx b/chrome/content/zotero/components/createParent/createParent.jsx deleted file mode 100644 index 7e86611bce..0000000000 --- a/chrome/content/zotero/components/createParent/createParent.jsx +++ /dev/null @@ -1,91 +0,0 @@ -/* - ***** BEGIN LICENSE BLOCK ***** - - Copyright © 2020 Corporation for Digital Scholarship - Vienna, Virginia, USA - https://digitalscholar.org - - This file is part of Zotero. - - Zotero is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Zotero is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with Zotero. If not, see . - - ***** END LICENSE BLOCK ***** -*/ - -'use strict'; - -import React, { memo, useEffect, useRef } from 'react'; -import PropTypes from 'prop-types'; -import cx from 'classnames'; - -function CreateParent({ loading, item, toggleAccept }) { - // With React 18, this is required for the window's dialog to be properly sized - const ref = useRef(); - useEffect(() => { - // Wait for Fluent to inject translated strings before resizing the dialog (fixes #5365). - const observer = new MutationObserver(() => window.sizeToContent()); - observer.observe(ref.current, { childList: true, subtree: true }); - return () => { - observer.disconnect(); - }; - }, []); - - // When the input has/does not have characters toggle the accept button on the dialog - const handleInput = (e) => { - if (e.target.value.trim() !== '') { - toggleAccept(true); - } - else { - toggleAccept(false); - } - }; - - return ( -
-
- { item.attachmentFilename } -
-

-

- -
-
-
-
-
- ); -} - - -CreateParent.propTypes = { - loading: PropTypes.bool, - item: PropTypes.object, - toggleAccept: PropTypes.func -}; - -Zotero.CreateParent = memo(CreateParent); - - -Zotero.CreateParent.render = (root, props) => { - root.render(); -}; diff --git a/chrome/content/zotero/createParentDialog.js b/chrome/content/zotero/createParentDialog.js index 9fe0f99ff5..897f6fd50d 100644 --- a/chrome/content/zotero/createParentDialog.js +++ b/chrome/content/zotero/createParentDialog.js @@ -23,68 +23,73 @@ ***** END LICENSE BLOCK ***** */ -"use strict"; +/* global Zotero_Lookup: false */ -import ReactDOM from "react-dom"; +const ZoteroCreateParentDialog = { // eslint-disable-line no-unused-vars + init() { + this.io = window.arguments[0]; -var io; -let createParent; -let root; + this.inputEl = document.getElementById('parent-item-identifier'); + this.progressEl = document.getElementById('progress'); + this.acceptBtnEl = document.querySelector('dialog').getButton("accept"); + this.manualEntryBtnEl = document.querySelector('dialog').getButton("extra2"); -function toggleAccept(enabled) { - document.querySelector('dialog').getButton("accept").disabled = !enabled; -} + // Set font size from pref + Zotero.UIProperties.registerRoot( + document.getElementById('zotero-create-parent-container') + ); -function doLoad() { - // Set font size from pref - let sbc = document.getElementById('zotero-create-parent-container'); - Zotero.UIProperties.registerRoot(sbc); + this.inputEl.addEventListener('input', this.handleInput.bind(this)); + document.addEventListener('dialogaccept', this.handleAcceptClick.bind(this)); + document.addEventListener('dialogextra2', this.handleManualEntry.bind(this)); - io = window.arguments[0]; + document.getElementById('title').textContent = this.io.dataIn.item.attachmentFilename; + this.inputEl.focus(); + }, - createParent = document.getElementById('create-parent'); - root = ReactDOM.createRoot(createParent); - Zotero.CreateParent.render(root, { - loading: false, - item: io.dataIn.item, - toggleAccept - }); + async performLookup() { + let newItems = await Zotero_Lookup.addItemsFromIdentifier( + this.inputEl, + this.io.dataIn.item, + this.handleStatusChange.bind(this) + ); - document.addEventListener('dialogaccept', (event) => { - doAccept(); - event.preventDefault(); - }); - document.addEventListener('dialogextra2', doManualEntry); -} - -function doUnload() { - root.unmount(); -} - -async function doAccept() { - let textBox = document.getElementById('parent-item-identifier'); - let childItem = io.dataIn.item; - let newItems = await Zotero_Lookup.addItemsFromIdentifier( - textBox, - childItem, - (on) => { - // Render react again with correct loading value - Zotero.CreateParent.render(root, { - loading: on, - item: childItem, - toggleAccept - }); + // If we successfully created a parent, return it + if (newItems.length) { + this.io.dataOut = { parent: newItems[0] }; + window.close(); } - ); + }, - // If we successfully created a parent, return it - if (newItems.length) { - io.dataOut = { parent: newItems[0] }; + handleInput(event) { + const input = event.target.value.trim(); + this.acceptBtnEl.disabled = input === ''; + }, + + handleStatusChange(isLookingUp) { + this.inputEl.disabled = isLookingUp; + this.acceptBtnEl.disabled = isLookingUp; + this.manualEntryBtnEl.disabled = isLookingUp; + if (isLookingUp) { + this.progressEl.setAttribute("status", "animate"); + } + else { + this.progressEl.removeAttribute("status"); + } + }, + + handleAcceptClick(ev) { + ev.preventDefault(); + + if (this.inputEl.value.trim() === '') { + return; + } + + this.performLookup(); + }, + + handleManualEntry() { + this.io.dataOut = { parent: false }; window.close(); } -} - -function doManualEntry() { - io.dataOut = { parent: false }; - window.close(); -} +}; diff --git a/chrome/content/zotero/createParentDialog.xhtml b/chrome/content/zotero/createParentDialog.xhtml index 3b57b9e465..dad8a683db 100644 --- a/chrome/content/zotero/createParentDialog.xhtml +++ b/chrome/content/zotero/createParentDialog.xhtml @@ -14,8 +14,8 @@ xmlns:html="http://www.w3.org/1999/xhtml" title="&zotero.createParent.title;" drawintitlebar-platforms="mac" - onload="doLoad();" - onunload="doUnload();"> + onload="ZoteroCreateParentDialog.init()" +>