UNPKG

@jupyterlab/docmanager

Version:
231 lines 8.23 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { Dialog, showDialog, showErrorMessage } from '@jupyterlab/apputils'; import { PathExt } from '@jupyterlab/coreutils'; import { nullTranslator } from '@jupyterlab/translation'; import { Widget } from '@lumino/widgets'; /** * The class name added to file dialogs. */ const FILE_DIALOG_CLASS = 'jp-FileDialog'; /** * The class name added for the new name label in the rename dialog */ const RENAME_NEW_NAME_TITLE_CLASS = 'jp-new-name-title'; /** * Default implementation of IDocumentManagerDialogs. */ export class DocumentManagerDialogs { constructor(options = {}) { this._translator = options.translator || nullTranslator; } /** * Show a dialog to rename a file. */ async rename(context) { const trans = this._translator.load('jupyterlab'); const localPath = context.localPath.split('/'); const fileName = localPath.pop() || context.localPath; const handler = new RenameHandler(fileName, this._translator); const result = await showDialog({ title: trans.__('Rename File'), body: handler, buttons: [ Dialog.cancelButton(), Dialog.okButton({ label: trans.__('Rename'), ariaLabel: trans.__('Rename File') }) ], focusNodeSelector: 'input' }); if (!result.button.accept) { return null; } const newPath = result.value; if (!newPath || newPath === fileName) { return null; } if (!isValidFileName(newPath)) { void showErrorMessage(trans.__('Rename Error'), Error(trans.__('"%1" is not a valid name for a file. Names must have nonzero length, and cannot include "/", "\\", or ":"', result.value))); return null; } return context.rename(newPath); } /** * Show a dialog asking whether to close a document. */ async confirmClose(options) { const trans = this._translator.load('jupyterlab'); const { fileName, isDirty } = options; const buttons = [ Dialog.cancelButton(), Dialog.okButton({ label: isDirty ? trans.__('Close and save') : trans.__('Close'), ariaLabel: isDirty ? trans.__('Close and save Document') : trans.__('Close Document') }) ]; if (isDirty) { buttons.splice(1, 0, Dialog.warnButton({ label: trans.__('Close without saving'), ariaLabel: trans.__('Close Document without saving') })); } const confirm = await showDialog({ title: trans.__('Confirmation'), body: trans.__('Please confirm you want to close "%1".', fileName), checkbox: isDirty ? null : { label: trans.__('Do not ask me again.'), caption: trans.__('If checked, no confirmation to close a document will be asked in the future.') }, buttons }); const shouldClose = confirm.button.accept; const ignoreSave = isDirty ? confirm.button.displayType === 'warn' : true; const doNotAskAgain = confirm.isChecked === true; return { shouldClose, ignoreSave, doNotAskAgain }; } /** * Show a dialog asking whether to save before closing a dirty document. */ async saveBeforeClose(options) { const trans = this._translator.load('jupyterlab'); const { fileName, writable } = options; const saveLabel = writable ? trans.__('Save') : trans.__('Save as'); const result = await showDialog({ title: trans.__('Save your work'), body: trans.__('Save changes in "%1" before closing?', fileName), buttons: [ Dialog.cancelButton(), Dialog.warnButton({ label: trans.__('Discard'), ariaLabel: trans.__('Discard changes to file') }), Dialog.okButton({ label: saveLabel }) ] }); const shouldClose = result.button.accept; const ignoreSave = result.button.displayType === 'warn'; return { shouldClose, ignoreSave }; } } /** * Rename a file with a dialog. * * @deprecated You should use {@link DocumentManagerDialogs.rename} */ export function renameDialog(manager, context, translator, dialogs) { if (dialogs) { return dialogs.rename(context); } const newDialogs = new DocumentManagerDialogs({ translator: translator }); return newDialogs.rename(context); } /** * Rename a file, asking for confirmation if it is overwriting another. */ export function renameFile(manager, oldPath, newPath) { return manager.rename(oldPath, newPath).catch(error => { if (error.response.status !== 409) { // if it's not caused by an already existing file, rethrow throw error; } // otherwise, ask for confirmation return shouldOverwrite(newPath).then((value) => { if (value) { return manager.overwrite(oldPath, newPath); } return Promise.reject('File not renamed'); }); }); } /** * Ask the user whether to overwrite a file. */ export function shouldOverwrite(path, translator) { translator = translator || nullTranslator; const trans = translator.load('jupyterlab'); const options = { title: trans.__('Overwrite file?'), body: trans.__('"%1" already exists, overwrite?', path), buttons: [ Dialog.cancelButton(), Dialog.warnButton({ label: trans.__('Overwrite'), ariaLabel: trans.__('Overwrite Existing File') }) ] }; return showDialog(options).then(result => { return Promise.resolve(result.button.accept); }); } /** * Test whether a name is a valid file name * * Disallows "/", "\", and ":" in file names, as well as names with zero length. */ export function isValidFileName(name) { const validNameExp = /[\/\\:]/; return name.length > 0 && !validNameExp.test(name); } /** * A widget used to rename a file. */ class RenameHandler extends Widget { /** * Construct a new "rename" dialog. */ constructor(oldPath, translator) { super({ node: Private.createRenameNode(oldPath, translator) }); this.addClass(FILE_DIALOG_CLASS); const ext = PathExt.extname(oldPath); const value = (this.inputNode.value = PathExt.basename(oldPath)); this.inputNode.setSelectionRange(0, value.length - ext.length); } /** * Get the input text node. */ get inputNode() { return this.node.getElementsByTagName('input')[0]; } /** * Get the value of the widget. */ getValue() { return this.inputNode.value; } } /** * A namespace for private data. */ var Private; (function (Private) { /** * Create the node for a rename handler. */ function createRenameNode(oldPath, translator) { translator = translator || nullTranslator; const trans = translator.load('jupyterlab'); const body = document.createElement('div'); const existingLabel = document.createElement('label'); existingLabel.textContent = trans.__('File Path'); const existingPath = document.createElement('span'); existingPath.textContent = oldPath; const nameTitle = document.createElement('label'); nameTitle.textContent = trans.__('New Name'); nameTitle.className = RENAME_NEW_NAME_TITLE_CLASS; const name = document.createElement('input'); body.appendChild(existingLabel); body.appendChild(existingPath); body.appendChild(nameTitle); body.appendChild(name); return body; } Private.createRenameNode = createRenameNode; })(Private || (Private = {})); //# sourceMappingURL=dialogs.js.map