Giter Site home page Giter Site logo

jupyterlab / lumino Goto Github PK

View Code? Open in Web Editor NEW
585.0 27.0 125.0 12.24 MB

Lumino is a library for building interactive web applications

Home Page: https://lumino.readthedocs.io/

License: Other

TypeScript 96.82% JavaScript 1.41% CSS 1.22% Jupyter Notebook 0.53% Shell 0.01% Python 0.01%
jupyter jupyterlab

lumino's Introduction

Lumino

Build Status Documentation Status GitHub Discourse Binder

Lumino is a set of JavaScript packages, written in TypeScript, that provide a rich toolkit of widgets, layouts, events, and data structures. These enable developers to construct extensible high-performance desktop-like web applications, such as JupyterLab. Lumino was formerly known as PhosphorJS.

Lumino is Jupyter project and follows the Jupyter Community Guides and Code of Conduct.

Examples

JupyterLab

JupyterLab is an extensible environment for interactive and reproducible computing.

You can try it live in a web browser (without installing anything) by clicking on the link below:

Binder

jupyterlab

Examples in the repository

This repository contains several examples making use of Lumino Widgets such as the DockPanel and the DataGrid.

The example can be interacted with live in the browser by following this link:

Binder

examples-binder

External Examples

Usage

To learn more on how to use Lumino, check out the documentation: https://lumino.readthedocs.io/en/latest/

Development

See CONTRIBUTING.md to know how to contribute and set up a development environment.

Learning Resources

lumino's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lumino's Issues

DockPanel inside a DockPanel

Given the following layout (adapted from the DockPanel example), where dock2 is a second DockPanel added to the first one with:

let dock2 = new DockPanel();
dock2.addWidget(g2);
dock2.addWidget(y2, { mode: 'split-right', ref: g2 });
dock2.id = 'dock2';
dock2.title.label = 'Dock 2';
dock.addWidget(dock2, { mode: 'split-bottom', ref: r1});

image

Is is possible to have control on where the widgets of an inner DockPanel can be docked? For example to disallow dropping them outside of "Dock 2":

inner-dockpanel

CSS selector discussion

Should all of the legacy p-* CSS selectors be renamed to lm-*?

  • In the interest of clarity (and purity), my intuition is "yes"
  • In the interest of lower churn and a smaller amount of of pain imposed on current consumers of these APIs, "no" seems better.

npm install noise

After instaling lumino I get:

npm WARN @lumino/[email protected] requires a peer of [email protected] but none is installed. You must install peer dependencies yourself.

But after installing crypto I get:

npm WARN deprecated [email protected]: This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.

Keymap based on .key or .code rather than .keyCode?

Early in Phosphor/Lumino's development, we decided to use .keyCode instead of .key for the basis of the keyboard shortcut system. This came about in part because (among other things) we realized that browsers were inconsistent with the .key attribute when modifiers were used. For example (and I just checked this is still the case with Firefox 76), in Firefox we saw that on a mac, pressing the numbers on a en-us keyboard layout while holding shift, control, alt, and command, led to the following sequence of event.key attributes:

1@345^7890

On Chrome, we get:

1€345fl7890

I suppose once browsers can be consistent about .key, we can look again at a keyboard shortcut system that depends on it. A nice thing about using .key is that presumably it would transparently support different keyboard maps, instead of relying on manually creating and selecting keyboard maps.

Another option, as @vidartf points out below, is moving to the .code attribute, especially since .keyCode is both deprecated and a bit convoluted across different browsers.

Context aware cells and cell merging in the lumino DataGrid

Team,

I am opening this issue to discuss a potential enhancement to the lumino DataGrid. Specifically, I am interested in hearing your opinions on adding some mechanism where the DataGrid can have "context aware" cells.

We are big fans of the DataGrid and rely on it quite extensively in some of our internal projects. Some of the frequent use cases we have involve the notion of cell merging so we can represent hierarchies of nested columns and rows. Other ones involve setting the formatting of one cell based on the value of another cell, for example.

Custom data models are not enough for this type of functionality as there is no clear notion of a cell object. Cells are painted based on a calculated region, but they are not aware of their neighbors. This means that conditional formatting in one column based on the values in another column cannot be done. The same idea applies to cell hierarchies when rendering nested column and row headers. The existing data model, actually, can accommodate multiple header rows/columns:

return 1; // TODO multiple column-header rows?

