@jupyterlab/apputils
Version:
JupyterLab - Application Utilities
513 lines • 19.1 kB
JavaScript
// 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