Skip to content

Commit

Permalink
Add readonly indicator (jupyterlab#14600)
Browse files Browse the repository at this point in the history
* add readonly to `DocumentWidget.contentHeader.dataset`

* added `readonly.svg` to icons

* add semi-loud readonly label to front of notebook toolbar

* Move readonly status to next to kernel/debugger, make it not a ToolbarButtonClass

* debugging

* notification is showing up

* rebasing work

* well formatted

* improve css classname and change label from document to notebook is read-only

* cleanup

* remove unused code

* remove unused code

* add translators

* added read-only tag to non-notebook files, updated tag to say notebook
vs file. file toolbar looks off when a file isn't read-only though

* toolbar is showing up properly on writeable fileeditors. indicator on
fileeditors is still on left side

* failing because of cirucular dependency

* Still fialing for circular dependency

* adding component

* read-only label injection is working

* working with toolbar injection

* fixes read-only label for RTC

* update yarn.lock

* Automatic application of license header

* undoin unneeded changes

* clean up code

* Update packages/docregistry/src/default.ts

Co-authored-by: Michał Krassowski <[email protected]>

* updates based on PR review

* adding font-size to .jp-ToolbarLabelComponent

* remove Notification, add dialog for save-all, update label on disabled
save icon when read-only

* Update packages/docregistry/src/components.tsx

Co-authored-by: Michał Krassowski <[email protected]>

* update

* fix save-all read-only notification

* Fix error on eslint:typed

* try making _translator private again

* updated on rebase

* change dialog back to Notification, add autoclose option

* Update packages/ui-components/style/toolbar.css

Co-authored-by: Michał Krassowski <[email protected]>

---------

Co-authored-by: telamonian <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Michał Krassowski <[email protected]>
  • Loading branch information
4 people authored Aug 30, 2023
1 parent ecf684f commit 272e390
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 17 deletions.
26 changes: 21 additions & 5 deletions packages/docmanager-extension/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ICommandPalette,
InputDialog,
ISessionContextDialogs,
Notification,
ReactWidget,
SessionContextDialogs,
showDialog,
Expand Down Expand Up @@ -824,6 +825,11 @@ function addCommands(
'In collaborative mode, the document is saved automatically after every change'
);
}
if (!isWritable()) {
return trans.__(
`document is permissioned readonly; "save" is disabled, use "save as..." instead`
);
}
}