But that model assumes all cells are distinct, and does not allow for a visual representation of any merged regions.

To get around that, at the moment, we are maintaining a fork which has a quick and dirty hack to allow the for visual representation of nested columns. However the current implementation is not something we think is sustainable to maintain, and ideally we could have a better, well thought-of, model to address all these use cases, and that's why I am opening this PR!

I would appreciate any feedback you have on this idea.

Many thanks

More flexible submenus?

Currently, the menu widgets are either:

  • commands,
  • separators, or
  • submenus

however, the developer API is not as convenient to use for submenus as it is for commands. For example, one can create a callback for a command to be asked about its visibility (isVisible) or state (isEnabled), but this does not seem to be possible for submenus:

get isVisible(): boolean {
if (this.type === 'command') {
return this._commands.isVisible(this.command, this.args);
}
if (this.type === 'submenu') {
return this.submenu !== null;
}
return true;
}

Lack of callbacks also means that the we do not have an easy way to:

  • dynamically change the name of the submenu
  • dynamically generate its content

This led me to use some quirky workarounds when implementing spellcheck suggestions.

One lazy way of getting what I want would be (pseudo-code follows):

type ItemType = 'command' | 'submenu' | 'separator' | 'subcommand';    // ← added

IItemOptions: {
    type?: ItemType;
    command?: string;
    args?: ReadonlyJSONObject;
    submenu?: Menu | null;
    subcommand?: string;   // ← added
}

this.app.commands.addCommand(
    'spellchecker:suggestions',
   {
        label: () => anySuggestions() ? 'Adjust to' : 'No suggestions',
        isEnabled: () => anySuggestions(),
        /* provides the menu items options; command defaults to subcommand */
        execute: () => getSuggestions().map(suggestion => {args: {name: suggestion}}) as IItemOptions[],
   }
)

this.app.commands.addCommand(
    'spellchecker:use-suggestion',
   {
        label: (args) => args.name as string,
        execute: (args) => replaceToken(args.name as string),
   }
)

this.app.contextMenu.addItem({
    selector: '.cm-spell-error',
    command: 'spellchecker:suggestions',
    subcommand: 'spellchecker:use-suggestion',
    type: 'subcommand'
});

Not sure how good it would be; the trick with re-using execute seems elegant but sadly the type checking would not be of much use here... Unless we would have additional addSubCommand method with items instead of execute.

Also, if I am the only one struggling with this it might not be worth introducing extra complexity.

Difficult to keep corner header from getting painted on when painting on the column header.

Hi, my name is Logan McNichols. I am an intern for Jupyter Cal Poly. I am working on a tabular data editor powered by the DataGrid along with @kgoo124 and @ryuntalan.

Our editor paints LabIcons onto the column-header of the grid. During a horizontal scroll, these icons can get partially painted onto the corner header. Sometimes, these kind of issues can be solved by bounding the dirty rect very precisely. However, in this case, our canvas is painting on complex svg paths. It would be challenging to bound this object to a given x-coordinate. Alternatively, we could be conservative and say that if any of the svg stands outside of the dirty rect, then don't paint it at all. But this means that our icons will not appear and disappear gradually on a scroll, but will abruptly appear and disappear.

I think the preferable solution is to redraw the corner header as the final draw operation. This is in fact how the DataGrid already does it. Can we allow extending classes access to _drawCornerHeader so that they can do this as well?

Cannot invoke throttle recursively

In the throttler, we have a special check:

if (this.poll.state.phase !== 'invoked') {
void this.poll.schedule({ interval: this.limit, phase: 'invoked' });
}

This (I think) means that the throttle callback cannot schedule a new callback by calling invoke recursively. For example, I might have a function that tries to do a lot of work (say process 100 items in an array) by breaking it up, doing a bit (like process 10 items), then scheduling a future call by invoking the throttle recursively and yielding back to the browser. I think this current logic of not scheduling a future call if we are currently being invoked means I cannot invoke recursively.

Of course, I can do a setTimeout(() => { x.invoke() }), but that seems like a weird workaround. Why do we have this check?

We ran into this in jupyter-widgets/ipywidgets#2732 (comment)

Question: correct way of removing a widget from a layout?

Hi,
I've got a fairly complex layout, which involves adding and removing TabBars from a SplitLayout dynamically - however I can't figure out the correct way of removing and disposing all TabBars from the layout. I've tried

      each(splitpanel.widgets, (child,i)=>{
        (child as TabBar<Widget>).dispose();
      });

