UNPKG

@jupyterlab/apputils

Version:
513 lines 19.1 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { ServerConnection } from '@jupyterlab/services'; import { jsonIcon, markdownIcon, spreadsheetIcon, VDomModel, VDomRenderer } from '@jupyterlab/ui-components'; import { PromiseDelegate } from '@lumino/coreutils'; import { Signal } from '@lumino/signaling'; import { h } from '@lumino/virtualdom'; import { Panel, SplitPanel, TabBar, Widget } from '@lumino/widgets'; import * as React from 'react'; const FILTER_SECTION_TITLE_CLASS = 'jp-Licenses-Filters-title'; /** * A license viewer */ export class Licenses extends SplitPanel { constructor(options) { super(); this.addClass('jp-Licenses'); this.model = options.model; this.initLeftPanel(); this.initFilters(); this.initBundles(); this.initGrid(); this.initLicenseText(); this.setRelativeSizes([1, 2, 3]); void this.model.initLicenses().then(() => this._updateBundles()); this.model.trackerDataChanged.connect(() => { this.title.label = this.model.title; }); } /** * Handle disposing of the widget */ dispose() { if (this.isDisposed) { return; } this._bundles.currentChanged.disconnect(this.onBundleSelected, this); this.model.dispose(); super.dispose(); } /** * Initialize the left area for filters and bundles */ initLeftPanel() { this._leftPanel = new Panel(); this._leftPanel.addClass('jp-Licenses-FormArea'); this.addWidget(this._leftPanel); SplitPanel.setStretch(this._leftPanel, 1); } /** * Initialize the filters */ initFilters() { this._filters = new Licenses.Filters(this.model); SplitPanel.setStretch(this._filters, 1); this._leftPanel.addWidget(this._filters); } /** * Initialize the listing of available bundles */ initBundles() { this._bundles = new TabBar({ orientation: 'vertical', renderer: new Licenses.BundleTabRenderer(this.model) }); this._bundles.addClass('jp-Licenses-Bundles'); SplitPanel.setStretch(this._bundles, 1); this._leftPanel.addWidget(this._bundles); this._bundles.currentChanged.connect(this.onBundleSelected, this); this.model.stateChanged.connect(() => this._bundles.update()); } /** * Initialize the listing of packages within the current bundle */ initGrid() { this._grid = new Licenses.Grid(this.model); SplitPanel.setStretch(this._grid, 1); this.addWidget(this._grid); } /** * Initialize the full text of the current package */ initLicenseText() { this._licenseText = new Licenses.FullText(this.model); SplitPanel.setStretch(this._grid, 1); this.addWidget(this._licenseText); } /** * Event handler for updating the model with the current bundle */ onBundleSelected() { var _a; if ((_a = this._bundles.currentTitle) === null || _a === void 0 ? void 0 : _a.label) { this.model.currentBundleName = this._bundles.currentTitle.label; } } /** * Update the bundle tabs. */ _updateBundles() { this._bundles.clearTabs(); let i = 0; const { currentBundleName } = this.model; let currentIndex = 0; for (const bundle of this.model.bundleNames) { const tab = new Widget(); tab.title.label = bundle; if (bundle === currentBundleName) { currentIndex = i; } this._bundles.insertTab(++i, tab.title); } this._bundles.currentIndex = currentIndex; } } /** A namespace for license components */ (function (Licenses) { /** * License report formats understood by the server (once lower-cased) */ Licenses.REPORT_FORMATS = { markdown: { id: 'markdown', title: 'Markdown', icon: markdownIcon }, csv: { id: 'csv', title: 'CSV', icon: spreadsheetIcon }, json: { id: 'json', title: 'JSON', icon: jsonIcon } }; /** * The default format (most human-readable) */ Licenses.DEFAULT_FORMAT = 'markdown'; /** * A class used for fetching licenses from the server. */ class LicensesClient { /** * Create a new license client. */ constructor(options = {}) { var _a; this._licensesUrl = options.licensesUrl || ''; this._serverSettings = (_a = options.serverSettings) !== null && _a !== void 0 ? _a : ServerConnection.makeSettings(); } /** * Download the licenses in the requested format. */ async download(options) { const url = `${this._licensesUrl}?format=${options.format}&download=1`; const element = document.createElement('a'); element.href = url; element.download = ''; document.body.appendChild(element); element.click(); document.body.removeChild(element); URL.revokeObjectURL(url); return void 0; } /** * Fetch the license bundles from the server. */ async getBundles() { const response = await ServerConnection.makeRequest(this._licensesUrl, {}, this._serverSettings); return response.json(); } } Licenses.LicensesClient = LicensesClient; /** * A model for license data */ class Model extends VDomModel { constructor(options) { super(); this._selectedPackageChanged = new Signal(this); this._trackerDataChanged = new Signal(this); this._currentPackageIndex = 0; this._licensesReady = new PromiseDelegate(); this._packageFilter = {}; this._trans = options.trans; this._client = options.client; if (options.currentBundleName) { this._currentBundleName = options.currentBundleName; } if (options.packageFilter) { this._packageFilter = options.packageFilter; } if (options.currentPackageIndex) { this._currentPackageIndex = options.currentPackageIndex; } } /** * Handle the initial request for the licenses from the server. */ async initLicenses() { try { this._serverResponse = await this._client.getBundles(); this._licensesReady.resolve(); this.stateChanged.emit(void 0); } catch (err) { this._licensesReady.reject(err); } } /** * Download the licenses in the requested format. */ async download(options) { return this._client.download(options); } /** * A promise that resolves when the licenses from the server change */ get selectedPackageChanged() { return this._selectedPackageChanged; } /** * A promise that resolves when the trackable data changes */ get trackerDataChanged() { return this._trackerDataChanged; } /** * The names of the license bundles available */ get bundleNames() { var _a; return Object.keys(((_a = this._serverResponse) === null || _a === void 0 ? void 0 : _a.bundles) || {}); } /** * The current license bundle */ get currentBundleName() { if (this._currentBundleName) { return this._currentBundleName; } if (this.bundleNames.length) { return this.bundleNames[0]; } return null; } /** * Set the current license bundle, and reset the selected index */ set currentBundleName(currentBundleName) { if (this._currentBundleName !== currentBundleName) { this._currentBundleName = currentBundleName; this.stateChanged.emit(void 0); this._trackerDataChanged.emit(void 0); } } /** * A promise that resolves when the licenses are available from the server */ get licensesReady() { return this._licensesReady.promise; } /** * All the license bundles, keyed by the distributing packages */ get bundles() { var _a; return ((_a = this._serverResponse) === null || _a === void 0 ? void 0 : _a.bundles) || {}; } /** * The index of the currently-selected package within its license bundle */ get currentPackageIndex() { return this._currentPackageIndex; } /** * Update the currently-selected package within its license bundle */ set currentPackageIndex(currentPackageIndex) { if (this._currentPackageIndex === currentPackageIndex) { return; } this._currentPackageIndex = currentPackageIndex; this._selectedPackageChanged.emit(void 0); this.stateChanged.emit(void 0); this._trackerDataChanged.emit(void 0); } /** * The license data for the currently-selected package */ get currentPackage() { var _a; if (this.currentBundleName && this.bundles && this._currentPackageIndex != null) { return this.getFilteredPackages(((_a = this.bundles[this.currentBundleName]) === null || _a === void 0 ? void 0 : _a.packages) || [])[this._currentPackageIndex]; } return null; } /** * A translation bundle */ get trans() { return this._trans; } get title() { return `${this._currentBundleName || ''} ${this._trans.__('Licenses')}`.trim(); } /** * The current package filter */ get packageFilter() { return this._packageFilter; } set packageFilter(packageFilter) { this._packageFilter = packageFilter; this.stateChanged.emit(void 0); this._trackerDataChanged.emit(void 0); } /** * Get filtered packages from current bundle where at least one token of each * key is present. */ getFilteredPackages(allRows) { let rows = []; let filters = Object.entries(this._packageFilter) .filter(([k, v]) => v && `${v}`.trim().length) .map(([k, v]) => [k, `${v}`.toLowerCase().trim().split(' ')]); for (const row of allRows) { let keyHits = 0; for (const [key, bits] of filters) { let bitHits = 0; let rowKeyValue = `${row[key]}`.toLowerCase(); for (const bit of bits) { if (rowKeyValue.includes(bit)) { bitHits += 1; } } if (bitHits) { keyHits += 1; } } if (keyHits === filters.length) { rows.push(row); } } return Object.values(rows); } } Licenses.Model = Model; /** * A filter form for limiting the packages displayed */ class Filters extends VDomRenderer { constructor(model) { super(model); /** * Render a filter input */ this.renderFilter = (key) => { const value = this.model.packageFilter[key] || ''; return (React.createElement("input", { type: "text", name: key, defaultValue: value, className: "jp-mod-styled", onInput: this.onFilterInput })); }; /** * Handle a filter input changing */ this.onFilterInput = (evt) => { const input = evt.currentTarget; const { name, value } = input; this.model.packageFilter = { ...this.model.packageFilter, [name]: value }; }; this.addClass('jp-Licenses-Filters'); this.addClass('jp-RenderedHTMLCommon'); } render() { const { trans } = this.model; return (React.createElement("div", null, React.createElement("label", null, React.createElement("strong", { className: FILTER_SECTION_TITLE_CLASS }, trans.__('Filter Licenses By'))), React.createElement("ul", null, React.createElement("li", null, React.createElement("label", null, trans.__('Package')), this.renderFilter('name')), React.createElement("li", null, React.createElement("label", null, trans.__('Version')), this.renderFilter('versionInfo')), React.createElement("li", null, React.createElement("label", null, trans.__('License')), this.renderFilter('licenseId'))), React.createElement("label", null, React.createElement("strong", { className: FILTER_SECTION_TITLE_CLASS }, trans.__('Distributions'))))); } } Licenses.Filters = Filters; /** * A fancy bundle renderer with the package count */ class BundleTabRenderer extends TabBar.Renderer { constructor(model) { super(); this.closeIconSelector = '.lm-TabBar-tabCloseIcon'; this.model = model; } /** * Render a full bundle */ renderTab(data) { let title = data.title.caption; let key = this.createTabKey(data); let style = this.createTabStyle(data); let className = this.createTabClass(data); let dataset = this.createTabDataset(data); return h.li({ key, className, title, style, dataset }, this.renderIcon(data), this.renderLabel(data), this.renderCountBadge(data)); } /** * Render the package count */ renderCountBadge(data) { const bundle = data.title.label; const { bundles } = this.model; const packages = this.model.getFilteredPackages((bundles && bundle ? bundles[bundle].packages : []) || []); return h.label({}, `${packages.length}`); } } Licenses.BundleTabRenderer = BundleTabRenderer; /** * A grid of licenses */ class Grid extends VDomRenderer { constructor(model) { super(model); /** * Render a single package's license information */ this.renderRow = (row, index) => { const selected = index === this.model.currentPackageIndex; const onCheck = () => (this.model.currentPackageIndex = index); return (React.createElement("tr", { key: row.name, className: selected ? 'jp-mod-selected' : '', onClick: onCheck }, React.createElement("td", null, React.createElement("input", { type: "radio", name: "show-package-license", value: index, onChange: onCheck, checked: selected })), React.createElement("th", null, row.name), React.createElement("td", null, React.createElement("code", null, row.versionInfo)), React.createElement("td", null, React.createElement("code", null, row.licenseId)))); }; this.addClass('jp-Licenses-Grid'); this.addClass('jp-RenderedHTMLCommon'); } /** * Render a grid of package license information */ render() { var _a; const { bundles, currentBundleName, trans } = this.model; const filteredPackages = this.model.getFilteredPackages(bundles && currentBundleName ? ((_a = bundles[currentBundleName]) === null || _a === void 0 ? void 0 : _a.packages) || [] : []); if (!filteredPackages.length) { return (React.createElement("blockquote", null, React.createElement("em", null, trans.__('No Packages found')))); } return (React.createElement("form", null, React.createElement("table", null, React.createElement("thead", null, React.createElement("tr", null, React.createElement("td", null), React.createElement("th", null, trans.__('Package')), React.createElement("th", null, trans.__('Version')), React.createElement("th", null, trans.__('License')))), React.createElement("tbody", null, filteredPackages.map(this.renderRow))))); } } Licenses.Grid = Grid; /** * A package's full license text */ class FullText extends VDomRenderer { constructor(model) { super(model); this.addClass('jp-Licenses-Text'); this.addClass('jp-RenderedHTMLCommon'); this.addClass('jp-RenderedMarkdown'); } /** * Render the license text, or a null state if no package is selected */ render() { const { currentPackage, trans } = this.model; let head = ''; let quote = trans.__('No Package selected'); let code = ''; if (currentPackage) { const { name, versionInfo, licenseId, extractedText } = currentPackage; head = `${name} v${versionInfo}`; quote = `${trans.__('License')}: ${licenseId || trans.__('No License ID found')}`; code = extractedText || trans.__('No License Text found'); } return [ React.createElement("h1", { key: "h1" }, head), React.createElement("blockquote", { key: "quote" }, React.createElement("em", null, quote)), React.createElement("code", { key: "code" }, code) ]; } } Licenses.FullText = FullText; })(Licenses || (Licenses = {})); //# sourceMappingURL=licenses.js.map