return trans.__('Save and create checkpoint');
Expand All @@ -838,9 +844,9 @@ function addCommands(
isEnabled: isWritable,
execute: async () => {
// Checks that shell.currentWidget is valid:
const widget = shell.currentWidget;
const context = docManager.contextForWidget(widget!);
if (isEnabled()) {
const widget = shell.currentWidget;
const context = docManager.contextForWidget(widget!);
if (!context) {
return showDialog({
title: trans.__('Cannot Save'),
Expand Down Expand Up @@ -947,9 +953,19 @@ function addCommands(
const paths = new Set<string>(); // Cache so we don't double save files.
for (const widget of shell.widgets('main')) {
const context = docManager.contextForWidget(widget);
if (context && !context.model.readOnly && !paths.has(context.path)) {
paths.add(context.path);
promises.push(context.save());
if (context && !paths.has(context.path)) {
if (context.contentsModel?.writable) {
paths.add(context.path);
promises.push(context.save());
} else {
Notification.warning(
trans.__(
`%1 is permissioned as readonly. Use "save as..." instead.`,
context.path
),
{ autoClose: 5000 }
);
}
}
}
return Promise.all(promises);
Expand Down
3 changes: 2 additions & 1 deletion packages/docregistry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
"@lumino/messaging": "^2.0.1",
"@lumino/properties": "^2.0.1",
"@lumino/signaling": "^2.1.2",
"@lumino/widgets": "^2.3.0"
"@lumino/widgets": "^2.3.0",
"react": "^18.2.0"
},
"devDependencies": {
"@jupyterlab/testing": "^4.1.0-alpha.1",
Expand Down
33 changes: 33 additions & 0 deletions packages/docregistry/src/components.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) Jupyter Development Team.
* Distributed under the terms of the Modified BSD License.
*/

import { ReactWidget } from '@jupyterlab/apputils';
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
import { Widget } from '@lumino/widgets';
import * as React from 'react';

import { IDocumentWidget } from './index';

/**
* create readonly label toolbar item
*/
export function createReadonlyLabel(
panel: IDocumentWidget,
translator?: ITranslator
): Widget {
let trans = (translator ?? nullTranslator).load('jupyterlab');
return ReactWidget.create(
<div>
<span
className="jp-ToolbarLabelComponent"
title={trans.__(
`Document is permissioned read-only; "save" is disabled, use "save as..." instead`
)}
>
{trans.__(`%1 is read-only`, panel.context.contentsModel?.type)}
</span>
</div>
);
}
19 changes: 18 additions & 1 deletion packages/docregistry/src/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { PartialJSONValue } from '@lumino/coreutils';
import { ISignal, Signal } from '@lumino/signaling';
import { Title, Widget } from '@lumino/widgets';
import { DocumentRegistry, IDocumentWidget } from './index';
import { createReadonlyLabel } from './components';

/**
* The default implementation of a document model.
Expand Down Expand Up @@ -545,7 +546,7 @@ export class DocumentWidget<
// Include the context ready promise in the widget reveal promise
options.reveal = Promise.all([options.reveal, options.context.ready]);
super(options);

this._trans = (options.translator ?? nullTranslator).load('jupyterlab');
this.context = options.context;

// Handle context path changes
Expand Down Expand Up @@ -616,6 +617,21 @@ export class DocumentWidget<
if (args.name === 'dirty') {
this._handleDirtyState();
}
if (!this.context.model.dirty) {
if (!this.context.model.collaborative) {
if (!this.context.contentsModel?.writable) {
const readOnlyIndicator = createReadonlyLabel(this);
let roi = this.toolbar.insertBefore(
'kernelName',
'read-only-indicator',
readOnlyIndicator
);
if (!roi) {
this.toolbar.addItem('read-only-indicator', readOnlyIndicator);
}
}
}
}
}

/**
Expand All @@ -633,6 +649,7 @@ export class DocumentWidget<
}

readonly context: DocumentRegistry.IContext<U>;
protected readonly _trans;

/**
* Whether the document has an auto-generated name or not.
Expand Down
1 change: 1 addition & 0 deletions packages/docregistry/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './context';
export * from './default';
export * from './mimedocument';
export * from './registry';
export * from './components';
11 changes: 2 additions & 9 deletions packages/notebook/src/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ import { isMarkdownCellModel } from '@jupyterlab/cells';
import { PageConfig } from '@jupyterlab/coreutils';
import { DocumentRegistry, DocumentWidget } from '@jupyterlab/docregistry';
import { Kernel, KernelMessage, Session } from '@jupyterlab/services';
import {
ITranslator,
nullTranslator,
TranslationBundle
} from '@jupyterlab/translation';
import { ITranslator } from '@jupyterlab/translation';
import { Token } from '@lumino/coreutils';
import { INotebookModel } from './model';
import { Notebook, StaticNotebook } from './widget';
Expand Down Expand Up @@ -43,8 +39,6 @@ export class NotebookPanel extends DocumentWidget<Notebook, INotebookModel> {
*/
constructor(options: DocumentWidget.IOptions<Notebook, INotebookModel>) {
super(options);
this.translator = options.translator || nullTranslator;
this._trans = this.translator.load('jupyterlab');

// Set up CSS classes
this.addClass(NOTEBOOK_PANEL_CLASS);
Expand Down Expand Up @@ -266,13 +260,12 @@ export class NotebookPanel extends DocumentWidget<Notebook, INotebookModel> {
});
}

translator: ITranslator;
private _trans: TranslationBundle;
/**
* Whether we are currently in a series of autorestarts we have already
* notified the user about.
*/
private _autorestarting = false;
translator: ITranslator;
}

/**
Expand Down
10 changes: 9 additions & 1 deletion packages/ui-components/style/toolbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ div.jp-ToolbarButton {
margin: 0;
}

button.jp-ToolbarButtonComponent {
button.jp-ToolbarButtonComponent,
.jp-ToolbarLabelComponent {
background: var(--jp-layout-color1);
border: none;
box-sizing: border-box;
Expand Down Expand Up @@ -101,6 +102,13 @@ button.jp-ToolbarButtonComponent .jp-ToolbarButtonComponent-label {
font-family: var(--jp-ui-font-family);
}

.jp-ToolbarLabelComponent {
background-color: var(--jp-brand-color1);
color: var(--jp-ui-inverse-font-color1);
font-size: var(--jp-ui-font-size0);
user-select: none;
}

#jp-main-dock-panel[data-mode='single-document']
.jp-MainAreaWidget
> .jp-Toolbar.jp-Toolbar-micro {
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2896,6 +2896,7 @@ __metadata:
"@lumino/widgets": ^2.3.0
"@types/jest": ^29.2.0
jest: ^29.2.0
react: ^18.2.0
rimraf: ~3.0.0
typedoc: ~0.24.7
typescript: ~5.0.4
Expand Down

0 comments on commit 272e390

Please sign in to comment.