which looks like this:
Image from Gyazo

the tab bars don't seem to get removed from the layout. Apparently, from setting the TabBar parent to null in the dispose function (which doesn't seem to actually remove it from the SplitPanel??).

The desired behaviour can be achieved by using .hide() instead of .dispose():
Image from Gyazo

Although this looks correct, I obviously don't want to use it seeing as the old TabBars remain in the dom, even when they should be long gone.

Am I just misunderstanding what dispose is supposed to be doing? What's the 'correct' way of permanently removing widgets from a layout?

Consider making dynamic extensions part of lumino

We could migrate the build-extension script here and add a method to Application to dynamically load extensions, opening up the possibility for other applications to use module federation. We'd also need a simplified way to build the "core" application bundle. These scripts would have to be less opinionated (or more configurable) here than they are currently in JupyterLab.

Task: Test UMD Approach Against JupyterLab

We need to test the effects of #40 against JupyterLab before making a release.

Any thoughts on how do to that cleanly?

Worst case it might entail a yarn link for all of the lumino packages.

Project status?

I've been playing around with Lumino for a few days now, and it appears to be an extremely powerful and resilient framework. However it doesn't seem particularly active: there's a decent amount of apparently ignored open issues, and pretty much no documentation, other than what is left from PhosphorJS - which could disappear any day.

I was therefore wondering if it would be possible to get an update about the development status of Lumino, and if it would be safe to use in production level software?

Command Palette - No easy way to clear search with mouse

In the current implementation of Command Palette search input is missing clear input option. It is making user not to use single mouse click option to clear input. Forcing user to use keyboard to clear text by selection or backspace.

solution:
We can add input type to search by default and make use of useragent (browser) default clear search feature.
By default in edge clear search is visible, for webkit based browser we need to specify the type to search so that clear input will appear.

searchwithclear

I would love to make PR if this solution is fine..

Function spliceArray in datastore/src/listfield.ts does not behave like Array.splice when items.length > 100000.

Hi, my name is Logan McNichols. I am an intern for Jupyter Cal Poly. I am working on a tabular data editor powered by the DataGrid along with @kgoo124 and @ryuntalan.

We are relying on the Datastore to store some data for us. In particular we have two ListField<number> objects tracking the position of row and columns respectively. I encountered some unexpected behavior when trying to work with a ListField<number> object who's length was greater than 100000.

I think I traced the problem to the function in listfield.ts called spliceArray. I copied this function to my workspace and ran the following test.

function spliceArray(arr, start, deleteCount, items) {
    if (!items) {
        return arr.splice(start, deleteCount);
    }
    let size = 100000;
    if (items.length < size) {
        return arr.splice(start, deleteCount || 0, ...items);
    }
    let deleted = arr.splice(start, deleteCount);
    let n = Math.floor(items.length / size);
    let idx = 0;
    for (let i = 0; i < n; i++, idx += size) {
        arr.splice(idx, 0, ...items.slice(idx, size));
    }
    arr.splice(idx, 0, ...items.slice(idx));
    return deleted;
}
const arr = new Array(500).fill(0);
const items = new Array(10**6).fill(0);
const start = 500;
const deleteCount = 0;
spliceArray(arr, start, deleteCount, items);
console.log("expected output", 1000500, "actual output", arr.length);

// expected output 1000500 actual output 100500

Only the first connected slot on a Signal is invoked

I'm not sure if this is an issue with how Signals work or with the specific callback function I'm running into.

