From 8a76aac83fa8a82599c48de89fba13d70f9f7de1 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:42:38 +0100 Subject: [PATCH] Update JSDoc for itemPaneManager and itemTreeManager --- .../zotero/xpcom/pluginAPI/itemPaneManager.js | 524 ++++++++++++++---- .../zotero/xpcom/pluginAPI/itemTreeManager.js | 124 +++-- .../content/zotero/xpcom/preferencePanes.js | 14 +- chrome/content/zotero/xpcom/reader.js | 34 +- 4 files changed, 524 insertions(+), 172 deletions(-) diff --git a/chrome/content/zotero/xpcom/pluginAPI/itemPaneManager.js b/chrome/content/zotero/xpcom/pluginAPI/itemPaneManager.js index 7c29f1978e..f8890fc799 100644 --- a/chrome/content/zotero/xpcom/pluginAPI/itemPaneManager.js +++ b/chrome/content/zotero/xpcom/pluginAPI/itemPaneManager.js @@ -29,109 +29,167 @@ /** - * @typedef SectionIcon - * @type {object} - * @property {string} icon - Icon URI - * @property {string} [darkIcon] - Icon URI in dark mode. If not set, use `icon` - * @typedef SectionL10n - * @type {object} - * @property {string} l10nID - data-l10n-id for localization of section header label - * @property {string} [l10nArgs] - data-l10n-args for localization - * @typedef SectionButton - * @type {object} - * @property {string} type - Button type, must be valid DOMString and without "," - * @property {string} icon - Icon URI - * @property {string} [darkIcon] - Icon URI in dark mode. If not set, use `icon` - * @property {string} [l10nID] - data-l10n-id for localization of button tooltiptext - * @property {(props: SectionEventHookArgs) => void} onClick - Button click callback - * @typedef SectionBasicHookArgs - * @type {object} - * @property {string} paneID - Registered pane id - * @property {Document} doc - Document of section - * @property {HTMLDivElement} body - Section body - * @typedef SectionUIHookArgs - * @type {object} - * @property {Zotero.Item} item - Current item - * @property {string} tabType - Current tab type - * @property {boolean} editable - Whether the section is in edit mode - * @property {(l10nArgs: string) => void} setL10nArgs - Set l10n args for section header - * @property {(l10nArgs: string) => void} setEnabled - Set pane enabled state - * @property {(summary: string) => void} setSectionSummary - Set pane section summary, - * the text shown in the section header when the section is collapsed. - * - * See the Abstract section as an example - * @property {(buttonType: string, options: {disabled?: boolean; hidden?: boolean}) => void} setSectionButtonStatus - Set pane section button status - * @typedef SectionHookArgs - * @type {SectionBasicHookArgs & SectionUIHookArgs} - * @typedef {SectionHookArgs & { refresh: () => Promise }} SectionInitHookArgs + * @namespace Zotero + */ + + + /** + * @typedef {Object} SectionIcon + * @property {string} icon - Icon URI. + * @property {string} [darkIcon] - Icon URI in dark mode. If not set, use `icon`. + */ + + /** + * @typedef {Object} SectionL10n + * @property {string} l10nID - data-l10n-id for localization of section header label. + * @property {string} [l10nArgs] - data-l10n-args for localization. + */ + + /** + * @typedef {Object} SectionButton + * @property {string} type - Button type, must be valid DOMString and without ",". + * @property {string} icon - Icon URI. + * @property {string} [darkIcon] - Icon URI in dark mode. If not set, use `icon`. + * @property {string} [l10nID] - data-l10n-id for localization of button tooltiptext. + * @property {SectionEventHook} onClick - Button click callback. + */ + + /** + * @typedef {Object} SectionBasicHookArgs + * @property {string} paneID - Registered pane id. + * @property {Document} doc - Document of section. + * @property {HTMLDivElement} body - Section body. + */ + + /** + * @typedef {Object} SectionUIHookArgs + * @property {Zotero.Item} item - Current item. + * @property {string} tabType - Current tab type. + * @property {boolean} editable - Whether the section is in edit mode. + * @property {SetSectionL10nArgs} setL10nArgs - Set l10n args for section header. + * @property {SetEnabled} setEnabled - Set pane enabled state. + * @property {SetSectionSummary} setSectionSummary - Set pane section summary, shown in collapsed header. + * @property {SetSectionButtonStatus} setSectionButtonStatus - Set pane section button status. + */ + + /** + * @typedef {Object} SectionHookArgs + * @property {string} paneID - Registered pane id. + * @property {Document} doc - Document of section. + * @property {HTMLDivElement} body - Section body. + * @property {Zotero.Item} item - Current item. + * @property {string} tabType - Current tab type. + * @property {boolean} editable - Whether the section is in edit mode. + * @property {SetSectionL10nArgs} setL10nArgs - Set l10n args for section header. + * @property {SetEnabled} setEnabled - Set pane enabled state. + * @property {SetSectionSummary} setSectionSummary - Set pane section summary, shown in collapsed header. + * @property {SetSectionButtonStatus} setSectionButtonStatus - Set pane section button status. + */ + + /** + * @typedef {Object} SectionInitHookArgs + * @property {string} paneID - Registered pane id. + * @property {Document} doc - Document of section. + * @property {HTMLDivElement} body - Section body. + * @property {Zotero.Item} item - Current item. + * @property {string} tabType - Current tab type. + * @property {boolean} editable - Whether the section is in edit mode. + * @property {SetSectionL10nArgs} setL10nArgs - Set l10n args for section header. + * @property {SetEnabled} setEnabled - Set pane enabled state. + * @property {SetSectionSummary} setSectionSummary - Set pane section summary, shown in collapsed header. + * @property {SetSectionButtonStatus} setSectionButtonStatus - Set pane section button status. + * @property {SectionRefresh} refresh - Refresh the section. * A `refresh` is exposed to plugins to allows plugins to refresh the section when necessary, * e.g. item modify notifier callback. Note that calling `refresh` during initialization * have no effect. - * @typedef {SectionHookArgs & { event: Event }} SectionEventHookArgs - * @typedef ItemDetailsSectionOptions - * @type {object} - * @property {string} paneID - Unique pane ID - * @property {string} pluginID - Set plugin ID to auto remove section when plugin is disabled/removed - * @property {SectionL10n & SectionIcon} header - Header options. Icon should be 16*16 and `label` need to be localized - * @property {SectionL10n & SectionIcon} sidenav - Sidenav options. Icon should be 20*20 and `tooltiptext` need to be localized - * @property {string} [bodyXHTML] - Pane body's innerHTML, default to XUL namespace - * @property {(props: SectionInitHookArgs) => void} [onInit] - * Lifecycle hook called when section is initialized. - * You can use destructuring assignment to get the props: - * ```js - * onInit({ paneID, doc, body, item, editable, tabType, setL10nArgs, setEnabled, - * setSectionSummary, setSectionButtonStatus, refresh }) { - * // Your code here - * } - * ``` - * - * Do: - * 1. Initialize data if necessary - * 2. Set up hooks, e.g. notifier callback - * - * Don't: - * 1. Render/refresh UI - * @property {(props: SectionBasicHookArgs) => void} [onDestroy] - * Lifecycle hook called when section is destroyed - * - * Do: - * 1. Remove data and release resource - * 2. Remove hooks, e.g. notifier callback - * - * Don't: - * 1. Render/refresh UI - * @property {(props: SectionHookArgs) => boolean} [onItemChange] - * Lifecycle hook called when section's item change received - * - * Do: - * 1. Update data (no need to render or refresh); - * 2. Update the section enabled state with `props.setEnabled`. For example, if the section - * is only enabled in the readers, you can use: - * ```js - * onItemChange({ setEnabled }) { - * setEnabled(newData.value === "reader"); - * } - * ``` - * - * Don't: - * 1. Render/refresh UI - * @property {(props: SectionHookArgs) => void} onRender - * Lifecycle hook called when section should do initial render. Cannot be async. - * - * Create elements and append them to `props.body`. - * - * If the rendering is slow, you should make the bottleneck async and move it to `onAsyncRender`. - * - * > Note that the rendering of section is fully controlled by Zotero to minimize resource usage. - * > Only render UI things when you are told to. - * @property {(props: SectionHookArgs) => void | Promise} [onAsyncRender] - * [Optional] Lifecycle hook called when section should do async render - * - * The best practice to time-consuming rendering with runtime decided section height is: - * 1. Compute height and create a box in sync `onRender`; - * 2. Render actual contents in async `onAsyncRender`. - * @property {(props: SectionEventHookArgs) => void} [onToggle] - Called when section is toggled - * @property {SectionButton[]} [sectionButtons] - Section button options + */ + + /** + * @typedef {Object} SectionEventHookArgs + * @property {string} paneID - Registered pane id. + * @property {Document} doc - Document of section. + * @property {HTMLDivElement} body - Section body. + * @property {Zotero.Item} item - Current item. + * @property {string} tabType - Current tab type. + * @property {boolean} editable - Whether the section is in edit mode. + * @property {SetSectionL10nArgs} setL10nArgs - Set l10n args for section header. + * @property {SetEnabled} setEnabled - Set pane enabled state. + * @property {SetSectionSummary} setSectionSummary - Set pane section summary, shown in collapsed header. + * @property {SetSectionButtonStatus} setSectionButtonStatus - Set pane section button status. + * @property {Event} event - Event object. + */ + + /** + * @callback SectionInitHook + * @param {SectionInitHookArgs} props - Props provided during section initialization. + * @returns {void} + */ + + /** + * @callback SectionBasicHook + * @param {SectionBasicHookArgs} props - Basic hook arguments. + */ + + /** + * @callback SectionItemChangeHook + * @param {SectionHookArgs} props - Hook arguments for item changes. + * @returns {boolean} + */ + + /** + * @callback SectionRenderHook + * @param {SectionHookArgs} props - Hook arguments for rendering. + * @returns {void} + */ + + /** + * @callback SectionAsyncRenderHook + * @param {SectionHookArgs} props - Hook arguments for asynchronous rendering. + * @returns {void | Promise} + */ + + /** + * @callback SectionToggleHook + * @param {SectionEventHookArgs} props - Event hook arguments. + * @returns {void} + */ + + /** + * @callback SectionEventHook + * @param {SectionEventHookArgs} props - Event hook arguments. + * @returns {void} + */ + + /** + * @callback SetSectionL10nArgs + * @param {string} l10nArgs - Localization arguments. + * @returns {void} + */ + + /** + * @callback SetEnabled + * @param {boolean} enabled - Enabled state. + * @returns {void} + */ + + /** + * @callback SetSectionSummary + * @param {string} summary - The summary for the section header. + * @returns {void} + */ + + /** + * @callback SetSectionButtonStatus + * @param {string} buttonType - The button type. + * @param {Object} options - Options for the button status. + * @param {boolean} [options.disabled] - Whether the button is disabled. + * @param {boolean} [options.hidden] - Whether the button is hidden. + * @returns {void} + */ + + /** + * @callback SectionRefresh + * @returns {Promise} */ @@ -250,6 +308,73 @@ } + /** + * @typedef {Object} InfoRowL10n + * @property {string} l10nID - data-l10n-id for localization of row label. + */ + + /** + * @typedef {"start" | "afterCreators" | "end"} InfoRowPosition - Position of the row. + */ + + /** + * @typedef {Object} InfoRowGetDataHookArgs + * @property {string} rowID - Row ID. + * @property {Zotero.Item} item - Current item. + * @property {string} tabType - Current tab type. + * @property {boolean} editable - Whether the row is in edit mode. + */ + + /** + * @typedef {Object} InfoRowSetDataHookArgs + * @property {string} rowID - Row ID. + * @property {Zotero.Item} item - Current item. + * @property {string} tabType - Current tab type. + * @property {string} editable - Whether the row is in edit mode. + * @property {string} value - New value. + */ + + /** + * @typedef {Object} InfoRowItemChangeHookArgs + * @property {string} rowID - Row ID. + * @property {Zotero.Item} item - Current item. + * @property {string} tabType - Current tab type. + * @property {boolean} editable - Whether the row is in edit mode. + * @property {SetInfoRowEnabled} setEnabled - Set row enabled state. + * @property {SetInfoRowEditable} setEditable - Set row editable state. + */ + + /** + * @typedef {function} InfoRowGetDataHook + * @param {InfoRowGetDataHookArgs} props - Hook arguments for getting data. + * @returns {string} - Row data. + */ + + /** + * @typedef {function} InfoRowSetDataHook + * @param {InfoRowSetDataHookArgs} props - Hook arguments for setting data. + * @returns {void} + */ + + /** + * @typedef {function} InfoRowItemChangeHook + * @param {InfoRowItemChangeHookArgs} props - Hook arguments for item changes. + * @returns {void} + */ + + /** + * @typedef {function} SetInfoRowEnabled + * @param {boolean} enabled - Enabled state. + * @returns {void} + */ + + /** + * @typedef {function} SetInfoRowEditable + * @param {boolean} editable - Editable state. + * @returns {void} + */ + + class ItemPaneInfoRowManagerInternal extends PluginAPIBase { constructor() { super(); @@ -315,22 +440,127 @@ } - class ItemPaneManager { - _sectionManager = new ItemPaneSectionManagerInternal(); + /** + * Manages item pane APIs. + * + * @memberof Zotero + */ + Zotero.ItemPaneManager = { + _sectionManager: new ItemPaneSectionManagerInternal(), - _infoRowManager = new ItemPaneInfoRowManagerInternal(); + _infoRowManager: new ItemPaneInfoRowManagerInternal(), + /** + * Register a custom item pane section. + * @param {Object} options - Section options. + * @param {string} options.paneID - Unique pane ID. + * @param {string} options.pluginID - Set plugin ID to auto-remove section when the plugin is disabled or removed. + * @param {SectionL10n | SectionIcon} options.header - Header options. Icon should be 16*16 and `label` need to be localized. + * @param {SectionL10n | SectionIcon} options.sidenav - Sidenav options. Icon should be 20*20 and `tooltiptext` need to be localized. + * @param {string} [options.bodyXHTML] - Pane body's innerHTML, defaults to XUL namespace. + * @param {SectionInitHook} [options.onInit] - Lifecycle hook called when section is initialized. + * + * Do: + * 1. Initialize data if necessary + * 2. Set up hooks, e.g. notifier callback + * + * Don't: + * 1. Render/refresh UI + * @param {SectionBasicHook} [options.onDestroy] - Lifecycle hook called when section is destroyed. + * + * Do: + * 1. Remove data and release resource + * 2. Remove hooks, e.g. notifier callback + * + * Don't: + * 1. Render/refresh UI + * @param {SectionItemChangeHook} [options.onItemChange] - Lifecycle hook called when the section's target item is changed. + * + * Do: + * 1. Update data (no need to render or refresh); + * 2. Update the section enabled state with `props.setEnabled`. + * + * Don't: + * 1. Render/refresh UI + * @param {SectionRenderHook} options.onRender - Lifecycle hook called for initial render. + * + * Cannot be async. + * + * Create elements and append them to `props.body`. + * + * If the rendering is slow, you should make the bottleneck async and move it to `onAsyncRender`. + * @param {SectionAsyncRenderHook} [options.onAsyncRender] - Lifecycle hook for asynchronous rendering. + * + * The best practice to time-consuming rendering with runtime decided section height is: + * 1. Compute height and create a box in sync `onRender`; + * 2. Render actual contents in async `onAsyncRender`. + * @param {SectionToggleHook} [options.onToggle] - Called when section is toggled. + * @param {SectionButton[]} [options.sectionButtons] - Section button options. + * @returns {string | false} - The registered pane ID or false if failed. + * + * @example + * ```javascript + * Zotero.ItemPaneManager.registerSection({ + * paneID: 'my-plugin-pane', + * pluginID: 'my-plugin@my-namespace.com', + * header: { + * l10nID: 'my-plugin-pane-header', // Must inject the corresponding `ftl` file + * icon: 'chrome://my-plugin/content/icon16.svg', + * }, + * sidenav: { + * l10nID: 'my-plugin-pane-sidenav', // Must inject the corresponding `ftl` file + * icon: 'chrome://my-plugin/content/icon20.svg', + * }, + * onInit: ({paneID, doc, body}) => { + * // Initialize data + * Zotero.debug('Section initialized'); + * }, + * onDestroy: ({paneID, doc, body}) => { + * // Release resource + * Zotero.debug('Section destroyed'); + * }, + * onItemChange: ({paneID, doc, body, item, tabType, editable, setEnabled}) => { + * // In this example, the section is enabled only for regular items + * setEnabled(item.isRegularItem()); + * }, + * onRender: ({doc, body, item}) => { + * // Create elements and append them to `body` + * const div = doc.createElement('div'); + * div.classList.add('my-plugin-section'); + * div.textContent = item.getField('title'); + * body.appendChild(div); + * }, + * onAsyncRender: async ({body}) => { + * // Put time-consuming rendering here + * await new Promise(resolve => setTimeout(resolve, 1000)); + * body.querySelector('.my-plugin-section')?.style.setProperty('color', 'red'); + * }, + * onToggle: ({paneID, doc, body, item, tabType, editable, setEnabled}) => { + * // Handle section toggle + * Zotero.debug('Section toggled'); + * }, + * sectionButtons: [ + * // Section button will appear in the header + * ], + * }); + * ``` + */ registerSection(options) { return this._sectionManager.register(options); - } + }, + /** + * Unregister a custom item pane section. + * @param {string} paneID - Pane ID to unregister. This is the value returned by `registerSection`. + * @returns {boolean} - True if the section is successfully unregistered, false if the paneID is not found. + */ unregisterSection(paneID) { return this._sectionManager.unregister(paneID); - } + }, get customSectionData() { return this._sectionManager.data; - } + }, isSectionOrderable(paneID) { let option = this._sectionManager._optionsCache[paneID]; @@ -338,23 +568,88 @@ return false; } return option.sidenav.orderable ?? true; - } + }, + /** + * Register a custom item pane info section row. + * @param {Object} options - Row options. + * @param {string} options.rowID - Unique row ID. + * @param {string} options.pluginID - Set plugin ID to auto-remove row when the plugin is disabled or removed. + * @param {InfoRowL10n} options.label - Label options. `label` need to be localized. + * @param {InfoRowPosition} [options.position] - Position of the row. + * @param {boolean} [options.multiline] - Whether the row is multiline. + * @param {boolean} [options.nowrap] - Whether the row is nowrap. + * @param {boolean} [options.editable] - Whether the row is editable. + * @param {InfoRowGetDataHook} options.onGetData - Lifecycle hook for getting row data for rendering. + * + * This is called when the row is rendered or refreshed. + * @param {InfoRowSetDataHook} [options.onSetData] - Lifecycle hook for saving row data changes after editing. + * + * Do: + * 1. Save the new value of the row + * + * Don't: + * 1. Render/refresh UI + * 2. Change the value in this hook + * @param {InfoRowItemChangeHook} [options.onItemChange] - Lifecycle hook for target item changes. + * + * Do: + * 1. Update the row attribute, e.g. enabled, editable + * + * Don't: + * 1. Render/refresh UI + * @returns {string | false} - The registered row ID or false if failed. + * + * @example + * ```javascript + * Zotero.ItemPaneManager.registerInfoRow({ + * rowID: 'my-plugin-row', + * pluginID: 'my-plugin@my-namespace.com', + * label: { + * l10nID: 'my-plugin-row-label', // Must inject the corresponding `ftl` file + * }, + * position: 'afterCreators', + * multiline: true, + * nowrap: false, + * editable: true, + * onGetData: ({rowID, item, tabType, editable}) => { + * return item.getField('title').toUpperCase(); + * }, + * onSetData: ({rowID, item, tabType, editable, value}) => { + * Zotero.debug('Info row data changed:', value); + * }, + * onItemChange: ({rowID, item, tabType, editable, setEnabled, setEditable}) => { + * // In this example, the row is enabled only for library tab + * setEnabled(tabType === 'library'); + * }, + * }); + * ``` + */ registerInfoRow(options) { return this._infoRowManager.register(options); - } + }, + /** + * Unregister a custom item pane info section row. + * @param {string} rowID - Row ID to unregister. This is the value returned by `registerInfoRow`. + * @returns {boolean} - True if the row is successfully unregistered, false if the rowID is not found. + */ unregisterInfoRow(rowID) { return this._infoRowManager.unregister(rowID); - } + }, + /** + * Refresh a custom item pane info section row. + * @param {string} rowID - Row ID to refresh. This is the value returned by `registerInfoRow`. + * @returns {void} + */ refreshInfoRow(rowID) { return this._infoRowManager.refresh(rowID); - } + }, get customInfoRowData() { return this._infoRowManager.data; - } + }, getInfoRowHook(rowID, type) { let option = this._infoRowManager._optionsCache[rowID]; @@ -362,9 +657,6 @@ return undefined; } return option[type]; - } - } - - - Zotero.ItemPaneManager = new ItemPaneManager(); + }, + }; } diff --git a/chrome/content/zotero/xpcom/pluginAPI/itemTreeManager.js b/chrome/content/zotero/xpcom/pluginAPI/itemTreeManager.js index 6cf0a35058..f9f3800511 100644 --- a/chrome/content/zotero/xpcom/pluginAPI/itemTreeManager.js +++ b/chrome/content/zotero/xpcom/pluginAPI/itemTreeManager.js @@ -32,12 +32,25 @@ import { COLUMNS } from 'zotero/itemTreeColumns'; /** - * @typedef {import("../../itemTreeColumns.jsx").ItemTreeColumnOptions} ItemTreeColumnOptions - * @typedef {"dataKey" | "label" | "pluginID"} RequiredCustomColumnOptionKeys - * @typedef {Required>} RequiredCustomColumnOptionsPartial - * @typedef {Omit} CustomColumnOptionsPartial - * @typedef {RequiredCustomColumnOptionsPartial & CustomColumnOptionsPartial} ItemTreeCustomColumnOptions - * @typedef {Partial>} ItemTreeCustomColumnFilters + * @namespace Zotero + */ + + + /** + * @typedef {function} ItemTreeColumnDataProvider + * @param {Zotero.Item} item - The item to get data from + * @param {string} dataKey - The dataKey of the column + * @returns {string} - The data to display in the column + */ + + /** + * @typedef {function} ItemTreeColumnRenderCell + * @param {number} index - The index of the row + * @param {string} data - The data to display in the column + * @param {ItemTreeColumnOptions | {className: string}} column - The column options + * @param {boolean} isFirstColumn - true if this is the first column + * @param {Document} doc - The document of the item tree + * @returns {HTMLElement} - The HTML to display in the cell */ @@ -165,25 +178,53 @@ import { COLUMNS } from 'zotero/itemTreeColumns'; } - class ItemTreeManager { - _columnManager = new ItemTreeColumnManagerInternal(); + /** + * Manages item tree APIs. + * + * @memberof Zotero + */ + Zotero.ItemTreeManager = { + _columnManager: new ItemTreeColumnManagerInternal(), /** * Register a custom column, must be valid with a unique dataKey. * * Note that the `dataKey` you use here may be different from the one returned by the function. * This is because the `dataKey` is prefixed with the `pluginID` to avoid conflicts after the column is registered. - * @param {ItemTreeCustomColumnOptions} option - An option or array of options to register + * @param {Object} option - An option or array of options to register + * @param {string} option.dataKey - Required, see use in ItemTree#_getRowData() + * @param {string} option.label - The column label. Either a string or the id to an i18n string. + * @param {string} option.pluginID - Set plugin ID to auto remove column when plugin is removed. + * @param {string[]} [option.enabledTreeIDs=[]] - Which tree ids the column should be enabled in. If undefined, enabled in main tree. If ["*"], enabled in all trees. + * @param {string[]} [option.defaultIn] - Will be deprecated. Types of trees the column is default in. Can be [default, feed]; + * @param {string[]} [option.disabledIn] - Will be deprecated. Types of trees where the column is not available + * @param {boolean} [option.sortReverse=false] - Default: false. Set to true to reverse the sort order + * @param {number} [option.flex=1] - Default: 1. When the column is added to the tree how much space it should occupy as a flex ratio + * @param {string} [option.width] - A column width instead of flex ratio. See above. + * @param {boolean} [option.fixedWidth] - Default: false. Set to true to disable column resizing + * @param {boolean} [option.staticWidth] - Default: false. Set to true to prevent columns from changing width when the width of the tree increases or decreases + * @param {boolean} [option.noPadding] - Set to true for columns with padding disabled in stylesheet + * @param {number} [option.minWidth] - Override the default [20px] column min-width for resizing + * @param {React.Component} [option.iconLabel] - Set an Icon label instead of a text-based one + * @param {string} [option.iconPath] - Set an Icon path, overrides {iconLabel} + * @param {string | React.Component} [option.htmlLabel] - Set an HTML label, overrides {iconLabel} and {label}. Can be a HTML string or a React component. + * @param {boolean} [option.showInColumnPicker=true] - Default: true. Set to true to show in column picker. + * @param {boolean} [option.columnPickerSubMenu=false] - Default: false. Set to true to display the column in "More Columns" submenu of column picker. + * @param {boolean} [option.primary] - Should only be one column at the time. Title is the primary column + * @param {ItemTreeColumnDataProvider} [option.dataProvider] - Custom data provider that is called when rendering cells + * @param {ItemTreeColumnRenderCell} [option.renderCell] - The cell renderer function + * @param {string[]} [option.zoteroPersist] - Which column properties should be persisted between zotero close * @returns {string | false} - The dataKey of the added column or false if no column is added + * * @example * A minimal custom column: - * ```js + * ```javascript * // You can unregister the column later with Zotero.ItemTreeManager.unregisterColumn(registeredDataKey); * const registeredDataKey = Zotero.ItemTreeManager.registerColumn( * { * dataKey: 'rtitle', * label: 'Reversed Title', - * pluginID: 'make-it-red@zotero.org', // Replace with your plugin ID + * pluginID: 'my-plugin@my-namespace.com', // Replace with your plugin ID * dataProvider: (item, dataKey) => { * return item.getField('title').split('').reverse().join(''); * }, @@ -192,7 +233,7 @@ import { COLUMNS } from 'zotero/itemTreeColumns'; * @example * A custom column using all available options. * Note that the column will only be shown in the main item tree. - * ```js + * ```javascript * const registeredDataKey = Zotero.ItemTreeManager.registerColumn( * { * dataKey: 'rtitle', @@ -208,7 +249,7 @@ import { COLUMNS } from 'zotero/itemTreeColumns'; * htmlLabel: 'reversed title', // use HTML in the label. This will override the label and iconPath property * showInColumnPicker: true, // show in the column picker * columnPickerSubMenu: true, // show in the column picker submenu - * pluginID: 'make-it-red@zotero.org', // plugin ID, which will be used to unregister the column when the plugin is unloaded + * pluginID: 'my-plugin@my-namespace.com', // plugin ID * dataProvider: (item, dataKey) => { * // item: the current item in the row * // dataKey: the dataKey of the column @@ -234,7 +275,7 @@ import { COLUMNS } from 'zotero/itemTreeColumns'; */ registerColumn(option) { return this._columnManager.register(option); - } + }, /** * @deprecated Use `registerColumn` instead. @@ -244,21 +285,16 @@ import { COLUMNS } from 'zotero/itemTreeColumns'; options = [options]; } return options.map(option => this.registerColumn(option)); - } + }, /** * Unregister a custom column. * @param {string} dataKey - The dataKey of the column to unregister - * @returns {boolean} true if the column is unregistered - * @example - * The `registeredDataKey` is returned by the `registerColumn` function. - * ```js - * Zotero.ItemTreeManager.unregisterColumn(registeredDataKey); - * ``` + * @returns {boolean} - true if the column was unregistered, false if the column was not found */ unregisterColumn(dataKey) { return this._columnManager.unregister(dataKey); - } + }, /** * @deprecated Use `unregisterColumn` instead. @@ -268,18 +304,20 @@ import { COLUMNS } from 'zotero/itemTreeColumns'; dataKeys = [dataKeys]; } return dataKeys.map(dataKey => this.unregisterColumn(dataKey)); - } + }, + + /** + * Refresh the columns in the item tree + * @returns {void} + */ + refreshColumns() { + this._columnManager.refresh(); + }, get customColumnUpdateID() { return this._columnManager.updateID; - } + }, - /** - * Get column(s) that matches the properties of option - * @param {string | string[]} [filterTreeIDs] - The tree IDs to match - * @param {ItemTreeCustomColumnFilters} [options] - An option or array of options to match - * @returns {ItemTreeCustomColumnOptions[]} - */ getCustomColumns(filterTreeIDs, options) { const allColumns = this._columnManager.options; if (!filterTreeIDs && !options) { @@ -312,36 +350,18 @@ import { COLUMNS } from 'zotero/itemTreeColumns'; }); } return filteredColumns; - } + }, - /** - * Check if a column is registered as a custom column - * @param {string} dataKey - The dataKey of the column - * @returns {boolean} true if the column is registered as a custom column - */ isCustomColumn(dataKey) { return !!this._columnManager._optionsCache[dataKey]; - } + }, - /** - * A centralized data source for custom columns. This is used by the ItemTreeRow to get data. - * @param {Zotero.Item} item - The item to get data from - * @param {string} dataKey - The dataKey of the column - * @returns {string} - */ getCustomCellData(item, dataKey) { const option = this._columnManager._optionsCache[dataKey]; if (option && option.dataProvider) { return option.dataProvider(item, dataKey); } return ""; - } - - refreshColumns() { - this._columnManager.refresh(); - } - } - - - Zotero.ItemTreeManager = new ItemTreeManager(); + }, + }; } diff --git a/chrome/content/zotero/xpcom/preferencePanes.js b/chrome/content/zotero/xpcom/preferencePanes.js index cfa72cd1cc..7a4f91c587 100644 --- a/chrome/content/zotero/xpcom/preferencePanes.js +++ b/chrome/content/zotero/xpcom/preferencePanes.js @@ -118,7 +118,19 @@ Zotero.PreferencePanes = { * relative to the plugin's root * @param {String} [options.helpURL] If provided, a help button will be displayed under the pane * and the provided URL will open when it is clicked - * @return {Promise} Resolves to the ID of the pane if successfully added + * @return {Promise} Resolves to the ID of the pane if successfully added + * + * @example + * Register a pane with a script and stylesheet: + * ```javascript + * Zotero.PreferencePanes.register({ + * pluginID: 'my-plugin@my-namespace.com', + * src: rootURI + 'my-pane.xhtml', + * id: 'my-plugin-pane', + * scripts: [rootURI + 'my-pane.js'], + * stylesheets: [rootURI + 'my-pane.css'] + * }); + * ``` */ register: async function (options) { if (!options.pluginID || !options.src) { diff --git a/chrome/content/zotero/xpcom/reader.js b/chrome/content/zotero/xpcom/reader.js index e493ba0263..0d756794c3 100644 --- a/chrome/content/zotero/xpcom/reader.js +++ b/chrome/content/zotero/xpcom/reader.js @@ -2087,6 +2087,32 @@ class Reader { } /** + * @typedef {"renderTextSelectionPopup" | "renderSidebarAnnotationHeader" | "renderToolbar" | + * "createColorContextMenu" | "createViewContextMenu" | "createAnnotationContextMenu" | + * "createThumbnailContextMenu" | "createSelectorContextMenu"} ReaderEventType + */ + + /** + * @typedef {Object} ReaderEvent + * @property {ReaderInstance} reader - Reader instance + * @property {Document} doc - Document + * @property {Object} params - Event parameters + * @property {function(...Element): void} append - Append function + */ + + /** + * @typedef {function} ReaderEventHandler + * @param {ReaderEvent} event - Event + * @returns {void} + */ + + /** + * @param {ReaderEventType} type - Event type + * @param {ReaderEventHandler} handler - Event handler + * @param {string} [pluginID] - Plugin ID + * @returns {void} + * + * @example * Inject DOM nodes to reader UI parts: * - renderTextSelectionPopup * - renderSidebarAnnotationHeader @@ -2098,9 +2124,10 @@ class Reader { * container.append('Loading…'); * append(container); * setTimeout(() => container.replaceChildren('Translated text: ' + params.annotation.text), 1000); - * }); - * + * }, 'my-plugin@my-namespace.com'); + * ``` * + * @example * Add options to context menus: * - createColorContextMenu * - createViewContextMenu @@ -2114,7 +2141,8 @@ class Reader { * label: 'Test', * onCommand(){ reader._iframeWindow.alert('Selected annotations: ' + params.ids.join(', ')); } * }); - * }); + * }, 'my-plugin@my-namespace.com'); + * ``` */ registerEventListener(type, handler, pluginID = undefined) { this._registeredListeners.push({ pluginID, type, handler });