When the _tabCloseRequested Signal in tabbar.ts is emitted (on line 790) it only handles the the _onTabCloseRequested slot that is connected on line 77 of tabpanel.ts, any slots connect later, such as in an extension are no invoked. Specifically in the Private.emit function in signaling/src/index.ts on line 565, after on return from invokeSlot (in my example it's the _onTabCloseRequested slot) the value of signal for every connection in the receivers array is set to null.

As far as I have been able to stack trace down into the call I have been unable to find where this happens. From my understanding of the codebase, somewhere the Signal that is being handled is being disposed, and thus on return from invokeSlot all references to it are now null.

This means that when extra slots are added to existing Signals in extensions they will return success on connection but never triggered on emission.

In order to more easily see this issue I have recreated a demo extension that show the issue: https://github.com/ajbozarth/demo-jl-ext

DataGrid does not display changes after receving a changed signal from it's DataModel with a type attribute of "rows-moved" or "columns-moved"

Hi, my name is Logan McNichols. I am an intern for Jupyter Cal Poly, working with @kgoo124 and @ryuntalan to develop a tabular data editor powered by the DataGrid. On of the editing functions we are including is the ability to move rows and columns.

We have already enabled adding and removing rows and columns. The pattern to do this has been (1) create the desired change to the DataModel and (2) emit a DataModel.ChangedArgs signal to the DataGrid with the appropriate type (e.g. if we were adding a row we would submit a signal with a type attribute 'rows-added').

We attempted this same pattern for moving columns and rows. But, submitting a 'rows-moved' or 'columns-moved' signal to the DataGrid does not seem to update it visually. Interestingly, the grid will update if we move to another panel in JupyterLab and then come back to it.

Give extending classes access to some of the data grid's paint utilities.

Hi, my name is Logan McNichols. I am an intern for Jupyter Cal Poly. I am working on a tabular data editor powered by the DataGrid along with @kgoo124 and @ryuntalan.

We are working on a design that involves painting LabIcons onto the DataGrid's canvas to correspond the the type of field in a given column. To achieve this, we would like to be able to extend the DataGrid class to add custom drawing functions. To do this, our class would need to be able to access some properties & methods of the DataGrid that are currently set to private.

The methods we would need access to are
_paintContent
_repaintContent
& _repaintOverlay

The properties we would need getters for are

  • _canvasGC
  • _rowSections
  • _columnSections
  • _columnHeaderSections
  • _rowHeaderSections.

Suppress all keyboard shortcuts in a specific region of an application.

We should add a way for a region of an application to ignore/suppress all keyboard shortcuts.

  • For example, imagine a button that puts a portion of the application in full-screen mode, it may wish to suppress all application-level keyboard shortcuts.
  • This situation arose in the JupyterLab shortcut UI, which needs all shortcuts to be disabled when a user is typing a new keyboard shortcut in.

Negate Selector

Hi,

I'd like to use not within a selector, but that does not seem to be working. The use case is having something apply for the whole selector except for specific children of it. Example:

       {
            "command": "runmenu:restart-and-run-all",
            "keys": [
                "Shift R"
            ],
            "selector": ".jp-Notebook.jp-mod-commandMode :not(.jp-Stdin)"
        }

Is this supported?

Add sourcemaps back to original TS

When debugging in JupyterLab (with the UMD bundles introduced in #40), I am seeing the ES6 bundle, we should be able to reach back to the original TS source files:

image

Is is OK to have Circular Dependency in typescript,since I found some in source code

Hello,I am using jupyterlab alot and I wrote python most of time.

I need to write some plugin for jupyterlab so I am learning to use lumino now

Since there is little document for lumino, I tried to read source code to figure out how thing go.

I am ok for most part,but when I try to figure out dependencies in lumino/widgets, I found

// widget.ts
import {
  Layout
} from './layout';

and

// layout.ts
import {
  Widget
} from './widget';

As far as I know,this is not OK in python,this is Circular Dependency

So is this ok in typescript?I am new user for typescript

Missing styles in default theme

The Lumino code appears to be switching the class naming conventions to lm-name_ rather than the Phosphor p-_name_ style. The @lumino/default-theme repo has not been updated with the new names. Specifically I noticed that the datagrid.css file is missing the styles that allow the cellEditors to work properly.

Difficult to extend lumino's Drag class to create custom drag behavior.

Hi, my name is Logan McNichols. I am an intern for Jupyter Cal Poly. I am working on a tabular data editor powered by the DataGrid along with @kgoo124 and @ryuntalan.

We would like to use lumino's Drag class to create a column/row shadow which appears on a mouse click of a column/row header and is drag-able.

In order to do this, we would need to extend the Drag class to change to how it updates the position of the drag image. For example, when a column header is selected we want to fix the style.top attribute to be the top edge of the DataGrid and keep style.left within the bounds of the DataGrid. The method we want to override with our child class is _moveDragImage, but this is currently a private method of the Drag class.

Touch event support

The old phosphorjs/phosphor#154 issue remains unresolved. Touchscreen laptops and larger tablets, both with touch events, could support a desktop-style web app. However Lumino DockPanel currently only responds to mouse events.

Another UX issue is that even with touch event support, the spacing between panels is a very small area to hit for resizing them. A larger handle, gesture and / or resizing mode is needed to manage them comfortably with a finger as the only pointing device.

I propose two options:

  • A resizing mode that app developers can decide how to trigger and render, but would add a floating resize widget in the middle of every panel subdivision, large enough to drag.
  • A gesture: dragging with two fingers, each in a different panel, could move the divider between the panels.

DataGrid.resizeRow and column don't work?

I'm trying to update this code to use jupyterlab 2.1. It had previously been using phosphor/datagrid 0.1.6.

I have two failing tests that rely on resizeRow and resizeColumn, and stepping through them, it looks to me like the resizeRow doesn't actually do anything. I thought maybe it was just async and there was a race condition, but it doesn't seem like it. It passes a message along but the row section size never changes afaict. If I call the private method, it works as expected. What am I missing?

Am I not using them correctly? Here's an example that I updated from resizeSection.

https://github.com/jseabold/jupyterlab-sql/blob/support-v2/__tests__/src/services/dataGridExtensions/events.test.ts#L101

Question: Proper use of save and restore layout

I have a fairly complex layout of widgets including some that are React widgets (i.e., wrapped in UseSignal), and multiple datagrid tabs with custom data models and custom selection models. When I leave the page I use saveLayout from the main DockPanel. When I come back to the page, I try to use restoreLayout. It does seem to restore the panels to the right positions, but the panels no longer work properly.

I thought the idea was that when I am attempting to recreate the page, I build the widgets and then tell the DockPanel to restoreLayout and it would put them back in the last saved positions, but I am beginning to think I am not using this properly as it looks like the restore is attempting to recreate the widgets and mine are detached from the parent.

Any advice on how to restore the layout would be appreciated.

Submenu close delay not activated when mouse moves out of menu

Consider these two paths of mouse movement:

Screen Shot 2020-10-22 at 10 22 24 AM

In the blue path, the submenu delays closing so you can get to the submenu item you are aiming for. However, if the mouse goes on the red path, the menu closes and a new top-level menu opens. This is frustrating if you are aiming for a submenu item.

Perhaps the delay at

/**
* Start the open timer, unless it is already pending.
*/
private _startOpenTimer(): void {
if (this._openTimerID === 0) {
this._openTimerID = window.setTimeout(() => {
this._openTimerID = 0;
this._openChildMenu();
}, Private.TIMER_DELAY);
}
}
/**
* Start the close timer, unless it is already pending.
*/
private _startCloseTimer(): void {
if (this._closeTimerID === 0) {
this._closeTimerID = window.setTimeout(() => {
this._closeTimerID = 0;
this._closeChildMenu();
}, Private.TIMER_DELAY);
}
}
/**
* Cancel the open timer, if the timer is pending.
*/
private _cancelOpenTimer(): void {
if (this._openTimerID !== 0) {
clearTimeout(this._openTimerID);
this._openTimerID = 0;
}
}
/**
* Cancel the close timer, if the timer is pending.
*/
private _cancelCloseTimer(): void {
if (this._closeTimerID !== 0) {
clearTimeout(this._closeTimerID);
this._closeTimerID = 0;
}
}
should work if the mouse pointed strays outside of the menu block as well?

See jupyterlab/jupyterlab#9201 for more context.

Step for the number cell editor

Currently, the number cell editor automatically uses step of 'any'. This results in the step for a decimal number always being 1. I figured I would try to tackle this problem at the source.

@lmcnichols @ryuntalan

Disable the drag / move / resize properties of the widgets by the user?

Hello,
I would like to disable the drag and drop and resize properties of the elements (e.g. green tab bar).
image

I know that DockPanel and TabBar Widgets have the "tabsMovable" property, but it still allows me to resize the panel. Is there any way to lock (at the element level, not at panel level) the drag, resize, and move properties. The goal is to have a completely static element in the DockPanel.

Make triplex id distribution for ranges more homogenous

The current behavior skews new ids to the end of the range for large inserts.

Context:
For creating a single triplex ID, a random path is generated within the start of the available range (uniformly in the first Math.sqrt(max - min) part of the range). Currently, when inserting a range, this ID generation is called sequentially, each time using the previous random ID as the start of the range.

Behavior:
With this logic, any sequence insert will have an ID distribution that is heavy towards the end of the insertion range (the density increases as more of the range is consumed). This effect is also further pronounced if the range is already small, i.e. for insertions within a previous insertion.

Proposed solution:
For range inserts:

  • Use a uniform spacing between characters (some random noise needed/useful?).
  • If the space is less than a certain threshold, we need to add more bytes. What is the best way to do this? Probably to add more bytes at the insertion point, and use the full new sub-range as the new insert range. Alternatively, we can intersperse a number of "subspaces" along the original range, and spread the new insert across those. This can probably be tweaked, but that shouldn't be a blocker.

[Critical Bug] Changes to `JSONObject` in [email protected] are breaking jupyterlab extension builds

If I try to build the latest master of jupyter-fs:

git clone [email protected]:jpmorganchase/jupyter-fs.git
cd jupyter-fs
make js

I get the following error on the tsc (ie typescript compilation) step:

$ tsc
../node_modules/@jupyterlab/observables/lib/modeldb.d.ts:227:9 - error TS2416: Property 'changed' in type 'ObservableValue' is not assignable to the same property in base type 'IObservableValue'.
  Type 'ISignal<this, IChangedArgs>' is not assignable to type 'ISignal<IObservableValue, IChangedArgs>'.
    Type 'this' is not assignable to type 'IObservableValue'.
      Type 'ObservableValue' is not assignable to type 'IObservableValue'.
        The types returned by 'get()' are incompatible between these types.
          Type 'JSONValue' is not assignable to type 'PartialJSONValue'.
            Type 'JSONObject' is not assignable to type 'PartialJSONValue'.
              Type 'JSONObject' is not assignable to type 'string'.

227     get changed(): ISignal<this, ObservableValue.IChangedArgs>;
            ~~~~~~~

../node_modules/@jupyterlab/observables/lib/modeldb.d.ts:231:5 - error TS2416: Property 'get' in type 'ObservableValue' is not assignable to the same property in base type 'IObservableValue'.
  Type '() => JSONValue' is not assignable to type '() => PartialJSONValue'.

231     get(): JSONValue;
        ~~~


Found 2 errors.

This is definitely being caused by the latest release of @lumino/coreutils, since if I add a resolution to the previous coreutils to my package.json:

    "resolutions": {
        "@lumino/coreutils": "1.5.0"
    }

the build error is fixed.

I beleive the underlying cause is the redefintion of JSONObject that was part of the 1.5.1 release.

@lumino/[email protected]:

/**
* A type definition for a JSON object.
*/
export
interface JSONObject { [key: string]: JSONValue; }

@lumino/[email protected]:

/**
* A type definition for a JSON object.
*/
export
interface JSONObject { [key: string]: JSONValue | ReadonlyJSONValue; }

Because this was a patch release of coretuils (1.5.0 => 1.5.1), you'll likely only see the bug if you delete your yarn.lock (or if start from a fresh build that never had one).

Cell editors render behind the DataGrid

Hi, my name is Kalen, and I am one of the Jupyter Cal Poly interns. Our team has been using the TextCell Editor class in attempts to create an editable DataGrid for the extension my team is working on, Tabular Data Editor.

However, the cell editor renders behind the DataGrid, and I think this is because the updatePosition method in CellEditor uses the styles top and left, but the position isn't set.

Problem: no way to ensure same Renderer/style is used for all Menu in a MenuBar, or for all sub-Menu in a parent Menu

Consider the following situation:

  • code I own constructs a new Menu with a custom Renderer
  • code you own constructs a new Menu (using the default Renderer) and adds it as a submenu to my Menu

My parent Menu and your sub-Menu will then have different renderers/styles. While in some circumstances this is a feature, in terms of enforcing a unified style across an application this is a bug. A similar situation exists with MenuBar and Menu.

The real issue is that there seems to be no clear way to extend the existing Lumino codebase in order to resolve the above. Here's some things I tried:

Refactoring/subclassing Menu.insertItem

Ideally if I'm the owner of the parent Menu I would have some way of inspecting any sub-Menu that gets added so that I could ensure it's Renderer/style. However, even if you try to subclass Menu.insertItem there's no easy way to alter an incoming sub-Menu:

  • Menu.Renderer is readonly, so you can't replace an existing sub-Menu's Renderer without a cast
  • There's also no public access to an existing Menu's items, so you also can't just dump the contents of an incoming sub-Menu into a new sub-Menu that you construct yourself

Replacing Menu.defaultRenderer

This works, but has some down sides:

  • Only works right if no else tries to set their own custom Renderer
  • Menu.defaultRenderer is readonly, so you need a cast
  • Creates a "race condition" (sort of), in that I have to somehow ensure that my code replacing Menu.defaultRenderer runs before any of your code that constructs a new Menu
  • Pollutes a globally shared Renderer, which may cause problems in code I don't own

Not quite sure how to untie this knot.

details

A little while ago in the Jupyterlab core code I tried to set up some Menu widgets with a custom Renderer, but kept running into weird problems. My goal was to add my own custom renderering for the check mark and submenu icons that are common to all Menu widgets. I was able to get the menus supplied by Jupyterlab core code to render correctly, but I had a whole bunch of trouble getting my icons to show up in any sub-Menu and any Menu supplied by an external extension.

Ultimately I came up with a bunch of ugly hacks to work around this. Said hacks work well enough, but this seems like a legit problem that deserves a built-in solution. I'm just not sure what.

Styling individual selections in the Datagrid selection model

Hi, I am one of the Cal Poly interns working on the Tabular Data Editor extension. We are looking to have some dashed borders around a cell when it has been copied, but not yet pasted. We have an issue here that has a picture of what we are looking to do.

It looks like all the potential Selections are stored in an array that is painted in datagrid.ts, but there is no way to isolate these selections. Some possible solutions would be using a CSS class and assigning a different render to a specific cell. The solution should be easily applied/removed. In our case, we would only want the dashed border to show up after copy, but before paste.

Refactoring crucial CSS for the DataGrid

Just wanted to open this as a place for communication and further discussion regarding issue #86 and PR #87. It was discussed that some of the DataGrid styles in the default-theme package are critical to the operation of the DataGrid. These styles should be refactored into from the default-theme package into the DataGrid package.

@mbektasbbg @afshin

Design: Lumino Logo

@ellisonbg brought up in the JupyterLab team meating today the interesting proptery of Lumino containing the consecutive letters LMNO. This may be a good place to start logo explorations. Other logo explorations are welcome, let's drop them here for discussion if anybody has sketches or ideas.

Formally describe command args and results

Commands are critical to the interaction between many parts of a lumino app, yet are very loosely-typed, and generally opaque to implementers and users. This limits the discovery, effective user interface, and reusability of commands. While these problems are likely unsolveable at compile time, runtime validation and discovery would improve all of these for the user of a lumino application, with minimal effort to implementers.

Individual issues and PRs

Current Challenges

Discovery

At present, commands are difficult to discover. Yes, one can listCommands to get their ids, and then iterate over all of their option values, and try calling them.

When commands are further abstracted, it becomes basically impossible to determine whether a command execution will be well-formed, and what is just nonsense that will fail in a fully-spun-up application.

User Interface

From the application perspective, commands are fire-and-forget, delegating the responsibility to individual implementations. Many commands would benefit from even minimal UI to modify their behavior, and could be, by default, presented in a single, consistent way vs some commands "just working", some opening dialogs, and other unpredictable/inconsistently accessible things that can occur.

Payoff

With well-typed and documented commands, it becomes possible to generate machine-readable representations of possible command states (to-be-executed, was-executed, etc). A command palette, thus enhanced, could provide significant value.

Additionally, with discoverable commands, hybrid gui/cli applications, a la clui become possible.

Finally, the ability to understand each command's arguments and result make it possible to serialize longer chains of commands that reference each other's output: DockPanel's layout serialization represents this in one, particular way, but more generalized approaches could truly make lumino an engine for lisp machine/smalltalk-style self-extensibility and introspection.

Out of scope

Selecting the validation description itself is probably neither viable nor useful: there still exists no standards-body adopted description-of-JSON-in-JSON, much less one implemented in browsers. While JSON Schema is the de facto representation, with ajv the de facto browser validator, it's worth exploring other options... once we have something that can accept any of it, with the usual caveats of performance, security, stability, and cross-language compatibility.

Proposal

I don't think any of these would require breaking changes, but it may be worth targeting/tabling this work until such a time as it would make sense. At any rate, I would be able to commit to implementing these feature, just not on what timeline.

  • @lumino/coreutils
    • add JSONValidationExt
      • add IValidator which defines a minimal validation scheme, e.g. validate(schema, instance) => Promise<any[]> (suitably genericized)
  • @lumino/commands
    • add and accept CommandRegistry.IOptions
      • an optional validator to a
      • add an optional validate: {args: true, result: false}
    • add an describedby to ICommandOptions which returns (a promise to) a schema for args
      • ...and result (if it even makes sense to validate outputs, as many are "fat" objects, e.g. Widget)
  • @lumino/application
    • add an optional validator to Application.IOptions (new)
  • @lumino/commandpalette
    • add an optional renderArgs to IRenderer
      • no implementation by default, for reasons above.
  • examples

Follow-On

  • A longer-term convention could introduce a package.json field, e.g. {"lumino": {"commands": {}} which would make available commands truly discoverable.
  • a metadata field on describedby could allow for application/implementation specific options, such as rjsf's uiSchema.

References

Downstreams:

Datagrid selection mode for mousedown

I am currently working on the Tabular Data Editor extension with @lmcnichols and @ryuntalan. We noticed the BasicMouseHandler doesn't utilize the different selection modes onMouseDown. Using these different selection modes for rows, columns, and cells will improve the UI of our extension.

Drop deprecated "p-*" Phosphor-based selectors

I've noticed that dragging tabs in the TabBar widget feels much laggier than before. I narrowed the culprit down to the merge that migrated the CSS selectors to new 'lm-*' based prefixes (#20) while keeping the original ones. Chrome seems to have some issues when an element has two different selectors with identical stylesheets. Is there a timeline to remove the deprecated selectors from the codebase? I assume JupyterLab has fully migrated over by now.

Make it easier to add custom cursors by extending BasicMouseHandler.

Hi, my name is Logan McNichols. I am an intern for Jupyter Cal Poly. I am working on a tabular data editor powered by the datagrid along with @kgoo124 and @ryuntalan.

We are working to get a grab cursor to display for moving rows and columns. The class BasicMouseHandler provides much of the core functionality we want, but it is tricky to extend it, as the cursors are determined by private properties/methods.

Adding ITranslator as an attribute of Widget

Wanted to open this up for discussion. Right, now we have to register and load the translator for every extension and every Widget inside of it, which seems inefficient.

I'm proposing that we add a translator attribute to Widget and take an optional parameter translator in the constructor. If no, translator is given, we can just set the translator as the nullTranslator (dummy-implementation that returns the same string). This would also allow us to translate labels, captions, and other common strings down at the Widget level making it easier for current and future extensions to implement localization.

This would allow us to reduce the amount of redundant code and standardize translation since every Widget would have a translator (possibly nullTranslator) by default. I don't think there would be any breaking changes as this would serve as an optional parameter.

The only issue I can see is that Lumino is depending on a JupyterLab package, but I think that would be okay? Not too sure where this fits in terms of the 3.0 release but wanted to get this conversation going before we start adding translations to every extension.

@goanpeca @blink1073

Yarn v NPM (if wanted)

Not sure if jupyterlab has any preferences for Yarn over NPM - But if there is any interest in switching to NPM I have a working branch for NPM (it uncovered a few missing dependencies - which I will try and issue a PR for either-way).

Grid does not have option to not edit a column

I have a grid based on DataGrid, and I am trying to make it so that certain columns are not editable.

DataGrid already has a method for selecting cells based on metadata, and this might be the best way to identify data that should not be editable.

I tried doing something like:

	const columnIdentifier = {'type': 'date'};
	grid.editorController!.setEditor(columnIdentifier, (config: CellEditor.CellConfig): ICellEditor => {
	    return null; 
	});

(which is a bit verbose) but could let you pick which types of cells can be edited.

However, that approached doesn't work because (I think) this code in CellEditorController:

  private _getMetadataBasedEditor(cell: CellEditor.CellConfig): ICellEditor | undefined {
    let editorMatched: ICellEditor | undefined;
    const metadata = cell.grid.dataModel!.metadata('body', cell.row, cell.column);
    if (metadata) {
      this._metadataBasedOverrides.forEach((value) => {
        if (!editorMatched) {
          let [identifier, editor] = value;
          if (this._metadataMatchesIdentifier(metadata, identifier)) {
            editorMatched = resolveOption(editor, cell);
          }
        }
      });
    }

    return editorMatched;
  }

Because editorMatched is not true when it is null, it continues searching. If this logic was changed to allow null to be returned, then it could provide a null Editor which would prevent the editor from editing this cell.

Does that sound correct and a viable solution for the problem?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.